본문으로 건너뛰기
Advertisement

8.4 경로 매핑 — tsconfig paths와 @/ 별칭 설정

경로 매핑이 필요한 이유

중첩된 디렉토리 구조에서는 상대 경로가 복잡해집니다.

// 경로 매핑 없이 — 길고 복잡한 상대 경로
import { UserService } from '../../../services/user.service';
import { AuthGuard } from '../../guards/auth.guard';
import { formatDate } from '../../../../utils/date';
import type { ApiResponse } from '../../../types/api';

**경로 별칭(Path Alias)**을 사용하면 깔끔해집니다.

// @/ 별칭 사용 — 짧고 명확한 경로
import { UserService } from '@/services/user.service';
import { AuthGuard } from '@/guards/auth.guard';
import { formatDate } from '@/utils/date';
import type { ApiResponse } from '@/types/api';

tsconfig.json 경로 매핑 설정

기본 설정

{
"compilerOptions": {
"baseUrl": ".", // 모든 경로의 기준점
"paths": {
"@/*": ["src/*"], // @/로 시작하면 src/ 로 매핑
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@types/*": ["src/types/*"]
}
}
}

baseUrl의 역할

baseUrl은 절대 경로 import의 기준이 됩니다.

{
"compilerOptions": {
"baseUrl": "./src" // src 디렉토리를 기준점으로
}
}
// baseUrl: "./src" 설정 시
import { UserService } from 'services/user.service'; // ./src/services/user.service
import { formatDate } from 'utils/date'; // ./src/utils/date

paths 패턴 규칙

{
"compilerOptions": {
"baseUrl": ".",
"paths": {
// 와일드카드 패턴
"@/*": ["src/*"],

// 여러 후보 경로 (순서대로 탐색)
"@shared/*": ["packages/shared/src/*", "libs/shared/*"],

// 정확한 경로 매핑
"@config": ["src/config/index.ts"],
"@types": ["src/types/index.ts"]
}
}
}

프로젝트 유형별 설정

React + Vite 프로젝트

// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
// vite.config.ts — 번들러에도 동일하게 설정 필요!
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
// 사용 예시
import { Button } from '@/components/ui/Button';
import { useAuth } from '@/hooks/useAuth';
import type { User } from '@/types/user';

Next.js 프로젝트

Next.js는 tsconfig.json의 paths를 자동으로 인식합니다.

// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"] // Next.js 루트 기준
}
}
}

또는 Next.js 9.4+ 에서 지원하는 설정:

// jsconfig.json 또는 tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/components/*": ["components/*"],
"@/lib/*": ["lib/*"],
"@/styles/*": ["styles/*"]
}
}
}

Node.js + Express 프로젝트

// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@controllers/*": ["src/controllers/*"],
"@services/*": ["src/services/*"],
"@models/*": ["src/models/*"],
"@middleware/*": ["src/middleware/*"],
"@config": ["src/config/index"]
}
}
}
// src/routes/user.routes.ts
import { UserController } from '@controllers/user.controller';
import { authMiddleware } from '@middleware/auth';
import { validateBody } from '@middleware/validation';
import type { CreateUserDto } from '@models/user.model';

중요: Node.js에서는 런타임에도 경로 매핑이 필요합니다.

npm install --save-dev tsconfig-paths
// package.json
{
"scripts": {
"dev": "ts-node -r tsconfig-paths/register src/index.ts",
"start": "node -r tsconfig-paths/register dist/index.js"
}
}

또는 tsc-alias를 사용하여 빌드 시 경로를 실제 경로로 변환:

npm install --save-dev tsc-alias
{
"scripts": {
"build": "tsc && tsc-alias"
}
}

모노레포 프로젝트

// tsconfig.json (루트)
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@myapp/shared": ["packages/shared/src/index.ts"],
"@myapp/shared/*": ["packages/shared/src/*"],
"@myapp/ui": ["packages/ui/src/index.ts"],
"@myapp/ui/*": ["packages/ui/src/*"],
"@myapp/types": ["packages/types/src/index.ts"]
}
}
}
// apps/web/src/pages/home.tsx
import { Button, Card } from '@myapp/ui';
import { formatDate } from '@myapp/shared';
import type { User, Product } from '@myapp/types';

번들러별 별칭 설정

tsconfig의 paths는 타입 체크용입니다. 런타임에서도 경로가 해석되려면 번들러에도 설정이 필요합니다.

webpack

// webpack.config.js
const path = require('path');

module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
};

Rollup

// rollup.config.js
import alias from '@rollup/plugin-alias';
import path from 'path';

export default {
plugins: [
alias({
entries: [
{ find: '@', replacement: path.resolve(__dirname, 'src') },
],
}),
],
};

esbuild

// build.ts
import * as esbuild from 'esbuild';
import { TsconfigPathsPlugin } from '@esbuild-plugins/tsconfig-paths';

esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
plugins: [TsconfigPathsPlugin({ tsconfig: './tsconfig.json' })],
});

자동 동기화 도구

tsconfig paths와 번들러 설정을 동기화하는 도구들:

vite-tsconfig-paths

npm install --save-dev vite-tsconfig-paths
// vite.config.ts
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
plugins: [tsconfigPaths()], // tsconfig.json의 paths 자동 적용
});

craco (Create React App)

// craco.config.js
const path = require('path');

module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src/'),
},
},
};

실전 완전한 설정 예시

my-app/
├── src/
│ ├── components/
│ ├── hooks/
│ ├── pages/
│ ├── services/
│ ├── stores/
│ ├── types/
│ └── utils/
├── tsconfig.json
└── vite.config.ts
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@hooks/*": ["src/hooks/*"],
"@pages/*": ["src/pages/*"],
"@services/*": ["src/services/*"],
"@stores/*": ["src/stores/*"],
"@types/*": ["src/types/*"],
"@utils/*": ["src/utils/*"]
}
},
"include": ["src"]
}
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
plugins: [react(), tsconfigPaths()],
});

고수 팁

1. 경로 별칭 설계 원칙

단순할수록 좋다: @/* 하나로 통일이 가장 관리하기 쉬움
세분화가 필요한 경우: 도메인별 분리 (@features/*, @shared/*)
피해야 할 것: 너무 많은 별칭 (헷갈림 유발)

2. VSCode 자동완성 활성화 확인

tsconfig.json의 paths 설정 후 VSCode에서 자동완성이 안 된다면:

  • TypeScript 서버 재시작: Ctrl+Shift+P → "TypeScript: Restart TS Server"
  • .vscode/settings.json에 TypeScript SDK 경로 확인

3. Jest에도 별칭 설정 필요

// jest.config.js
{
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/src/$1",
"^@components/(.*)$": "<rootDir>/src/components/$1"
}
}
Advertisement