728x90
Vue.js 컴포넌트 생성 및 사용: 상세 가이드
소개
Vue.js의 강력한 기능 중 하나인 컴포넌트는 독립적인 코드 재사용 단위입니다. 컴포넌트를 효과적으로 사용하면 복잡한 UI를 작은 조각으로 나누어 관리하고 재사용할 수 있어 개발 효율성을 높이고 코드 유지보수를 용이하게 합니다. 이 글에서는 Vue.js 컴포넌트 생성부터 사용 방법까지 상세하게 다루어, 여러분이 Vue.js 개발에 능숙해지는 데 도움을 드리고자 합니다.
컴포넌트 생성
Vue.js에서 컴포넌트를 생성하는 방법은 크게 두 가지가 있습니다.
1. Single File Component (SFC):
- 장점: 템플릿, 스크립트, 스타일을 하나의 .vue 파일에서 관리하여 가독성이 좋고, 컴포넌트 간의 의존성 관리가 편리합니다.
- 구조:
<template> </template> <script> export default { // 컴포넌트 로직 } </script> <style> /* 스타일 영역 */ </style>
728x90
2. JavaScript 객체:
- 장점: 다른 JavaScript 객체와의 통합이 쉽고, 간단한 컴포넌트를 빠르게 생성할 수 있습니다.
- 예시:
const MyComponent = { template: '<div>Hello, world!</div>' }
컴포넌트 등록
생성한 컴포넌트를 사용하려면 Vue 인스턴스에 등록해야 합니다.
1. 전역 등록:
- Vue.component() 메서드를 사용하여 모든 Vue 인스턴스에서 사용 가능하도록 등록합니다.
- 예시:
Vue.component('my-component', MyComponent)
2. 로컬 등록:
- 특정 Vue 인스턴스 내에서만 사용하도록 등록합니다.
- 예시:
new Vue({ el: '#app', components: { MyComponent } })
컴포넌트 사용
등록된 컴포넌트는 HTML 템플릿에서 custom tag 형태로 사용할 수 있습니다.
<div id="app">
<my-component></my-component>
</div>
컴포넌트의 속성과 메서드
- props: 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 데 사용됩니다.
- data: 컴포넌트 내부 데이터를 관리합니다.
- methods: 컴포넌트 내부에서 사용할 메서드를 정의합니다.
- computed: 계산된 속성을 정의하여 데이터를 가공합니다.
- watch: 데이터 변화를 감지하고 특정 작업을 수행합니다.
- lifecycle hooks: 컴포넌트의 생성, 업데이트, 삭제 등 각 단계에서 실행되는 함수입니다.
예시:
<template>
<div>
<p>현재 카운트: {{ count }}</p>
<button @click="increment">증가</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
슬롯 (Slots)
부모 컴포넌트에서 자식 컴포넌트의 내용을 커스터마이징할 수 있는 기능입니다.
<my-component>
<p>슬롯 내용</p>
</my-component>
<template>
<div>
<slot></slot>
</div>
</template>
컴포넌트 간 통신
- props: 부모에서 자식으로 데이터 전달
- events: 자식에서 부모로 이벤트 발생
- Vuex: 전역 상태 관리
- provide/inject: 조상 컴포넌트에서 후손 컴포넌트로 데이터 전달
컴포넌트 스타일링
- scoped 스타일: 컴포넌트 내부 스타일만 적용
- CSS Modules: 스타일 클래스명 충돌 방지
- CSS Preprocessors: Sass, Less 등을 사용하여 스타일 관리
컴포넌트 재사용
- Mixins: 여러 컴포넌트에서 공통적으로 사용하는 기능을 모듈화
- Custom directives: 특정 기능을 구현하기 위한 지시어 생성
주어진 예제는 Vue.js에서 슬롯(Slot)을 사용한 간단한 컴포넌트 구조입니다. 이를 보다 풍성하게 확장하여 다양한 활용 사례와 함께 설명하겠습니다. 슬롯은 컴포넌트의 재사용성을 높이고, 부모 컴포넌트에서 자식 컴포넌트로 콘텐츠를 동적으로 전달할 수 있게 해주는 강력한 기능입니다.
기본 예제 복습
먼저 주어진 예제를 살펴보겠습니다:
<my-component>
<p>슬롯 내용</p>
</my-component>
<template>
<div>
<slot></slot>
</div>
</template>
-
부모 컴포넌트: <my-component> 태그 안에 <p>슬롯 내용</p>를 전달.
-
자식 컴포넌트: <slot></slot> 태그가 부모로부터 전달된 콘텐츠(여기서는 <p>슬롯 내용</p>)를 렌더링.
-
결과: <div><p>슬롯 내용</p></div>가 출력됨.
이제 이를 확장해서 더 풍성한 예제를 만들어 보겠습니다.
확장된 예제 1: 다중 슬롯과 네임드 슬롯(Named Slots)
단일 슬롯 대신 여러 개의 슬롯을 사용하고, 각 슬롯에 이름을 지정하여 특정 위치에 콘텐츠를 배치할 수 있습니다.
코드
<!-- 부모 컴포넌트에서 사용 -->
<my-component>
<template v-slot:header>
<h1>제목 슬롯</h1>
</template>
<template v-slot:content>
<p>본문 내용입니다.</p>
</template>
<template v-slot:footer>
<button>확인</button>
</template>
</my-component>
<!-- 자식 컴포넌트 정의 -->
<template>
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot name="content"></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<style>
.container {
border: 1px solid #ccc;
padding: 10px;
}
header { background: #f0f0f0; }
footer { text-align: right; }
</style>
설명
-
네임드 슬롯: <slot name="header">처럼 이름을 지정하여 부모에서 특정 슬롯에 콘텐츠를 전달함. 부모에서는 v-slot:이름으로 해당 슬롯을 채움.
-
결과:
<div class="container"> <header><h1>제목 슬롯</h1></header> <main><p>본문 내용입니다.</p></main> <footer><button>확인</button></footer> </div>
-
활용 사례: 레이아웃 컴포넌트(예: 카드, 모달, 페이지 템플릿 등)에서 각 섹션을 유연하게 커스터마이징할 때 유용.
확장된 예제 2: 기본값이 있는 슬롯(Default Fallback)
슬롯에 부모가 콘텐츠를 제공하지 않을 경우 기본 콘텐츠를 설정할 수 있습니다.
코드
<!-- 부모 컴포넌트에서 사용 (콘텐츠 생략 가능) -->
<my-component></my-component>
<!-- 자식 컴포넌트 정의 -->
<template>
<div>
<slot>기본 콘텐츠입니다.</slot>
</div>
</template>
설명
-
기본값: 부모에서 슬롯에 콘텐츠를 전달하지 않으면 <slot> 태그 안에 작성된 "기본 콘텐츠입니다."가 렌더링됨.
-
부모에서 콘텐츠 제공 시:
<my-component><p>커스텀 콘텐츠</p></my-component>
결과: <div><p>커스텀 콘텐츠</p></div> -
활용 사례: 선택적으로 콘텐츠를 채우고, 그렇지 않으면 기본 UI를 제공해야 할 때 (예: 버튼 텍스트, placeholder 등).
확장된 예제 3: 슬롯 프롭(Slot Props)으로 데이터 전달
슬롯을 통해 자식 컴포넌트의 데이터를 부모로 전달하여 동적으로 사용할 수 있습니다.
코
<!-- 부모 컴포넌트에서 사용 -->
<my-component>
<template v-slot:default="slotProps">
<p>{{ slotProps.message }} - {{ slotProps.count }}번 반복</p>
</template>
</my-component>
<!-- 자식 컴포넌트 정의 -->
<template>
<div>
<slot :message="greeting" :count="iteration"></slot>
</div>
</template>
<script>
export default {
data() {
return {
greeting: "안녕하세요",
iteration: 3
};
}
};
</script>
설명
-
슬롯 프롭: 자식 컴포넌트에서 <slot>에 :message와 :count를 바인딩하여 데이터를 부모로 전달.
-
부모에서 사용: v-slot:default="slotProps"로 데이터를 받아 동적으로 렌더링.
-
결과: <div><p>안녕하세요 - 3번 반복</p></div>
-
활용 사례: 리스트 아이템을 렌더링하거나, 자식 컴포넌트의 상태를 부모에서 커스터마이징할 때 유용.
확장된 예제 4: 조건부 슬롯과 스타일 적용
슬롯 콘텐츠를 조건부로 렌더링하고 스타일을 추가한 예제입니다.
코드
<!-- 부모 컴포넌트에서 사용 -->
<my-component>
<template v-slot:default>
<span v-if="true">활성화된 슬롯</span>
<span v-else>비활성화된 슬롯</span>
</template>
</my-component>
<!-- 자식 컴포넌트 정의 -->
<template>
<div class="slot-wrapper">
<slot></slot>
</div>
</template>
<style>
.slot-wrapper {
padding: 20px;
background-color: #e0f7fa;
border-radius: 8px;
}
.slot-wrapper span {
font-weight: bold;
color: #007bff;
}
</style>
설명
-
조건부 렌더링: 부모에서 v-if를 사용해 슬롯 콘텐츠를 동적으로 제어.
-
스타일링: 자식 컴포넌트에서 슬롯 콘텐츠를 감싸는 wrapper에 스타일을 적용해 시각적 효과 추가.
-
결과: <div class="slot-wrapper"><span>활성화된 슬롯</span></div>
-
활용 사례: 상태에 따라 다른 콘텐츠를 보여주거나, 슬롯에 일관된 스타일을 적용할 때.
요약 및 추가 팁
-
단일 슬롯: 기본 콘텐츠 전달에 사용.
-
네임드 슬롯: 복잡한 레이아웃에서 섹션별로 콘텐츠를 구성.
-
기본값: 부모가 콘텐츠를 제공하지 않을 때 대비.
-
슬롯 프롭: 자식 데이터를 부모에서 동적으로 활용.
-
조건부/스타일: 유연성과 시각적 일관성 확보.
슬롯은 Vue.js 컴포넌트 설계에서 매우 강력한 도구로, 위 예제를 기반으로 프로젝트 요구사항에 맞게 자유롭게 확장해보세요! 추가 질문이 있다면 말씀해주세요.
실무에서 실제로 자주 활용될 만한 그럴듯한 예제를 만들어 보겠습니다. 여기서는 슬롯을 활용한 재사용 가능한 모달 컴포넌트를 예로 들어보겠습니다. 모달은 웹 애플리케이션에서 사용자 인터랙션을 처리하거나 알림을 표시할 때 자주 사용되며, 슬롯을 통해 콘텐츠와 버튼을 유연하게 커스터마이징할 수 있습니다.
실무 예제: 재사용 가능한 모달 컴포넌트
시나리오
-
목적: 사용자가 로그인, 확인 메시지, 양식 제출 등 다양한 상황에서 모달을 재사용 가능하도록 설계.
-
요구사항:
-
모달 제목, 본문, 버튼을 커스터마이징 가능.
-
버튼 클릭 시 동작을 부모에서 정의.
-
기본 스타일과 애니메이션을 포함.
-
코드
자식 컴포넌트 (Modal.vue)
<template>
<div class="modal-overlay" v-if="isOpen" @click.self="closeModal">
<div class="modal-container">
<!-- 제목 슬롯 -->
<header class="modal-header">
<slot name="header">
<h2>기본 제목</h2>
</slot>
</header>
<!-- 본문 슬롯 -->
<main class="modal-body">
<slot name="body">
<p>기본 본문 내용입니다.</p>
</slot>
</main>
<!-- 버튼 슬롯 -->
<footer class="modal-footer">
<slot name="footer" :close="closeModal">
<button class="btn btn-secondary" @click="closeModal">취소</button>
<button class="btn btn-primary" @click="closeModal">확인</button>
</slot>
</footer>
</div>
</div>
</template>
<script>
export default {
props: {
isOpen: {
type: Boolean,
default: false,
},
},
methods: {
closeModal() {
this.$emit('update:isOpen', false); // 부모에게 모달 닫기 이벤트 전송
},
},
};
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal-container {
background: white;
width: 400px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
animation: fadeIn 0.3s ease-in-out;
}
.modal-header {
padding: 15px;
border-bottom: 1px solid #eee;
}
.modal-body {
padding: 20px;
}
.modal-footer {
padding: 15px;
text-align: right;
}
.btn {
padding: 8px 16px;
margin-left: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-secondary {
background: #6c757d;
color: white;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
부모 컴포넌트에서 사용
<template>
<div>
<button @click="showModal = true">모달 열기</button>
<Modal :isOpen.sync="showModal">
<!-- 제목 슬롯 -->
<template v-slot:header>
<h2>로그인 필요</h2>
</template>
<!-- 본문 슬롯 -->
<template v-slot:body>
<p>계속 진행하려면 로그인이 필요합니다.</p>
<input type="text" placeholder="아이디" class="form-input" />
<input type="password" placeholder="비밀번호" class="form-input" />
</template>
<!-- 버튼 슬롯 (슬롯 프롭 사용) -->
<template v-slot:footer="{ close }">
<button class="btn btn-secondary" @click="close">취소</button>
<button class="btn btn-primary" @click="handleLogin(close)">로그인</button>
</template>
</Modal>
</div>
</template>
<script>
import Modal from './Modal.vue';
export default {
components: { Modal },
data() {
return {
showModal: false,
};
},
methods: {
handleLogin(close) {
console.log('로그인 처리 로직 실행');
// 로그인 로직 수행 후 모달 닫기
close();
},
},
};
</script>
<style>
.form-input {
display: block;
width: 100%;
padding: 8px;
margin-top: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
</style>
설명
-
구조와 슬롯 활용:
-
header 슬롯: 모달 제목을 커스터마이징. 기본값으로 "기본 제목" 제공.
-
body 슬롯: 본문 콘텐츠를 유연하게 채움. 예제에서는 로그인 폼을 추가.
-
footer 슬롯: 버튼을 동적으로 구성하며, close 메서드를 슬롯 프롭으로 전달해 부모에서 모달을 제어 가능.
-
-
실무적 특징:
-
재사용성: 이 모달 컴포넌트는 로그인, 경고 메시지, 확인 대화상자 등 다양한 용도로 재사용 가능.
-
유연성: 부모에서 슬롯을 통해 UI와 동작을 자유롭게 정의.
-
상태 관리: :isOpen.sync를 사용해 부모와 자식 간 모달 상태 동기화.
-
-
스타일과 UX:
-
오버레이와 애니메이션으로 사용자 경험 개선.
-
버튼 스타일과 레이아웃을 실무에서 흔히 사용하는 방식으로 구성.
-
-
결과:
-
모달이 열리면 제목은 "로그인 필요", 본문은 입력 폼, 푸터는 "취소"와 "로그인" 버튼으로 표시.
-
"로그인" 버튼 클릭 시 콘솔에 메시지가 출력되고 모달이 닫힘.
-
실무 활용 예시
-
경고 모달:
<Modal :isOpen.sync="showWarning">
<template v-slot:header>
<h2>경고</h2>
</template>
<template v-slot:body>
<p>정말 삭제하시겠습니까?</p>
</template>
<template v-slot:footer="{ close }">
<button class="btn btn-secondary" @click="close">아니요</button>
<button class="btn btn-danger" @click="deleteItem(close)">예</button>
</template>
</Modal>
-
정보 표시 모달:
<Modal :isOpen.sync="showInfo">
<template v-slot:body>
<p>업데이트가 완료되었습니다!</p>
</template>
<template v-slot:footer="{ close }">
<button class="btn btn-primary" @click="close">확인</button>
</template>
</Modal>
왜 실무에 적합한가?
-
유지보수성: 슬롯을 활용해 컴포넌트 로직과 UI를 분리, 코드 중복 감소.
-
확장성: 다양한 상황에 맞게 커스터마이징 가능.
-
팀 협업: 디자이너와 개발자가 슬롯을 통해 UI를 독립적으로 작업 가능.
이 예제는 실무에서 자주 마주치는 모달 요구사항을 충족하며, 슬롯의 강점을 잘 보여줍니다. 추가로 궁금한 점이나 다른 실무 예제가 필요하면 말씀해주세요!
결론
Vue.js 컴포넌트는 복잡한 UI를 효율적으로 관리하고 재사용성을 높이는 데 필수적인 기능입니다. 이 글에서 다룬 내용을 바탕으로 여러분만의 Vue.js 애플리케이션을 개발해 보세요.
728x90
'Vue.js 를 배워보자 > 2. Vue.js 기본 개념' 카테고리의 다른 글
컴포넌트 슬롯 (0) | 2024.10.04 |
---|---|
컴포넌트 props와 events (0) | 2024.10.04 |
데이터 감시자 (0) | 2024.10.04 |
데이터 계산 속성 (0) | 2024.10.04 |
데이터 선언 및 변경 (0) | 2024.10.04 |