학습 공간 개념 · 판단 · 실험

Frontend Performance Lab

TypeScript

프로젝트 설정과 모듈 경계

tsconfig.json은 타입 검사 범위와 모듈 해석, 출력 문법의 기준을 정의하며, 실제 코드가 실행될 브라우저나 Node.js 환경과 일치해야 합니다.

이럴 때 필요해요

에디터에서는 타입 오류가 없는데 실제 빌드나 런타임에서 모듈을 찾지 못하거나 API가 없어 실패하는 환경 불일치를 예방할 수 있습니다.

어떻게 동작하나요?

에디터에서는 아무런 타입 에러가 없었는데 막상 코드를 실행하니 모듈 경로를 찾지 못하거나 런타임 API가 없다는 에러를 만날 때가 있습니다. 이는 TypeScript에게 알려 준 가상 환경 설정과 실제 코드가 돌아가는 런타임·번들러 환경이 서로 어긋났기 때문입니다. 프로젝트 루트의 tsconfig.json은 바로 이 기준을 맞추는 설정 파일로, include/exclude를 통해 검사할 시작 파일 범위를 정하고 strict, target, module, moduleResolution 같은 컴파일러 옵션을 정의합니다.

특히 target과 lib 옵션의 역할을 정확히 구분해야 합니다. target을 높이면 최신 JavaScript 문법으로 출력되고 그 버전에 맞는 기본 타입 선언(lib)이 활성화되지만, 구형 실행 환경에 없는 내장 API의 폴리필(Polyfill)을 자동으로 설치해 주지는 않습니다. 마찬가지로 lib에 DOM을 넣으면 코드에서 window나 document를 써도 타입 검사는 통과하지만, 그 코드를 Node.js 서버에서 실행하면 런타임 에러가 발생합니다. 또한 Vite 같은 번들러가 코드 변환을 담당할 때는 noEmit: true로 출력을 끄고 CI에서 tsc --noEmit으로 타입 검사만 별도 실행합니다.

파일 간 코드 분리에는 표준 import/export를 사용하는 ES 모듈을 사용하며, 모듈을 찾는 규칙인 moduleResolution은 번들러 환경이면 Bundler, Node.js 직접 실행 환경이면 NodeNext처럼 실제 도구에 맞춰 지정합니다. 과거 TypeScript의 namespace 문법은 파일 모듈 시스템을 대체하는 도구가 아니므로 새 애플리케이션에서는 사용하지 않으며, tsc --init으로 만든 기본 설정 파일 역시 시작 템플릿일 뿐이므로 프로젝트 환경에 맞게 직접 다듬어 사용해야 합니다.

코드 예제

동작을 설명하는 읽기 전용 예제입니다.

example.ts
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noEmit": true
  },
  "include": ["src"]
}
typescript · 읽기 전용10줄

Vite 같은 번들러가 실제 코드 변환을 담당하는 프런트엔드 프로젝트의 대표적인 설정입니다. Node.js에서 직접 실행하는 프로젝트라면 해당 환경에 맞는 module과 moduleResolution(예: NodeNext)을 선택해야 합니다.

자주 헷갈리는 포인트

  • tsconfig의 target이나 lib 버전을 높이는 것만으로 구형 브라우저용 API 폴리필이 자동으로 추가되지는 않습니다.
  • 번들러 환경과 Node.js 직접 실행 환경에 똑같은 moduleResolution 설정을 그대로 복사해 쓰지 않도록 주의합니다.

내 말로 설명해 보기

tsconfig 설정 덕분에 코드에서 fetch나 window를 써도 타입 검사가 통과한다는 사실이, 실제 실행 환경에서도 항상 에러 없이 동작함을 보장할까요?

함께 읽으면 좋은 개념

관련 면접 질문 3개

tsconfig.json은 어떤 프로젝트 계약을 정의하나요?

tsconfig.json은 프로젝트에 포함할 파일 범위와 타입 검사·모듈 해석·코드 출력 규칙을 정의하는 설정 파일입니다. strict로 검사 강도를 높이고, target과 lib으로 대상 런타임의 문법 수준과 기본 환경 타입을 맞추며, module과 moduleResolution을 실제 번들러나 Node.js 환경과 일치시킵니다. 또한 Vite 같은 번들러가 출력을 맡는 환경에서는 noEmit: true를 켜서 TypeScript를 타입 검사 전용으로 활용할 수 있습니다.

꼬리질문과 참고자료 →

tsc --init은 무엇을 생성하며 무엇을 설정하지 않나요?

tsc --init은 현재 폴더에 기본 옵션과 주석이 담긴 tsconfig.json 초안 파일을 생성하는 명령어입니다. 다만 이 명령어는 컴파일러 설정 파일만 만들 뿐, package.json이나 소스 폴더 구조, 번들러(Vite, Webpack), 테스트 도구, 런타임 폴리필과 @types 의존성까지 구성해 주지는 않습니다. 따라서 생성 후에는 대상 런타임과 빌드 방식에 맞춰 moduleResolution, target, noEmit 등을 직접 조정해야 합니다.

꼬리질문과 참고자료 →

Namespace와 Module은 어떤 경계가 다른가요?

모듈(ES Module)은 파일 하나를 독립된 스코프로 보고 import/export로 파일 간 의존성과 공개 경계를 명시하는 표준 시스템이며, 브라우저·번들러·Node.js의 모듈 로더와 직접 연결됩니다. 반면 namespace는 모듈 표준 이전에 전역 이름 충돌을 피하려고 TypeScript가 도입했던 문법으로, 일반 객체 코드로 변환될 뿐 의존성 로딩이나 트리 셰이킹을 처리해 주지 않습니다. 따라서 현대 프로젝트에서는 파일 기반의 ES 모듈을 사용합니다.

꼬리질문과 참고자료 →

공식 참고 자료

← TypeScript