본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
AI Generated
2026. 1. 31. · 13 Views
TypeScript 프로젝트 구조 분석 완벽 가이드
현대적인 TypeScript 프로젝트의 구조를 체계적으로 분석합니다. pnpm workspace부터 tsconfig 설정, tsc 빌드, rolldown 번들러까지 프로덕션 레벨의 프로젝트 구성을 마스터할 수 있습니다.
목차
1. 복잡한 프로젝트 구조 이해하기
김개발 씨가 새로운 회사에 입사했습니다. 첫 출근 날, 선배가 회사의 메인 프로젝트 저장소를 공유해주었는데요.
폴더를 열어보니 packages, apps, libs 같은 낯선 디렉토리들이 가득했습니다. "이게 다 뭐지?" 김개발 씨는 당황하지 않을 수 없었습니다.
현대적인 TypeScript 프로젝트는 단일 폴더 구조가 아닌 모노레포 형태로 구성되는 경우가 많습니다. 마치 대형 쇼핑몰이 여러 매장을 하나의 건물에서 관리하는 것처럼, 여러 패키지와 애플리케이션을 하나의 저장소에서 효율적으로 관리합니다.
이 구조를 이해하면 대규모 프로젝트에서도 길을 잃지 않고 원하는 코드를 빠르게 찾을 수 있습니다.
다음 코드를 살펴봅시다.
project-root/
├── packages/ # 공유 라이브러리들
│ ├── core/ # 핵심 비즈니스 로직
│ ├── ui/ # 공통 UI 컴포넌트
│ └── utils/ # 유틸리티 함수들
├── apps/ # 실제 애플리케이션들
│ ├── web/ # 웹 프론트엔드
│ ├── api/ # 백엔드 API 서버
│ └── admin/ # 관리자 페이지
├── pnpm-workspace.yaml # 워크스페이스 설정
├── tsconfig.base.json # 공통 TS 설정
└── package.json # 루트 패키지 설정
김개발 씨는 입사 3개월 차 주니어 개발자입니다. 이전 회사에서는 프로젝트마다 별도의 저장소를 사용했기 때문에, 이렇게 복잡해 보이는 구조는 처음이었습니다.
선배 개발자 박시니어 씨가 다가와 차근차근 설명을 시작했습니다. "처음엔 복잡해 보이지만, 사실 굉장히 합리적인 구조야.
천천히 살펴보자." 그렇다면 왜 이런 구조가 필요한 걸까요? 쉽게 비유하자면, 모노레포는 마치 대형 백화점과 같습니다.
백화점에는 의류 매장, 식품 매장, 가전 매장이 있지만 모두 하나의 건물에서 관리됩니다. 공용 주차장, 에스컬레이터, 냉난방 시스템을 함께 사용하죠.
프로젝트도 마찬가지입니다. 예전에는 각 프로젝트를 별도의 저장소에서 관리했습니다.
하지만 이 방식에는 심각한 문제가 있었습니다. 공통으로 사용하는 유틸리티 함수를 수정하면, 이를 사용하는 모든 프로젝트에 가서 일일이 업데이트해야 했습니다.
더 큰 문제는 버전 불일치였습니다. A 프로젝트는 유틸리티 1.0을 쓰고, B 프로젝트는 1.2를 쓰다 보니 예상치 못한 버그가 발생하곤 했습니다.
디버깅에 며칠을 허비하는 일도 허다했습니다. 모노레포는 이런 문제를 한 번에 해결합니다.
packages 폴더에는 여러 앱에서 공유하는 라이브러리를 둡니다. core 패키지에는 핵심 비즈니스 로직이, ui 패키지에는 버튼이나 모달 같은 공통 컴포넌트가 들어갑니다.
apps 폴더에는 실제로 사용자에게 배포되는 애플리케이션들이 있습니다. 웹 프론트엔드, API 서버, 관리자 페이지가 각각 독립적인 앱으로 존재하지만, packages의 코드를 함께 공유합니다.
루트 디렉토리에는 전체 프로젝트를 관리하는 설정 파일들이 있습니다. pnpm-workspace.yaml은 어떤 폴더들이 워크스페이스에 포함되는지 정의하고, tsconfig.base.json은 모든 패키지가 공유하는 TypeScript 설정을 담고 있습니다.
실제 현업에서 이 구조의 위력은 엄청납니다. 공통 유틸리티를 수정하면 이를 사용하는 모든 앱에서 즉시 반영됩니다.
버전 충돌 걱정 없이 일관된 코드를 유지할 수 있습니다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.
"마치 레고 블록처럼 조립하는 거군요!" 맞습니다. 잘 설계된 프로젝트 구조는 레고 블록처럼 필요한 조각을 조합해서 원하는 것을 만들 수 있게 해줍니다.
실전 팁
💡 - packages 폴더의 각 패키지는 독립적으로 빌드하고 테스트할 수 있어야 합니다
- 순환 의존성을 피하세요. A가 B를 참조하면 B는 A를 참조하면 안 됩니다
2. pnpm workspace 설정
김개발 씨가 npm install을 실행했더니 node_modules 폴더가 1GB를 훌쩍 넘었습니다. 디스크 용량도 문제지만, 설치 시간만 5분이 넘게 걸립니다.
박시니어 씨가 웃으며 말했습니다. "우리 회사에선 pnpm을 써.
훨씬 빠르고 효율적이지."
pnpm은 빠르고 디스크 효율적인 패키지 매니저입니다. 마치 도서관에서 같은 책을 여러 권 사지 않고 한 권만 사서 대출해주는 것처럼, 동일한 패키지를 한 번만 저장하고 여러 프로젝트에서 공유합니다.
workspace 기능을 활용하면 모노레포 내의 여러 패키지를 하나의 명령으로 관리할 수 있습니다.
다음 코드를 살펴봅시다.
# pnpm-workspace.yaml
packages:
# packages 폴더 하위의 모든 패키지 포함
- 'packages/*'
# apps 폴더 하위의 모든 앱 포함
- 'apps/*'
# 특정 폴더 제외하기
- '!**/test/**'
# 루트 package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"build": "pnpm -r build",
"dev": "pnpm -r --parallel dev",
"test": "pnpm -r test"
}
}
김개발 씨는 이전 회사에서 npm만 사용해봤습니다. 그래서 pnpm이라는 단어가 낯설었습니다.
"npm이랑 뭐가 다른 거예요?" 박시니어 씨가 화이트보드에 그림을 그리며 설명했습니다. "npm은 각 프로젝트마다 node_modules에 패키지를 복사해.
10개 프로젝트가 lodash를 쓰면 lodash가 10번 저장되는 거지." 반면 pnpm은 다릅니다. 마치 중앙 도서관 시스템과 같습니다.
도서관에서는 인기 있는 책을 한 권만 구매하고, 필요한 사람에게 대출해줍니다. pnpm도 마찬가지로 패키지를 글로벌 저장소에 한 번만 저장하고, 각 프로젝트에서는 심볼릭 링크로 연결합니다.
이 방식의 장점은 명확합니다. 첫째, 디스크 공간을 획기적으로 절약합니다.
같은 패키지를 여러 번 저장할 필요가 없으니까요. 둘째, 설치 속도가 빨라집니다.
이미 저장된 패키지는 다시 다운로드하지 않고 링크만 걸면 됩니다. pnpm-workspace.yaml 파일을 살펴봅시다.
packages 배열에는 워크스페이스에 포함할 경로 패턴을 적습니다. 'packages/*'는 packages 폴더 바로 아래의 모든 폴더를 포함하라는 뜻입니다.
느낌표(!)로 시작하는 패턴은 제외를 의미합니다. '!/test/'는 모든 test 폴더를 워크스페이스에서 제외합니다.
테스트 코드는 보통 별도로 관리하니까요. 루트 package.json의 scripts를 보면 pnpm -r 명령이 눈에 띕니다.
여기서 -r은 recursive의 약자로, 모든 워크스페이스 패키지에서 해당 스크립트를 실행하라는 뜻입니다. pnpm -r build를 실행하면 packages/core, packages/ui, apps/web 등 모든 패키지의 build 스크립트가 순차적으로 실행됩니다.
의존성 순서를 자동으로 파악해서 core를 먼저 빌드하고, 그 다음 core에 의존하는 패키지를 빌드합니다. --parallel 플래그를 추가하면 병렬 실행도 가능합니다.
dev 스크립트처럼 서로 의존성이 없는 작업은 동시에 실행해서 시간을 절약할 수 있습니다. 실무에서 자주 사용하는 명령어도 알아두면 좋습니다.
pnpm add lodash --filter @my-app/web은 특정 패키지에만 의존성을 추가합니다. pnpm why lodash는 왜 이 패키지가 설치되었는지 의존성 트리를 보여줍니다.
김개발 씨가 pnpm install을 실행해봤습니다. 놀랍게도 1분도 안 되어 설치가 끝났습니다.
node_modules 용량도 절반 이하로 줄었습니다. "이거 완전 신세계네요!"
실전 팁
💡 - pnpm add -D는 devDependencies에 추가, pnpm add -w는 루트 워크스페이스에 추가합니다
- .npmrc 파일에 shamefully-hoist=true를 설정하면 일부 호환성 문제를 해결할 수 있습니다
3. tsconfig.json 분석
김개발 씨가 새 기능을 개발하다가 이상한 에러를 만났습니다. "Cannot find module '@my-app/utils'" 분명히 packages/utils 폴더에 코드가 있는데, TypeScript가 찾지 못하는 것입니다.
박시니어 씨가 tsconfig.json을 열어보더니 문제를 바로 찾아냈습니다.
tsconfig.json은 TypeScript 컴파일러의 동작을 제어하는 설정 파일입니다. 마치 요리사에게 레시피를 건네주는 것처럼, 어떤 파일을 어떻게 컴파일할지 상세한 지시사항을 담고 있습니다.
모노레포에서는 공통 설정을 상속받아 각 패키지마다 필요한 부분만 오버라이드하는 방식으로 관리합니다.
다음 코드를 살펴봅시다.
// tsconfig.base.json (루트 공통 설정)
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
// 경로 별칭 설정
"paths": {
"@my-app/*": ["./packages/*/src"]
},
"baseUrl": "."
}
}
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
김개발 씨는 tsconfig.json을 열어봤지만 옵션이 너무 많아서 어디서부터 봐야 할지 막막했습니다. "이거 다 외워야 하나요?" 박시니어 씨가 웃으며 대답했습니다.
"다 외울 필요 없어. 중요한 몇 가지만 이해하면 돼." tsconfig.json을 이해하려면 먼저 TypeScript 컴파일러가 하는 일을 알아야 합니다.
컴파일러는 크게 두 가지 역할을 합니다. 첫째, 타입을 검사합니다.
둘째, TypeScript 코드를 JavaScript로 변환합니다. compilerOptions는 이 두 작업을 어떻게 수행할지 결정합니다.
가장 중요한 옵션들을 살펴봅시다. target은 어떤 버전의 JavaScript로 변환할지 결정합니다.
ES2022로 설정하면 최신 문법을 그대로 사용하고, ES5로 설정하면 오래된 브라우저도 지원하는 코드로 변환합니다. module은 모듈 시스템을 지정합니다.
ESNext는 최신 ES 모듈 형식(import/export)을 사용합니다. CommonJS로 설정하면 Node.js의 require/module.exports 형식으로 변환됩니다.
moduleResolution은 모듈을 어떻게 찾을지 결정하는 중요한 옵션입니다. bundler로 설정하면 Vite, webpack 같은 번들러와 잘 호환됩니다.
node로 설정하면 Node.js 방식으로 모듈을 찾습니다. strict를 true로 설정하면 엄격한 타입 검사가 활성화됩니다.
처음엔 귀찮게 느껴질 수 있지만, 런타임 에러를 컴파일 타임에 잡아주므로 반드시 켜두는 것이 좋습니다. paths 옵션은 김개발 씨가 겪은 문제의 해결책입니다.
"@my-app/": ["./packages//src"]로 설정하면, import { something } from '@my-app/utils'라고 작성했을 때 TypeScript가 ./packages/utils/src 폴더에서 모듈을 찾습니다. 모노레포에서는 extends 키워드가 핵심입니다.
각 패키지의 tsconfig.json에서 "extends": "../../tsconfig.base.json"으로 루트 설정을 상속받습니다. 공통 설정은 한 곳에서 관리하고, 각 패키지는 필요한 부분만 오버라이드하면 됩니다.
outDir은 컴파일된 JavaScript 파일이 저장될 위치, rootDir은 소스 파일의 루트 경로입니다. include 배열은 컴파일에 포함할 파일 패턴을 지정합니다.
김개발 씨가 paths 설정을 추가하고 에디터를 재시작하니, 드디어 에러가 사라졌습니다. "설정 파일 하나가 이렇게 중요한 거였군요!"
실전 팁
💡 - skipLibCheck: true로 설정하면 node_modules의 타입 검사를 건너뛰어 빌드 속도가 빨라집니다
- VSCode에서 TypeScript 버전을 워크스페이스 버전으로 설정해야 경로 별칭이 제대로 인식됩니다
4. tsc 빌드 프로세스
김개발 씨가 pnpm build를 실행했습니다. 터미널에 뭔가 주르륵 출력되더니 dist 폴더가 생겼습니다.
그런데 그 안에는 .js 파일뿐 아니라 .d.ts, .js.map 파일도 있었습니다. "이 파일들은 다 뭐예요?"
tsc는 TypeScript Compiler의 약자로, TypeScript 코드를 JavaScript로 변환하는 공식 도구입니다. 마치 번역가가 한국어 소설을 영어로 번역하면서 주석과 참고자료도 함께 만드는 것처럼, tsc는 코드 변환과 함께 타입 정의 파일과 소스맵도 생성합니다.
다음 코드를 살펴봅시다.
// package.json의 빌드 스크립트
{
"scripts": {
"build": "tsc --project tsconfig.build.json",
"build:watch": "tsc --watch",
"typecheck": "tsc --noEmit"
}
}
// tsconfig.build.json (빌드 전용 설정)
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts", "**/*.spec.ts"]
}
// 빌드 결과물 구조
dist/
├── index.js # 변환된 JavaScript
├── index.d.ts # 타입 정의 파일
├── index.js.map # 소스맵
└── index.d.ts.map # 타입 정의 소스맵
김개발 씨는 dist 폴더를 열어보며 궁금해했습니다. 파일 확장자가 다양해서 각각 무슨 역할인지 알 수 없었습니다.
박시니어 씨가 설명을 시작했습니다. "tsc가 생성하는 파일들은 각각 명확한 목적이 있어." 먼저 .js 파일은 가장 기본적인 결과물입니다.
TypeScript 코드에서 타입 정보를 제거하고 순수한 JavaScript로 변환한 것입니다. 브라우저나 Node.js가 실제로 실행하는 파일이죠.
.d.ts 파일은 타입 정의 파일입니다. 마치 도서관의 목록표처럼, 이 라이브러리에 어떤 함수가 있고 어떤 타입을 받는지 정보만 담고 있습니다.
다른 TypeScript 프로젝트에서 이 패키지를 사용할 때 타입 검사에 활용됩니다. .js.map 파일은 소스맵입니다.
변환된 JavaScript 코드와 원본 TypeScript 코드의 연결 정보를 담고 있습니다. 브라우저 개발자 도구에서 디버깅할 때, 소스맵 덕분에 원본 TypeScript 코드를 보면서 디버깅할 수 있습니다.
이제 빌드 스크립트를 살펴봅시다. tsc --project tsconfig.build.json은 빌드 전용 설정 파일을 사용하라는 뜻입니다.
왜 별도의 설정 파일이 필요할까요? 개발할 때는 테스트 파일도 타입 검사가 필요합니다.
하지만 빌드 결과물에는 테스트 코드가 포함되면 안 됩니다. 그래서 tsconfig.build.json에서 exclude로 테스트 파일을 제외합니다.
--watch 플래그는 파일이 변경될 때마다 자동으로 다시 빌드합니다. 개발 중에 유용합니다.
파일을 저장할 때마다 수동으로 빌드할 필요가 없죠. --noEmit 플래그는 타입 검사만 수행하고 파일을 생성하지 않습니다.
CI/CD 파이프라인에서 타입 에러만 확인하고 싶을 때 사용합니다. 번들러가 별도로 있는 프로젝트에서도 타입 검사 용도로 활용합니다.
declaration과 declarationMap 옵션이 true일 때만 .d.ts와 .d.ts.map 파일이 생성됩니다. 라이브러리를 만들 때는 반드시 켜야 하고, 최종 애플리케이션에서는 필요 없을 수도 있습니다.
김개발 씨가 직접 tsc --watch를 실행해봤습니다. 코드를 수정하고 저장할 때마다 터미널에 "File change detected.
Starting incremental compilation..."이라는 메시지가 나타났습니다. "이제 빌드가 어떻게 돌아가는지 알겠어요!"
실전 팁
💡 - incremental: true 옵션을 추가하면 변경된 파일만 다시 컴파일하여 빌드 속도가 향상됩니다
- composite: true는 프로젝트 참조 기능을 활성화하여 모노레포에서 의존성 순서대로 빌드할 수 있습니다
5. rolldown 번들러 활용
김개발 씨가 빌드된 패키지를 배포하려고 보니 dist 폴더에 파일이 수십 개였습니다. "이걸 다 배포해야 하나요?" 박시니어 씨가 대답했습니다.
"아니, 번들러를 써서 하나로 합쳐야지. 요즘은 rolldown이 엄청 빠르더라."
rolldown은 Rust로 작성된 차세대 JavaScript 번들러입니다. 마치 여러 권의 책을 한 권의 전집으로 묶는 것처럼, 여러 모듈 파일을 하나 또는 적은 수의 파일로 합칩니다.
Rollup과 호환되는 API를 제공하면서도 훨씬 빠른 빌드 속도를 자랑합니다.
다음 코드를 살펴봅시다.
// rolldown.config.js
import { defineConfig } from 'rolldown';
export default defineConfig({
input: './src/index.ts',
output: {
dir: './dist',
format: 'esm', // ES 모듈 형식
sourcemap: true,
// 코드 스플리팅 활성화
entryFileNames: '[name].js',
chunkFileNames: 'chunks/[name]-[hash].js'
},
// TypeScript 지원 내장
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
// 외부 의존성 제외 (라이브러리용)
external: ['react', 'react-dom'],
// 트리 쉐이킹으로 사용하지 않는 코드 제거
treeshake: true
});
// package.json
{
"scripts": {
"bundle": "rolldown --config rolldown.config.js"
}
}
김개발 씨는 번들러라는 개념이 처음이었습니다. "tsc로 빌드하면 끝 아닌가요?
왜 또 다른 도구가 필요해요?" 박시니어 씨가 비유를 들어 설명했습니다. "tsc는 번역가야.
한국어를 영어로 번역해주지. 하지만 번들러는 편집자야.
여러 장의 원고를 한 권의 책으로 엮어주는 거지." TypeScript 프로젝트를 tsc로 빌드하면 각 소스 파일마다 하나의 JavaScript 파일이 생성됩니다. 파일이 100개면 100개의 .js 파일이 만들어집니다.
이걸 그대로 배포하면 브라우저가 100번의 HTTP 요청을 보내야 합니다. 번들러는 이 파일들을 분석해서 하나 또는 적은 수의 파일로 합칩니다.
파일 수가 줄면 네트워크 요청도 줄고, 페이지 로딩 속도가 빨라집니다. rolldown은 최근 등장한 번들러로, Rust라는 시스템 프로그래밍 언어로 작성되었습니다.
JavaScript로 만든 기존 번들러들보다 훨씬 빠릅니다. 특히 대규모 프로젝트에서 빌드 시간 차이가 크게 납니다.
설정 파일을 살펴봅시다. input은 번들링의 시작점입니다.
이 파일에서 시작해서 import된 모든 모듈을 추적합니다. output.format은 출력 형식을 결정합니다.
esm은 ES 모듈 형식으로, 최신 브라우저와 Node.js에서 사용합니다. cjs는 CommonJS 형식으로 Node.js 전용입니다.
라이브러리를 만들 때는 두 형식 모두 제공하기도 합니다. external 배열은 번들에 포함하지 않을 패키지를 지정합니다.
라이브러리를 만들 때 react 같은 의존성을 번들에 포함하면, 이 라이브러리를 사용하는 앱에서 react가 중복됩니다. 그래서 외부 의존성으로 선언합니다.
treeshake 옵션은 트리 쉐이킹을 활성화합니다. 마치 나무에서 죽은 가지를 쳐내듯, 코드에서 실제로 사용하지 않는 부분을 자동으로 제거합니다.
export된 함수 중 실제로 import되지 않은 것은 최종 번들에 포함되지 않습니다. 코드 스플리팅도 중요한 기능입니다.
하나의 거대한 파일 대신, 논리적으로 분리된 여러 청크로 나눕니다. 사용자가 특정 페이지에 접근할 때 필요한 코드만 로드할 수 있습니다.
김개발 씨가 rolldown을 실행해봤습니다. 수십 개의 파일이 단 몇 개로 줄었고, 빌드 시간도 1초가 채 걸리지 않았습니다.
"이렇게 빠를 수가!"
실전 팁
💡 - format: 'esm'과 format: 'cjs' 두 가지를 모두 출력하면 다양한 환경을 지원할 수 있습니다
- minify 옵션으로 코드 압축까지 하면 번들 크기를 더 줄일 수 있습니다
6. 프로덕션 빌드 최적화
드디어 김개발 씨의 첫 프로젝트 배포일이 다가왔습니다. 박시니어 씨가 말했습니다.
"개발 환경에서는 잘 돌아가도 프로덕션에서는 다를 수 있어. 최적화 설정을 꼼꼼히 확인해보자."
프로덕션 빌드 최적화는 실제 사용자에게 서비스를 배포하기 위한 마지막 단계입니다. 마치 출판 전에 교정과 편집을 거치는 것처럼, 코드를 압축하고 불필요한 부분을 제거하며 성능을 극대화합니다.
빌드 속도와 번들 크기, 런타임 성능의 균형을 찾는 것이 핵심입니다.
다음 코드를 살펴봅시다.
// build.config.ts (통합 빌드 설정)
import { defineConfig } from 'rolldown';
export default defineConfig({
input: {
index: './src/index.ts',
utils: './src/utils/index.ts'
},
output: {
dir: './dist',
format: 'esm',
sourcemap: 'hidden', // 소스맵은 생성하되 번들에서 참조 제거
minify: true, // 코드 압축
entryFileNames: '[name].[hash].js', // 캐시 버스팅용 해시
},
treeshake: {
moduleSideEffects: false // 사이드 이펙트 없는 모듈만 트리 쉐이킹
},
// 환경 변수 주입
define: {
'process.env.NODE_ENV': JSON.stringify('production'),
'__DEV__': 'false'
}
});
// package.json 최적화 스크립트
{
"scripts": {
"build:prod": "NODE_ENV=production rolldown -c build.config.ts",
"analyze": "rolldown -c --analyze",
"clean": "rm -rf dist && rm -rf .rolldown-cache"
}
}
김개발 씨는 개발 환경에서 만든 빌드를 그대로 배포하려 했습니다. 하지만 박시니어 씨가 제지했습니다.
"잠깐, 프로덕션 빌드는 따로 해야 해." 프로덕션 빌드가 개발 빌드와 다른 이유가 뭘까요? 개발 환경에서는 디버깅 편의성이 중요합니다.
에러가 발생하면 정확한 위치를 알아야 하고, 코드를 수정하면 빠르게 반영되어야 합니다. 그래서 소스맵도 완전히 포함하고, 코드도 압축하지 않습니다.
반면 프로덕션 환경에서는 성능이 최우선입니다. 사용자의 브라우저로 전송되는 파일 크기는 작을수록 좋고, 실행 속도는 빠를수록 좋습니다.
minify 옵션은 코드를 최대한 압축합니다. 변수 이름을 짧게 바꾸고, 불필요한 공백과 주석을 제거합니다.
사람이 읽기는 어렵지만 파일 크기가 크게 줄어듭니다. **sourcemap: 'hidden'**은 소스맵 파일은 생성하되, 번들 파일에서 소스맵 참조를 제거합니다.
필요할 때 에러 추적 서비스에 소스맵을 업로드해서 사용할 수 있지만, 일반 사용자에게는 노출되지 않습니다. 파일명에 해시를 추가하는 것은 캐시 버스팅을 위해서입니다.
브라우저는 같은 이름의 파일을 캐시합니다. 코드가 변경되어도 파일명이 같으면 이전 버전을 계속 보여줄 수 있습니다.
해시를 추가하면 코드가 변경될 때 파일명도 변경되어 항상 최신 버전을 로드합니다. define 옵션은 빌드 시점에 상수를 주입합니다.
process.env.NODE_ENV를 'production'으로 설정하면, 많은 라이브러리가 개발용 경고 메시지를 제거하고 최적화된 코드를 사용합니다. moduleSideEffects: false는 트리 쉐이킹을 더 공격적으로 수행합니다.
모듈을 import만 하고 사용하지 않으면 번들에서 완전히 제거합니다. 단, 실제로 사이드 이펙트가 있는 모듈(CSS import 등)은 제외해야 합니다.
--analyze 플래그를 사용하면 번들의 구성을 시각적으로 볼 수 있습니다. 어떤 라이브러리가 번들 크기의 대부분을 차지하는지 파악하고, 최적화 포인트를 찾을 수 있습니다.
김개발 씨가 프로덕션 빌드를 실행했습니다. 번들 크기가 개발 빌드의 절반도 안 되었습니다.
"이제 진짜 배포할 준비가 됐네요!" 박시니어 씨가 어깨를 두드리며 말했습니다. "축하해.
이제 TypeScript 프로젝트 구조의 기본은 마스터한 거야."
실전 팁
💡 - CI/CD 파이프라인에서 빌드 캐시를 활용하면 배포 시간을 크게 단축할 수 있습니다
- 번들 분석 도구로 정기적으로 번들 크기를 모니터링하세요. 의존성 추가 시 크기 증가를 바로 파악할 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
UX와 협업 패턴 완벽 가이드
AI 에이전트와 사용자 간의 효과적인 협업을 위한 UX 패턴을 다룹니다. 프롬프트 핸드오프부터 인터럽트 처리까지, 현대적인 에이전트 시스템 설계의 핵심을 배웁니다.
자가 치유 및 재시도 패턴 완벽 가이드
AI 에이전트와 분산 시스템에서 필수적인 자가 치유 패턴을 다룹니다. 에러 감지부터 서킷 브레이커까지, 시스템을 스스로 복구하는 탄력적인 코드 작성법을 배워봅니다.
Feedback Loops 컴파일러와 CI/CD 완벽 가이드
컴파일러 피드백 루프부터 CI/CD 파이프라인, 테스트 자동화, 자가 치유 빌드까지 현대 개발 워크플로우의 핵심을 다룹니다. 초급 개발자도 쉽게 이해할 수 있도록 실무 예제와 함께 설명합니다.
실전 MCP 통합 프로젝트 완벽 가이드
Model Context Protocol을 활용한 실전 통합 프로젝트를 처음부터 끝까지 구축하는 방법을 다룹니다. 아키텍처 설계부터 멀티 서버 통합, 모니터링, 배포까지 운영 레벨의 MCP 시스템을 구축하는 노하우를 담았습니다.
MCP 동적 도구 업데이트 완벽 가이드
AI 에이전트의 도구를 런타임에 동적으로 로딩하고 관리하는 방법을 알아봅니다. 플러그인 시스템 설계부터 핫 리로딩, 보안까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.