방명록
- Vue.js Bootstrap 모달에서 Input 자동 포커싱 구현하기2025년 08월 28일 20시 51분 25초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
Vue.js Bootstrap 모달에서 Input 자동 포커싱 구현하기
1. 개요
1-1. 문제 상황
Vue.js와 Bootstrap을 함께 사용하여 모달을 구현할 때, 모달이 열릴 때마다 특정 input 필드에 자동으로 포커스를 설정하는 것은 사용자 경험 향상에 중요한 요소이다. 하지만 다음과 같은 문제들이 발생할 수 있다.
- 모달이 열릴 때 포커스가 즉시 해제되는 현상
- X 버튼으로 모달을 닫고 다시 열 때 포커스가 작동하지 않는 문제
- Bootstrap 모달의 렌더링 타이밍과 Vue 컴포넌트 라이프사이클 간의 동기화 이슈
1-2. 해결 목표
- 모달이 열릴 때마다 일관되게 첫 번째 input 필드에 포커스 설정
- 모달을 여러 번 열고 닫아도 안정적인 포커스 동작 보장
- Bootstrap 모달 이벤트와 Vue 컴포넌트 상태의 완전한 동기화
2. 구현 방법
2-1. SlotModal 컴포넌트 수정
Bootstrap 모달의 네이티브 이벤트를 Vue 컴포넌트로 전달하기 위해 SlotModal 컴포넌트를 수정한다.
<template> <div class="modal fade" :id="modalId" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true" ref="modalElement" > <div class="modal-dialog"> <div class="modal-content modal-content-custom"> <div class="modal-header"> <h5 class="modal-title" id="staticBackdropLabel"> <slot name="title"></slot> </h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" ></button> </div> <div class="modal-body"><slot name="body"></slot></div> <div class="modal-footer"> <slot name="footer"></slot> </div> </div> </div> </div> </template>2-2. JavaScript 이벤트 리스너 등록
Vue 템플릿에서 Bootstrap 이벤트를 직접 사용할 수 없으므로, JavaScript에서 이벤트 리스너를 등록한다.
export default { props: { modalId: { type: String, default: 'myModal' } }, emits: ['modal-hidden', 'modal-shown'], mounted() { // Bootstrap 모달 이벤트 리스너 등록 if (this.$refs.modalElement) { this.$refs.modalElement.addEventListener( 'hidden.bs.modal', this.handleModalHidden ) this.$refs.modalElement.addEventListener( 'shown.bs.modal', this.handleModalShown ) } }, beforeUnmount() { // 이벤트 리스너 정리 if (this.$refs.modalElement) { this.$refs.modalElement.removeEventListener( 'hidden.bs.modal', this.handleModalHidden ) this.$refs.modalElement.removeEventListener( 'shown.bs.modal', this.handleModalShown ) } }, methods: { handleModalHidden() { this.$emit('modal-hidden') }, handleModalShown() { this.$emit('modal-shown') } } }2-3. UserFormModal 컴포넌트에서 이벤트 처리
부모 컴포넌트에서 SlotModal의 이벤트를 받아 포커스 로직을 처리한다.
<template> <slot-modal class="modal-position" :modalId="modalId" @keydown="handleModalKeydown" @modal-hidden="handleModalHidden" @modal-shown="handleModalShown" data-bs-backdrop="static" data-bs-keyboard="false" > <!-- 모달 내용 --> </slot-modal> </template>3. 포커스 로직 구현
3-1. 모달 이벤트 핸들러
Bootstrap 모달의 생명주기에 맞춰 포커스를 설정한다.
methods: { // 모달이 완전히 열린 후 포커스 설정 handleModalShown() { setTimeout(() => { this.attemptFocus() }, 50) }, // 모달이 닫힌 후 정리 작업 handleModalHidden() { this.$emit('cancel') }, // 실제 포커스 설정 로직 attemptFocus() { if (!this.isEditMode) { const usernameField = document.getElementById('userId') if (usernameField && usernameField.offsetParent !== null) { usernameField.focus() } } else { const passwordField = document.getElementById('userPassword') if (passwordField && passwordField.offsetParent !== null) { passwordField.focus() } } } }3-2. 요소 가시성 검증
포커스를 설정하기 전에 요소가 실제로 화면에 보이는지 확인한다.
offsetParent !== null조건을 사용하여 요소의 가시성을 검증- 숨겨진 요소나 렌더링되지 않은 요소에 포커스를 설정하는 것을 방지
4. 주요 해결 포인트
4-1. 타이밍 이슈 해결
- Bootstrap의
shown.bs.modal이벤트를 활용하여 모달이 완전히 열린 후 포커스 설정 setTimeout을 사용하여 DOM 렌더링 완료를 보장
4-2. ESLint 오류 해결
Vue 템플릿에서
@hidden.bs.modal형식의 이벤트 리스너 사용 시 발생하는 ESLint 오류를 JavaScript 이벤트 리스너로 해결한다.// 오류 발생 코드 <div @hidden.bs.modal="handleModalHidden"></div> // 해결 코드 mounted() { this.$refs.modalElement.addEventListener('hidden.bs.modal', this.handleModalHidden) }4-3. 메모리 누수 방지
컴포넌트가 언마운트될 때 이벤트 리스너를 정리하여 메모리 누수를 방지한다.
beforeUnmount() { if (this.$refs.modalElement) { this.$refs.modalElement.removeEventListener('hidden.bs.modal', this.handleModalHidden) this.$refs.modalElement.removeEventListener('shown.bs.modal', this.handleModalShown) } }5. 결과 및 장점
5-1. 개선된 사용자 경험
- 모달이 열릴 때마다 자동으로 첫 번째 입력 필드에 포커스가 설정됨
- 키보드 네비게이션이 향상되어 접근성이 개선됨
- 사용자가 즉시 입력을 시작할 수 있어 작업 효율성이 증대됨
5-2. 안정적인 동작
- X 버튼으로 모달을 닫고 다시 열어도 일관된 포커스 동작
- Bootstrap 모달과 Vue 컴포넌트 간의 완전한 동기화
- 다양한 브라우저 환경에서 안정적인 작동 보장
5-3. 유지보수성
- 모듈화된 구조로 재사용 가능한 SlotModal 컴포넌트
- 명확한 이벤트 기반 아키텍처로 디버깅 용이
- 메모리 누수 방지를 통한 장기적인 안정성 확보
이러한 구현을 통해 Vue.js와 Bootstrap을 사용한 모달에서 완벽한 자동 포커싱 기능을 구현할 수 있으며, 사용자 경험과 접근성을 크게 향상시킬 수 있다.
728x90반응형'언어·프레임워크 > Vue.js' 카테고리의 다른 글
Vue.js에서 HTML 레이블과 체크박스 상태 관리 문제 해결 (0) 2025.09.03 CSS !important 없이 스타일 적용하기: 특이성을 활용한 해결법 (3) 2025.08.29 [Vue.js] `v-if`와 `<transition>`을 사용하여 동적인 화면을 만드는 방법 (0) 2025.07.03 [Vue.js] 옵션(Options) 방식: 데이터 변경에 따른 리렌더링 완벽 가이드 (0) 2025.07.03 [Vue.js] 환경 변수 관리(.env): `--mode` 옵션으로 깔끔하게 해결하는 법 (1) 2025.06.23 다음글이 없습니다.이전글이 없습니다.댓글