Vue는 :class:style에 객체와 배열을 바인딩할 수 있는 특별한 문법을 제공합니다. 조건부 스타일링을 깔끔하게 처리하는 핵심 기능입니다.

면접에서 "동적 스타일링은 어떻게 처리하나요?"라는 질문에 인라인 스타일보다 클래스 바인딩을 선호하는 이유까지 설명할 수 있으면 좋습니다.


클래스 바인딩 — 객체 문법

VUE
<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>

클래스 바인딩 — 배열 문법

VUE
<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>

컴포넌트의 클래스 상속

VUE
<!-- BaseButton.vue -->
<template>
  <!-- 단일 루트 엘리먼트 — 부모의 class가 자동 병합 -->
  <button class="btn">
    <slot />
  </button>
</template>
VUE
<!-- 부모 컴포넌트 -->
<template>
  <!-- 결과: class="btn btn-primary" -->
  <BaseButton class="btn-primary">클릭</BaseButton>
  <BaseButton :class="{ 'btn-active': isActive }">동적</BaseButton>
</template>

다중 루트 엘리먼트에서는 $attrs.class로 명시적으로 지정해야 합니다.

VUE
<!-- MultiRoot.vue -->
<template>
  <header>헤더</header>
  <!-- $attrs.class를 원하는 엘리먼트에 적용 -->
  <main :class="$attrs.class">메인</main>
  <footer>푸터</footer>
</template>

인라인 스타일 바인딩

VUE
<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 속성을 자동으로 감지하고 추가합니다.

VUE
<template>
  <!-- Vue가 자동으로 -webkit-transform 등을 추가 -->
  <div :style="{ transform: 'rotate(45deg)' }">
    자동 벤더 접두사
  </div>

  <!-- 다중 값 — 브라우저가 지원하는 마지막 값을 사용 -->
  <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">
    폴백 스타일
  </div>
</template>

실전 패턴 — 테마 시스템

VUE
<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과 함께 사용

VUE
<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로 분리하고, 가능하면 인라인 스타일보다 클래스 바인딩을 사용하세요.

댓글 로딩 중...