| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
Tags
- TODO
- todo-list
- URL
- goroutines
- Dictionary
- websocket
- 행렬
- method
- cli
- CDN
- container
- toggle
- App.vue
- goroutine
- reactivity
- map
- golang
- component
- emit
- Vue.js
- Refactoring
- graceful shutdown
- localStorage
- go
- Server
- SFC
- Matrix
- channel
- Vue
- PROPS
Archives
- Today
- Total
ksundev 님의 블로그
[TODO App] Modal 기능 구현 - Slot과 내장 Transition 활용 본문
이번 포스트에서는 Vue.js에서 Modal 기능을 구현하는 방법을 알아보겠습니다. 특히 Vue의 강력한 Slot 시스템과 내장 Transition 컴포넌트를 활용한 Modal 구현에 대해 중점적으로 다뤄보겠습니다.
1. 현재 상황과 해결해야 할 문제
컴포넌트 구조 개선: UI와 비즈니스 로직 분리
이전 포스트들과 마찬가지로, 이번에도 컴포넌트의 책임을 명확히 분리했습니다:
- TodoInput 컴포넌트: UI 구현만 담당 (프레젠테이션 컴포넌트)
- App 컴포넌트: 비즈니스 로직과 데이터 관리 담당 (컨테이너 컴포넌트)
- Modal 컴포넌트: 재사용 가능한 UI 컴포넌트
해결해야 할 문제
- 빈 입력 시 사용자에게 알림 표시
- 재사용 가능한 Modal 컴포넌트 구현
- Vue의 Slot 시스템을 활용한 유연한 Modal 설계
2. Vue의 내장 기능들
Transition 컴포넌트
Vue는 애니메이션을 위한 내장 Transition 컴포넌트를 제공합니다:
<template>
<Transition name="modal">
<div v-if="show" class="modal-mask">
<!-- Modal 내용 -->
</div>
</Transition>
</template>
<style>
.modal-enter-from {
opacity: 0;
}
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
transform: scale(1.1);
}
</style>
이벤트 시스템
Vue의 강력한 이벤트 시스템으로 컴포넌트 간 통신이 간단합니다:
<!-- 자식 컴포넌트에서 이벤트 발생 -->
<button @click="$emit('close')">확인</button>
<!-- 부모 컴포넌트에서 이벤트 수신 -->
<Modal @close="closeModal" />
3. Slot 시스템의 강력함
Slot이란?
Slot은 Vue의 핵심 기능 중 하나로, 컴포넌트의 내용을 동적으로 전달할 수 있게 해줍니다.
기본 Slot vs Named Slots
<!-- 기본 Slot -->
<template>
<div>
<slot>기본 내용</slot>
</div>
</template>
<!-- Named Slots -->
<template>
<div>
<header>
<slot name="header">기본 헤더</slot>
</header>
<main>
<slot name="body">기본 내용</slot>
</main>
<footer>
<slot name="footer">기본 푸터</slot>
</footer>
</div>
</template>
Modal에서 Slot 활용
<!-- Modal.vue -->
<template>
<Transition name="modal">
<div v-if="show" class="modal-mask" @click="$emit('close')">
<div class="modal-container" @click.stop>
<div class="modal-header">
<slot name="header">default header</slot>
</div>
<div class="modal-body">
<slot name="body">default body</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" @click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</Transition>
</template>
Slot 사용의 장점
- 재사용성: 하나의 Modal 컴포넌트로 다양한 용도 사용 가능
- 유연성: 부모 컴포넌트에서 내용을 자유롭게 커스터마이징
- 일관성: 모든 Modal이 동일한 스타일과 동작을 가짐
4. TodoInput 컴포넌트 구현
TodoInput.vue 구조
<template>
<div class="inputBox shadow">
<input
type="text"
placeholder="할 일을 입력하세요"
v-model="newTodo"
@keyup.enter="addTodo"
/>
<span class="addContainer" @click="addTodo">
<i class="fa-solid fa-plus addBtn"></i>
</span>
</div>
</template>
<script>
export default {
data() {
return {
newTodo: "",
};
},
methods: {
addTodo() {
if (this.newTodo !== "") {
this.$emit("addTodo", this.newTodo);
this.clearInput();
} else {
this.$emit("showModal");
}
},
clearInput() {
this.newTodo = "";
},
},
};
</script>
프레젠테이션 컴포넌트로서의 역할
TodoInput은 다음과 같은 UI 관련 책임만 가집니다:
- 입력 필드 렌더링: 할 일 입력을 위한 텍스트 필드
- 이벤트 처리: Enter 키와 클릭 이벤트 처리
- 유효성 검사: 빈 입력 시 Modal 표시 이벤트 발생
5. App 컴포넌트에서 Modal 관리
App.vue에서 Modal 구현
<template>
<div id="app">
<TodoHeader />
<TodoInput @addTodo="addOneItem" @showModal="handleShowModal" />
<TodoList
:todoItems="todoItems"
@removeItem="removeOneItem"
@toggleComplete="toggleOneItem"
/>
<TodoFooter @clearTodo="clearAllItems" />
<Modal :show="showModal" @close="closeModal">
<template #header>
<h3>알림</h3>
</template>
<template #body>
<p>아무것도 입력하지 않았습니다.</p>
</template>
</Modal>
</div>
</template>
컨테이너 컴포넌트로서의 역할
App 컴포넌트는 다음과 같은 비즈니스 로직을 담당합니다:
- Modal 상태 관리:
showModal상태로 Modal 표시/숨김 제어 - 이벤트 처리: 자식 컴포넌트로부터 받은 이벤트 처리
- Slot 내용 정의: Modal의 헤더와 바디 내용 정의
6. 이벤트 기반 통신 패턴
부모-자식 컴포넌트 통신 원리
자식 컴포넌트 (TodoInput) - UI 담당
↓ $emit("showModal")
부모 컴포넌트 (App) - 비즈니스 로직 담당
↓ @showModal="handleShowModal"
Modal 표시 및 상태 관리Props Down, Events Up 패턴
// Props Down: 부모에서 자식으로 데이터 전달
<Modal :show="showModal" />
// Events Up: 자식에서 부모로 이벤트 발생
<TodoInput @showModal="handleShowModal" />
7. 실제 동작 과정
Modal 표시 플로우
1. 사용자 액션: 빈 입력으로 Enter 또는 클릭
↓
2. 유효성 검사: TodoInput에서 빈 입력 확인
↓
3. 이벤트 발생: showModal 이벤트 발생
↓
4. 상태 변경: App에서 showModal = true
↓
5. Modal 렌더링: Transition과 함께 Modal 표시
↓
6. Slot 내용 표시: 헤더와 바디 내용 렌더링Modal 닫기 플로우
1. 사용자 액션: OK 버튼 클릭 또는 배경 클릭
↓
2. 이벤트 발생: Modal에서 close 이벤트 발생
↓
3. 상태 변경: App에서 showModal = false
↓
4. Modal 숨김: Transition과 함께 Modal 사라짐8. Vue의 내장 기능 활용
Transition 컴포넌트
<Transition name="modal">
<div v-if="show" class="modal-mask">
<!-- Modal 내용 -->
</div>
</Transition>
이벤트 수식어
<!-- 배경 클릭 시 닫기 -->
<div class="modal-mask" @click="$emit('close')">
<!-- 내용 클릭 시 이벤트 전파 방지 -->
<div class="modal-container" @click.stop>
<!-- Modal 내용 -->
</div>
</div>
9. Vue의 장점
내장 기능의 편리함
- Transition 컴포넌트: 애니메이션을 위한 전용 컴포넌트 제공
- 자동 클래스 관리: Vue가 enter/leave 클래스를 자동으로 적용
- 간단한 설정:
name속성만으로 애니메이션 설정 - 성능 최적화: Vue 엔진에서 최적화된 애니메이션 처리
Slot 시스템의 유연성
- 재사용성: 하나의 Modal 컴포넌트로 다양한 용도 사용
- 커스터마이징: 부모 컴포넌트에서 내용을 자유롭게 변경
- 일관성: 모든 Modal이 동일한 스타일과 동작 유지
10. 결론
이번 시간에는 Modal을 간단하지만 확실하게 구현해보았습니다. slot을 통해 재사용성도 높였습니다.
다음 포스트에서는 transition-group을 활용하여 리스트 쪽에서 부드러운 애니메이션을 구현해보겠습니다.
'[개발] Vue.js > 중급' 카테고리의 다른 글
| [Vue][퀴즈] 10문제 (0) | 2025.07.09 |
|---|---|
| [TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 4 (5) | 2025.07.09 |
| [TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 3 (2) | 2025.07.07 |
| [TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 2 (0) | 2025.07.07 |
| [TODO App][Refactoring] App 컴포넌트를 컨테이너로 만들기 1 (0) | 2025.07.07 |