useModal 사용한 모달창 구현
import { useState } from 'react';
const useModal = () => {
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
return {
isOpen,
openModal,
closeModal,
};
};
export default useModal;
기본 모달창 생성.
import React from 'react';
import './Modal.scss';
interface ModalProps {
isOpen: boolean;
onClose: () => void;
}
const Modal = ({ isOpen, onClose }: ModalProps) => {
if (!isOpen) return null;
return (
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<p>아직 작업 중에 있습니다.</p>
<button className="modal-button" onClick={onClose}>
확인
</button>
</div>
</div>
);
};
export default Modal;
onClick{(e) => e.stopPropagation()} 을 사용하여 부모 태그의 이벤트가 발생하지 않도록 막음.
근데 생각해보면 button태그만 클릭되면 되는거라 2개의 div는 비인터랙션 태그임.
=> 2개의 비인터랙션 div 태그에는 키보드 이벤트 및 role, tab 지원이 필요 없음.
1. 접근성 고려
- 직접 키보드 이벤트와 role 지정.
return (
<div
className="modal-backdrop"
onClick={onClose}
role="button"
tabIndex={0}
onKeyDown={handleKeyDown}
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<p>아직 작업 중에 있습니다.</p>
<button className="modal-button" onClick={onClose}>
확인
</button>
</div>
</div>
);
- 키보드 이벤트 추가: onKeyDown 이벤트를 추가하여 Enter 또는 Space 키로 모달을 닫을 수 있도록 합니다.
- 적절한 role 지정: div가 인터랙티브한 요소처럼 작동하므로, role="button"을 추가하여 의미를 명확히 합니다.
- tabIndex 추가: tabIndex={0}를 추가하여 요소가 탭 키를 통해 포커스를 받을 수 있도록 합니다.
- button 태그 사용.
return (
<button
className="modal-backdrop"
onClick={onClose}
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<p>아직 작업 중에 있습니다.</p>
<button className="modal-button" onClick={onClose}>
확인
</button>
</div>
</button>
);
div태그 대신 button 태그 사용. button 안에 button이기에 이벤트 버블링 주의!
- dialog 태그 사용
// 참고: `<dialog>` 요소를 열고 닫을 때 `open` 속성을 직접 다뤄야 함.
return (
<dialog open={isOpen} className="modal-dialog" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<p>아직 작업 중에 있습니다.</p>
<button className="modal-button" onClick={onClose}>
확인
</button>
</div>
</dialog>
);
dialog 태그는 HTML5으로 모달창을 고려한 태그임. 따라서, 접근성을 내장하고 있음. But, 오래된 브라우저에는 지원안됨.
- section, article 사용
return (
<section
className="modal-backdrop"
onClick={onClose}
role="dialog"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
onClose();
}
}}
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<p>아직 작업 중에 있습니다.</p>
<button className="modal-button" onClick={onClose}>
확인
</button>
</div>
</section>
);
모달 내용이 복잡하고 의미 있는 경우 사용. role 및 키보드 이벤트 직접 추가해줘야 함.
결론:
- 단순한 모달 기능을 빠르게 구현하고 싶다면 <button> 태그를 사용하는 것이 가장 간단하고 오류 없이 작업할 수 있습니다.
- 더 의미론적이고 표준적인 접근을 원한다면 <dialog> 태그를 사용할 수 있습니다. 다만, 브라우저 호환성을 고려해야 합니다.
- 내용이 복잡하고 의미적으로 구분이 필요한 경우 <section> 또는 <article> 태그를 사용하면서 적절한 role과 키보드 이벤트를 추가하는 것이 좋습니다.
2. 스크린 리더 고려
- role과 aria 속성 추가: 모달의 의미를 스크린 리더에게 알리고, 제목과 내용을 참조하도록 개선.
- 배경 스크롤 잠금: 모달이 열릴 때 배경 페이지의 스크롤을 막아 사용자 경험을 개선.
- 포커스 트랩 추가: 탭 키를 사용해도 포커스가 모달 내부에 머무르도록 개선.
- Escape 키로 모달 닫기: Escape 키로 모달을 닫을 수 있도록 키보드 접근성 개선.
=> 모달이 접근성 표준에 부합하고 스크린 리더에 적절한 정보를 제공하며, 포커스 제어를 통해 사용자 경험도 향상됨.
😎😎😎
추천 학습 및 작업 순서
- 기본 접근성 원칙 이해: role과 aria 속성에 대해 배우는 것부터 시작하세요. role="button", aria-label 등 간단한 속성을 추가하는 것만으로도 접근성이 크게 개선될 수 있습니다.
- 키보드 접근성 연습: 모달을 만들고 탭 키로 요소를 순회하는 것부터 해보세요. 키보드만으로 모든 기능을 사용할 수 있도록 하는 것이 첫 번째 목표입니다.
- 스크린 리더 테스트 도구 사용: Chrome의 Lighthouse 같은 접근성 테스트 도구를 활용하여 코드의 접근성 문제를 찾아보세요. 필요한 경우 무료 스크린 리더 소프트웨어인 NVDA(Windows)나 VoiceOver(Mac)을 사용해보는 것도 추천합니다.
💯💯💯
신입이 집중할 수 있는 핵심 포인트
- 키보드 인터랙션: 탭, Enter, Esc 키 등 키보드만으로 모달을 사용할 수 있게 하는 것에 먼저 집중하세요.
- 포커스 관리: 모달이 열릴 때 첫 번째 포커스 가능한 요소로 포커스를 이동시키고, 닫힐 때 원래 포커스 위치로 돌아가도록 관리하세요.
- 스크린 리더 인디케이션: role="dialog"와 aria-labelledby 같은 속성을 추가해 스크린 리더가 제대로 모달을 설명할 수 있게 하는 간단한 것부터 시작하세요.
'프로젝트' 카테고리의 다른 글
[PBL] npx와 npm 차이 (0) | 2024.10.27 |
---|---|
[PBL] 트러블슈팅 ESLint 9.0.0 (0) | 2024.10.24 |
[PBL] .editorconfig vs .prettierrc.js vs .eslintrc.js (0) | 2024.10.23 |
[PBL] 템플릿 vs 보일러플레이트 (0) | 2024.10.23 |
[해양물류] 트러블 슈팅 모달창 (0) | 2024.10.13 |