docs:모달창 생성시 유의사항 개발문서 추가
- pointer-events 문제와 해결방법 - AJAX HTML에서 함수 호출 문제 - 이벤트 델리게이션 사용법 - 완전한 모달 구현 예시 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
233
claudedocs/모달창_생성시_유의사항.md
Normal file
233
claudedocs/모달창_생성시_유의사항.md
Normal file
@@ -0,0 +1,233 @@
|
||||
# 모달창 생성 시 유의사항
|
||||
|
||||
## 개요
|
||||
|
||||
이 문서는 SAM 프로젝트에서 모달창을 구현할 때 발생할 수 있는 문제점과 해결 방법을 정리한 것입니다.
|
||||
|
||||
---
|
||||
|
||||
## 1. pointer-events 문제
|
||||
|
||||
### 문제 상황
|
||||
|
||||
모달 배경 클릭을 방지하면서 모달 내부만 클릭 가능하게 하려고 다음과 같은 구조를 사용했을 때:
|
||||
|
||||
```html
|
||||
<!-- 문제가 발생하는 구조 -->
|
||||
<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-auto` div 안에 들어가도, 그 안의 요소들에 pointer-events가 제대로 상속되지 않을 수 있음
|
||||
- 특히 동적으로 로드된 HTML에서 이 문제가 자주 발생
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`pointer-events-none/auto` 구조를 사용하지 않고 단순화:
|
||||
|
||||
```html
|
||||
<!-- 올바른 구조 -->
|
||||
<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에서 함수 호출 문제
|
||||
|
||||
### 문제 상황
|
||||
|
||||
```html
|
||||
<!-- AJAX로 로드된 HTML -->
|
||||
<button onclick="closeModal()">닫기</button>
|
||||
```
|
||||
|
||||
**증상**: `closeModal is not defined` 오류 발생
|
||||
|
||||
### 원인
|
||||
|
||||
- 함수가 `function closeModal() {}` 형태로 정의되면 호이스팅되지만, 모듈 스코프나 블록 스코프 안에 있을 수 있음
|
||||
- AJAX로 로드된 HTML에서 전역 함수에 접근하지 못할 수 있음
|
||||
|
||||
### 해결 방법
|
||||
|
||||
**방법 1: window 객체에 명시적 등록**
|
||||
|
||||
```javascript
|
||||
// 전역 스코프에 함수 등록
|
||||
window.closeModal = function() {
|
||||
document.getElementById('modal').classList.add('hidden');
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
```
|
||||
|
||||
**방법 2: 이벤트 델리게이션 (권장)**
|
||||
|
||||
```html
|
||||
<!-- HTML: data 속성 사용 -->
|
||||
<button data-close-modal>닫기</button>
|
||||
```
|
||||
|
||||
```javascript
|
||||
// JavaScript: document 레벨에서 이벤트 감지
|
||||
document.addEventListener('click', function(e) {
|
||||
const closeBtn = e.target.closest('[data-close-modal]');
|
||||
if (closeBtn) {
|
||||
e.preventDefault();
|
||||
window.closeModal();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 배경 스크롤 방지
|
||||
|
||||
### 모달 열 때
|
||||
|
||||
```javascript
|
||||
document.body.style.overflow = 'hidden';
|
||||
```
|
||||
|
||||
### 모달 닫을 때
|
||||
|
||||
```javascript
|
||||
document.body.style.overflow = '';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. ESC 키로 모달 닫기
|
||||
|
||||
```javascript
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
window.closeModal();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 완전한 모달 구현 예시
|
||||
|
||||
### HTML 구조
|
||||
|
||||
```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
|
||||
|
||||
```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로 로드되는 부분 뷰
|
||||
|
||||
```html
|
||||
<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` - 수정 모달 부분 뷰
|
||||
Reference in New Issue
Block a user