출시·고도화 중
TypeScript 안내서 · 4/6
프로그램이 커지면 코드를 여러 파일로 나누고, 다른 사람이 만든 패키지를 가져다 쓰게 됩니다. TypeScript는 JavaScript 표준 모듈 시스템인 ES 모듈을 그대로 쓰고, 그 위에 타입 정보를 함께 주고받는 방법을 더합니다. 이 장에서는 import와 export, npm 패키지와 package.json, @types 타입 선언 패키지, 모듈 관련 tsconfig 옵션, 빌드 방법을 살펴봅니다.
파일 하나가 모듈 하나입니다. 다른 파일에서 쓰게 할 값과 타입에는 export를 붙이고, 쓰는 쪽에서는 import로 가져옵니다.
// src/math.ts
export const PI = 3.14159;
export function circleArea(radius: number): number {
return PI * radius * radius;
}
export interface Range {
min: number;
max: number;
}
export default function clamp(value: number, range: Range): number {
return Math.min(range.max, Math.max(range.min, value));
}// src/index.ts
import clamp, { circleArea, PI } from "./math.js";
import type { Range } from "./math.js";
import * as math from "./math.js";
const range: Range = { min: 0, max: 10 };
console.log(clamp(15, range)); // 10
console.log(circleArea(2), PI);
console.log(math.circleArea(1));몇 가지 눈여겨볼 점이 있습니다.
export function ...)는 중괄호로, 기본 내보내기(export default)는 중괄호 없이 가져옵니다. 이름이 고정되는 이름 있는 내보내기가 자동 완성과 이름 바꾸기에 유리해 더 많이 쓰입니다.import type은 타입만 가져온다는 표시입니다. 컴파일 결과에서 완전히 지워지므로 불필요한 실행 시점 의존을 만들지 않습니다.module이 nodenext일 때 상대 경로에는 확장자를 적어야 하며, 원본이 .ts여도 컴파일 결과 파일 이름인 .js로 적습니다. TypeScript가 이를 원본 .ts 파일과 연결해 줍니다.package.json은 프로젝트의 이름, 의존성, 스크립트를 담는 파일입니다. 실행할 때 필요한 패키지는 dependencies, 개발할 때만 필요한 도구는 devDependencies에 넣습니다.
npm install zod # 실행에 필요한 패키지
npm install --save-dev vitest # 개발 도구
npm install # package.json 기준으로 모두 설치
npm outdated # 새 버전이 있는 패키지 확인{
"name": "hello-ts",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "vitest"
}
}"type": "module"은 이 패키지의 .js 파일을 ES 모듈로 다루라는 뜻입니다. npm install을 실행하면 dependencies와 devDependencies 항목이 자동으로 추가되며, 버전은 ^가 붙은 범위로 기록되어 호환되는 범위 안의 업데이트를 허용합니다. package-lock.json은 실제로 설치된 정확한 버전을 기록하므로 저장소에 함께 커밋합니다.
TypeScript는 .d.ts 선언 파일을 통해 JavaScript 라이브러리의 타입을 압니다. 요즘 많은 패키지는 자체 선언 파일을 포함하고 있어 설치만 하면 바로 타입이 보입니다. 선언이 없는 패키지는 커뮤니티가 관리하는 DefinitelyTyped 저장소의 @types/패키지이름을 따로 설치합니다.
npm install express
npm install --save-dev @types/express @types/node@types/node는 fs, path, process 같은 Node.js 내장 모듈의 타입입니다. Node.js 내장 모듈은 node: 접두사로 가져오는 것이 명확합니다.
import { readFile } from "node:fs/promises";
import path from "node:path";
const file = path.join(process.cwd(), "package.json");
const pkg = JSON.parse(await readFile(file, "utf8")) as { name: string };
console.log(pkg.name);최상위 await는 ES 모듈에서만 쓸 수 있으므로 module이 nodenext 같은 ES 모듈 출력 설정이어야 합니다. as는 타입 단언으로, 컴파일러에게 "이 값은 이 타입이다"라고 알려 줄 뿐 실제로 검사하지는 않습니다. 외부 입력은 검증 라이브러리로 확인하는 편이 안전합니다.
| 옵션 | 쓰임 |
|---|---|
target | 출력 문법 수준. 실행 환경이 지원하는 수준으로 맞춥니다 |
module | Node.js에서 직접 실행한다면 nodenext |
moduleResolution | 번들러를 거친다면 bundler와 module: esnext 조합 |
lib | 쓸 수 있는 내장 API 타입(DOM 등) |
declaration | 라이브러리용 .d.ts 파일 생성 |
verbatimModuleSyntax | 타입 전용 import에 import type을 강제 |
esModuleInterop | CommonJS 패키지를 기본 내보내기처럼 가져오기 |
Node.js 백엔드라면 module: nodenext, Vite 같은 번들러를 쓰는 프런트엔드라면 module: esnext와 moduleResolution: bundler를 쓰는 것이 일반적입니다.
tsc는 타입 검사와 JavaScript 출력을 함께 합니다. 라이브러리를 배포한다면 declaration을 켜서 .d.ts도 함께 내보내고, package.json에 진입점을 적습니다.
{
"name": "my-lib",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"]
}애플리케이션에서는 esbuild, Vite 같은 번들러가 TypeScript를 빠르게 변환합니다. 이런 도구는 대부분 타입을 지우기만 하고 검사하지 않으므로, CI에서 tsc --noEmit을 반드시 함께 실행합니다.
export로 내보내고 import로 가져옵니다. 타입만 가져올 때는 import type을 씁니다.nodenext에서는 상대 경로 import에 .js 확장자를 적습니다.dependencies, 개발 도구는 devDependencies에 넣고 잠금 파일도 커밋합니다.@types/패키지이름을 설치합니다.tsc --noEmit을 별도로 실행합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.