목차
메타 설명:TypeScript 프로젝트의 핵심 설정 파일인 `tsconfig.json`을 입문자 눈높이에서 알아봅니다. `target`, `module`, `rootDir`, `outDir`, `strict`, `include`, `exclude`의 의미와 프로젝트 폴더 구성, 컴파일 방법까지 실습으로 정리합니다.
지난 시간에는 TypeScript 코드가 JavaScript로 변신하는 과정을 추적했습니다.
우리가 사용한 명령어는 다음과 같았습니다.
npx tsc app.ts --target ES2022 --noEmitOnError아직 옵션이 두 개뿐이라 크게 복잡해 보이지 않습니다.
하지만 프로젝트에 필요한 설정을 하나씩 추가하면 명령어가 점점 길어집니다.
npx tsc app.ts \
--target ES2022 \
--module CommonJS \
--rootDir src \
--outDir dist \
--strict \
--noEmitOnError여기에 파일이 열 개, 백 개로 늘어난다면 어떻게 될까요?
app.ts
user.ts
product.ts
order.ts
payment.ts
auth.ts
notification.ts모든 파일을 명령어에 하나씩 적는 것은 꽤 고된 작업입니다.
npx tsc app.ts user.ts product.ts order.ts payment.ts개발자의 손가락이 조용히 퇴사 준비를 시작할지도 모릅니다.
더 큰 문제는 프로젝트를 실행할 때마다 똑같은 옵션을 반복해서 입력해야 한다는 점입니다.
이때 필요한 것이 바로 다음 파일입니다.
tsconfig.json`tsconfig.json`은 TypeScript 프로젝트의 설정을 한곳에 저장하는 파일입니다.
어떤 파일을 컴파일할지, 어떤 JavaScript 버전으로 변환할지, 결과 파일을 어디에 저장할지, 얼마나 엄격하게 타입을 검사할지를 이 파일에서 결정합니다.
TypeScript 공식 문서에서는 `tsconfig.json`이 있는 디렉터리를 프로젝트의 루트로 보고, 이 파일이 프로젝트의 루트 파일과 컴파일러 옵션을 지정한다고 설명합니다.
오늘은 TypeScript 프로젝트의 조종석에 앉아 하나씩 스위치를 켜 보겠습니다.
1. 오늘의 조종 미션
이번 편을 마치면 다음 작업을 할 수 있습니다.
- `tsconfig.json`의 역할을 설명할 수 있습니다.
- `npx tsc --init`으로 설정 파일을 만들 수 있습니다.
- `compilerOptions`의 의미를 이해할 수 있습니다.
- `target`과 `module`의 차이를 구분할 수 있습니다.
- `rootDir`과 `outDir`로 소스와 결과물을 분리할 수 있습니다.
- `strict`를 이용해 타입 검사를 강화할 수 있습니다.
- `include`와 `exclude`로 컴파일 대상을 관리할 수 있습니다.
- 파일 이름을 입력하지 않고 프로젝트 전체를 컴파일할 수 있습니다.
- `--watch`, `--project`, `--showConfig` 같은 명령어를 사용할 수 있습니다.
- 자주 발생하는 설정 오류를 해결할 수 있습니다.
오늘 완성할 프로젝트 구조는 다음과 같습니다.
typescript-study/
├── src/
│ ├── index.ts
│ └── calculator.ts
├── dist/
│ ├── index.js
│ └── calculator.js
├── node_modules/
├── package.json
├── package-lock.json
└── tsconfig.json`src`에는 우리가 작성한 TypeScript 코드가 들어갑니다.
`dist`에는 컴파일된 JavaScript 코드가 들어갑니다.
소스 코드와 실행 결과물이 서로 다른 방에 깔끔하게 정리되는 구조입니다.
2. tsconfig.json이 필요한 이유
설정 파일이 없다면 컴파일할 때마다 옵션을 직접 입력해야 합니다.
npx tsc src/index.ts \
--target ES2022 \
--module CommonJS \
--outDir dist \
--strict다음 날에도 다시 입력합니다.
npx tsc src/index.ts \
--target ES2022 \
--module CommonJS \
--outDir dist \
--strict팀원도 똑같은 옵션을 입력해야 합니다.
팀원 A: target이 뭐였죠?
팀원 B: ES2022였던 것 같은데요.
팀원 C: 저는 ES2015로 했는데요.
컴파일러: 여러분, 회의부터 하고 오시겠습니까?명령어에 옵션을 직접 작성하면 다음 문제가 생길 수 있습니다.
- 옵션을 빼먹을 수 있습니다.
- 개발자마다 다른 설정을 사용할 수 있습니다.
- 명령어가 길어집니다.
- 프로젝트 파일이 늘어나면 관리하기 어렵습니다.
- 빌드 환경을 재현하기 어려워집니다.
`tsconfig.json`을 사용하면 설정을 프로젝트에 저장할 수 있습니다.
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"strict": true
}
}이제 컴파일 명령어가 아주 짧아집니다.
npx tscTypeScript 컴파일러가 현재 폴더에서 가장 가까운 `tsconfig.json`을 찾아 그 설정으로 프로젝트를 컴파일합니다. 반대로 명령어에 `app.ts`처럼 입력 파일을 직접 지정하면 `tsconfig.json` 설정은 무시됩니다.
3. tsconfig.json 만들기
지난 시간에 만든 `typescript-study` 폴더를 계속 사용하겠습니다.
터미널의 현재 위치가 프로젝트 폴더인지 확인합니다.
pwdWindows PowerShell에서는 다음 명령어로 현재 위치를 확인할 수 있습니다.
Get-Location현재 폴더에 `package.json`이 있는지도 확인합니다.
Windows:
dirmacOS 또는 Linux:
ls다음과 비슷한 파일이 보이면 준비가 된 것입니다.
node_modules
package.json
package-lock.json이제 다음 명령어를 실행합니다.
npx tsc --init`--init`은 TypeScript 프로젝트를 초기화하고 `tsconfig.json` 파일을 생성하는 공식 CLI 옵션입니다.
프로젝트에 새로운 파일이 생깁니다.
tsconfig.json현재 구조는 다음과 같습니다.
typescript-study/
├── node_modules/
├── package.json
├── package-lock.json
└── tsconfig.json컴파일러의 프로젝트 개업식
`npx tsc --init`을 실행하는 순간 컴파일러가 팻말을 하나 세웁니다.
이 디렉터리는 이제
TypeScript 프로젝트입니다.`tsconfig.json`이 있는 위치가 프로젝트의 기준점이 됩니다.
컴파일러는 이 위치를 기준으로 다음을 판단합니다.
어떤 파일을 읽을까?
어떤 문법으로 변환할까?
결과 파일을 어디에 둘까?
타입 검사를 얼마나 엄격하게 할까?TypeScript 버전에 따라 `--init`으로 생성되는 파일의 기본 내용은 조금 달라질 수 있습니다. 최근 TypeScript에서는 예전처럼 수많은 주석 옵션을 생성하기보다 비교적 간결한 설정 파일을 생성하는 방향으로 바뀌었습니다.
따라서 여러분의 화면이 예제와 완전히 같지 않아도 이상한 것이 아닙니다.
4. tsconfig.json의 기본 구조
`tsconfig.json`을 열어 보면 다음과 비슷한 형태를 볼 수 있습니다.
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"strict": true
}
}가장 중요한 영역은 다음입니다.
"compilerOptions": {
}이름 그대로 TypeScript 컴파일러의 동작을 설정하는 공간입니다.
compiler
컴파일러
options
선택 사항 또는 설정즉, 다음과 같은 의미입니다.
“컴파일러님, 이번 프로젝트에서는 이 규칙에 따라 작업해 주세요.”
프로젝트 조종석 구조
`tsconfig.json`을 조종석으로 표현하면 다음과 같습니다.
tsconfig.json
│
├── compilerOptions
│ ├── target
│ ├── module
│ ├── rootDir
│ ├── outDir
│ ├── strict
│ └── noEmitOnError
│
├── include
│
└── exclude`compilerOptions`는 변환과 타입 검사 방식을 결정합니다.
`include`는 프로젝트에 포함할 파일을 선택합니다.
`exclude`는 `include`로 찾은 파일 중 제외할 대상을 지정합니다.
5. 오늘 사용할 기본 설정
이번 실습에서는 다음 설정을 사용하겠습니다.
`tsconfig.json`을 아래와 같이 수정합니다.
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"noEmitOnError": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}처음 보면 설정이 많아 보입니다.
하지만 실제로는 각 스위치가 하나의 역할만 담당합니다.
target
어떤 JavaScript 문법으로 변환할까?
module
import와 export를 어떤 모듈 방식으로 처리할까?
rootDir
TypeScript 원본 파일의 기준 폴더는 어디인가?
outDir
JavaScript 결과물을 어디에 저장할까?
strict
타입 검사를 엄격하게 할까?
noEmitOnError
오류가 있을 때 결과물을 만들까?
include
어떤 파일을 프로젝트에 포함할까?
exclude
어떤 파일을 기본 검색 대상에서 제외할까?이제 각 스위치를 직접 눌러 보겠습니다.
6. target: JavaScript 시간대를 결정한다
첫 번째 옵션은 `target`입니다.
{
"compilerOptions": {
"target": "ES2022"
}
}`target`은 TypeScript 코드를 어떤 버전의 JavaScript 문법으로 변환할지 결정합니다.
지난 편에서 다음 코드를 실험했습니다.
const greet = (name: string): string => {
return `안녕하세요, ${name}님`;
};낮은 버전의 JavaScript를 대상으로 변환하면 화살표 함수와 템플릿 리터럴이 이전 문법으로 바뀔 수 있습니다.
var greet = function (name) {
return "안녕하세요, ".concat(name, "님");
};비교적 최신 버전을 대상으로 하면 최신 문법이 유지됩니다.
const greet = (name) => {
return `안녕하세요, ${name}님`;
};target 시간 여행기
`target`은 JavaScript 시간 여행 엘리베이터입니다.
ES5
오래된 JavaScript 시대로 이동
ES2015
let, const, 클래스 등이 등장한 시대로 이동
ES2022
비교적 현대적인 JavaScript 환경으로 이동
ESNext
최신 문법을 최대한 유지낮은 target을 선택하면 오래된 실행 환경을 위해 더 많은 문법이 변환됩니다.
높은 target을 선택하면 원본의 최신 JavaScript 문법이 더 많이 유지됩니다.
무엇을 선택해야 할까?
이번 연재의 Node.js 실습에서는 다음 값을 사용하겠습니다.
"target": "ES2022"비교적 현대적인 실행 환경을 기준으로 하면서 결과 코드도 읽기 쉬운 편입니다.
다만 실제 프로젝트에서는 실행 환경에 맞춰 결정해야 합니다.
최신 Node.js 서버
비교적 높은 target 사용 가능
오래된 브라우저까지 지원
더 낮은 target 또는 별도 빌드 도구 필요
최신 브라우저만 지원
높은 target 사용 가능`target`은 단순히 출력 코드의 모양만 바꾸는 것이 아니라, TypeScript가 기본으로 알고 있는 JavaScript 내장 API 타입에도 영향을 줄 수 있습니다.
7. module: 파일들이 대화하는 방식을 결정한다
다음 옵션은 `module`입니다.
{
"compilerOptions": {
"module": "CommonJS"
}
}프로젝트가 커지면 코드를 여러 파일로 나누게 됩니다.
calculator.ts
user.ts
order.ts
index.ts파일 사이에서 함수나 값을 주고받을 때 `import`와 `export`를 사용합니다.
export function add(a: number, b: number): number {
return a + b;
}다른 파일에서는 가져옵니다.
import { add } from "./calculator";`module` 옵션은 이러한 모듈 코드를 어떤 JavaScript 모듈 형식으로 처리할지 결정합니다.
TypeScript 공식 문서에 따르면 `module`은 생성되는 JavaScript의 모듈 형식을 제어할 뿐 아니라, TypeScript가 파일의 모듈 종류와 `import`·`export` 관계를 이해하는 데도 영향을 줍니다.
CommonJS 방식
이번 입문 실습에서는 다음 설정을 사용합니다.
"module": "CommonJS"TypeScript 코드:
import { add } from "./calculator";컴파일 결과는 다음과 비슷할 수 있습니다.
const calculator_1 = require("./calculator");CommonJS는 오랫동안 Node.js 프로젝트에서 널리 사용된 방식입니다.
입문 실습에서는 `package.json`의 추가 모듈 설정 없이 비교적 단순하게 실행할 수 있다는 장점이 있습니다.
현대 Node.js 프로젝트에서는?
최신 Node.js 환경에서는 다음과 같은 모듈 설정도 사용합니다.
"module": "NodeNext"또는 실행 환경에 따라 `Node16`, `Node18`, `Node20` 같은 설정을 선택할 수 있습니다.
이 모드들은 Node.js의 ESM과 CommonJS 동작, 파일 확장자, `package.json`의 `"type"` 값 등을 함께 반영합니다.
하지만 모듈 시스템은 TypeScript 입문자에게 꽤 넓은 미로입니다.
이번 편에서는 우선 다음 설정으로 흐름을 단순화하겠습니다.
"module": "CommonJS"이후 `import`와 `export`를 다루는 모듈 편에서 ESM과 CommonJS의 차이를 자세히 살펴보겠습니다.
8. rootDir: 원본 코드의 기준 폴더
다음 설정은 `rootDir`입니다.
{
"compilerOptions": {
"rootDir": "./src"
}
}`rootDir`은 컴파일할 TypeScript 원본 파일들이 어느 폴더 구조를 기준으로 배치되어 있는지 알려줍니다.
이번 프로젝트에서는 TypeScript 코드를 `src` 폴더에 저장합니다.
typescript-study/
└── src/
├── index.ts
└── calculator.ts따라서 다음처럼 설정합니다.
"rootDir": "./src"rootDir에 대한 중요한 오해
많은 입문자가 이렇게 생각합니다.
“rootDir을 `src`로 지정했으니 `src` 파일만 컴파일되겠군요.”
하지만 정확히는 그렇지 않습니다.
`rootDir`은 어떤 파일이 컴파일 대상에 포함되는지를 결정하지 않습니다.
공식 문서에서도 `rootDir`은 `include`, `exclude`, `files` 설정과 상호작용하지 않으며, 컴파일 대상 선택에 영향을 주지 않는다고 설명합니다.
컴파일할 파일을 선택하는 역할은 주로 `include`가 담당합니다.
rootDir
출력 폴더 구조를 만들 때 사용할 입력 기준점
include
실제로 어떤 파일을 프로젝트에 포함할지 결정rootDir의 진짜 역할
다음 구조를 살펴보겠습니다.
src/
├── index.ts
└── utils/
└── calculator.ts`rootDir`을 `src`로 설정하고 `outDir`을 `dist`로 설정하면 폴더 구조를 유지하며 JavaScript가 생성됩니다.
dist/
├── index.js
└── utils/
└── calculator.js원본 폴더 안의 상대적인 구조가 결과 폴더에도 유지됩니다.
9. outDir: 컴파일 결과물의 도착지
다음 설정은 `outDir`입니다.
{
"compilerOptions": {
"outDir": "./dist"
}
}`outDir`은 컴파일된 JavaScript 파일을 저장할 폴더입니다.
out
출력
directory
디렉터리 또는 폴더즉, 출력 폴더라는 뜻입니다.
설정 전
`outDir`을 지정하지 않고 다음 명령어를 실행하면,
npx tsc src/index.tsJavaScript 파일이 TypeScript 파일 옆에 생성될 수 있습니다.
src/
├── index.ts
└── index.js파일이 몇 개 없을 때는 괜찮아 보입니다.
하지만 프로젝트가 커지면 `.ts`와 `.js` 파일이 뒤섞입니다.
src/
├── index.ts
├── index.js
├── user.ts
├── user.js
├── product.ts
├── product.js
├── order.ts
└── order.js폴더가 TypeScript와 JavaScript의 합숙소가 됩니다.
설정 후
다음처럼 설정합니다.
"rootDir": "./src",
"outDir": "./dist"이제 원본과 결과물이 분리됩니다.
typescript-study/
├── src/
│ ├── index.ts
│ └── calculator.ts
│
└── dist/
├── index.js
└── calculator.js`src`는 개발자가 작업하는 공간입니다.
`dist`는 실행하거나 배포할 결과물이 들어가는 공간입니다.
`dist`는 일반적으로 distribution의 줄임말로 사용됩니다.
폴더 호텔 배정표
프로젝트 관리자가 파일들에게 방을 배정합니다.
.ts 파일 여러분
src 객실로 이동하세요.
.js 파일 여러분
dist 객실로 이동하세요.`rootDir`과 `outDir`을 사용하면 서로 다른 손님이 한 방에 뒤섞이는 일을 막을 수 있습니다.
10. strict: 타입 검사관의 집중 근무 모드
다음 설정은 매우 중요합니다.
{
"compilerOptions": {
"strict": true
}
}`strict`는 TypeScript의 여러 엄격한 타입 검사 옵션을 한꺼번에 활성화합니다.
strict: false
컴파일러가 일부 문제를 비교적 관대하게 처리
strict: true
컴파일러가 모호하고 위험한 부분을 적극적으로 확인TypeScript를 사용하는 핵심 이유가 타입 안전성이라면 새로운 프로젝트에서는 일반적으로 `strict`를 켜고 시작하는 편이 좋습니다.
strict를 끈 코드
다음 함수를 살펴보겠습니다.
function greet(name) {
return `안녕하세요, ${name}님`;
}`name`의 타입이 작성되어 있지 않습니다.
엄격한 설정이 꺼져 있다면 상황에 따라 `name`이 `any`로 처리될 수 있습니다.
`any`는 타입 검사를 사실상 우회하는 타입입니다.
greet("김타입");
greet(100);
greet(true);
greet({ name: "김객체" });함수는 다양한 값을 받지만, 정말 이것이 개발자가 의도한 것인지 불분명합니다.
strict를 켠 코드
`strict`가 켜져 있으면 매개변수 타입이 모호한 부분에서 오류를 확인할 수 있습니다.
Parameter 'name' implicitly has an 'any' type.컴파일러 통역기를 켜 보겠습니다.
“name의 타입을 정하지 않으셨군요. 저는 이것이 문자열인지 숫자인지 알 수 없습니다.”
타입을 명확하게 작성합니다.
function greet(name: string): string {
return `안녕하세요, ${name}님`;
}이제 다음 호출은 정상입니다.
greet("김타입");다음 호출은 오류입니다.
greet(100);strict 경비대
`strict`는 하나의 기능이 아니라 여러 엄격한 검사 옵션을 묶어 켜는 대표 스위치입니다.
초보자에게는 오류가 늘어난 것처럼 느껴질 수 있습니다.
strict를 켜기 전
조용함
strict를 켠 후
빨간 밑줄 축제하지만 오류가 새로 생긴 것이 아닙니다.
기존에 숨어 있던 불확실성이 화면에 드러난 것입니다.
컴파일러가 갑자기 까다로워진 것이 아니라, 프로젝트의 어두운 구석에 손전등을 켠 셈입니다.
11. noEmitOnError: 오류가 있으면 출고 금지
다음 설정을 추가합니다.
{
"compilerOptions": {
"noEmitOnError": true
}
}`emit`은 컴파일 결과 파일을 생성하는 것을 의미합니다.
따라서 `noEmitOnError`는 다음 의미를 가집니다.
오류가 발생하면
JavaScript 결과물을 생성하지 않는다.다음 코드가 있다고 가정해 보겠습니다.
const price: number = "89000";타입 오류가 있습니다.
`noEmitOnError`가 꺼져 있다면 오류 메시지가 표시되면서도 JavaScript가 만들어질 수 있습니다.
const price = "89000";하지만 다음 설정이 켜져 있다면,
"noEmitOnError": true타입 오류를 해결하기 전까지 새로운 JavaScript 결과물을 생성하지 않습니다.
불량품 출고 관리자
컴파일러 공장에 출고 관리자가 등장했습니다.
타입 검사 결과: 불합격
JavaScript 출고: 중단오류를 수정합니다.
const price: number = 89000;다시 컴파일합니다.
타입 검사 결과: 합격
JavaScript 출고: 승인개발 과정에서 잘못된 코드가 결과물로 만들어지는 일을 줄일 수 있습니다.
12. esModuleInterop: 모듈 사이의 통역관
다음 설정은 `esModuleInterop`입니다.
{
"compilerOptions": {
"esModuleInterop": true
}
}JavaScript 생태계에는 서로 다른 모듈 작성 방식이 존재합니다.
CommonJS
require와 module.exports 중심
ES Module
import와 export 중심일부 라이브러리를 가져올 때 두 방식의 차이로 인해 import 문법이 예상과 다르게 동작할 수 있습니다.
`esModuleInterop`은 CommonJS 모듈과 ES Module 스타일 import 사이의 호환성을 개선하는 데 사용됩니다.
초보 단계에서는 다음처럼 기억하면 충분합니다.
서로 다른 모듈 방식 사이에서 import가 조금 더 자연스럽게 동작하도록 돕는 통역 설정
모듈 시스템을 자세히 배우기 전까지는 Node.js 기반 입문 프로젝트에서 켜 두겠습니다.
"esModuleInterop": true13. forceConsistentCasingInFileNames: 대소문자 질서 유지
다음 설정은 이름이 상당히 깁니다.
{
"compilerOptions": {
"forceConsistentCasingInFileNames": true
}
}단어를 나눠 보겠습니다.
force
강제한다
consistent
일관된
casing
대소문자 사용을
in file names
파일 이름에서파일 이름의 대소문자를 일관되게 사용하도록 검사하는 옵션입니다.
예를 들어 실제 파일 이름이 다음과 같다고 가정해 보겠습니다.
UserService.ts그런데 다른 파일에서 다음처럼 가져옵니다.
import { UserService } from "./userservice";Windows처럼 파일 이름 대소문자를 관대하게 처리하는 환경에서는 동작할 수 있습니다.
하지만 Linux 서버에서는 다른 파일로 판단해 오류가 발생할 수 있습니다.
UserService.ts
userservice.ts
Linux: 서로 다른 이름입니다.이 설정은 개발 컴퓨터에서는 되는데 배포 서버에서는 실패하는 난감한 상황을 줄여 줍니다.
14. skipLibCheck: 외부 타입 파일 검사를 건너뛰기
다음 설정입니다.
{
"compilerOptions": {
"skipLibCheck": true
}
}TypeScript 프로젝트에는 직접 작성한 코드 외에도 라이브러리가 제공하는 타입 선언 파일이 존재합니다.
타입 선언 파일은 보통 다음 확장자를 사용합니다.
.d.ts예를 들면 다음과 같습니다.
node_modules/
└── 어떤 라이브러리/
└── index.d.ts`skipLibCheck`를 켜면 이러한 선언 파일 내부를 완전히 검사하는 작업을 일부 건너뜁니다.
입문 프로젝트에서는 외부 라이브러리 타입 선언의 내부 충돌 때문에 학습 흐름이 끊기는 일을 줄이는 데 도움이 될 수 있습니다.
다만 다음을 구분해야 합니다.
skipLibCheck: true
내가 작성한 TypeScript 코드 검사를 끄는 것
아님
선언 파일 내부의 전체 검사를 생략하는 것
맞음우리 코드의 타입 검사는 계속 수행됩니다.
15. include: 컴파일할 파일을 초대한다
이제 `compilerOptions` 밖으로 나가 보겠습니다.
{
"include": ["src/**/*.ts"]
}`include`는 프로젝트에 포함할 파일이나 경로 패턴을 지정합니다.
"include": ["src/**/*.ts"]이 패턴은 다음 의미입니다.
src/
src 아래 모든 하위 폴더/
모든 .ts 파일예를 들면 다음 파일이 포함됩니다.
src/index.ts
src/calculator.ts
src/user/service.ts
src/order/model/order.tsTSConfig의 `include`는 파일 이름과 와일드카드 패턴을 사용해 프로젝트에 포함할 파일을 지정합니다. `*`, `?`, `**/` 같은 패턴을 사용할 수 있습니다.
와일드카드 해석하기
별표 하나
*현재 경로 구간에서 여러 문자와 일치합니다.
"include": ["src/*.ts"]다음 파일은 포함됩니다.
src/index.ts
src/user.ts하지만 하위 폴더 파일은 포함되지 않을 수 있습니다.
src/services/user.ts별표 두 개와 슬래시
**/하위 폴더를 여러 단계 탐색합니다.
"include": ["src/**/*.ts"]다음 파일이 모두 포함됩니다.
src/index.ts
src/services/user.ts
src/features/order/order.ts프로젝트 폴더가 깊어져도 TypeScript 파일을 찾을 수 있습니다.
16. exclude: 기본 검색에서 제외한다
다음 설정입니다.
{
"exclude": ["node_modules", "dist"]
}`exclude`는 `include` 패턴으로 파일을 찾을 때 제외할 경로를 지정합니다.
node_modules
설치된 외부 패키지 폴더
dist
컴파일된 결과물 폴더`dist`를 제외하지 않으면 생성된 JavaScript나 부가 파일이 프로젝트 검색 과정에 섞여 혼란을 만들 수 있습니다.
exclude는 철벽 방화벽이 아니다
여기에는 중요한 함정이 있습니다.
`exclude`는 해당 파일이 어떤 경우에도 프로젝트에 들어오지 못하게 막는 절대적인 차단 장치가 아닙니다.
공식 문서에 따르면 `exclude`는 주로 `include`가 찾는 결과를 조정합니다. 제외된 파일이라도 다른 코드에서 `import`하거나 `files` 목록에 직접 넣는 등의 방식으로 프로젝트에 포함될 수 있습니다.
다음 설정이 있다고 가정해 보겠습니다.
{
"include": ["src/**/*.ts"],
"exclude": ["src/legacy"]
}그런데 `src/index.ts`에서 다음처럼 가져옵니다.
import { oldFunction } from "./legacy/oldFunction";`legacy` 폴더가 `exclude`에 있어도 import 관계 때문에 프로젝트에 들어올 수 있습니다.
따라서 다음처럼 기억하면 좋습니다.
exclude
기본 파일 검색에서 빼기
접근 금지 보안 장벽
아님17. files, include, exclude 차이
TSConfig에는 파일을 선택하는 세 가지 대표적인 설정이 있습니다.
files
include
excludefiles
정확한 파일 목록을 직접 지정합니다.
{
"files": [
"src/index.ts",
"src/calculator.ts"
]
}파일 수가 적고 명확할 때 사용할 수 있습니다.
하지만 파일이 추가될 때마다 설정도 수정해야 합니다.
새 파일 추가
↓
files 목록에도 추가include
패턴을 사용해 여러 파일을 포함합니다.
{
"include": ["src/**/*.ts"]
}`src` 아래에 TypeScript 파일을 새로 만들면 자동으로 프로젝트에 포함됩니다.
일반적인 프로젝트에서 관리하기 편리합니다.
exclude
`include`가 찾은 범위에서 기본적으로 제외할 경로를 지정합니다.
{
"exclude": ["node_modules", "dist"]
}공연장 비유
프로젝트를 공연장이라고 생각해 보겠습니다.
files
출연자 이름을 한 명씩 명단에 작성
include
“연기팀 전원 입장”처럼 그룹으로 초대
exclude
그룹 중 일부는 기본 초대에서 제외이번 연재에서는 파일이 계속 늘어날 예정이므로 `include` 방식을 사용합니다.
18. 프로젝트 폴더 만들기
이제 실제 폴더를 구성하겠습니다.
프로젝트 루트에 `src` 폴더를 만듭니다.
VS Code 탐색기에서 다음 구조를 만들어 주세요.
typescript-study/
├── src/
├── node_modules/
├── package.json
├── package-lock.json
└── tsconfig.json`src` 폴더 안에 `index.ts`를 만듭니다.
src/index.ts다음 코드를 작성합니다.
const courseName: string = "TypeScript 완전정복";
const chapter: number = 4;
const configReady: boolean = true;
console.log(`강좌명: ${courseName}`);
console.log(`현재 회차: ${chapter}`);
console.log(`설정 완료: ${configReady}`);현재 구조는 다음과 같습니다.
typescript-study/
├── src/
│ └── index.ts
├── node_modules/
├── package.json
├── package-lock.json
└── tsconfig.json19. 파일 이름 없이 프로젝트 컴파일하기
지난 편에서는 파일 이름을 직접 입력했습니다.
npx tsc src/index.ts이제 `tsconfig.json`이 있으므로 파일 이름을 입력하지 않습니다.
npx tscTypeScript 컴파일러는 현재 디렉터리에서 `tsconfig.json`을 찾고 설정에 따라 프로젝트를 컴파일합니다.
컴파일이 성공하면 `dist` 폴더가 생성됩니다.
typescript-study/
├── src/
│ └── index.ts
│
├── dist/
│ └── index.js
│
├── node_modules/
├── package.json
├── package-lock.json
└── tsconfig.json생성된 `dist/index.js`를 열어 보겠습니다.
"use strict";
const courseName = "TypeScript 완전정복";
const chapter = 4;
const configReady = true;
console.log(`강좌명: ${courseName}`);
console.log(`현재 회차: ${chapter}`);
console.log(`설정 완료: ${configReady}`);타입 정보가 제거됐습니다.
: string
: number
: boolean그리고 `outDir` 설정에 따라 결과물이 `dist`에 저장됐습니다.
20. 컴파일된 코드 실행하기
Node.js로 결과 파일을 실행합니다.
node dist/index.js결과는 다음과 같습니다.
강좌명: TypeScript 완전정복
현재 회차: 4
설정 완료: true전체 흐름을 다시 정리해 보겠습니다.
src/index.ts 작성
↓
npx tsc 실행
↓
tsconfig.json 읽기
↓
타입 검사
↓
dist/index.js 생성
↓
node dist/index.js 실행이제 컴파일할 때마다 옵션을 길게 입력하지 않아도 됩니다.
21. 두 번째 파일 추가하기
프로젝트에 파일을 하나 더 만들어 보겠습니다.
src/calculator.ts다음 코드를 작성합니다.
export function add(
firstNumber: number,
secondNumber: number
): number {
return firstNumber + secondNumber;
}
export function multiply(
firstNumber: number,
secondNumber: number
): number {
return firstNumber * secondNumber;
}`src/index.ts`를 다음과 같이 수정합니다.
import { add, multiply } from "./calculator";
const firstNumber: number = 10;
const secondNumber: number = 5;
console.log(`더하기 결과: ${add(firstNumber, secondNumber)}`);
console.log(`곱하기 결과: ${multiply(firstNumber, secondNumber)}`);현재 소스 구조입니다.
src/
├── index.ts
└── calculator.ts다시 컴파일합니다.
npx tsc`include`가 `src/**/*.ts`로 설정되어 있으므로 두 파일을 모두 컴파일합니다.
dist/
├── index.js
└── calculator.js실행합니다.
node dist/index.js결과는 다음과 같습니다.
더하기 결과: 15
곱하기 결과: 50파일을 명령어에 추가하지 않았는데도 자동으로 컴파일됐습니다.
npx tsc`include`가 프로젝트 파일을 찾아 주었기 때문입니다.
22. 감시 모드 사용하기
코드를 수정할 때마다 `npx tsc`를 실행하는 것도 반복되면 번거롭습니다.
감시 모드를 사용합니다.
npx tsc --watch짧게 작성할 수도 있습니다.
npx tsc -w컴파일러가 프로젝트 파일의 변경을 기다립니다.
파일 감시 시작
↓
src 파일 수정
↓
저장
↓
자동 타입 검사
↓
dist 파일 다시 생성
↓
다시 대기`calculator.ts`를 수정해 보겠습니다.
export function subtract(
firstNumber: number,
secondNumber: number
): number {
return firstNumber - secondNumber;
}파일을 저장하면 TypeScript 컴파일러가 자동으로 다시 빌드합니다.
컴파일 관제탑
감시 모드의 TypeScript 컴파일러는 관제탑 직원과 같습니다.
index.ts 변경 감지!
calculator.ts 변경 감지!
재컴파일을 시작합니다.
오류 0개, 활주로 정상입니다.감시 모드를 종료하려면 터미널에서 다음 키를 누릅니다.
Ctrl + C23. 파일을 직접 지정하면 설정이 무시되는 함정
`tsconfig.json`에 다음 설정이 있다고 가정해 보겠습니다.
{
"compilerOptions": {
"target": "ES2022",
"rootDir": "./src",
"outDir": "./dist",
"strict": true
}
}그런데 다음 명령어를 실행합니다.
npx tsc src/index.ts많은 입문자가 이렇게 생각합니다.
“index.ts만 컴파일하면서 tsconfig 설정도 적용되겠지?”
하지만 명령줄에 입력 파일을 직접 지정하면 `tsconfig.json`이 무시됩니다.
따라서 `outDir` 설정이 적용되지 않고 `src/index.js`가 만들어질 수 있습니다.
src/
├── index.ts
└── index.js프로젝트 설정을 사용하려면 파일명을 생략합니다.
npx tsc기억하기 쉽게 정리하면 다음과 같습니다.
npx tsc
프로젝트 설정으로 컴파일
npx tsc src/index.ts
해당 파일을 명령줄 기본 설정으로 컴파일이 함정은 TypeScript 입문자가 상당히 자주 만납니다.
24. 다른 설정 파일 사용하기
실제 프로젝트에서는 개발용 설정과 배포용 설정을 분리할 수 있습니다.
tsconfig.json
tsconfig.production.json
tsconfig.test.json특정 설정 파일을 사용하려면 `--project` 옵션을 사용합니다.
npx tsc --project tsconfig.production.json짧게 작성할 수도 있습니다.
npx tsc -p tsconfig.production.json`--project` 또는 `-p`는 사용할 `tsconfig` 파일이나 해당 파일이 있는 디렉터리를 지정합니다.
예를 들어 다음처럼 사용할 수 있습니다.
{
"compilerOptions": {
"target": "ES2022",
"outDir": "./build",
"strict": true,
"removeComments": true
},
"include": ["src/**/*.ts"]
}실행:
npx tsc -p tsconfig.production.json지금은 기본 `tsconfig.json` 하나만 사용하지만, 프로젝트가 커지면 유용한 기능입니다.
25. 현재 적용된 설정 확인하기
설정 파일이 복잡해지면 실제로 어떤 옵션이 적용되는지 궁금할 수 있습니다.
다음 명령어를 사용합니다.
npx tsc --showConfigTypeScript가 최종적으로 해석한 설정을 출력합니다.
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"strict": true
},
"files": [
"./src/calculator.ts",
"./src/index.ts"
]
}상속된 설정이나 기본값까지 반영된 결과를 확인할 때 유용합니다.
조종석 계기판 검사
설정을 여러 개 바꾸다 보면 이런 상황이 생깁니다.
분명 strict를 켰는데 왜 오류가 안 나오지?
이 파일은 왜 컴파일되고 있지?
결과물이 왜 여기 생성됐지?이때 `--showConfig`는 조종석 계기판을 펼쳐 보여 줍니다.
npx tsc --showConfig개발자의 추측 대신 컴파일러가 실제로 이해한 설정을 확인할 수 있습니다.
26. 어떤 파일이 포함됐는지 확인하기
예상하지 않은 파일이 컴파일된다면 다음 명령어를 사용할 수 있습니다.
npx tsc --listFilesOnly프로젝트에 포함된 파일 목록을 출력하고 컴파일 과정을 종료합니다.
공식 CLI 문서에서도 `--listFilesOnly`는 컴파일에 포함된 파일 이름을 출력하는 옵션으로 안내합니다.
출력에는 우리가 작성한 파일뿐 아니라 TypeScript의 기본 타입 선언 파일도 포함될 수 있습니다.
node_modules/typescript/lib/lib.es5.d.ts
node_modules/typescript/lib/lib.es2022.d.ts
src/calculator.ts
src/index.ts예상한 파일이 목록에 없다면 `include` 패턴을 확인합니다.
예상하지 않은 파일이 있다면 import 관계와 `include`, `exclude`를 점검합니다.
27. sourceMap: 오류 위치를 원본으로 연결하기
선택적으로 다음 설정을 추가할 수 있습니다.
{
"compilerOptions": {
"sourceMap": true
}
}TypeScript 코드는 JavaScript로 변환된 뒤 실행됩니다.
실행 중 오류가 발생하면 JavaScript 파일의 위치가 표시될 수 있습니다.
dist/index.js:15하지만 개발자가 실제로 수정하고 싶은 것은 다음 파일입니다.
src/index.tsSource Map은 변환된 JavaScript와 원본 TypeScript 코드의 위치를 연결하는 지도입니다.
`sourceMap`을 켜고 컴파일하면 다음 파일이 생성됩니다.
dist/
├── index.js
└── index.js.map`.map` 파일은 실행 도구와 디버거가 JavaScript 위치를 원본 TypeScript 위치로 연결하는 데 사용합니다.
코드 내비게이션
Source Map 없이 오류가 나면 다음 상황과 비슷합니다.
사고 위치:
번역본 18페이지 7번째 줄개발자는 원본 책에서 해당 위치를 다시 찾아야 합니다.
Source Map이 있으면 이렇게 안내합니다.
원본 TypeScript:
src/index.ts 12번째 줄입니다.디버깅이 필요한 프로젝트에서는 유용한 설정입니다.
이번 실습 설정에 추가하려면 다음처럼 작성합니다.
"sourceMap": true28. removeComments: 결과물에서 주석 제거하기
다음 설정도 선택적으로 사용할 수 있습니다.
{
"compilerOptions": {
"removeComments": true
}
}TypeScript 코드에 주석이 있습니다.
// 상품의 최종 가격을 계산합니다.
function calculatePrice(
price: number,
quantity: number
): number {
return price * quantity;
}`removeComments`가 `false`라면 주석이 JavaScript에 남을 수 있습니다.
// 상품의 최종 가격을 계산합니다.
function calculatePrice(price, quantity) {
return price * quantity;
}`removeComments`가 `true`라면 일반 주석이 결과물에서 제거됩니다.
function calculatePrice(price, quantity) {
return price * quantity;
}다만 코드 용량을 본격적으로 줄이거나 난독화하는 기능은 아닙니다.
배포 파일 최적화는 번들러와 압축 도구가 별도로 담당하는 경우가 많습니다.
29. declaration: 타입 설명서 만들기
라이브러리를 만드는 프로젝트에서는 다음 설정을 사용할 수 있습니다.
{
"compilerOptions": {
"declaration": true
}
}TypeScript 파일:
export function add(a: number, b: number): number {
return a + b;
}컴파일 결과:
dist/
├── calculator.js
└── calculator.d.ts`calculator.d.ts`에는 다음과 같은 타입 정보가 들어갑니다.
export declare function add(
a: number,
b: number
): number;`.d.ts`는 실제 실행 코드가 아니라 다른 TypeScript 프로젝트가 사용할 수 있는 타입 설명서입니다.
지금 만드는 입문용 실행 프로젝트에서는 반드시 필요하지 않습니다.
다음 상황에서 주로 고려합니다.
npm 라이브러리를 만들 때
다른 프로젝트에 TypeScript 타입을 제공할 때
패키지의 공개 API를 설명할 때이번 기본 설정에서는 생략하겠습니다.
30. 실습용 최종 tsconfig.json
지금까지 살펴본 설정을 정리해 보겠습니다.
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"noEmitOnError": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}각 옵션의 역할입니다.
| 옵션 | 역할 |
|---|---|
| `target` | 생성할 JavaScript 문법 버전 |
| `module` | 모듈 코드를 변환하는 방식 |
| `rootDir` | TypeScript 원본 폴더의 기준점 |
| `outDir` | JavaScript 결과물 저장 위치 |
| `strict` | 엄격한 타입 검사 활성화 |
| `noEmitOnError` | 오류 발생 시 결과물 생성 중단 |
| `esModuleInterop` | 모듈 간 호환성 개선 |
| `forceConsistentCasingInFileNames` | 파일 이름 대소문자 일관성 검사 |
| `skipLibCheck` | 선언 파일 내부 검사 생략 |
| `sourceMap` | JavaScript와 TypeScript 위치 연결 |
| `include` | 프로젝트에 포함할 파일 패턴 |
| `exclude` | 기본 검색에서 제외할 경로 |
한 번에 전부 외울 필요는 없습니다.
처음에는 다음 여섯 개만 확실히 이해해도 충분합니다.
target
module
rootDir
outDir
strict
include31. npm 스크립트로 명령어 줄이기
컴파일 명령어는 이미 짧습니다.
npx tsc하지만 실제 프로젝트에서는 `package.json`에 자주 사용하는 명령어를 등록합니다.
`package.json`의 `scripts`를 수정합니다.
{
"scripts": {
"build": "tsc",
"watch": "tsc --watch",
"start": "node dist/index.js"
}
}이제 다음처럼 사용할 수 있습니다.
프로젝트 빌드
npm run build내부적으로 다음 명령어가 실행됩니다.
tsc감시 모드
npm run watch내부적으로 다음 명령어가 실행됩니다.
tsc --watch프로그램 실행
npm start내부적으로 다음 명령어가 실행됩니다.
node dist/index.js개발 명령어 안내소
팀원이 프로젝트를 처음 받았다고 가정해 보겠습니다.
복잡한 명령어를 설명하는 대신 이렇게 안내할 수 있습니다.
npm run build
프로젝트를 컴파일합니다.
npm run watch
변경 사항을 자동 컴파일합니다.
npm start
컴파일된 프로그램을 실행합니다.`package.json`이 프로젝트의 명령어 메뉴판 역할을 합니다.
32. dist 폴더를 삭제하고 다시 빌드하기
컴파일을 여러 번 하다 보면 삭제된 TypeScript 파일의 오래된 JavaScript 결과물이 `dist`에 남을 수 있습니다.
예를 들어 다음 파일을 삭제했다고 가정해 보겠습니다.
src/oldFeature.ts하지만 이전 컴파일 결과는 남아 있을 수 있습니다.
dist/oldFeature.js`tsc`는 일반적으로 `outDir`을 자동으로 깨끗하게 비우는 청소 기능까지 수행하지 않습니다.
따라서 빌드 전에 `dist`를 삭제하는 스크립트를 사용하는 프로젝트도 많습니다.
운영체제마다 폴더 삭제 명령어가 달라질 수 있으므로, 실무에서는 `rimraf` 같은 도구나 별도의 빌드 도구를 사용하기도 합니다.
입문 단계에서는 `dist` 폴더를 직접 삭제한 뒤 다시 컴파일해도 충분합니다.
npm run build새로운 `dist` 폴더가 다시 생성됩니다.
33. 오류 실험실: rootDir 밖에 파일이 있다
다음 구조를 만들어 보겠습니다.
typescript-study/
├── src/
│ └── index.ts
├── helper.ts
└── tsconfig.json설정은 다음과 같습니다.
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["**/*.ts"]
}`include`가 모든 TypeScript 파일을 찾으므로 프로젝트 루트의 `helper.ts`도 포함됩니다.
하지만 `rootDir`은 `src`입니다.
`helper.ts`는 `src` 밖에 있습니다.
컴파일러는 결과 파일을 `outDir` 밖에 생성해야 하는 상황을 허용하지 않기 때문에 오류를 표시할 수 있습니다. `rootDir` 아래에 출력 대상 파일이 있어야 한다는 제약이 있기 때문입니다.
해결 방법은 두 가지입니다.
해결 방법 1
`helper.ts`를 `src` 안으로 이동합니다.
src/
├── index.ts
└── helper.ts이 방법이 프로젝트 구조상 가장 자연스럽습니다.
해결 방법 2
실제로 프로젝트 루트의 여러 폴더를 소스로 사용해야 한다면 `rootDir`을 조정합니다.
"rootDir": "."다만 지금 연재에서는 모든 TypeScript 코드를 `src`에 모으는 구조를 사용하겠습니다.
34. 오류 실험실: strict에서 갑자기 오류가 쏟아진다
기존 JavaScript 코드를 TypeScript로 옮기다가 `strict`를 켜면 오류가 많이 나타날 수 있습니다.
function printUser(user) {
console.log(user.name);
}오류:
Parameter 'user' implicitly has an 'any' type.해결을 위해 무조건 다음처럼 작성하는 것은 좋지 않습니다.
function printUser(user: any) {
console.log(user.name);
}오류는 사라지지만 타입 검사도 함께 약해집니다.
사용자 타입을 정의합니다.
type User = {
name: string;
};
function printUser(user: User): void {
console.log(user.name);
}`strict`가 요구한 것은 개발자를 괴롭히는 추가 노동이 아닙니다.
함수의 계약을 명확하게 작성하라는 요청입니다.
35. 오류 실험실: dist에 파일이 생성되지 않는다
`npx tsc`를 실행했는데 `dist` 폴더가 생성되지 않는다면 다음 항목을 확인합니다.
첫 번째 확인
타입 오류가 있는지 살펴봅니다.
"noEmitOnError": true이 설정이 켜져 있으면 오류를 모두 해결하기 전까지 JavaScript가 생성되지 않습니다.
터미널 오류 메시지를 확인합니다.
두 번째 확인
`include` 패턴을 확인합니다.
"include": ["source/**/*.ts"]실제 폴더 이름이 `src`인데 `source`라고 잘못 작성했다면 파일을 찾지 못합니다.
수정:
"include": ["src/**/*.ts"]세 번째 확인
파일 확장자를 확인합니다.
index.ts.txt겉으로는 TypeScript처럼 보여도 실제로는 텍스트 파일일 수 있습니다.
VS Code 탐색기에서 파일 이름을 확인합니다.
네 번째 확인
명령어에 파일명을 직접 적지 않았는지 확인합니다.
npx tsc src/index.ts이 명령은 `tsconfig.json`의 `outDir`을 사용하지 않습니다.
프로젝트 설정을 사용하려면 다음처럼 실행합니다.
npx tsc36. 오류 실험실: 컴파일 성공인데 실행이 안 된다
컴파일은 성공했지만 다음 명령어에서 오류가 날 수 있습니다.
node dist/index.js확인할 항목은 다음과 같습니다.
결과 파일 위치
실제 파일이 다음 위치에 있는지 확인합니다.
dist/index.js폴더 구조에 따라 다음처럼 생성됐을 수도 있습니다.
dist/src/index.js`rootDir` 설정을 확인합니다.
module 설정
프로젝트가 ESM 방식으로 작성됐는데 CommonJS 방식으로 실행하거나, 반대로 CommonJS 결과물을 ESM으로 취급하면 모듈 오류가 발생할 수 있습니다.
Cannot use import statement outside a module
require is not defined in ES module scope이 경우 다음 설정을 함께 확인해야 합니다.
tsconfig.json의 module
package.json의 type
파일 확장자
Node.js 실행 환경이번 실습에서는 혼란을 줄이기 위해 다음 설정을 사용합니다.
"module": "CommonJS"37. 초보자 함정 지도
함정 1. rootDir이 컴파일 파일을 선택한다고 생각하기
"rootDir": "./src"`rootDir`은 파일 선택 옵션이 아닙니다.
컴파일 대상을 선택하려면 `include`를 사용합니다.
"include": ["src/**/*.ts"]함정 2. 파일명을 직접 입력하면서 tsconfig가 적용된다고 생각하기
npx tsc src/index.ts입력 파일을 직접 지정하면 `tsconfig.json`이 무시됩니다.
프로젝트 전체를 설정대로 컴파일하려면 다음처럼 실행합니다.
npx tsc함정 3. exclude가 완전한 차단 장치라고 생각하기
`exclude`된 파일도 다른 파일에서 import하면 프로젝트에 들어올 수 있습니다.
함정 4. target과 module을 같은 것으로 생각하기
target
JavaScript 문법 버전
module
파일 간 import와 export 처리 방식서로 다른 설정입니다.
함정 5. strict 오류를 전부 any로 해결하기
function run(value: any) {
}오류는 사라지지만 TypeScript의 장점도 함께 사라질 수 있습니다.
가능하면 실제 데이터 구조를 타입으로 작성합니다.
함정 6. outDir이 자동으로 청소된다고 생각하기
삭제된 소스 파일의 오래된 JavaScript가 `dist`에 남을 수 있습니다.
필요하면 `dist`를 삭제한 뒤 다시 빌드합니다.
함정 7. 모든 프로젝트에 같은 tsconfig를 사용하기
Node.js 서버, 브라우저 애플리케이션, React 프로젝트, 라이브러리는 실행 환경과 빌드 방식이 다릅니다.
하나의 `tsconfig.json`이 모든 환경에 완벽하게 맞는 만능 설정은 아닙니다.
38. tsconfig 관제실 상황극
프로젝트에 새로운 TypeScript 파일이 도착했습니다.
src/features/payment/payment.ts관제실이 확인합니다.
include 확인:
src/**/*.ts와 일치합니다.
입장 승인!컴파일러가 코드를 검사합니다.
strict 확인:
매개변수 타입이 없습니다.
입장 보류!개발자가 타입을 추가합니다.
function pay(amount: number): void {
console.log(`${amount}원을 결제합니다.`);
}다시 확인합니다.
타입 검사 통과!
target 확인:
ES2022 방식으로 변환합니다.
module 확인:
CommonJS 방식으로 모듈을 처리합니다.
outDir 확인:
dist/features/payment/payment.js로 이동합니다.`tsconfig.json`의 각 설정이 한 파일의 여행 경로를 결정하고 있습니다.
39. 오늘의 실전 프로젝트
간단한 장바구니 계산 프로젝트를 만들어 보겠습니다.
프로젝트 구조입니다.
src/
├── index.ts
├── product.ts
└── cart.tsproduct.ts
export type Product = {
name: string;
price: number;
};cart.ts
import type { Product } from "./product";
export function calculateCartTotal(
product: Product,
quantity: number
): number {
return product.price * quantity;
}index.ts
import type { Product } from "./product";
import { calculateCartTotal } from "./cart";
const keyboard: Product = {
name: "기계식 키보드",
price: 89000
};
const quantity: number = 2;
const total: number = calculateCartTotal(
keyboard,
quantity
);
console.log(`상품명: ${keyboard.name}`);
console.log(`수량: ${quantity}`);
console.log(`총금액: ${total}원`);컴파일
npm run build또는 다음 명령어를 사용합니다.
npx tsc컴파일 결과입니다.
dist/
├── index.js
├── product.js
├── product.js.map
├── cart.js
└── cart.js.map`product.ts`에는 타입만 있지만 설정과 TypeScript 버전에 따라 비어 있는 모듈 JavaScript가 생성될 수 있습니다.
실행
npm start또는 직접 실행합니다.
node dist/index.js결과:
상품명: 기계식 키보드
수량: 2
총금액: 178000원40. 오류 미션
`index.ts`에서 가격을 문자열로 바꿔 보세요.
const keyboard: Product = {
name: "기계식 키보드",
price: "89000"
};컴파일합니다.
npm run build오류가 발생합니다.
Type 'string' is not assignable to type 'number'.`noEmitOnError`가 켜져 있으므로 오류가 있는 상태에서는 새로운 JavaScript 결과물이 생성되지 않습니다.
수정합니다.
const keyboard: Product = {
name: "기계식 키보드",
price: 89000
};다시 컴파일합니다.
npm run build이번에는 정상적으로 결과물이 생성됩니다.
41. 타입 탐정사무소
다음 설정을 살펴보세요.
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"strict": true
},
"include": ["src/**/*.ts"]
}프로젝트 구조는 다음과 같습니다.
src/
├── index.ts
└── services/
└── user.ts컴파일 후 예상되는 결과는 무엇일까요?
정답:
dist/
├── index.js
└── services/
└── user.js`rootDir` 아래의 상대적인 폴더 구조가 `outDir`에도 유지됩니다.
42. 미니 퀴즈
문제 1
`tsconfig.json`의 주요 역할은 무엇일까요?
- 데이터베이스를 생성한다.
- TypeScript 프로젝트의 파일과 컴파일 옵션을 관리한다.
- 웹 브라우저를 설치한다.
- npm 패키지를 자동으로 배포한다.
정답은 2번입니다.
문제 2
`tsconfig.json`을 생성하는 명령어는 무엇일까요?
정답:
npx tsc --init문제 3
다음 설정의 역할은 무엇일까요?
"target": "ES2022"정답:
TypeScript 코드를 어떤 수준의 JavaScript 문법으로 변환할지 결정합니다.
문제 4
다음 두 옵션의 차이는 무엇일까요?
"rootDir": "./src",
"outDir": "./dist"정답:
rootDir
원본 TypeScript 파일 구조의 기준 폴더
outDir
컴파일된 JavaScript를 저장할 폴더문제 5
다음 설정의 역할은 무엇일까요?
"strict": true정답:
여러 엄격한 타입 검사 옵션을 활성화해 모호하거나 위험한 타입 사용을 더 적극적으로 확인합니다.
문제 6
다음 명령어 중 `tsconfig.json`을 기준으로 프로젝트 전체를 컴파일하는 것은 무엇일까요?
- `npx tsc src/index.ts`
- `node src/index.ts`
- `npx tsc`
- `npm install tsc`
정답은 3번입니다.
문제 7
`rootDir`을 `src`로 지정하면 `src` 파일만 컴파일될까요?
정답:
반드시 그렇지는 않습니다.
`rootDir`은 컴파일 대상 파일을 선택하지 않습니다. 대상 파일은 `include`, `files`, import 관계 등을 통해 결정됩니다.
문제 8
`exclude`에 넣은 파일은 어떤 상황에서도 프로젝트에 포함되지 않을까요?
정답:
아닙니다.
다른 파일에서 import하거나 `files`에 직접 지정하는 등의 방식으로 포함될 수 있습니다.
문제 9
타입 오류가 있을 때 JavaScript 출력을 막는 설정은 무엇일까요?
정답:
"noEmitOnError": true문제 10
프로젝트에 실제로 포함된 파일 목록을 확인하는 명령어는 무엇일까요?
정답:
npx tsc --listFilesOnly43. 오늘의 핵심 정리
- `tsconfig.json`은 TypeScript 프로젝트의 파일 범위와 컴파일 옵션을 관리합니다.
- `npx tsc --init`으로 기본 설정 파일을 만들 수 있습니다.
- `compilerOptions`에는 JavaScript 변환 방식과 타입 검사 설정을 작성합니다.
- `target`은 생성할 JavaScript 문법 버전을 결정합니다.
- `module`은 `import`와 `export`를 처리할 모듈 방식을 결정합니다.
- `rootDir`은 원본 소스 폴더 구조의 기준점입니다.
- `outDir`은 컴파일된 JavaScript 결과물의 저장 위치입니다.
- `rootDir`은 컴파일 대상 파일을 선택하지 않습니다.
- `strict`는 엄격한 타입 검사를 활성화합니다.
- `noEmitOnError`는 오류가 있을 때 JavaScript 생성을 중단합니다.
- `include`는 프로젝트에 포함할 파일 패턴을 지정합니다.
- `exclude`는 `include`가 찾는 기본 범위에서 파일을 제외합니다.
- `exclude`는 완전한 접근 차단 장치가 아닙니다.
- 파일명을 직접 지정한 `npx tsc index.ts`는 `tsconfig.json`을 무시합니다.
- 프로젝트 설정으로 컴파일하려면 파일명 없이 `npx tsc`를 실행합니다.
- `npx tsc --watch`를 사용하면 파일 변경 시 자동으로 다시 컴파일합니다.
- `npx tsc --showConfig`로 최종 적용 설정을 확인할 수 있습니다.
- `npx tsc --listFilesOnly`로 프로젝트에 포함된 파일을 확인할 수 있습니다.
오늘 사용한 주요 명령어입니다.
# tsconfig.json 생성
npx tsc --init
# 프로젝트 전체 컴파일
npx tsc
# 감시 모드
npx tsc --watch
# 특정 설정 파일로 컴파일
npx tsc --project tsconfig.production.json
# 최종 적용 설정 확인
npx tsc --showConfig
# 포함된 파일 목록 확인
npx tsc --listFilesOnly
# 컴파일 결과 실행
node dist/index.js추천하는 입문용 프로젝트 구조입니다.
typescript-study/
├── src/
│ └── index.ts
├── dist/
│ └── index.js
├── package.json
└── tsconfig.json추천하는 입문용 설정입니다.
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"noEmitOnError": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}44. 다음 편 예고
지금까지 우리는 변수마다 타입을 직접 작성했습니다.
const userName: string = "김타입";
const age: number = 25;
const isLearning: boolean = true;그런데 TypeScript는 개발자가 타입을 적지 않아도 값을 보고 타입을 알아낼 수 있습니다.
const userName = "김타입";
const age = 25;
const isLearning = true;TypeScript는 어떻게 각각 문자열, 숫자, 불리언이라고 판단한 것일까요?
반대로 타입을 반드시 직접 작성해야 하는 순간은 언제일까요?
다음 편에서는 개발자 대신 타입을 추리하는 TypeScript의 탐정 능력을 살펴보겠습니다.
다음 이야기
[TypeScript 완전정복 #5] 타입은 꼭 직접 써야 할까? | 타입 추론과 타입 명시의 차이
- 타입 추론은 어떻게 동작할까요?
- `const`와 `let`은 왜 다르게 추론될까요?
- 함수 매개변수에는 왜 타입을 적어야 할까요?
- 반환 타입은 생략해도 될까요?
- 타입을 너무 많이 적으면 왜 코드가 복잡해질까요?
다음 편에서는 타입을 직접 적어야 할 때와 TypeScript에게 맡겨도 될 때를 구분해 보겠습니다.
