# Vue.js 면접 핵심 질문: 합격을 위한 25문항
> Vue.js 면접을 위한 25개의 핵심 질문. 반응성부터 composables까지, 다음 면접에서 빛날 핵심 개념을 정리합니다.
- Published: 2026-02-05
- Updated: 2026-04-27
- Author: SharpSkill
- Tags: vue.js, interview, frontend, javascript, technical questions
- Reading time: 15 min
---
Vue.js 면접에서는 프레임워크 문법보다 훨씬 더 많은 것을 평가합니다. 채용 담당자는 반응성 시스템에 대한 이해도, Composition API를 활용한 코드 구성 능력, 그리고 실제 성능과 아키텍처 문제를 해결할 수 있는 역량을 확인하고 싶어합니다.
> **준비 팁**
>
> 모든 문항에는 자세한 답변과 코드 예제가 포함되어 있습니다. 기술 면접에서는 실제 면접처럼 개념을 소리 내어 설명하는 연습이 효과적입니다.
## Vue.js 기본 문항
### 1. Vue 3에서 ref와 reactive의 차이는 무엇입니까
이 질문은 Vue 3의 핵심인 반응성 시스템에 대한 이해를 확인합니다. 주요 차이는 다루는 데이터 타입과 접근 문법에 있습니다.
`ref`는 원시값(string, number, boolean)에 대한 반응형 참조를 만들고 스크립트에서는 `.value`로 값에 접근해야 합니다. `reactive`는 객체에 대한 반응형 프록시를 만들며 속성에 직접 접근할 수 있습니다.
```javascript
// ref와 reactive 비교 예시
import { ref, reactive } from 'vue'
// ref: for primitives
// Requires .value in the script
const count = ref(0)
count.value++ // Access with .value
// reactive: for complex objects
// Direct property access
const user = reactive({
name: 'Alice',
age: 25
})
user.age++ // No .value needed
// Warning: reactive loses reactivity if reassigned
// user = { name: 'Bob', age: 30 } // ❌ Breaks reactivity
Object.assign(user, { name: 'Bob', age: 30 }) // ✅ Correct
```
일반 원칙은 단순한 값에는 `ref`를, 서로 연관된 여러 속성을 가진 객체에는 `reactive`를 사용하는 것입니다.
### 2. Vue 3의 반응성 시스템은 어떻게 동작합니까
Vue 3는 반응형 객체에 대한 작업을 가로채기 위해 JavaScript Proxy(ES6)를 사용합니다. `Object.defineProperty`를 사용한 Vue 2와 달리, 이 방식은 속성의 추가와 제거를 동적으로 감지합니다.
```javascript
// Simplified demonstration of the reactivity principle
// Vue uses Proxies to track dependencies
const handler = {
// Intercept reading
get(target, key, receiver) {
track(target, key) // Register the dependency
return Reflect.get(target, key, receiver)
},
// Intercept writing
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver)
trigger(target, key) // Trigger updates
return result
}
}
// Creating a reactive proxy
const reactiveObject = new Proxy(originalObject, handler)
```
Proxy의 장점에는 새로운 속성 감지, Map과 Set 지원, 큰 객체에서의 더 나은 성능이 포함됩니다.
### 3. computed와 watch의 차이를 설명해 주세요
`computed`와 `watch`는 반응성 관리에서 서로 다른 역할을 수행합니다.
**computed**: 다른 반응형 데이터에서 파생된 값을 계산합니다. 값은 캐시되며, 의존성이 변경될 때만 재계산됩니다. 데이터 변환에 적합합니다.
**watch**: 변경에 응답하여 부수 효과를 실행합니다. API 호출, DOM 조작, 비동기 작업에 유용합니다.
```javascript
// Computed vs watch comparison
import { ref, computed, watch } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// computed: derived value with cache
// Recalculates only if firstName or lastName changes
const fullName = computed(() => {
console.log('Computing full name') // Called only once
return `${firstName.value} ${lastName.value}`
})
// Multiple accesses = single execution (cached)
console.log(fullName.value) // "John Doe"
console.log(fullName.value) // No recalculation
// watch: side effect without cache
// Executed on every change
watch(firstName, async (newName, oldName) => {
// Side effect: API call
await saveToServer({ firstName: newName })
console.log(`Name changed from ${oldName} to ${newName}`)
})
```
### 4. Virtual DOM이 무엇이며 Vue는 이를 어떻게 사용합니까
Virtual DOM은 실제 DOM의 가벼운 JavaScript 표현입니다. Vue는 가상 트리를 메모리에 유지하고, 이전 상태와 새 상태의 차이(diffing)를 계산하여 실제 DOM에는 필요한 변경만 적용합니다.
```javascript
// Conceptual representation of the Virtual DOM
// Vue creates this structure internally
const vnode = {
type: 'div',
props: {
class: 'container',
id: 'app'
},
children: [
{
type: 'h1',
props: {},
children: 'Title'
},
{
type: 'p',
props: {},
children: 'Paragraph content'
}
]
}
// When changes occur, Vue compares vnodes
// and updates only the modified elements
```
Vue 3의 최적화에는 정적 노드의 호이스팅, 변경 유형을 식별하기 위한 패치 플래그, 컴파일러 단계의 트리 셰이킹이 포함됩니다.
### 5. 직접 관계가 없는 컴포넌트 간 통신은 어떻게 처리합니까
부모-자식 관계가 직접 없는 컴포넌트끼리 통신하기 위한 패턴은 여러 가지가 있습니다.
```javascript
// Solution 1: Event Bus (small applications)
// eventBus.js
import { ref } from 'vue'
const bus = ref(new Map())
export function useEventBus() {
// Emit an event
const emit = (event, payload) => {
const callbacks = bus.value.get(event) || []
callbacks.forEach(cb => cb(payload))
}
// Listen to an event
const on = (event, callback) => {
if (!bus.value.has(event)) {
bus.value.set(event, [])
}
bus.value.get(event).push(callback)
}
return { emit, on }
}
```
```javascript
// Solution 2: Provide/Inject for nested components
// Ancestor
import { provide, ref } from 'vue'
const sharedState = ref('shared value')
provide('stateKey', sharedState)
// Descendant (any level)
import { inject } from 'vue'
const state = inject('stateKey')
```
복잡한 애플리케이션에서는 전역 상태 관리에 Pinia가 권장됩니다.
## Composition API 관련 문항
### 6. Composition API는 Options API에 비해 어떤 장점이 있습니까
Composition API는 전통적인 Options API에 비해 여러 구조적 장점을 제공합니다.
**기능 단위 구성**: 같은 기능에 관련된 코드가 한곳에 묶이며, Options API처럼 타입(data, methods, computed)별로 흩어지지 않습니다.
**composables를 통한 재사용**: 로직을 재사용 가능한 함수로 손쉽게 분리할 수 있습니다.
**더 나은 TypeScript 지원**: 데코레이터 없이 자연스러운 타입 추론이 가능합니다.
```javascript
// Options API: code fragmented by type
export default {
data() {
return {
searchQuery: '',
results: []
}
},
computed: {
hasResults() {
return this.results.length > 0
}
},
methods: {
async search() {
this.results = await fetchResults(this.searchQuery)
}
},
watch: {
searchQuery: 'search'
}
}
// Composition API: code grouped by feature
import { ref, computed, watch } from 'vue'
export function useSearch() {
const searchQuery = ref('')
const results = ref([])
const hasResults = computed(() => results.value.length > 0)
const search = async () => {
results.value = await fetchResults(searchQuery.value)
}
watch(searchQuery, search)
return { searchQuery, results, hasResults, search }
}
```
### 7. 재사용 가능한 composable은 어떻게 만듭니까
composable은 반응성 로직을 캡슐화한 함수입니다. 일반적인 규약은 `use` 접두사 사용, 상태와 메서드를 담은 객체 반환, 정리(cleanup) 처리 등이 있습니다.
```javascript
// composables/useLocalStorage.js
import { ref, watch } from 'vue'
// Composable to synchronize state with localStorage
export function useLocalStorage(key, defaultValue) {
// Retrieve initial value from localStorage
const storedValue = localStorage.getItem(key)
const data = ref(
storedValue ? JSON.parse(storedValue) : defaultValue
)
// Synchronize changes to localStorage
watch(
data,
(newValue) => {
if (newValue === null) {
localStorage.removeItem(key)
} else {
localStorage.setItem(key, JSON.stringify(newValue))
}
},
{ deep: true } // Observe nested objects
)
return data
}
// Usage in a component
const theme = useLocalStorage('theme', 'light')
const userPrefs = useLocalStorage('prefs', { notifications: true })
```
> **명명 규약**
>
> composable은 재사용 가능하다는 점을 강조하기 위해 `useXxx` 규약을 따릅니다. 이 규약은 가독성을 높이고 반응형 의존성을 식별하기 쉽게 만듭니다.
### 8. watchEffect와 watch를 비교해 설명해 주세요
`watchEffect`와 `watch`는 모두 변경에 반응하지만 접근 방식이 다릅니다.
**watchEffect**: 즉시 실행되며 반응형 의존성이 변경될 때 자동으로 재실행됩니다. 의존성 추적은 자동으로 이뤄집니다.
**watch**: 특정 소스를 관찰하고 이전 값과 새 값을 함께 제공합니다. 트리거 시점에 대한 더 세밀한 제어가 가능합니다.
```javascript
// watchEffect vs watch comparison
import { ref, watch, watchEffect } from 'vue'
const userId = ref(1)
const userData = ref(null)
// watchEffect: automatic tracking
// Runs immediately
watchEffect(async () => {
// userId is automatically tracked
const response = await fetch(`/api/users/${userId.value}`)
userData.value = await response.json()
})
// watch: explicit sources with old values
watch(userId, async (newId, oldId) => {
console.log(`User changed from ${oldId} to ${newId}`)
const response = await fetch(`/api/users/${newId}`)
userData.value = await response.json()
}, {
immediate: true // Run immediately like watchEffect
})
// watchEffect with cleanup
watchEffect((onCleanup) => {
const controller = new AbortController()
fetch(`/api/users/${userId.value}`, {
signal: controller.signal
}).then(/* ... */)
// Cleanup: cancel previous request
onCleanup(() => controller.abort())
})
```
### 9. script setup에서 TypeScript와 함께 props를 어떻게 다룹니까
`
```
## 성능 관련 문항
### 10. 어떤 성능 최적화 기법이 있습니까
Vue 3는 다양한 성능 최적화 메커니즘을 제공합니다.
```javascript
// 1. v-once: single render for static content
// 2. v-memo: conditional memoization
{{ item.name }}
// 3. shallowRef/shallowReactive: shallow reactivity
import { shallowRef, triggerRef } from 'vue'
// Only tracks ref replacement, not internal mutations
const largeList = shallowRef([/* thousands of elements */])
// Force update after mutation
largeList.value.push(newItem)
triggerRef(largeList) // Manually trigger re-render
```
```javascript
// 4. Async components for code-splitting
import { defineAsyncComponent } from 'vue'
const HeavyComponent = defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: LoadingSpinner,
delay: 200, // Delay before showing loading
errorComponent: ErrorDisplay,
timeout: 3000
})
// 5. KeepAlive for component caching
```
### 11. 불필요한 재렌더를 어떻게 피합니까
불필요한 재렌더는 성능에 악영향을 줍니다. 다음과 같은 전략으로 최소화할 수 있습니다.
```javascript
// Problem: function created on each render
handleClick(item.id)" />
// Solution: use a method or ref
```
```javascript
// Using computed for expensive calculations
import { computed } from 'vue'
// ❌ Recalculated on every render
const getFilteredItems = () => {
return items.value.filter(/* complex logic */)
}
// ✅ Cached, recalculated only if items changes
const filteredItems = computed(() => {
return items.value.filter(/* complex logic */)
})
```
### 12. 컴포넌트와 라우트의 lazy loading을 설명해 주세요
lazy loading은 코드를 필요할 때 로드하여 초기 번들 크기를 줄입니다.
```javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
// Immediate loading (main bundle)
component: () => import('@/views/Home.vue')
},
{
path: '/dashboard',
// Separate chunk with custom name
component: () => import(
/* webpackChunkName: "dashboard" */
'@/views/Dashboard.vue'
),
// Lazy loading child routes
children: [
{
path: 'analytics',
component: () => import('@/views/Analytics.vue')
}
]
},
{
path: '/admin',
// Prefetch on link hover
component: () => import('@/views/Admin.vue'),
meta: { prefetch: true }
}
]
})
export default router
```
## Vue Router 관련 문항
### 13. 가드를 사용해 라우트를 어떻게 보호합니까
내비게이션 가드는 라우트 접근을 제어합니다.
```javascript
// router/index.js
import { createRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
routes: [
{
path: '/dashboard',
component: Dashboard,
meta: { requiresAuth: true, roles: ['admin', 'user'] }
}
]
})
// Global guard: checks authentication
router.beforeEach(async (to, from, next) => {
const auth = useAuthStore()
// Public route
if (!to.meta.requiresAuth) {
return next()
}
// Check authentication
if (!auth.isAuthenticated) {
return next({
path: '/login',
query: { redirect: to.fullPath }
})
}
// Check roles if specified
if (to.meta.roles && !to.meta.roles.includes(auth.user.role)) {
return next('/unauthorized')
}
next()
})
// Component-level guard
export default {
beforeRouteEnter(to, from, next) {
// No access to this here
next(vm => {
// Access component instance via vm
vm.loadData()
})
},
beforeRouteLeave(to, from, next) {
// Confirm before leaving if form modified
if (this.hasUnsavedChanges) {
const answer = confirm('Leave without saving?')
next(answer)
} else {
next()
}
}
}
```
### 14. 라우트에 props를 어떻게 전달합니까
Vue Router를 사용하면 컴포넌트를 라우트 매개변수와 분리할 수 있습니다.
```javascript
// Route configuration with props
const routes = [
{
path: '/user/:id',
component: UserProfile,
// Boolean mode: passes params as props
props: true
},
{
path: '/search',
component: SearchResults,
// Function mode: custom transformation
props: (route) => ({
query: route.query.q,
page: parseInt(route.query.page) || 1,
filters: route.query.filters?.split(',') || []
})
},
{
path: '/static',
component: StaticPage,
// Object mode: static props
props: { sidebar: true, theme: 'dark' }
}
]
```
```javascript
// UserProfile.vue
// SearchResults.vue
```
## Pinia와 상태 관리 관련 문항
### 15. Pinia와 Vuex의 차이는 무엇입니까
Pinia는 Vue 3의 공식 상태 관리자이며 단순화된 API로 Vuex를 대체합니다.
| 기능 | Vuex | Pinia |
|---------|------|-------|
| Mutations | 필수 | 필요 없음 |
| 모듈 | 복잡한 설정 | 독립 스토어 |
| TypeScript | 제한적 지원 | 네이티브 완전 지원 |
| API | Options | Composition + Options |
| DevTools | 지원 | 완전 지원 |
```javascript
// Pinia Store with Composition API
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCartStore = defineStore('cart', () => {
// State
const items = ref([])
const discountCode = ref(null)
// Getters (computed)
const totalItems = computed(() =>
items.value.reduce((sum, item) => sum + item.quantity, 0)
)
const totalPrice = computed(() => {
const subtotal = items.value.reduce(
(sum, item) => sum + item.price * item.quantity, 0
)
return discountCode.value ? subtotal * 0.9 : subtotal
})
// Actions (direct functions)
function addItem(product) {
const existing = items.value.find(i => i.id === product.id)
if (existing) {
existing.quantity++
} else {
items.value.push({ ...product, quantity: 1 })
}
}
function removeItem(productId) {
const index = items.value.findIndex(i => i.id === productId)
if (index > -1) {
items.value.splice(index, 1)
}
}
async function checkout() {
const response = await api.createOrder(items.value)
items.value = []
return response
}
return {
items, discountCode,
totalItems, totalPrice,
addItem, removeItem, checkout
}
})
```
### 16. Pinia 스토어의 상태를 어떻게 영속화합니까
영속화는 사용자 세션 사이에 상태를 유지하도록 도와줍니다.
```javascript
// plugins/piniaPersistedState.js
import { watch } from 'vue'
export function createPersistedState(options = {}) {
const {
key = 'pinia',
storage = localStorage,
paths = null
} = options
return ({ store }) => {
// Restore state on startup
const savedState = storage.getItem(`${key}-${store.$id}`)
if (savedState) {
store.$patch(JSON.parse(savedState))
}
// Persist changes
watch(
() => store.$state,
(state) => {
const toSave = paths
? paths.reduce((acc, path) => {
acc[path] = state[path]
return acc
}, {})
: state
storage.setItem(
`${key}-${store.$id}`,
JSON.stringify(toSave)
)
},
{ deep: true }
)
}
}
// main.js
import { createPinia } from 'pinia'
import { createPersistedState } from './plugins/piniaPersistedState'
const pinia = createPinia()
pinia.use(createPersistedState({
key: 'app-state',
paths: ['user', 'preferences'] // Persist only these keys
}))
```
> **민감한 데이터**
>
> 민감한 데이터(토큰, 비밀번호)는 localStorage에 저장하지 않는 편이 좋습니다. 인증 토큰은 httpOnly 쿠키로 다루는 것이 더 안전합니다.
## 고급 문항
### 17. Vue에서 플러그인 시스템은 어떻게 구현합니까
플러그인은 Vue를 전역 기능으로 확장합니다.
```javascript
// plugins/analyticsPlugin.js
export const AnalyticsPlugin = {
install(app, options = {}) {
const { trackingId, debug = false } = options
// Global injection available in all components
const analytics = {
trackEvent(category, action, label) {
if (debug) {
console.log('Analytics:', { category, action, label })
}
// Logic to send to analytics service
window.gtag?.('event', action, {
event_category: category,
event_label: label
})
},
trackPage(path) {
window.gtag?.('config', trackingId, { page_path: path })
}
}
// Make available via inject
app.provide('analytics', analytics)
// Add global property (discouraged in Composition API)
app.config.globalProperties.$analytics = analytics
// Custom directive for click tracking
app.directive('track', {
mounted(el, binding) {
el.addEventListener('click', () => {
analytics.trackEvent('click', binding.value, el.textContent)
})
}
})
// Automatic route change tracking
app.mixin({
mounted() {
if (this.$route) {
analytics.trackPage(this.$route.path)
}
}
})
}
}
// main.js
import { AnalyticsPlugin } from './plugins/analyticsPlugin'
app.use(AnalyticsPlugin, {
trackingId: 'UA-XXXXX-X',
debug: import.meta.env.DEV
})
```
### 18. 렌더 함수와 그 활용을 설명해 주세요
렌더 함수는 렌더링을 완전히 제어할 수 있어 매우 동적인 컴포넌트에 유용합니다.
```javascript
// components/DynamicHeading.js
import { h } from 'vue'
// Functional component with render function
export const DynamicHeading = {
props: {
level: {
type: Number,
default: 1,
validator: (v) => v >= 1 && v <= 6
}
},
setup(props, { slots }) {
// h() creates a vnode
// Arguments: type, props, children
return () => h(
`h${props.level}`,
{ class: 'dynamic-heading' },
slots.default?.()
)
}
}
// Component with complex conditional logic
export const ConditionalWrapper = {
props: ['condition', 'wrapper'],
setup(props, { slots }) {
return () => {
if (props.condition) {
return h(props.wrapper, null, slots.default?.())
}
return slots.default?.()
}
}
}
// Usage
Level 2 Title
Conditional content
```
### 19. Vitest로 Vue 컴포넌트를 어떻게 테스트합니까
단위 테스트는 컴포넌트의 격리된 동작을 검증합니다.
```javascript
// components/__tests__/Counter.spec.js
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from '../Counter.vue'
describe('Counter', () => {
it('displays the initial value', () => {
const wrapper = mount(Counter, {
props: { initialValue: 5 }
})
expect(wrapper.text()).toContain('5')
})
it('increments the value on click', async () => {
const wrapper = mount(Counter)
await wrapper.find('button.increment').trigger('click')
expect(wrapper.text()).toContain('1')
})
it('emits an event when changed', async () => {
const wrapper = mount(Counter)
await wrapper.find('button.increment').trigger('click')
expect(wrapper.emitted('change')).toBeTruthy()
expect(wrapper.emitted('change')[0]).toEqual([1])
})
it('calls the service on submit', async () => {
const mockSubmit = vi.fn()
const wrapper = mount(Counter, {
global: {
provide: {
submitService: mockSubmit
}
}
})
await wrapper.find('form').trigger('submit')
expect(mockSubmit).toHaveBeenCalled()
})
})
```
### 20. Vue에서 오류를 전역으로 어떻게 처리합니까
Vue 3는 오류를 잡고 처리하기 위한 여러 가지 메커니즘을 제공합니다.
```javascript
// main.js
import { createApp } from 'vue'
const app = createApp(App)
// Global handler for component errors
app.config.errorHandler = (err, instance, info) => {
// err: the error
// instance: the component instance
// info: string describing where the error occurred
console.error('Vue error:', err)
console.error('Component:', instance?.$options?.name)
console.error('Info:', info)
// Send to monitoring service
errorTracker.captureException(err, {
component: instance?.$options?.name,
info
})
}
// Handler for warnings (dev only)
app.config.warnHandler = (msg, instance, trace) => {
console.warn('Vue warning:', msg)
}
```
```javascript
// ErrorBoundary component
An error occurred
{{ error.message }}
```
## 모범 사례 관련 문항
### 21. Vue에서 어떤 명명 규약을 따라야 합니까
명명 규약은 코드의 가독성과 유지 보수성을 높입니다.
```javascript
// Component naming
// PascalCase for files and names
// BaseButton.vue, AppHeader.vue, TheNavbar.vue
// Props: camelCase in JS, kebab-case in template
defineProps<{
userName: string // JS
isActive: boolean // JS
}>()
//
// Events: camelCase with action prefix
const emit = defineEmits<{
(e: 'updateValue', value: string): void // ✅
(e: 'submit'): void // ✅
(e: 'value-updated'): void // ❌ Avoid
}>()
// Composables: use prefix
// useAuth.js, useFetch.js, useLocalStorage.js
// Pinia stores: use prefix + Store suffix
// useUserStore, useCartStore, useSettingsStore
// Constants: SCREAMING_SNAKE_CASE
const MAX_RETRY_COUNT = 3
const API_BASE_URL = '/api/v1'
```
### 22. 대규모 Vue 프로젝트는 어떻게 구성합니까
모듈 단위 구조는 탐색과 유지 보수를 더 쉽게 만듭니다.
```
src/
├── assets/ # Static files
├── components/
│ ├── ui/ # Generic components (Button, Modal)
│ └── common/ # Reusable business components
├── composables/ # Reusable logic
│ ├── useAuth.js
│ └── useFetch.js
├── layouts/ # Page layouts
│ ├── DefaultLayout.vue
│ └── AuthLayout.vue
├── modules/ # Functional modules
│ ├── auth/
│ │ ├── components/
│ │ ├── composables/
│ │ ├── stores/
│ │ └── views/
│ └── dashboard/
├── plugins/ # Vue plugins
├── router/
│ ├── index.js
│ └── guards.js
├── stores/ # Global Pinia stores
├── types/ # TypeScript types
├── utils/ # Pure utilities
└── views/ # Pages/Routes
```
### 23. v-if와 v-show는 언제 선택합니까
`v-if`와 `v-show` 중 어떤 것을 선택하느냐는 토글 빈도에 달려 있습니다.
```javascript
// v-if: low initial cost, expensive toggle
// Removes/adds element from DOM
// Ideal for: rarely modified conditions
// v-show: higher initial cost, fast toggle
// Uses display: none
// Ideal for: frequent toggles
Contextual information
{{ content }}
```
### 24. v-for로 만든 리스트를 어떻게 최적화합니까
요소가 많은 리스트에서는 성능을 위해 최적화가 필수입니다.
```javascript
// Always use :key with a unique stable identifier
{{ item.name }}
{{ item.name }}
// Filtering and sorting: use computed
// Virtualization for very long lists
{{ item.data.name }}
```
### 25. Renderless component 패턴을 설명해 주세요
renderless 컴포넌트는 HTML 구조를 강제하지 않고 로직만 캡슐화합니다.
```javascript
// components/MouseTracker.vue
```
```javascript
// Usage: full control over rendering
Position: {{ x }}, {{ y }}
```
이 패턴은 로직과 표현을 완전히 분리하여 재사용성을 극대화합니다.
## 결론
이 25개의 문항은 Vue.js 면접에서 평가되는 핵심 개념을 모두 다룹니다.
- ✅ **반응성**: `ref`, `reactive`, `computed`, `watch`
- ✅ **Composition API**: composable, `script setup`, TypeScript
- ✅ **성능**: lazy loading, 가상화, 최적화 기법
- ✅ **Vue Router**: 가드, props, 내비게이션
- ✅ **Pinia**: 스토어, 영속화, 비동기 액션
- ✅ **모범 사례**: 구조, 규약, 고급 패턴
효과적인 준비는 이론적 이해와 코드 연습을 병행하는 데 있습니다. 여기서 다룬 모든 개념은 면접에서 더 깊은 후속 질문으로 이어질 수 있습니다.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/ko/blog/vue-nuxt/essential-vuejs-interview-questions