TIP 왼쪽 메뉴에서 항목을 탭하면 바로 이동합니다. 검색창에 키워드를 입력하면 메뉴가 필터링됩니다.

애플리케이션 생성

Vue3

Vue3 앱의 진입점. createApp으로 루트 인스턴스를 만들고 마운트한다.

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
app.mount('#app')
Vue2와 차이: Vue2는 new Vue({...}), Vue3는 createApp() 팩토리 함수를 사용. 전역 설정(컴포넌트, 디렉티브)도 app 인스턴스에 등록.

템플릿 문법

Vue3

데이터 바인딩과 디렉티브의 기본 형태.

<!-- 텍스트 보간 -->
<p>{{ message }}</p>

<!-- 속성 바인딩 -->
<img :src="imageUrl" />

<!-- 이벤트 바인딩 -->
<button @click="increment">+1</button>

<!-- 양방향 바인딩 -->
<input v-model="name" />
축약 표기전체 표기
:srcv-bind:src
@clickv-on:click
#defaultv-slot:default

ref / reactive

Vue3면접 빈출

Composition API의 반응형 상태 선언 방식. ref는 원시값+객체 모두 가능, reactive는 객체 전용.

import { ref, reactive } from 'vue'

// ref: .value로 접근 (스크립트 내부에서)
const count = ref(0)
count.value++

// reactive: 객체 자체가 반응형 (proxy)
const state = reactive({ name: 'Eisen', age: 40 })
state.age++
핵심 차이: ref는 내부적으로 .value를 통해 getter/setter를 거치며, 템플릿에서는 자동 언래핑된다. reactive는 구조분해 시 반응성을 잃는다 (이때 toRefs 사용).

computed

Vue3

의존 데이터가 바뀔 때만 재계산되는 캐시된 값.

import { ref, computed } from 'vue'

const firstName = ref('길동')
const lastName = ref('홍')

const fullName = computed(() => `${lastName.value}${firstName.value}`)
주의: computed 내부에서 비동기 작업이나 부수효과(side effect)를 일으키지 말 것. 순수 함수처럼 사용.

watch / watchEffect

Vue3

반응형 데이터 변화를 감지해 부수효과(API 호출, DOM 조작 등)를 실행.

import { ref, watch, watchEffect } from 'vue'

const keyword = ref('')

// 특정 소스를 명시적으로 감시
watch(keyword, (newVal, oldVal) => {
  console.log(`${oldVal} → ${newVal}`)
})

// 사용된 반응형 값을 자동 추적
watchEffect(() => {
  console.log('현재 검색어:', keyword.value)
})
구분watchwatchEffect
추적 대상명시적으로 지정콜백 내 사용된 값 자동 추적
초기 실행X (기본값)O (즉시 실행)
이전 값 접근가능불가

v-model

Vue3

폼 입력과 상태의 양방향 바인딩. 내부적으로 :value + @input 조합.

<input v-model="text" />
<!-- 위는 아래와 동일 -->
<input :value="text" @input="text = $event.target.value" />

<!-- 수식어 -->
<input v-model.trim="text" />   // 공백 제거
<input v-model.number="age" />   // 숫자로 변환

v-if / v-for

Vue3면접 빈출

조건부 렌더링과 리스트 렌더링.

<p v-if="score >= 90">A</p>
<p v-else-if="score >= 80">B</p>
<p v-else>F</p>

<li v-for="item in items" :key="item.id">
  {{ item.name }}
</li>
금지: v-ifv-for를 같은 태그에 함께 쓰지 말 것 (Vue3는 v-if가 우선 적용되어 의도와 다르게 동작). 부모에 v-for, 자식에 v-if를 분리하거나 <template> 래핑.
:key 필수: v-for에는 항상 고유한 key를 부여해야 가상 DOM diff가 올바르게 동작한다. index를 key로 쓰면 리스트 순서 변경 시 버그 발생 가능.

props (부모→자식 데이터 전달)

Vue3
// 자식 컴포넌트 (Child.vue)
<script setup>
const props = defineProps({
  title: { type: String, required: true },
  count: { type: Number, default: 0 }
})
</script>

<!-- 부모 컴포넌트 -->
<Child :title="'안녕'" :count="5" />
주의: props는 자식에서 직접 변경 불가 (단방향 데이터 흐름). 변경이 필요하면 로컬 ref에 복사하거나 emit으로 부모에 알린다.

emit (자식→부모 이벤트)

Vue3
// 자식 컴포넌트
<script setup>
const emit = defineEmits(['update', 'delete'])

function handleClick() {
  emit('update', { id: 1, value: 'new' })
}
</script>

<!-- 부모 -->
<Child @update="onUpdate" />

slots

Vue3

부모가 자식 컴포넌트 내부에 마크업/콘텐츠를 주입할 수 있게 하는 기능.

<!-- Card.vue (자식) -->
<div class="card">
  <slot name="header"></slot>
  <slot></slot> <!-- 기본 슬롯 -->
</div>

<!-- 사용처 -->
<Card>
  <template #header><h3>제목</h3></template>
  <p>본문 내용</p>
</Card>

생명주기 훅

Vue3면접 빈출
import { onMounted, onUpdated, onUnmounted } from 'vue'

onMounted(() => {
  // DOM이 화면에 붙은 직후 — API 호출, DOM 접근에 적합
})
onUpdated(() => { /* 반응형 데이터로 인해 DOM 갱신 후 */ })
onUnmounted(() => { /* 정리 작업 — 타이머/이벤트리스너 해제 */ })
시점
onBeforeMount마운트 직전
onMountedDOM 부착 완료
onBeforeUpdate리렌더 직전
onUpdated리렌더 완료
onBeforeUnmount제거 직전
onUnmounted제거 완료

컴포저블 (재사용 로직)

Vue3

Composition API 함수를 모듈화해 여러 컴포넌트에서 재사용. use 접두사 관례.

// composables/useCounter.js
import { ref } from 'vue'

export function useCounter(initial = 0) {
  const count = ref(initial)
  const increment = () => count.value++
  return { count, increment }
}

// 컴포넌트에서 사용
import { useCounter } from '@/composables/useCounter'
const { count, increment } = useCounter(10)

Vue Router

Vue3
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  { path: '/', component: Home },
  { path: '/user/:id', component: UserView, props: true }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// 네비게이션 가드
router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isLoggedIn()) {
    return '/login'
  }
})
<!-- 템플릿에서 -->
<router-link to="/about">소개</router-link>
<router-view />

Pinia (상태관리)

Vue3Vuex 대체
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    doubled: (state) => state.count * 2
  },
  actions: {
    increment() { this.count++ }
  }
})

// 컴포넌트에서
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
store.increment()
Vuex 대비 장점: 보일러플레이트 감소(mutations 불필요), TypeScript 친화적, devtools 통합, Composition API 스타일 작성(defineStore setup 함수형) 가능.

axios 연동 (Spring Boot API 호출)

Vue3
// api/client.js
import axios from 'axios'

const api = axios.create({
  baseURL: '/api',
  timeout: 5000
})

// 요청 인터셉터 — JWT 토큰 자동 첨부
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token')
  if (token) config.headers.Authorization = `Bearer ${token}`
  return config
})

export default api
// 컴포넌트에서 사용 (async/await + try-catch)
import { ref, onMounted } from 'vue'
import api from '@/api/client'

const users = ref([])

onMounted(async () => {
  try {
    const res = await api.get('/users')
    users.value = res.data
  } catch (e) {
    console.error('조회 실패', e)
  }
})

기본 문서 구조

HTML
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>페이지 제목</title>
</head>
<body>
  ...
</body>
</html>
DOCTYPE: 브라우저에게 표준 모드(standards mode)로 렌더링하라고 알리는 선언. 생략 시 quirks mode로 동작해 레이아웃이 깨질 수 있음.

시맨틱 태그

HTML면접 빈출
태그의미
<header>머리글 영역 (로고, 네비게이션)
<nav>주요 네비게이션 링크 모음
<main>문서의 핵심 콘텐츠 (페이지당 1개)
<section>주제별로 묶인 콘텐츠 그룹
<article>독립적으로 배포 가능한 콘텐츠 (게시글, 카드)
<aside>본문과 간접적으로 연관된 사이드 콘텐츠
<footer>바닥글 영역
<figure> / <figcaption>이미지·도표와 그 설명 캡션 묶음
<time>날짜/시간 (datetime 속성으로 기계 판독 가능 형식 제공)
<mark>하이라이트(강조 표시) 텍스트
<details> / <summary>JS 없이 열고 닫는 아코디언/접이식 콘텐츠
<dialog>네이티브 모달 다이얼로그 (showModal() 지원)
<details>
  <summary>자세히 보기</summary>
  <p>JS 없이 펼쳐지는 내용</p>
</details>
왜 중요한가: 접근성(스크린리더), SEO, 코드 가독성 향상. <div> 남용보다 의미 있는 태그 사용이 면접에서도 자주 언급됨.

텍스트 태그

HTML
<h1>~<h6>          // 제목 (문서당 h1 1개 권장, 계층 건너뛰지 말 것)
<p>문단</p>
<strong>중요(굵게+의미)</strong>
<em>강조(기울임+의미)</em>
<br>               // 줄바꿈
<hr>               // 수평 구분선
<span>인라인 묶음</span>
<blockquote cite="출처url">인용문</blockquote>
<code>코드 조각</code>
<pre>공백/줄바꿈 유지 텍스트</pre>
b/i vs strong/em: <b>, <i>는 시각적 스타일만 의미하지만 <strong>, <em>은 의미(중요도/강조)를 함께 전달해 스크린리더가 다르게 읽는다. 의미가 있다면 strong/em 사용.

목록 태그

HTML
<!-- 순서 없는 목록 -->
<ul>
  <li>항목1</li>
  <li>항목2</li>
</ul>

<!-- 순서 있는 목록 -->
<ol start="3">
  <li>3번부터 시작</li>
</ol>

<!-- 정의 목록 -->
<dl>
  <dt>HTML</dt>
  <dd>웹 문서 마크업 언어</dd>
</dl>

테이블

HTML
<table>
  <caption>월별 매출</caption>
  <thead>
    <tr><th scope="col"></th><th scope="col">매출</th></tr>
  </thead>
  <tbody>
    <tr><td>1월</td><td>1,200</td></tr>
    <tr><td colspan="2">합계 행</td></tr>
  </tbody>
  <tfoot>
    <tr><td>총계</td><td>1,200</td></tr>
  </tfoot>
</table>
colspan / rowspan: 셀 병합 시 사용. scope="col"|"row"는 헤더 셀이 어느 축을 설명하는지 명시해 접근성을 높인다.

링크 / 이미지

HTML
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  새 탭에서 열기
</a>

<img src="photo.jpg" alt="제품 사진 설명" width="400" loading="lazy" />

<!-- 반응형 이미지: 화면 크기별 다른 이미지 제공 -->
<picture>
  <source media="(max-width: 600px)" srcset="small.jpg">
  <img src="large.jpg" alt="설명">
</picture>
target="_blank" 보안 주의: 반드시 rel="noopener noreferrer"를 함께 써야 한다. 없으면 새로 열린 페이지가 window.opener를 통해 원본 페이지를 조작할 수 있는 보안 취약점(탭내빙) 발생.
alt 속성: 이미지가 로드되지 않거나 스크린리더 사용 시 대체 텍스트. 장식용 이미지는 alt=""로 비워 스크린리더가 건너뛰게 한다.

오디오 / 비디오

HTML
<video controls poster="thumb.jpg" width="640">
  <source src="movie.mp4" type="video/mp4">
  브라우저가 비디오를 지원하지 않습니다.
</video>

<audio controls autoplay loop muted>
  <source src="song.mp3" type="audio/mpeg">
</audio>
autoplay 정책: 대부분 브라우저는 muted 없이 autoplay를 차단한다. 소리 있는 자동재생이 필요하면 사용자 상호작용 이후 JS로 play() 호출.

폼 기본 구조

HTML
<form @submit.prevent="onSubmit">
  <fieldset>
    <legend>회원 정보</legend>

    <label for="email">이메일</label>
    <input id="email" type="email" v-model="email" required />

    <select v-model="role">
      <optgroup label="내부">
        <option value="admin">관리자</option>
      </optgroup>
    </select>

    <textarea v-model="memo" rows="4"></textarea>
  </fieldset>

  <button type="submit">저장</button>
  <button type="reset">초기화</button>
</form>
fieldset/legend: 관련 폼 요소를 묶고 그룹 제목을 제공해 접근성과 가독성을 높인다. 회원가입처럼 섹션이 나뉘는 폼에서 자주 사용.

input type 전체

HTML실무 필수
type용도
text일반 한 줄 텍스트
password입력값 마스킹 처리
email이메일 형식 기본 검증 + 모바일 키보드 자동 전환
number숫자 입력, min/max/step 사용 가능
tel전화번호 (형식 검증은 없으나 모바일 숫자 키패드 표시)
date / time / datetime-local네이티브 날짜/시간 선택기
checkbox다중 선택
radio단일 선택 (같은 name 그룹)
file파일 업로드 (multiple, accept 속성 조합)
range슬라이더
color색상 선택기
search검색창 (모바일에서 검색 아이콘 키보드)
hidden화면에 안 보이지만 폼 전송에 포함되는 값
<input type="file" accept="image/*" multiple @change="onFileSelect" />
<input type="number" min="0" max="100" step="5" />

폼 검증 속성

HTML
<input
  type="text"
  required
  minlength="2" maxlength="20"
  pattern="[A-Za-z0-9]+"
  placeholder="영문/숫자만 입력"
/>
속성용도
required필수 입력 (빈 값이면 제출 차단 + 브라우저 기본 메시지)
pattern정규식 기반 형식 검증
min / max숫자·날짜 범위 제한
minlength / maxlength문자 길이 제한
readonly값 수정 불가하지만 폼 전송에는 포함됨
disabled비활성화, 폼 전송에서 제외됨
readonly vs disabled: 면접에서 자주 묻는 차이. disabled는 값이 서버로 전송되지 않고 탭 포커스도 받지 않는다. readonly는 값을 못 바꾸지만 전송되고 포커스도 가능하다.

전역/중요 속성

HTML
속성용도
id문서 내 고유 식별자 (CSS/JS 단일 대상)
class스타일/스크립트용 그룹 식별자 (다수 적용 가능)
data-*사용자 정의 데이터 속성 (JS에서 dataset으로 접근)
aria-*접근성(스크린리더용) 속성
tabindex키보드 탭 이동 순서 제어 (0=자연순서, -1=탭 제외, 양수=비권장)
loading="lazy"이미지/iframe 지연 로딩
contenteditable요소를 직접 편집 가능하게 만듦
draggable드래그 앤 드롭 활성화
hidden요소를 렌더링에서 제외 (display:none과 유사)
title마우스 hover 시 툴팁 텍스트
<div data-user-id="42" data-role="admin"></div>
// JS: el.dataset.userId === '42'

메타 태그

HTML
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="페이지 설명 (검색결과 노출)">

<!-- 카카오톡/SNS 공유 미리보기 (Open Graph) -->
<meta property="og:title" content="제목">
<meta property="og:image" content="thumb.jpg">

<link rel="icon" href="/favicon.ico">

iframe / embed

HTML
<iframe
  src="https://example.com"
  width="600" height="400"
  loading="lazy"
  sandbox="allow-scripts allow-same-origin"
></iframe>
sandbox 속성: iframe 안 콘텐츠의 권한을 제한해 보안을 강화. 값을 비우면 모든 권한 차단, 필요한 권한만 명시적으로 허용하는 것이 안전.

웹 접근성 (ARIA)

HTML실무에서 점점 중요해짐
<!-- 버튼처럼 보이는 div에 역할/상태 부여 -->
<div role="button" tabindex="0" aria-pressed="false" @click="toggle">
  토글
</div>

<!-- 스크린리더 전용 라벨 (시각적으로는 숨김) -->
<button aria-label="닫기"></button>

<!-- 동적으로 갱신되는 영역을 스크린리더에 알림 -->
<div aria-live="polite">{{ statusMessage }}</div>
속성용도
role요소의 의미적 역할 명시 (button, dialog, alert 등)
aria-label화면에 보이지 않는 접근성 라벨 제공
aria-hidden="true"장식용 요소를 스크린리더에서 숨김
aria-livepolite(대기 후 알림)/assertive(즉시 알림) — 동적 콘텐츠 변경 안내
aria-expanded아코디언/드롭다운의 펼침 상태
우선순위: 가능하면 시맨틱 태그(button, nav 등)를 먼저 쓰고, 부득이하게 div/span으로 인터랙션을 만들 때만 ARIA로 보완한다 (No ARIA is better than Bad ARIA).

SVG 기초

HTML
<svg width="100" height="100" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="40" fill="#42b883" />
  <rect x="10" y="10" width="30" height="30" />
</svg>
벡터 그래픽: 확대해도 깨지지 않아 아이콘/로고에 적합. CSS로 색상(fill/stroke) 제어가 가능해 다크모드 대응이 쉽다.

template / slot 태그

HTMLVue 연관
<!-- 네이티브 HTML template: 렌더링되지 않고 JS로 복제해 사용 -->
<template id="row-tpl">
  <tr><td></td></tr>
</template>
Vue와의 관계: Vue 컴포넌트의 <template> 블록은 이 네이티브 태그를 차용한 것으로, 실제 DOM에는 렌더링되지 않고 컴파일 단계에서 가상 DOM으로 변환된다.

기본 문법 / 적용 방식

CSS
/* 선택자 { 속성: 값; } */
.title {
  color: #333;
  font-size: 18px;
}
적용 방식예시특징
외부 스타일시트<link rel="stylesheet" href="style.css">유지보수 용이, 캐싱됨 (권장)
내부 스타일시트<style>...</style>해당 문서에만 적용
인라인 스타일<div style="color:red">명시도 가장 높음, 재사용 불가 (지양)

선택자 전체

CSS면접 빈출
/* 기본 선택자 */
* { }                  /* 전체 선택자 */
p { }                  /* 태그 선택자 */
.card { }              /* 클래스 선택자 */
#header { }            /* id 선택자 */

/* 결합자 */
.card p { }            /* 후손 선택자 (자손 전체) */
.card > p { }          /* 자식 선택자 (직계만) */
h2 + p { }            /* 인접 형제 (바로 다음) */
h2 ~ p { }            /* 일반 형제 (이후 전체) */

/* 속성 선택자 */
input[type="text"] { }
a[href^="https"] { }   /* https로 시작 */
a[href$=".pdf"] { }    /* .pdf로 끝남 */
a[href*="naver"] { }   /* naver 포함 */

/* 그룹 선택자 */
h1, h2, h3 { margin: 0; }

우선순위 (명시도)

CSS면접 빈출

동일 요소에 여러 규칙이 충돌할 때 적용 순서를 결정하는 점수 체계.

우선순위점수예시
1 (최고)!importantcolor: red !important;
2인라인 스타일style="color:red"
3id 선택자#header (0,1,0,0)
4class/속성/가상클래스.card, [type], :hover (0,0,1,0)
5 (최저)태그/가상요소div, ::before (0,0,0,1)
!important 지양: 디버깅을 어렵게 만드는 주요 원인. 가급적 선택자 구조나 CSS 작성 순서로 우선순위를 해결하고, 라이브러리 스타일 강제 override 같은 예외적 상황에만 사용.

박스 모델

CSS면접 빈출
.box {
  box-sizing: border-box; /* width에 padding+border 포함 */
  width: 200px;
  padding: 16px;
  border: 1px solid #ccc;
  margin: 8px;
}
content-box vs border-box: 기본값 content-box는 width가 콘텐츠 영역만 의미해 padding/border가 추가되어 실제 크기가 커짐. 실무에서는 거의 항상 border-box로 전역 설정.
/* 실무 관행: 전역 리셋 */
*, *::before, *::after { box-sizing: border-box; }
margin 상쇄(collapsing): 인접한 블록 요소의 위/아래 margin은 더 큰 값 하나로 합쳐진다(겹쳐짐). flex/grid 컨테이너 내부 아이템에는 발생하지 않는다.

display 속성

CSS면접 빈출
특징
block줄 전체 차지, width/height 적용 가능 (div, p, section)
inline내용 크기만 차지, width/height 무시됨 (span, a)
inline-block인라인처럼 배치되지만 width/height 적용 가능
none렌더링에서 완전히 제거 (공간도 차지 안 함)
flex플렉스 컨테이너 — 1차원 레이아웃
grid그리드 컨테이너 — 2차원 레이아웃
inline-flex / inline-grid컨테이너 자체는 인라인처럼 배치
visibility:hidden vs display:none: 면접 단골. visibility:hidden은 안 보이지만 공간은 차지하고 이벤트도 안 받음. display:none은 레이아웃에서 완전히 제거됨. opacity:0은 보이지 않지만 공간 유지 + 클릭 이벤트는 여전히 발생.

Flexbox

CSS실무 필수
.container {
  display: flex;
  flex-direction: row;             /* row | column | row-reverse | column-reverse */
  justify-content: space-between;  /* 주축 정렬 */
  align-items: center;             /* 교차축 정렬 */
  flex-wrap: wrap;                 /* 줄바꿈 허용 */
  gap: 12px;
}
.item {
  flex: 1;          /* flex-grow flex-shrink flex-basis 축약 */
  flex-shrink: 0;   /* 줄어들지 않음 */
  order: 2;         /* 시각적 순서 변경 (DOM 순서는 유지) */
  align-self: flex-end; /* 개별 아이템만 교차축 정렬 재정의 */
}
속성값 예시
justify-contentflex-start / center / space-between / space-around / space-evenly
align-itemsstretch / center / flex-start / flex-end / baseline
flex-directionrow / column / row-reverse
flex: 1 의 의미: flex-grow:1; flex-shrink:1; flex-basis:0%;의 축약. 컨테이너의 남는 공간을 균등하게 나눠 가지라는 뜻.

Grid

CSS
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;     /* 사이드바 + 본문 */
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "sidebar header"
    "sidebar content"
    "sidebar footer";
  gap: 16px;
}
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }

/* 반응형 카드 그리드 (자동 줄바꿈) */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}
Flexbox vs Grid: Flexbox는 1차원(행 또는 열) 정렬에 적합, Grid는 2차원(행+열) 레이아웃 설계에 적합. 페이지 전체 레이아웃은 Grid, 내부 컴포넌트 정렬은 Flexbox가 일반적 조합.
repeat(auto-fit, minmax()): 컨테이너 너비에 따라 컬럼 수가 자동으로 조절되는 반응형 그리드의 핵심 패턴. 태블릿 가로/세로 전환 시 매우 유용.

Position

CSS면접 빈출
동작
static기본값, 문서 흐름대로 배치 (top/left 등 무시)
relative자기 원래 위치 기준 이동 (공간 유지)
absolute가장 가까운 position:static이 아닌 부모 기준
fixed뷰포트 기준 고정 (스크롤해도 그대로)
sticky스크롤 중 임계점까지 relative, 이후 fixed처럼 고정
.modal-wrap { position: relative; }   /* 기준점 생성 */
.modal-close { position: absolute; top: 12px; right: 12px; }

.table-header { position: sticky; top: 0; } /* 스크롤시 헤더 고정 */
absolute 기준점: 부모 중 position이 static이 아닌(relative/absolute/fixed) 가장 가까운 요소를 찾는다. 없으면 body(뷰포트) 기준으로 동작 — 의도치 않은 위치 버그의 흔한 원인.

Float / Clear (레거시)

CSS
.thumbnail { float: left; margin-right: 12px; }
.clearfix::after { content: ""; display: block; clear: both; }
현재 위치: 과거엔 레이아웃 전체를 float로 잡았지만 현재는 Flexbox/Grid로 대체됨. 다만 텍스트가 이미지를 감싸는 "위키백과 스타일" 레이아웃에는 여전히 float가 쓰인다. 면접에서 "float를 언제 쓰나요" 질문에는 이 용도로 답하면 좋다.

단위 (rem / % / vw)

CSS
단위기준
px절대 픽셀
rem루트(html) font-size 기준 — 반응형 디자인 권장
em부모 요소 font-size 기준 (중첩 시 누적됨에 주의)
%부모 요소 크기 기준
vw / vh뷰포트 너비/높이의 1%
vmin / vmax뷰포트 가로/세로 중 작은(큰) 쪽 기준 1%
frGrid 전용, 남는 공간을 비율로 분배
실무 팁: 폰트 크기는 rem, 컴포넌트 내부 간격은 px 또는 rem, 전체 레이아웃 너비는 % 또는 fr을 섞어 쓰는 경우가 많다. 갤럭시탭처럼 화면이 다양한 환경에서는 vw 단독 사용보다 clamp() 조합을 권장.

색상 표기법

CSS
.box {
  color: red;                       /* 키워드 */
  color: #42b883;                   /* HEX */
  color: rgb(66, 184, 131);         /* RGB */
  color: rgba(66, 184, 131, 0.5);   /* RGB + 투명도 */
  color: hsl(152, 47%, 49%);        /* 색상/채도/명도 */
  color: hsl(152 47% 49% / 0.5);    /* HSL + 투명도 (최신 표기) */
}
HSL의 장점: 색상(hue)을 고정하고 명도/채도만 바꿔 톤온톤 팔레트를 만들기 쉬워 디자인 시스템 구축에 유리.

텍스트 / 폰트

CSS
.text {
  font-family: 'Noto Sans KR', sans-serif;
  font-size: 16px;
  font-weight: 700;        /* 100~900, normal=400, bold=700 */
  line-height: 1.6;        /* 단위 없는 값 = font-size의 배수 (권장) */
  letter-spacing: -0.02em;
  text-align: center;
  text-decoration: underline;
  text-transform: uppercase;
  white-space: nowrap;     /* 줄바꿈 방지 */
  overflow: hidden;
  text-overflow: ellipsis; /* 말줄임표(...) — white-space/overflow와 세트 */
}
말줄임표 3종 세트: white-space:nowrap; overflow:hidden; text-overflow:ellipsis; 세 줄이 항상 함께 다닌다. 카드 제목, 목록 텍스트 자르기에 실무에서 매우 자주 사용.
/* 여러 줄 말줄임 (line-clamp) */
.desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

배경 (background)

CSS
.hero {
  background-color: #1b1f2a;
  background-image: url('bg.jpg');
  background-size: cover;       /* 비율 유지하며 영역 꽉 채움 */
  background-position: center;
  background-repeat: no-repeat;

  /* 축약형 */
  background: #1b1f2a url('bg.jpg') no-repeat center / cover;
}
background-size동작
cover비율 유지하며 영역을 꽉 채움 (일부 잘릴 수 있음)
contain비율 유지하며 영역 안에 전부 보이게 (여백 생길 수 있음)
100% 100%비율 무시하고 영역에 강제로 맞춤

테두리 / border-radius

CSS
.card {
  border: 1px solid #2a2f3d;
  border-radius: 12px;
  border-top: 3px solid #42b883;   /* 한쪽 면만 다르게 */
}
.avatar { border-radius: 50%; }   /* 원형 */
.pill { border-radius: 999px; }   /* 알약 모양 */

box-shadow / filter

CSS
/* box-shadow: x오프셋 y오프셋 blur spread color */
.card { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }

/* 안쪽 그림자 */
.inset { box-shadow: inset 0 0 8px rgba(0,0,0,0.3); }

/* filter: 이미지/요소 시각효과 */
.thumb { filter: blur(4px) grayscale(50%) brightness(1.1); }
.backdrop { backdrop-filter: blur(10px); }  /* 뒤 배경에 적용 (유리효과) */

overflow / 스크롤

CSS
.scroll-box {
  overflow: auto;        /* 넘칠 때만 스크롤바 */
  overflow-x: hidden;
  overflow-y: scroll;    /* 항상 스크롤바 표시 */
  -webkit-overflow-scrolling: touch;  /* iOS 관성 스크롤 */
}
동작
visible기본값, 넘친 콘텐츠가 영역 밖으로 그대로 보임
hidden넘친 부분 잘라서 숨김
scroll항상 스크롤바 표시
auto필요할 때만 스크롤바 표시 (실무에서 가장 흔히 사용)

z-index / 쌓임 맥락

CSS면접 빈출
.modal { position: fixed; z-index: 1000; }
.dropdown { position: absolute; z-index: 10; }
z-index 함정: z-index는 position이 static이 아닌 요소에만 적용된다. 또한 부모가 새로운 "쌓임 맥락(stacking context)"을 만들면(예: transform, opacity<1, filter 적용) 자식의 z-index가 아무리 커도 부모 바깥의 다른 요소를 절대 넘어설 수 없다 — 실무에서 모달이 다른 요소 뒤에 숨는 버그의 단골 원인.

transform

CSS
.box {
  transform: translate(10px, 20px);  /* 이동 */
  transform: scale(1.1);             /* 확대/축소 */
  transform: rotate(45deg);          /* 회전 */
  transform: skew(10deg);            /* 기울이기 */

  /* 여러 변형 조합 (공백으로 구분, 순서대로 적용) */
  transform: translateX(-50%) rotate(10deg);
}

/* 정중앙 배치 패턴 (width/height 모를 때) */
.center {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
}
성능 이점: transformopacity는 GPU 가속을 받아 레이아웃 재계산(reflow) 없이 동작하므로, top/left로 애니메이션하는 것보다 훨씬 부드럽고 성능이 좋다.

transition

CSS실무 필수
.btn {
  background: #2a2f3d;
  transition: background 0.2s ease, transform 0.2s ease;
}
.btn:hover {
  background: #42b883;
  transform: translateY(-2px);
}

/* 축약형: property duration timing-function delay */
.fade { transition: all 0.3s ease-in-out 0.1s; }
timing-function느낌
ease기본값, 천천히 시작-빠르게-천천히 끝
linear일정한 속도
ease-in천천히 시작해서 가속
ease-out빠르게 시작해서 감속 (UI 등장 효과에 자주 사용)

animation / @keyframes

CSS
@keyframes spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
.spinner {
  animation: spin 1s linear infinite;
  /* 축약: name duration timing-function delay iteration-count direction fill-mode */
}

@keyframes fadeInUp {
  0%   { opacity: 0; transform: translateY(10px); }
  100% { opacity: 1; transform: translateY(0); }
}
.card { animation: fadeInUp 0.4s ease-out forwards; }
transition vs animation: transition은 상태 변화(hover 등) 시 한 번 보간하는 용도, animation은 @keyframes로 여러 단계를 정의하고 반복(infinite)도 가능한 좀 더 강력한 기능.

그라데이션

CSS
.linear { background: linear-gradient(135deg, #5b8def, #42b883); }
.radial { background: radial-gradient(circle, #fff, #ccc); }

/* 텍스트에 그라데이션 적용 */
.gradient-text {
  background: linear-gradient(90deg, #5b8def, #42b883);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

가상 클래스 / 가상 요소

CSS면접 빈출
/* 가상 클래스(:) — 요소의 특정 '상태' */
.btn:hover { }       /* 마우스 올림 */
.btn:active { }      /* 클릭 중 */
.btn:focus { }       /* 포커스됨 */
input:disabled { }
input:checked { }
li:first-child { }
li:last-child { }
li:nth-child(2n) { }     /* 짝수번째 */
li:nth-child(odd) { }    /* 홀수번째 */
p:not(.special) { }      /* 제외 */

/* 가상 요소(::) — 요소 안에 '존재하지 않던 콘텐츠'를 생성 */
.box::before { content: "★ "; }
.box::after { content: ""; display: block; clear: both; }
.box::placeholder { color: #999; }
.box::selection { background: yellow; }
콜론 개수의 의미: 표준상 가상 클래스는 콜론 1개(:hover), 가상 요소는 콜론 2개(::before)로 구분한다 (단, 브라우저 호환을 위해 :before 1개도 동작은 함). 면접에서 "차이가 뭐냐"고 물으면 "클래스는 상태, 요소는 새 콘텐츠 생성"이라고 답하면 된다.

미디어 쿼리 (반응형)

CSS
/* 태블릿 이하 */
@media (max-width: 880px) {
  .sidebar { display: none; }
}

/* 갤럭시 탭 가로/세로 전환 대응 */
@media (orientation: portrait) { }
@media (orientation: landscape) { }

/* 범위 동시 지정 */
@media (min-width: 600px) and (max-width: 1100px) {
  .grid { grid-template-columns: repeat(2, 1fr); }
}

/* 다크모드 감지 */
@media (prefers-color-scheme: dark) { }

/* 호버 가능 여부 (터치 기기 구분에 유용) */
@media (hover: hover) { .btn:hover { } }
모바일 우선(Mobile First): min-width 기준으로 작은 화면부터 스타일을 작성하고 큰 화면에서 추가/override하는 방식이 실무 표준. max-width 기준(데스크톱 우선)도 가능하지만 우선순위 충돌을 줄이려면 한 방향으로 통일하는 것이 좋다.

컨테이너 쿼리

CSS최신 기능

뷰포트가 아닌 부모 컨테이너의 크기를 기준으로 스타일을 바꾸는 최신 CSS 기능. 사이드바 유무에 따라 같은 카드 컴포넌트가 다르게 보여야 할 때 유용.

.card-wrap { container-type: inline-size; container-name: card; }

@container card (min-width: 400px) {
  .card { display: flex; flex-direction: row; }
}
미디어 쿼리와 차이: 미디어 쿼리는 전체 화면 크기 기준, 컨테이너 쿼리는 컴포넌트가 속한 부모 박스 크기 기준이라 재사용 가능한 컴포넌트 설계에 더 적합하다. Vue 컴포넌트 단위 개발과 궁합이 좋음.

CSS 변수 (Custom Properties)

CSS
:root {
  --primary-color: #42b883;
  --spacing-md: 16px;
}
.btn {
  background: var(--primary-color);
  padding: var(--spacing-md);
  color: var(--text-color, #333);  /* 두 번째 인자: fallback 값 */
}

/* 다크모드 전환 예시 */
[data-theme="dark"] {
  --primary-color: #5b8def;
}
장점: 다크모드 전환, 테마 시스템, 디자인 토큰 관리에 유용. Sass 변수와 달리 런타임에 JS로 동적 변경 가능 (element.style.setProperty('--primary-color', '#000')).

최신 기능 (:has, clamp 등)

CSS최신 기능
/* :has() — 부모 선택자 (자식 상태에 따라 부모 스타일 변경) */
.form-group:has(input:invalid) { border-color: red; }
.card:has(img) { padding-top: 0; }

/* clamp(최소, 권장, 최대) — 반응형 값 자동 계산 */
h1 { font-size: clamp(20px, 4vw, 36px); }

/* min() / max() */
.container { width: min(90%, 1200px); }

/* aspect-ratio — 비율 고정 (썸네일 등) */
.thumb { aspect-ratio: 16 / 9; object-fit: cover; }

/* gap — flex에도 적용 가능 (예전엔 grid 전용) */
.flex-list { display: flex; gap: 8px; }
clamp(): 갤럭시탭처럼 화면 크기가 다양한 환경에서 미디어쿼리 없이도 부드럽게 크기가 조절되는 텍스트/여백을 만들 때 매우 유용하다.

초기화 / 리셋 CSS

CSS
/* 브라우저마다 다른 기본 스타일을 통일 */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
ul, ol { list-style: none; }
a { text-decoration: none; color: inherit; }
img { max-width: 100%; display: block; }
button, input, textarea { font: inherit; }
실무 관행: 직접 짜기보다 normalize.cssmodern-css-reset 같은 라이브러리를 사용하거나, Tailwind/UI 프레임워크 사용 시 자체 내장된 리셋을 그대로 활용하는 경우가 많다.

출근 첫날 준비 체크리스트

D-DAY

탭하면 체크/완료 표시되며, 새로고침 시 초기화됩니다 (오프라인 단독 파일이라 저장은 되지 않습니다).

  • Vue3 + Vite 프로젝트 구조 한 번 더 훑어보기 (src/components, src/views, src/router, src/stores)
  • Spring Boot 쪽 API 명세(Swagger 등) 접근 권한 요청
  • Git 저장소 clone 및 브랜치 전략(Git Flow 등) 확인
  • 로컬 개발 환경 (.env, DB 접속정보) 셋업
  • 팀 코드 컨벤션 / ESLint·Prettier 설정 확인
  • v-model, props/emit, Pinia 패턴 — 팀 코드베이스에서 실제 사용 예시 찾아보기
  • JWT 토큰 흐름 (로그인 → 저장 → axios 인터셉터 첨부) 팀 구현 방식 파악