클래스와 스타일 바인딩
Vue는
:class와:style에 객체와 배열을 바인딩할 수 있는 특별한 문법을 제공합니다. 조건부 스타일링을 깔끔하게 처리하는 핵심 기능입니다.
면접에서 "동적 스타일링은 어떻게 처리하나요?"라는 질문에 인라인 스타일보다 클래스 바인딩을 선호하는 이유까지 설명할 수 있으면 좋습니다.
클래스 바인딩 — 객체 문법
<script setup lang="ts">
import { ref, computed } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const errorType = ref<'warning' | 'danger'>('warning')
// computed로 복잡한 클래스 로직 분리
const classObject = computed(() => ({
active: isActive.value,
'text-danger': hasError.value,
[`alert-${errorType.value}`]: hasError.value
}))
</script>
<template>
<!-- 객체 문법 — 값이 truthy면 클래스 적용 -->
<div :class="{ active: isActive, 'text-danger': hasError }">
조건부 클래스
</div>
<!-- computed 사용 -->
<div :class="classObject">computed 클래스</div>
<!-- 정적 class와 함께 사용 가능 -->
<div class="base-card" :class="{ active: isActive }">
결과: class="base-card active"
</div>
</template>
클래스 바인딩 — 배열 문법
<script setup lang="ts">
import { ref } from 'vue'
const activeClass = ref('active')
const errorClass = ref('text-danger')
const isActive = ref(true)
</script>
<template>
<!-- 배열 문법 — 여러 클래스를 동적으로 -->
<div :class="[activeClass, errorClass]">배열 클래스</div>
<!-- 배열 안에서 삼항 연산자 -->
<div :class="[isActive ? activeClass : '', errorClass]">
조건부 배열
</div>
<!-- 배열 안에 객체 혼합 -->
<div :class="[{ active: isActive }, errorClass]">
배열 + 객체 혼합
</div>
</template>
컴포넌트의 클래스 상속
<!-- BaseButton.vue -->
<template>
<!-- 단일 루트 엘리먼트 — 부모의 class가 자동 병합 -->
<button class="btn">
<slot />
</button>
</template>
<!-- 부모 컴포넌트 -->
<template>
<!-- 결과: class="btn btn-primary" -->
<BaseButton class="btn-primary">클릭</BaseButton>
<BaseButton :class="{ 'btn-active': isActive }">동적</BaseButton>
</template>
다중 루트 엘리먼트에서는 $attrs.class로 명시적으로 지정해야 합니다.
<!-- MultiRoot.vue -->
<template>
<header>헤더</header>
<!-- $attrs.class를 원하는 엘리먼트에 적용 -->
<main :class="$attrs.class">메인</main>
<footer>푸터</footer>
</template>
인라인 스타일 바인딩
<script setup lang="ts">
import { ref, computed } from 'vue'
const fontSize = ref(16)
const primaryColor = ref('#42b883')
// 객체 문법 — camelCase 또는 kebab-case 모두 가능
const styleObject = computed(() => ({
color: primaryColor.value,
fontSize: `${fontSize.value}px`,
'background-color': '#f0f0f0' // kebab-case도 가능
}))
</script>
<template>
<!-- 객체 문법 -->
<div :style="{ color: primaryColor, fontSize: fontSize + 'px' }">
인라인 스타일
</div>
<!-- computed 사용 -->
<div :style="styleObject">computed 스타일</div>
<!-- 배열 문법 — 여러 스타일 객체 병합 -->
<div :style="[styleObject, { border: '1px solid #ccc' }]">
병합된 스타일
</div>
</template>
자동 벤더 접두사
Vue는 :style에서 벤더 접두사가 필요한 CSS 속성을 자동으로 감지하고 추가합니다.
<template>
<!-- Vue가 자동으로 -webkit-transform 등을 추가 -->
<div :style="{ transform: 'rotate(45deg)' }">
자동 벤더 접두사
</div>
<!-- 다중 값 — 브라우저가 지원하는 마지막 값을 사용 -->
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
폴백 스타일
</div>
</template>
실전 패턴 — 테마 시스템
<script setup lang="ts">
import { ref, computed } from 'vue'
type Theme = 'light' | 'dark' | 'blue'
const currentTheme = ref<Theme>('light')
// CSS 변수를 활용한 동적 테마
const themeVars = computed(() => {
const themes = {
light: { '--bg': '#ffffff', '--text': '#333333', '--primary': '#42b883' },
dark: { '--bg': '#1a1a1a', '--text': '#ffffff', '--primary': '#42d392' },
blue: { '--bg': '#e3f2fd', '--text': '#1565c0', '--primary': '#1976d2' }
}
return themes[currentTheme.value]
})
// 상태 기반 클래스
const buttonVariant = ref<'primary' | 'secondary' | 'danger'>('primary')
const buttonSize = ref<'sm' | 'md' | 'lg'>('md')
const buttonClasses = computed(() => [
'btn',
`btn-${buttonVariant.value}`,
`btn-${buttonSize.value}`
])
</script>
<template>
<div :style="themeVars" class="app-container">
<button
v-for="theme in (['light', 'dark', 'blue'] as Theme[])"
:key="theme"
:class="{ active: currentTheme === theme }"
@click="currentTheme = theme"
>
{{ theme }}
</button>
<button :class="buttonClasses">
동적 버튼
</button>
</div>
</template>
<style>
.app-container {
background-color: var(--bg);
color: var(--text);
transition: all 0.3s ease;
}
</style>
CSS Modules과 함께 사용
<script setup lang="ts">
import { ref } from 'vue'
const isActive = ref(true)
</script>
<template>
<!-- $style로 CSS Module 클래스 접근 -->
<div :class="{ [$style.active]: isActive, [$style.card]: true }">
CSS Modules
</div>
</template>
<style module>
.card {
padding: 16px;
border-radius: 8px;
}
.active {
border: 2px solid #42b883;
}
</style>
면접 팁
- 인라인 스타일보다 클래스 바인딩을 선호 하는 이유: 재사용성, 특이성(specificity) 관리, 성능(CSS는 브라우저가 최적화)
- 컴포넌트의 클래스 상속 동작(단일 루트 vs 다중 루트)을 알아두면 컴포넌트 설계 질문에 대응할 수 있습니다
- CSS 변수 +
:style바인딩은 테마 시스템 구현에 실전적인 패턴입니다
요약
Vue의 :class는 객체와 배열 문법으로 조건부 클래스를 선언적으로 처리합니다. :style은 객체로 인라인 스타일을 바인딩하며 벤더 접두사를 자동 처리합니다. 복잡한 로직은 computed로 분리하고, 가능하면 인라인 스타일보다 클래스 바인딩을 사용하세요.
댓글 로딩 중...