이동된 파일: - 2025-12-02_file-attachment-feature.md - ai-config-설정.md - archive-restore-feature-analysis.md - barobill-members-migration.md - super-admin-protection.md - 명함추출로직.md - 모달창_생성시_유의사항.md - 상품관리정보.md - 수당지급.md - 영업파트너구조.md - 홈택스 매입매출 조회성공.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
모달창 생성 시 유의사항
개요
이 문서는 SAM 프로젝트에서 모달창을 구현할 때 발생할 수 있는 문제점과 해결 방법을 정리한 것입니다.
1. pointer-events 문제
문제 상황
모달 배경 클릭을 방지하면서 모달 내부만 클릭 가능하게 하려고 다음과 같은 구조를 사용했을 때:
<!-- 문제가 발생하는 구조 -->
<div class="fixed inset-0 z-50">
<div class="absolute inset-0 bg-black bg-opacity-50"></div>
<div class="absolute inset-0 flex items-center justify-center pointer-events-none">
<div class="bg-white rounded-xl pointer-events-auto">
<!-- AJAX로 로드되는 내용 -->
</div>
</div>
</div>
증상: 모달은 표시되지만 내부의 버튼, 입력 필드 등 모든 요소가 클릭되지 않음 (마치 돌덩어리처럼 동작)
원인
pointer-events-none이 부모에 있고pointer-events-auto가 자식에 있는 구조- AJAX로 로드된 내용이
pointer-events-autodiv 안에 들어가도, 그 안의 요소들에 pointer-events가 제대로 상속되지 않을 수 있음 - 특히 동적으로 로드된 HTML에서 이 문제가 자주 발생
해결 방법
pointer-events-none/auto 구조를 사용하지 않고 단순화:
<!-- 올바른 구조 -->
<div id="modal" class="hidden fixed inset-0 z-50 overflow-y-auto">
<!-- 배경 오버레이 -->
<div class="fixed inset-0 bg-black bg-opacity-50"></div>
<!-- 모달 컨텐츠 wrapper -->
<div class="flex min-h-full items-center justify-center p-4">
<div id="modalContent" class="relative bg-white rounded-xl shadow-2xl w-full max-w-3xl">
<!-- 내용 -->
</div>
</div>
</div>
2. AJAX로 로드된 HTML에서 함수 호출 문제
문제 상황
<!-- AJAX로 로드된 HTML -->
<button onclick="closeModal()">닫기</button>
증상: closeModal is not defined 오류 발생
원인
- 함수가
function closeModal() {}형태로 정의되면 호이스팅되지만, 모듈 스코프나 블록 스코프 안에 있을 수 있음 - AJAX로 로드된 HTML에서 전역 함수에 접근하지 못할 수 있음
해결 방법
방법 1: window 객체에 명시적 등록
// 전역 스코프에 함수 등록
window.closeModal = function() {
document.getElementById('modal').classList.add('hidden');
document.body.style.overflow = '';
};
방법 2: 이벤트 델리게이션 (권장)
<!-- HTML: data 속성 사용 -->
<button data-close-modal>닫기</button>
// JavaScript: document 레벨에서 이벤트 감지
document.addEventListener('click', function(e) {
const closeBtn = e.target.closest('[data-close-modal]');
if (closeBtn) {
e.preventDefault();
window.closeModal();
}
});
3. 배경 스크롤 방지
모달 열 때
document.body.style.overflow = 'hidden';
모달 닫을 때
document.body.style.overflow = '';
4. ESC 키로 모달 닫기
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
window.closeModal();
}
});
5. 완전한 모달 구현 예시
HTML 구조
<!-- 모달 -->
<div id="exampleModal" class="hidden fixed inset-0 z-50 overflow-y-auto">
<!-- 배경 오버레이 -->
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"></div>
<!-- 모달 컨텐츠 wrapper -->
<div class="flex min-h-full items-center justify-center p-4">
<div id="exampleModalContent" class="relative bg-white rounded-xl shadow-2xl w-full max-w-3xl">
<!-- 로딩 표시 또는 내용 -->
</div>
</div>
</div>
JavaScript
// 전역 함수 등록
window.openExampleModal = function(id) {
const modal = document.getElementById('exampleModal');
const content = document.getElementById('exampleModalContent');
modal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
// AJAX로 내용 로드
fetch(`/api/example/${id}`)
.then(response => response.text())
.then(html => {
content.innerHTML = html;
});
};
window.closeExampleModal = function() {
document.getElementById('exampleModal').classList.add('hidden');
document.body.style.overflow = '';
};
// ESC 키 지원
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
window.closeExampleModal();
}
});
// 이벤트 델리게이션 (닫기 버튼)
document.addEventListener('click', function(e) {
if (e.target.closest('[data-close-modal]')) {
e.preventDefault();
window.closeExampleModal();
}
});
AJAX로 로드되는 부분 뷰
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">모달 제목</h2>
<!-- data-close-modal 속성 사용 -->
<button type="button" data-close-modal class="text-gray-400 hover:text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- 내용 -->
<div class="flex justify-end gap-3 mt-6">
<button type="button" data-close-modal class="px-4 py-2 border rounded-lg">취소</button>
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg">확인</button>
</div>
</div>
6. 체크리스트
모달 구현 시 다음 사항을 확인하세요:
pointer-events-none/auto구조를 사용하지 않음- 함수를
window객체에 등록했음 - 닫기 버튼에
data-close-modal속성을 추가했음 - document 레벨 이벤트 델리게이션을 설정했음
- 모달 열 때
body.style.overflow = 'hidden'설정 - 모달 닫을 때
body.style.overflow = ''복원 - ESC 키 이벤트 리스너 등록
- z-index가 다른 요소들과 충돌하지 않음 (보통 z-50 사용)
관련 파일
/resources/views/sales/managers/index.blade.php- 영업파트너 관리 모달 구현 예시/resources/views/sales/managers/partials/show-modal.blade.php- 상세 모달 부분 뷰/resources/views/sales/managers/partials/edit-modal.blade.php- 수정 모달 부분 뷰