[TIL 15] 컴포넌트 심화

nini·2025년 3월 26일

KB IT's Your Life

목록 보기
15/40

컴포넌트 심화

단일 파일 컴포넌트에서의 스타일

전역 css

  • src/main.js에서 임포트한 CSS 스타일(assets/main.css)

  • 페이지 전체에 적용

  • 다른 컴포넌트의 스타일과 충돌 피하기

    • 특정 컴포넌트만의 스타일 지정
    • 범위 CSS(Scoped CSS)
    • CSS 모듈

<실습>

  • 취소선 → 같은 child css가 계속 들어와서 전부 무시 → 마지막꺼만 살아남음

  • 기본 → 전부 orange로

  • <style>을 <style scoped>로 변경했을 때

    • 변화없이 정의해서 쓰고 싶을 때



CSS 모듈

  • CSS 스타일을 마치 객체처럼 처리
  • <style module>로 지정
  • 자바스크립트에서 프로그래밍으로 값을 조정해야 할 때 사용

슬롯

슬롯(slot)

  • 부모 컴포넌트와 자식 컴포넌트 사이의 정보 교환 방법 → props와 이벤트
  • 부모 컴포넌트에서 자식 컴포넌트로 템플릿 정보를 전달하는 방법 → 슬롯

슬롯(Slot)은 Vue에서 컴포넌트의 재사용성을 높이고, 유연성을 강화하기 위해 사용된다. 컴포넌트 내부에 어떤 내용(content)이 들어올지 미리 정하지 않고,
부모 컴포넌트에서 원하는 내용을 넣을 수 있도록 도와준다.

// src/components/NoSlotTest.vue(부모)
methods : {
	CheckBoxChanged(e) { // (e) => {id, checked}
		let item = this.items.find((item)=> item.id === e.id);
		item.checked = e.checked;
	}
}

App → NoSlotTest(부모) → CheckBox1(자식)


슬롯의 기본 사용법

  • 부모 컴포넌트에서 자식 컴포넌트로 템플릿을 전달하는 방법
    • 부모 - 템플릿(모양) 결정, 자식 - 위치 결정(부모가 전달한 데이터를 어디에 배치할 것인지)
// src/components/CheckBox2.vue
<slot>Item</slot> // 부모가 전달한 템플릿을 여기에 배치
// Item - default 문자열(부모가 전달 안 할 때)

명명된 슬롯

  • 식별자를 쓰겠다?
  • 자식 컴포넌트에 slot 영역을 여러 개 지정한 경우
  • 각 영역에 이름을 지정 <slot name="슬롯명"></slot>
  • 부모 컴포넌트는 v-slot 디렉티브로 어떤 슬롯에 대한 템플릿인지 이름으로 지정 <...v-slot:슬롯명></...> → 슬롯명에 따옴표 X

명명된 슬롯의 활용

  • 화면 레이아웃 관리 목적으로 사용

각각에 들어갈 요소들을 슬롯에 배정

<template>
	<Layout>
		<template v-slot:header>
			<h2>헤더 영역</h2> => Component화 시킬 수 있음?!
		</template>

		<template v-slot:sidebar>
			<h3>사이드</h3>
			<h3>사이드</h3>
			<h3>사이드</h3>
		</template>

		<template v-slot:default>
			<h1>컨텐트 영역</h1>
			<h1>컨텐트 영역</h1>
			<h1>컨텐트 영역</h1>
			<h1>컨텐트 영역</h1>
			<h1>컨텐트 영역</h1>
			<h1>컨텐트 영역</h1>
		</template>

		<template v-slot:footer>
			<h2>Footer text</h2>
		</template>
	</Layout>
</template>

범위 슬롯(Scoped Slot)

  • 부모 컴포넌트에서 템플릿에 데이터를 바인딩할 때
    • 자식 컴포넌트의 데이터를 이용해 바인딩하는 경우 → 범위 슬롯 사용
    • 전달된 데이터는 슬롯 템플릿 내부 범위에서만 사용 가능

자식의 상태에 따라 부모가 결정


동적 컴포넌트

동적 컴포넌트(dynamic component)

  • 화면의 동일한 위치에 여러 컴포넌트를 표현해야 하는 경우 사용

    • 예) 탭
  • 요소를 템플릿에 작성

  • v-bind 디렉티브를 이용해 is 특성 값으로 어떤 컴포넌트를 그 위치에 나타낼지 결정(컴포넌트 명을 문자열로 대입-대소문자 구분)

  • 컴포넌트가 변경될 때

    • 컴포넌트가 다시 생성됨
    • 정적 컴포넌트인 경우 include 속성으로 캐싱
<keep-alive include="CoralSeaTab,LeyteGulfTab">
	<component :is="currentTab"></component>
</keep-alive>
<!-- keep-alive는 띄어쓰기 없이 쉼표로만 나열해야 정상 동작함 -->
<!-- Vue는 include 값을 문자열로 처리할 때 정확한 컴포넌트 이름과 매칭해야 하는데, LeyteGulfTab 앞에 공백이 있으면 매칭이 제대로 되지 않음 -->

사용자 정의 v-model 만들기

  • 부모 컴포넌트
    • 사용자 정의 이름이 들어감(message → 사용자 정의 모델명)
    • <child-component v-model:message="parentMessage" />
  • 자식 컴포넌트
    • 모델에 해당하는 속성을 반드시 정의

    • 이벤트명은 반드시 ‘update:속성명’ 형식을 사용

      {
      	name: "ChildComponent",
      	props: {message: String},
      	template: `
      		<input type="text" :value="message"
      			@input="$emit('update:message', $event.target.value)"/>
      	`
      }

provide, inject를 이용한 공용 데이터 사용

props를 이용한 정보 전달

  • 컴포넌트의 계층 구조가 복잡해지면 → 계층 구조를 따라 연속적으로 속성을 전달해야 하는 문제 발생

provide, inject

  • 공용 데이터를 부모 컴포넌트(App)에서 제공(provide)
  • 하위 컴포넌트 트리상의 어느 컴포넌트에서나 필요한 데이터를 주입(inject)하여 사용

Vue에서는 상위 컴포넌트 → 하위 컴포넌트 간 데이터 전달을 props를 이용해 처리한다. 하지만 컴포넌트 계층이 깊어지면 중간 컴포넌트들이 데이터를 단순히 전달하는 역할만 하게 되어 유지보수가 어려워진다.

이러한 문제를 해결하기 위해 Vue에서는 provide와 inject 를 사용하여 중간 컴포넌트를 거치지 않고 데이터를 주입(inject)할 수 있도록 한다.

provide 함수가 리턴하는 것은 모든 자식이 필요할 때 요청만 하면 바로 받아서 쓸 수 있다(객체 → 모든 자식이 접근 가능)

한계? 공통 조상인 경우에 작동한다

무조건 루트가 다 가지는 것보다 기능별로 구분하는게 좋다?!

상태관리 라이브러리 - Pinia


텔레포트

텔레포트 Teleport

  • 애플리케이션에서 모달, 툴팁과 같이 메인 화면과 독립적이면서 공유 UI를 제공하는 경우
  • 컴포넌트 트리의 계층 구조와 관계없이 별도의 요소에 렌더링해야 함

→ 이런 경우 사용하는 기능이 텔레포트임

(예. 팝업 띄우는 것)

profile
사용자를 고려한 디자인과 UX에 관심있는 개발자

0개의 댓글