Vue 3 + TypeScript 아키텍처 패턴 가이드

3 minute read

Vue 3 프로젝트에서 TypeScript를 활용할 때 선택할 수 있는 다양한 아키텍처 패턴을 시각화와 함께 정리한 가이드입니다. 각 패턴은 프로젝트 규모와 팀 구성에 따라 선택적으로 적용할 수 있습니다.


아키텍처 패턴 개요

패턴 장점 단점 적용 규모
분리형 타입 관리 재사용성 우수, 유지보수 편리 디렉토리 복잡도 증가 중대형 프로젝트
인라인 타입 정의 빠른 개발, 컴포넌트 구조 명확 재사용 불가, 중복 증가 소형 프로젝트
전역 선언 타입 어디서든 타입 사용 가능 네이밍 충돌, 전역 오염 위험 공통 타입 정의
유틸/헬퍼 모듈화 테스트 용이, 책임 분리 의존성/폴더 구조 관리 필요 로직 집약 프로젝트
명시적 Props/Emits IDE 지원 뛰어남, API 명확화 복잡한 타입은 외부 분리 필요 모든 규모

1. 분리형 타입 관리 아키텍처

타입을 별도 디렉토리에서 관리하며, 각 컴포넌트에서 필요시 import하여 사용하는 패턴입니다.

graph TD
  A[project-root] --> B[src/]
  B --> C[components/]
  B --> D[types/]
  B --> E[utils/]
  C --> C1[UserCard.vue]
  C --> C2[DataTable.vue]
  D --> D1[user.ts]
  D --> D2[common.ts]
  E --> E1[validators.ts]
  
  C1 -->|import types| D1
  C2 -->|import types| D1
  C1 -->|import utils| E1

  style D1 fill:#ccf,stroke:#333,stroke-width:2px
  style D2 fill:#ccf,stroke:#333,stroke-width:2px
  style C1 fill:#bbf,stroke:#333,stroke-width:2px
  style C2 fill:#bbf,stroke:#333,stroke-width:2px

특징

  • 타입 재사용성이 높음
  • 유지보수와 협업에 유리
  • 구조가 커져 복잡도 증가

2. 인라인 타입 정의 아키텍처

모든 타입을 각 컴포넌트 파일 내에서 직접 정의하고 관리하는 패턴입니다.

graph TD
  A[project-root] --> B[src/]
  B --> C[components/]
  C --> C1[ProfileCard.vue]
  C --> C2[ContactForm.vue]
  C1 --> C1A[props & emits 정의]
  C1 --> C1B[로컬 함수 타입 정의]
  C2 --> C2A[props & emits 정의]
  C2 --> C2B[로컬 함수 타입 정의]

  style C1 fill:#def,stroke:#333,stroke-width:2px
  style C2 fill:#def,stroke:#333,stroke-width:2px

특징

  • 빠른 개발 가능
  • 파일 하나로 구조 이해 용이
  • 재사용성 낮음, 중복 증가

3. 전역 선언 타입 아키텍처

.d.ts 파일에 전역 타입을 선언하여 어디서든 사용하는 패턴입니다. 두 가지 방식으로 구현할 수 있습니다.

graph TD
  A[project-root] --> B[src/]
  A --> C[global.d.ts]
  A --> D[env.d.ts]
  B --> E[components/]
  E --> E1[OrderItem.vue]
  E --> E2[ProductList.vue]
  E1 -->|전역 타입 사용| C
  E2 -->|전역 타입 사용| C

  style C fill:#fdd,stroke:#333,stroke-width:2px
  style D fill:#fdd,stroke:#333,stroke-width:2px

특징

  • 반복되는 타입 정의 최소화
  • 어디서든 접근 가능
  • 전역 오염 위험, 충돌 가능성

구현 방식 구분

  • 진짜 전역 타입: declare global 블록 사용, import 불필요
  • Ambient 모듈: 일반 .d.ts 파일, import 필요

파일 확장자 구분:

  • .d.ts: 타입 선언만 포함 (전역 타입, 라이브러리 타입)
  • .ts: 타입 정의 + 실제 코드 (일반적인 타입 파일)

4. 유틸/헬퍼 모듈화 아키텍처

비즈니스 로직을 utils 디렉토리에 모듈화하고 타입도 함수별로 분리하여 작성하는 패턴입니다.

graph TD
  A[project-root] --> B[src/]
  B --> C[utils/]
  B --> D[components/]
  C --> C1[mathUtils.ts]
  C --> C2[validators.ts]
  C --> C3[dateUtils.ts]
  D --> D1[Calculator.vue]
  D --> D2[FormComponent.vue]
  D1 -->|import| C1
  D2 -->|import| C2
  D2 -->|import| C3

  style C1 fill:#bfb,stroke:#333,stroke-width:2px
  style C2 fill:#bfb,stroke:#333,stroke-width:2px
  style C3 fill:#bfb,stroke:#333,stroke-width:2px
  style D1 fill:#cfc,stroke:#333,stroke-width:2px
  style D2 fill:#cfc,stroke:#333,stroke-width:2px

특징

  • 코드 테스트 쉬움
  • 함수 책임 분리 용이
  • 디렉토리 및 의존성 관리 필요

5. 명시적 Props/Emits 아키텍처

Vue 3 <script setup> 구문과 함께 defineProps, defineEmits에 명시적인 타입을 지정하는 패턴입니다.

graph TD
  A[project-root] --> B[src/]
  B --> C[components/]
  C --> C1[TodoItem.vue]
  C --> C2[TaskList.vue]
  C1 --> C1A[defineProps<TodoProps>]
  C1 --> C1B[defineEmits<TodoEvents>]
  C2 --> C2A[defineProps<TaskProps>]
  C2 --> C2B[defineEmits<TaskEvents>]

  style C1 fill:#e6f,stroke:#333,stroke-width:2px
  style C2 fill:#e6f,stroke:#333,stroke-width:2px

특징

  • 타입 안정성 높고, IDE 지원 우수
  • 컴포넌트 API 명확화
  • 복잡한 경우 외부 분리 필요함

혼합 패턴: 하이브리드 아키텍처

실제 프로젝트에서는 여러 패턴을 조합하여 사용합니다. 다음은 Vue Pivottable 프로젝트 구조를 기반으로 한 혼합 아키텍처 예시입니다.

graph TD
  A[project-root] --> B[src/]
  B --> C[components/]
  B --> D[composables/]
  B --> E[helper/]
  B --> F[types/]
  B --> G[global.d.ts]

  C --> C1[pivottable/]
  C --> C2[pivottable-ui/]
  C1 --> C1A[VPivottable.vue]
  C1 --> C1B[VPivottableHeader.vue]
  C1 --> C1C[renderer/]
  C2 --> C2A[VPivottableUi.vue]
  C2 --> C2B[VDragAndDropCell.vue]

  D --> D1[useProvidePivotData.ts]
  D --> D2[usePivotUiState.ts]
  D --> D3[useMaterializeInput.ts]

  E --> E1[utilities.ts]
  E --> E2[defaultProps.ts]
  E --> E3[redColorScaleGenerator.ts]

  F --> F1[pivottable.ts]
  F --> F2[renderer.ts]
  F --> F3[composables.ts]

  C1A -->|defineProps<PivotProps>| F1
  C2A -->|defineEmits<PivotEvents>| F1
  C1A -->|import| E1
  C2B -->|import| D1
  D1 -->|types| F3
  E1 -->|types| F2

  G -->|CommonTypes, Enums| C1A
  G -->|CommonTypes, Enums| C2A

  style C1A fill:#e6f,stroke:#333,stroke-width:2px
  style C2A fill:#e6f,stroke:#333,stroke-width:2px
  style D1 fill:#cfc,stroke:#333,stroke-width:2px
  style D2 fill:#cfc,stroke:#333,stroke-width:2px
  style E1 fill:#bfb,stroke:#333,stroke-width:2px
  style E2 fill:#bfb,stroke:#333,stroke-width:2px
  style F1 fill:#ccf,stroke:#333,stroke-width:2px
  style F2 fill:#ccf,stroke:#333,stroke-width:2px
  style F3 fill:#ccf,stroke:#333,stroke-width:2px
  style G fill:#fdd,stroke:#333,stroke-width:2px

혼합 패턴 구성 요소

1. 분리형 타입 관리 + 명시적 Props/Emits

  • /types 디렉토리에서 타입 통합 관리
  • 컴포넌트에서 defineProps<T>, defineEmits<T> 명시적 타입 지정

2. 유틸/헬퍼 모듈화

  • /helper 디렉토리에 비즈니스 로직 분리
  • /composables 디렉토리에 Vue 3 Composition API 로직 분리

3. 전역 선언 타입 (선택적)

  • 프로젝트 전역에서 사용하는 Enum, 공통 인터페이스 정의

파일 확장자 가이드

.ts (TypeScript Files)

  • 용도: 타입 정의 + 실제 구현 코드
  • 위치: /types, /utils, /composables
  • 예시: user.ts, api.ts, validators.ts

.d.ts (Declaration Files)

  • 용도: 타입 선언만 포함 (런타임 코드 없음)
  • 위치: 프로젝트 루트, 전역 선언
  • 예시: global.d.ts, env.d.ts, vite-env.d.ts