본문으로 건너뛰기

[Medium] \U0001F3A8 다중 테마 전환 구현

면접 시나리오 문제​

Q: 페이지에 2가지 다른 스타일(예: 라이트/다크 테마)을 적용할 때, CSS를 어떻게 구성하나요?

이것은 CSS 아키텍처 설계와 실무 경험을 평가하는 질문으로, 다음을 포함합니다:

  1. CSS 아키텍처 설계
  2. 테마 전환 전략
  3. 현대적 도구 활용 (Tailwind CSS, CSS Variables)
  4. 성능과 유지보수성 고려

솔루션 개요​

방안적용 시나리오장점단점추천도
CSS Variables모던 브라우저 프로젝트동적 전환, 성능 우수IE 미지원5/5 강력 추천
Quasar + Pinia + SCSSVue 3 + Quasar 프로젝트완전한 생태계, 상태 관리, 유지보수 용이Quasar Framework 필요5/5 강력 추천
Tailwind CSS빠른 개발, 디자인 시스템개발 속도 빠름, 일관성 높음학습 곡선, HTML 장황5/5 강력 추천
CSS Class 전환구형 브라우저 호환 필요호환성 좋음CSS 용량 큼4/5 추천
CSS ModulesReact/Vue 컴포넌트 프로젝트스코프 격리빌드 도구 필요4/5 추천
Styled ComponentsReact 프로젝트CSS-in-JS, 동적 스타일Runtime 오버헤드4/5 추천
SASS/LESS 변수컴파일 시 테마 결정 필요기능 강력동적 전환 불가3/5 고려 가능
독립 CSS 파일테마 차이가 큰 경우명확한 분리로딩 오버헤드, 중복 코드2/5 비추천

방안 1: CSS Variables​

핵심 개념​

CSS 사용자 정의 속성(CSS Custom Properties)을 사용하여, 루트 요소의 class를 전환함으로써 변수 값을 변경합니다.

구현 방식​

1. 테마 변수 정의​

/* styles/themes.css */

/* 라이트 테마 (기본) */
:root {
--color-primary: #3b82f6;
--color-secondary: #8b5cf6;
--color-background: #ffffff;
--color-text: #1f2937;
--color-border: #e5e7eb;
--color-card: #f9fafb;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

/* 다크 테마 */
[data-theme='dark'] {
--color-primary: #60a5fa;
--color-secondary: #a78bfa;
--color-background: #1f2937;
--color-text: #f9fafb;
--color-border: #374151;
--color-card: #111827;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* 세 번째 테마가 있는 경우 (예: 눈 보호 모드) */
[data-theme='sepia'] {
--color-primary: #92400e;
--color-secondary: #78350f;
--color-background: #fef3c7;
--color-text: #451a03;
--color-border: #fde68a;
--color-card: #fef9e7;
--shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

2. 변수 사용​

/* components/Button.css */
.button {
background-color: var(--color-primary);
color: var(--color-text);
border: 1px solid var(--color-border);
box-shadow: var(--shadow);
transition: all 0.3s ease;
}

.card {
background-color: var(--color-card);
color: var(--color-text);
border: 1px solid var(--color-border);
}

body {
background-color: var(--color-background);
color: var(--color-text);
}

3. JavaScript 테마 전환​

// utils/theme.js

// 현재 테마 가져오기
function getCurrentTheme() {
return localStorage.getItem('theme') || 'light';
}

// 테마 설정
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}

// 테마 전환
function toggleTheme() {
const currentTheme = getCurrentTheme();
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
}

// 초기화 (localStorage에서 사용자 설정 읽기)
function initTheme() {
const savedTheme = getCurrentTheme();
setTheme(savedTheme);

// 시스템 테마 변경 감지
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
// 사용자가 설정을 하지 않은 경우, 시스템을 따름
setTheme(e.matches ? 'dark' : 'light');
}
});
}

// 페이지 로드 시 초기화
initTheme();

4. Vue 3 통합 예시​

<template>
<div>
<button @click="toggleTheme" class="theme-toggle">
<span v-if="currentTheme === 'light'">🌙 다크 모드</span>
<span v-else>☀️ 라이트 모드</span>
</button>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const currentTheme = ref('light');

function toggleTheme() {
const newTheme = currentTheme.value === 'light' ? 'dark' : 'light';
currentTheme.value = newTheme;
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
}

onMounted(() => {
const savedTheme = localStorage.getItem('theme') || 'light';
currentTheme.value = savedTheme;
document.documentElement.setAttribute('data-theme', savedTheme);
});
</script>

장점​

  • ✅ 동적 전환: CSS 파일을 다시 로드할 필요 없음
  • ✅ 성능 우수: 브라우저 네이티브 지원, 변수 값만 변경
  • ✅ 유지보수 용이: 테마를 집중 관리, 수정이 편리
  • ✅ 확장 가능: 세 번째, 네 번째 테마를 쉽게 추가

단점​

  • ❌ IE 미지원: polyfill 또는 대체 방안 필요
  • ❌ 전처리기 통합: SASS/LESS 변수와 혼합 사용 시 주의 필요

방안 2: Tailwind CSS​

핵심 개념​

Tailwind CSS의 dark: 변형과 커스텀 테마 설정을 사용하고, class 전환으로 테마를 구현합니다.

구현 방식​

1. Tailwind 설정​

// tailwind.config.js
module.exports = {
darkMode: 'class', // class 전략 사용 (media query가 아닌)
theme: {
extend: {
colors: {
// 커스텀 색상 (여러 세트의 테마 색상 정의 가능)
primary: {
light: '#3b82f6',
dark: '#60a5fa',
},
background: {
light: '#ffffff',
dark: '#1f2937',
},
text: {
light: '#1f2937',
dark: '#f9fafb',
},
},
},
},
plugins: [],
};

2. Tailwind 테마 클래스 사용​

<template>
<!-- 방식 1: dark: 변형 사용 -->
<div class="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100">
<h1 class="text-blue-600 dark:text-blue-400">제목</h1>

<button
class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700 text-white px-4 py-2 rounded"
>
버튼
</button>

<div
class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg shadow-md dark:shadow-lg"
>
<p class="text-gray-700 dark:text-gray-300">내용 텍스트</p>
</div>
</div>

<!-- 테마 전환 버튼 -->
<button @click="toggleTheme" class="fixed top-4 right-4">
<svg v-if="isDark" class="w-6 h-6">
<!-- 태양 아이콘 -->
</svg>
<svg v-else class="w-6 h-6">
<!-- 달 아이콘 -->
</svg>
</button>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const isDark = ref(false);

function toggleTheme() {
isDark.value = !isDark.value;
updateTheme();
}

function updateTheme() {
if (isDark.value) {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
}
}

onMounted(() => {
// 저장된 테마 설정 읽기
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

isDark.value = savedTheme === 'dark' || (!savedTheme && prefersDark);
updateTheme();
});
</script>

3. 고급: 다중 테마 커스터마이징 (2개 이상)​

// tailwind.config.js
module.exports = {
darkMode: 'class',
theme: {
extend: {
colors: {
theme: {
bg: 'var(--theme-bg)',
text: 'var(--theme-text)',
primary: 'var(--theme-primary)',
},
},
},
},
};
/* styles/themes.css */
:root {
--theme-bg: #ffffff;
--theme-text: #000000;
--theme-primary: #3b82f6;
}

[data-theme='dark'] {
--theme-bg: #1f2937;
--theme-text: #f9fafb;
--theme-primary: #60a5fa;
}

[data-theme='sepia'] {
--theme-bg: #fef3c7;
--theme-text: #451a03;
--theme-primary: #92400e;
}
<template>
<!-- 커스텀 테마 변수 사용 -->
<div class="bg-theme-bg text-theme-text">
<button class="bg-theme-primary">버튼</button>
</div>

<!-- 테마 선택기 -->
<select @change="setTheme($event.target.value)">
<option value="light">라이트</option>
<option value="dark">다크</option>
<option value="sepia">눈 보호</option>
</select>
</template>

<script setup>
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
</script>

Tailwind의 장점​

  • ✅ 빠른 개발: utility-first, CSS 작성 불필요
  • ✅ 일관성: 디자인 시스템 내장, 스타일 통일 유지
  • ✅ tree-shaking: 사용하지 않는 스타일 자동 제거
  • ✅ RWD 친화적: sm:, md:, lg: 반응형 변형
  • ✅ 테마 변형: dark:, hover:, focus: 등 풍부한 변형

단점​

  • ❌ HTML 장황: class가 많아 가독성에 영향 가능
  • ❌ 학습 곡선: utility class 명명에 익숙해져야 함
  • ❌ 커스터마이징: 깊은 커스터마이징에는 설정 이해 필요

방안 3: Quasar + Pinia + SCSS (최근 경험)​

실제 프로젝트 경험: 이것은 실제 프로젝트에서 사용한 방안으로, Quasar Framework, Pinia 상태 관리, SCSS 변수 시스템을 통합했습니다.

핵심 개념​

다층 아키텍처 설계 채택:

  1. Quasar Dark Mode API - 프레임워크 레벨의 테마 지원
  2. Pinia Store - 테마 상태 중앙 관리
  3. SessionStorage - 사용자 설정 영속화
  4. SCSS Variables + Mixin - 테마 변수와 스타일 관리

아키텍처 흐름​

사용자가 전환 버튼 클릭
↓
Quasar $q.dark.toggle()
↓
Pinia Store 상태 업데이트
↓
SessionStorage에 동기화
↓
Body class 전환 (.body--light / .body--dark)
↓
CSS 변수 업데이트
↓
UI 자동 업데이트

구현 방식​

1. Pinia Store (상태 관리)​

// src/stores/darkModeStore.ts
import { defineStore } from 'pinia';
import { useSessionStorage } from '@vueuse/core';

export const useDarkModeStore = defineStore('darkMode', () => {
// SessionStorage로 상태 영속화
const isDarkMode = useSessionStorage<boolean>('isDarkMode', false);

// Dark Mode 상태 업데이트
const updateIsDarkMode = (status: boolean) => {
isDarkMode.value = status;
};

return {
isDarkMode,
updateIsDarkMode,
};
});

2. Quasar 설정​

// quasar.config.js
module.exports = configure(function (/* ctx */) {
return {
framework: {
config: {
dark: 'true', // Dark Mode 지원 활성화
},
plugins: ['Notify', 'Loading', 'Dialog'],
},
};
});

3. SCSS 테마 변수 시스템​

// assets/css/_variable.scss

// Light와 Dark 두 가지 테마의 변수 매핑 정의
$themes: (
light: (
--bg-main: #ffffff,
--bg-side: #f0f1f4,
--text-primary: #000000,
--text-secondary: #666666,
--primary-color: #2d7eff,
--border-color: #e5ebf2,
),
dark: (
--bg-main: #081f2d,
--bg-side: #0d2533,
--text-primary: #ffffff,
--text-secondary: #b0b0b0,
--primary-color: #2d7eff,
--border-color: #14384d,
),
);

// Mixin: 테마에 따른 CSS 변수 적용
@mixin theme-vars($theme) {
@each $key, $value in map-get($themes, $theme) {
#{$key}: #{$value};
}
}

// Mixin: Light Mode 전용 스타일
@mixin light {
.body--light & {
@content;
}
}

// Mixin: Dark Mode 전용 스타일
@mixin dark {
.body--dark & {
@content;
}
}

4. 전역 테마 적용​

// src/css/app.scss
@import 'assets/css/_variable.scss';

// 기본값으로 Light Theme 적용
:root {
@include theme-vars('light');
}

// Dark Mode에서 Dark Theme 적용
.body--dark {
@include theme-vars('dark');
}

5. 컴포넌트에서 사용​

방식 A: CSS 변수 사용 (추천)

<template>
<div class="my-card">
<h2 class="title">제목</h2>
<p class="content">내용 텍스트</p>
</div>
</template>

<style scoped lang="scss">
.my-card {
background: var(--bg-main);
color: var(--text-primary);
border: 1px solid var(--border-color);
padding: 1rem;
}

.title {
color: var(--primary-color);
font-size: 1.5rem;
}

.content {
color: var(--text-secondary);
}
</style>

방식 B: SCSS Mixin 사용 (고급)

<template>
<button class="custom-btn">버튼</button>
</template>

<style scoped lang="scss">
@import 'assets/css/_variable.scss';

.custom-btn {
padding: 0.5rem 1rem;
border-radius: 0.5rem;
transition: all 0.3s ease;

@include light {
background: #2d7eff;
color: #ffffff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

&:hover {
background: #1a5fd9;
}
}

@include dark {
background: #1677ff;
color: #ffffff;
box-shadow: 0 2px 4px rgba(255, 255, 255, 0.1);

&:hover {
background: #0d5acc;
}
}
}
</style>

6. 전환 기능​

<template>
<button @click="toggleDarkMode" class="theme-toggle">
<q-icon :name="isDarkMode ? 'light_mode' : 'dark_mode'" />
{{ isDarkMode ? '라이트로 전환' : '다크로 전환' }}
</button>
</template>

<script setup lang="ts">
import { useQuasar } from 'quasar';
import { onMounted } from 'vue';
import { useDarkModeStore } from 'stores/darkModeStore';

const $q = useQuasar();
const { isDarkMode, updateIsDarkMode } = useDarkModeStore();

// 테마 전환
const toggleDarkMode = () => {
$q.dark.toggle(); // Quasar 전환
updateIsDarkMode($q.dark.isActive); // Store에 동기화
};

// 페이지 로드 시 사용자 설정 복원
onMounted(() => {
if (isDarkMode.value) {
$q.dark.set(true);
}
});
</script>

<style scoped lang="scss">
.theme-toggle {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: var(--primary-color);
color: var(--text-primary);
border: none;
border-radius: 0.5rem;
cursor: pointer;
transition: opacity 0.3s ease;

&:hover {
opacity: 0.8;
}
}
</style>

장점​

  • ✅ 완전한 생태계: Quasar + Pinia + VueUse 원스톱 솔루션
  • ✅ 상태 관리: Pinia 중앙 관리, 테스트와 유지보수 용이
  • ✅ 영속화: SessionStorage 자동 저장, 새로고침해도 유지
  • ✅ 타입 안전: TypeScript 지원, 오류 감소
  • ✅ 개발 경험: SCSS Mixin으로 스타일 개발 간소화
  • ✅ 성능 우수: CSS Variables 동적 업데이트, 리로드 불필요

단점​

  • ❌ 프레임워크 의존: Quasar Framework 사용 필요
  • ❌ 학습 비용: Quasar, Pinia, SCSS에 익숙해져야 함
  • ❌ 용량 큼: 완전한 프레임워크는 순수 CSS보다 무거움

모범 사례​

// composables/useTheme.ts
import { computed } from 'vue';
import { useQuasar } from 'quasar';
import { useDarkModeStore } from 'stores/darkModeStore';

export function useTheme() {
const $q = useQuasar();
const store = useDarkModeStore();

const isDark = computed(() => store.isDarkMode);

const toggleTheme = () => {
$q.dark.toggle();
store.updateIsDarkMode($q.dark.isActive);
};

const setTheme = (dark: boolean) => {
$q.dark.set(dark);
store.updateIsDarkMode(dark);
};

return {
isDark,
toggleTheme,
setTheme,
};
}

면접에서 어떻게 보여줄 것인가​

"이전 프로젝트에서 Quasar + Pinia + SCSS로 완전한 Dark Mode 시스템을 구현했습니다:

  1. 상태 관리: Pinia Store로 테마 상태를 통합 관리하고, VueUse의 useSessionStorage로 영속화 구현
  2. 스타일 시스템: SCSS의 Map + Mixin으로 테마 변수를 정의하고, :root와 .body--dark에 적용
  3. 전환 메커니즘: Quasar의 $q.dark API로 제어하며, <body>에 해당 class 자동 추가
  4. 개발 경험: @include light와 @include dark mixin을 제공하여 컴포넌트 스타일 개발을 더 직관적으로

이 방안은 프로젝트에서 안정적으로 운영되었으며, 전환이 부드럽고 상태가 안정적이며 유지보수가 용이합니다."


방안 4: CSS Class 전환​

구현 방식​

/* styles/themes.css */

/* 라이트 테마 */
body.theme-light {
background-color: #ffffff;
color: #000000;
}

body.theme-light .button {
background-color: #3b82f6;
color: #ffffff;
}

body.theme-light .card {
background-color: #f9fafb;
border: 1px solid #e5e7eb;
}

/* 다크 테마 */
body.theme-dark {
background-color: #1f2937;
color: #f9fafb;
}

body.theme-dark .button {
background-color: #60a5fa;
color: #000000;
}

body.theme-dark .card {
background-color: #111827;
border: 1px solid #374151;
}
// 테마 전환
function setTheme(theme) {
document.body.className = `theme-${theme}`;
localStorage.setItem('theme', theme);
}

적용 시나리오​

  • IE 등 구형 브라우저 지원이 필요한 경우
  • 테마 차이가 커서 변수 사용이 부적합한 경우
  • 추가 의존성을 도입하고 싶지 않은 경우

방안 5: 독립 CSS 파일 (비추천)​

구현 방식​

<!-- CSS 동적 로딩 -->
<link id="theme-stylesheet" rel="stylesheet" href="/styles/theme-light.css" />
function setTheme(theme) {
const link = document.getElementById('theme-stylesheet');
link.href = `/styles/theme-${theme}.css`;
}

단점​

  • ❌ 로딩 오버헤드: 전환 시 CSS 다시 다운로드 필요
  • ❌ FOUC: 짧은 시간 동안 스타일이 없는 깜빡임 발생 가능
  • ❌ 중복 코드: 공용 스타일을 중복 정의해야 함

RWD 반응형 디자인 통합​

Tailwind CSS + RWD + 테마 전환​

<template>
<div
class="
/* 기본 스타일 */
p-4 rounded-lg transition-colors

/* 라이트 테마 */
bg-white text-gray-900

/* 다크 테마 */
dark:bg-gray-800 dark:text-gray-100

/* RWD: 모바일 */
text-sm

/* RWD: 태블릿 이상 */
md:text-base md:p-6

/* RWD: 데스크톱 이상 */
lg:text-lg lg:p-8

/* 인터랙션 상태 */
hover:shadow-lg hover:scale-105
"
>
<h2
class="
font-bold
text-xl md:text-2xl lg:text-3xl
text-blue-600 dark:text-blue-400
"
>
반응형 제목
</h2>

<p class="mt-2 text-gray-700 dark:text-gray-300">내용 텍스트</p>

<!-- 반응형 그리드 -->
<div
class="
grid
grid-cols-1 /* 모바일: 1열 */
sm:grid-cols-2 /* 소형 태블릿: 2열 */
md:grid-cols-3 /* 태블릿: 3열 */
lg:grid-cols-4 /* 데스크톱: 4열 */
gap-4
"
>
<div
v-for="item in items"
:key="item.id"
class="
p-4 rounded
bg-gray-100 dark:bg-gray-700
hover:bg-gray-200 dark:hover:bg-gray-600
"
>
{{ item.name }}
</div>
</div>
</div>
</template>

CSS Variables + Media Queries​

/* 기본 변수 */
:root {
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--font-size-base: 16px;
}

/* 태블릿 이상에서 간격 조정 */
@media (min-width: 768px) {
:root {
--spacing-sm: 0.75rem;
--spacing-md: 1.5rem;
--spacing-lg: 2rem;
}
}

/* 데스크톱 이상에서 폰트 조정 */
@media (min-width: 1024px) {
:root {
--font-size-base: 18px;
}
}

/* 변수 사용 */
.container {
padding: var(--spacing-md);
font-size: var(--font-size-base);
}

/* 다크 테마 + RWD */
@media (min-width: 768px) {
[data-theme='dark'] {
--shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
}
}

성능 최적화 제안​

1. FOUC (Flash of Unstyled Content) 방지​

<!-- <head>에서 즉시 실행하여 깜빡임 방지 -->
<script>
(function () {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', theme);
if (theme === 'dark') {
document.documentElement.classList.add('dark');
}
})();
</script>

2. prefers-color-scheme 사용​

/* 시스템 테마 자동 감지 */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
/* 사용자가 설정하지 않은 경우, 시스템을 따름 */
--color-background: #1f2937;
--color-text: #f9fafb;
}
}
// JavaScript 감지
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (!localStorage.getItem('theme')) {
setTheme(prefersDark ? 'dark' : 'light');
}

3. CSS 애니메이션 전환​

/* 부드러운 전환 */
* {
transition: background-color 0.3s ease, color 0.3s ease,
border-color 0.3s ease;
}

/* 또는 특정 요소에 적용 */
body,
.card,
.button {
transition: all 0.3s ease;
}

4. Reflow 줄이기​

/* 너비/높이를 직접 변경하는 대신 transform 사용 */
.theme-switching {
transform: scale(1);
transition: transform 0.3s ease;
}

.theme-switching:hover {
transform: scale(1.05); /* GPU 가속 */
}

실제 프로젝트 아키텍처​

파일 구조​

src/
├── styles/
│ ├── themes/
│ │ ├── variables.css # CSS Variables 정의
│ │ ├── light.css # 라이트 테마
│ │ ├── dark.css # 다크 테마
│ │ └── sepia.css # 눈 보호 테마
│ ├── base.css # 기본 스타일
│ └── components/ # 컴포넌트 스타일
│ ├── button.css
│ └── card.css
├── utils/
│ └── theme.js # 테마 전환 로직
└── components/
└── ThemeToggle.vue # 테마 전환 컴포넌트

모범 사례​

// composables/useTheme.js (Vue 3 Composition API)
import { ref, onMounted, watch } from 'vue';

export function useTheme() {
const theme = ref('light');
const themes = ['light', 'dark', 'sepia'];

function setTheme(newTheme) {
if (!themes.includes(newTheme)) return;

theme.value = newTheme;
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);

// Tailwind dark mode
if (newTheme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}

function toggleTheme() {
const currentIndex = themes.indexOf(theme.value);
const nextIndex = (currentIndex + 1) % themes.length;
setTheme(themes[nextIndex]);
}

function initTheme() {
const savedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;

if (savedTheme) {
setTheme(savedTheme);
} else if (prefersDark) {
setTheme('dark');
}
}

onMounted(() => {
initTheme();

// 시스템 테마 변경 감지
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (e) => {
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light');
}
});
});

return {
theme,
themes,
setTheme,
toggleTheme,
};
}

면접 답변 템플릿​

면접관: 페이지에 2가지 다른 스타일을 적용할 때, CSS를 어떻게 구성하나요?

답변 방식 A: 실제 경험 보여주기 (추천)

"프로젝트의 기술 스택에 따라 가장 적합한 방안을 선택합니다. 이전 프로젝트에서 Quasar + Pinia + SCSS로 구현했습니다:

1. 상태 관리 (30초)

  • Pinia Store로 테마 상태를 통합 관리
  • VueUse의 useSessionStorage로 영속화
  • Quasar의 $q.dark API로 테마 제어

2. 스타일 시스템 (1분)

// SCSS Map으로 테마 변수 정의
$themes: (
light: (
--bg-main: #fff,
--text: #000,
),
dark: (
--bg-main: #081f2d,
--text: #fff,
),
);

// :root와 .body--dark에 적용
:root {
@include theme-vars('light');
}
.body--dark {
@include theme-vars('dark');
}
  • 컴포넌트에서 var(--bg-main) 사용으로 자동 전환
  • @include light / @include dark mixin으로 복잡한 스타일 처리

3. 전환 메커니즘 (30초)

const toggleTheme = () => {
$q.dark.toggle(); // Quasar 전환
store.updateIsDarkMode($q.dark.isActive); // Store에 동기화
};

4. 실제 성과 (30초)

  • 전환이 부드럽고 깜빡임 없음 (CSS Variables 동적 업데이트)
  • 상태 영속화 (페이지 새로고침해도 테마 유지)
  • 유지보수 용이 (테마 변수 중앙 관리)
  • 높은 개발 효율 (Mixin으로 스타일 개발 간소화)"

답변 방식 B: 범용 방안 (대안)

"현대 프로젝트에서는 CSS Variables + Tailwind CSS 사용을 추천합니다:

1. 아키텍처 설계 (30초)

  • CSS Variables로 테마 변수 정의 (색상, 간격, 그림자 등)
  • data-theme 속성으로 루트 요소의 테마 전환
  • Tailwind의 dark: 변형으로 빠른 개발

2. 구현 포인트 (1분)

:root {
--color-bg: #fff;
--color-text: #000;
}
[data-theme='dark'] {
--color-bg: #1f2937;
--color-text: #f9fafb;
}

JavaScript 전환 시 data-theme 속성만 변경하면, 브라우저가 자동으로 해당 변수를 적용합니다.

3. RWD 통합 (30초)

<div class="text-sm md:text-base lg:text-lg dark:bg-gray-800"></div>

RWD와 테마 전환을 동시에 처리할 수 있습니다.

4. 모범 사례 (30초)

  • <head>에서 테마 초기화를 즉시 실행하여 FOUC 방지
  • localStorage로 사용자 설정 저장
  • prefers-color-scheme 감지로 시스템 테마 따르기"

추가 질문​

Q1: IE를 지원해야 하는 경우 어떻게 하나요?

A: CSS Class 전환 방안을 사용하거나, css-vars-ponyfill polyfill을 사용합니다.

Q2: 테마 전환 시 깜빡임을 어떻게 방지하나요?

A: HTML <head>에서 스크립트를 즉시 실행하여, 페이지 렌더링 전에 테마를 설정합니다.

Q3: 여러 테마는 어떻게 관리하나요?

A: Design Tokens 시스템을 사용하여 모든 테마 변수를 통합 관리하고, Figma Variables와 동기화하는 것을 권장합니다.

Q4: 다른 테마를 어떻게 테스트하나요?

A: Storybook과 storybook-addon-themes를 사용하여, 모든 테마 변형을 시각적으로 테스트합니다.


관련 주제​

Reference​