tsconfig.json은 많은 개발자들이 초기에 생성된 그대로 두고 잘 들여다보지 않는 파일 중 하나입니다. 하지만 이 파일은 TypeScript 컴파일러의 '두뇌' 역할을 하는 매우 중요한 설정집입니다.
tsconfig.json을 제대로 이해하고 활용하면 코드의 안정성을 높이고, 최신 JavaScript 기능을 적극적으로 사용하며, 개발 생산성까지 향상시킬 수 있습니다. 오늘은 tsconfig.json이 Angular 프로젝트에서 어떻게 작동하는지, 그리고 어떤 키를 조정하여 프로젝트의 활용도를 극대화할 수 있는지 알아보겠습니다.
Angular CLI로 생성된 프로젝트의 루트 폴더를 보면 tsconfig.json 파일이 여러 개 있는 것을 발견할 수 있습니다. 이는 각기 다른 목적을 위해 설정을 분리하고 상속하기 위함입니다.
이러한 구조 덕분에 우리는 빌드용, 테스트용 설정을 분리하여 깔끔하게 관리할 수 있습니다.
tsconfig.json의 가장 중요한 부분은 compilerOptions 객체입니다. 여기에 명시된 키와 값에 따라 TypeScript 코드가 JavaScript로 어떻게 변환될지 결정됩니다. Angular 프로젝트에 큰 영향을 미치는 핵심 옵션들은 다음과 같습니다.
| Key | 역할 및 Angular에 미치는 영향 | 추천 값 (Angular 18) |
|---|---|---|
| target | 컴파일 결과로 나올 JavaScript 버전을 지정합니다. Angular는 최신 브라우저를 타겟으로 하므로 모던한 버전을 사용합니다. | "ES2022" |
| module | 모듈 시스템을 지정합니다. 동적 import() 등 최신 기능을 위해 ESNext나 ES2022를 사용합니다. | "ES2022" |
| lib | 컴파일 시 포함할 기본 타입 정의(라이브러리) 목록입니다. 브라우저 환경이므로 DOM은 필수입니다. | ["ES2022", "DOM"] |
| strict | 가장 중요한 옵션 중 하나. true로 설정하면 strictNullChecks, noImplicitAny 등 여러 엄격한 타입 검사 규칙이 한 번에 활성화됩니다. 코드의 안정성을 비약적으로 높여주므로 반드시 true로 유지해야 합니다. | true |
| forceConsistentCasingInFileNames | true일 경우, 파일 이름의 대소문자를 엄격하게 구분합니다. 협업 시 OS 차이(Windows vs macOS/Linux)로 인한 잠재적 버그를 예방합니다. | true |
| experimentalDecorators | Angular의 핵심인 @Component, @Injectable 같은 데코레이터를 사용하기 위해 반드시 필요한 옵션입니다. 이게 false면 Angular 앱이 동작하지 않습니다. | true |
| emitDecoratorMetadata | 데코레이터가 붙은 클래스나 프로퍼티에 대한 타입 정보를 메타데이터로 저장합니다. Angular의 의존성 주입(Dependency Injection) 시스템이 타입을 인식하고 객체를 주입하기 위해 필수적입니다. | true |
| paths | 모듈을 불러올 때 경로에 별칭(alias)을 부여합니다. 복잡한 상대 경로(../../../)를 깔끔하게 만들어줍니다. (아래 예제 참고) | (사용자 정의) |
이론은 충분하니, 이제 tsconfig.json 설정을 변경하여 실제 프로젝트를 어떻게 개선할 수 있는지 구체적인 예시를 통해 살펴보겠습니다.
가장 강력한 기능입니다. strict: true가 왜 중요한지 코드로 확인해 보겠습니다.
// user.service.ts
@Injectable({ providedIn: 'root' })
export class UserService {
// 사용자가 없을 수도 있음 (null)
currentUser: { name: string; email: string } | null = null;
}
// profile.component.ts
@Component({ ... })
export class ProfileComponent {
constructor(private userService: UserService) {}
// strict: false 라면 이 코드는 아무런 에러 없이 컴파일됩니다.
// 하지만 런타임에 currentUser가 null이면 "Cannot read properties of null (reading 'name')" 에러가 발생합니다!
welcomeMessage = `환영합니다, ${this.userService.currentUser.name}님!`;
}
개발자는 이 에러를 보고 아래와 같이 코드를 수정하여 런타임 에러를 원천 차단할 수 있습니다.컴파일 에러: Object is possibly 'null'.
// profile.component.ts (수정 후)
export class ProfileComponent {
welcomeMessage: string;
constructor(private userService: UserService) {
// currentUser가 존재할 때만 name에 접근하도록 안전하게 처리 (null-safety)
const userName = this.userService.currentUser?.name ?? '방문자';
this.welcomeMessage = `환영합니다, ${userName}님!`;
}
}
프로젝트 구조가 깊어지면 상대 경로 지옥에 빠지기 쉽습니다.
// src/app/features/orders/components/order-details/order-details.component.ts
import { AuthService } from '../../../../core/services/auth.service';
import { Product } from '../../../../shared/models/product.model';
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./", // paths 옵션을 사용하려면 baseUrl이 필수
"paths": {
"@core/*": ["src/app/core/*"],
"@shared/*": ["src/app/shared/*"],
"@features/*": ["src/app/features/*"]
}
}
}
// src/app/features/orders/components/order-details/order-details.component.ts
import { AuthService } from '@core/services/auth.service';
import { Product } from '@shared/models/product.model';
쓰지 않는 변수나 import는 코드 가독성을 해치고 잠재적인 버그의 원인이 될 수 있습니다.
// tsconfig.app.json
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true // 사용되지 않는 함수 파라미터도 잡아줍니다.
}
}
tsconfig.json은 단순한 설정 파일이 아니라, 우리 프로젝트의 코드 품질과 안정성을 지키는 든든한 가드레일입니다. 특히 Angular 18과 같은 최신 프레임워크는 TypeScript의 강력한 타입 시스템 위에서 동작하기 때문에, 컴파일러 옵션을 잘 이해하고 활용하는 것이 더욱 중요해졌습니다.
지금 바로 여러분의 tsconfig.json 파일을 열어 strict 모드가 켜져 있는지 확인하고, paths를 설정하여 코드를 더 깔끔하게 만들어보세요. 작은 설정 변경 하나가 여러분의 개발 경험과 애플리케이션의 품질을 크게 향상시킬 것입니다.