frontend

TypeScript strict 모드 설정 완벽 가이드 — tsconfig 옵션과 자주 만나는 타입 오류 해결

TypeScript strict 모드를 켜면 런타임 버그를 컴파일 타임에 잡을 수 있다. strictNullChecks, noImplicitAny 등 핵심 옵션의 동작 방식과 활성화 시 자주 나타나는 오류 해결법을 정리했다.

TypeScriptstricttsconfig타입오류frontend
tsconfig.json에 strict: true 옵션이 설정된 코드와 VS Code에서 TypeScript 타입 오류가 빨간 줄로 표시된 화면
  • ·strict: true — strictNullChecks, noImplicitAny, strictFunctionTypes 등 8개 옵션을 한 번에 활성화
  • ·strictNullChecks: null과 undefined를 별도 타입으로 취급, 할당 전 가드 필요
  • ·noImplicitAny: 타입 추론이 any로 떨어지는 경우 오류 발생
  • ·Next.js, Create React App 기본 tsconfig에는 strict: true가 이미 포함됨
기존 JavaScript 프로젝트를 TypeScript로 마이그레이션하면서 strict 모드를 한꺼번에 켰다가 수백 개의 타입 오류가 쏟아져서 당황했다. 그때 strictNullChecks부터 하나씩 켜는 방식으로 단계적으로 마이그레이션했다. 이후 새 프로젝트에서는 처음부터 strict: true로 시작하는데, 런타임에 터졌을 오류들이 코드 작성 단계에서 걸러지는 걸 보면서 타입 안전성의 실질적인 효과를 체감하고 있다.

TypeScript strict 모드란 무엇인가

TypeScript strict 모드가 코드 품질에 미치는 영향

TypeScript의 strict 모드는 tsconfig.json에 strict: true 하나를 추가하는 것으로 활성화되는 옵션 묶음이다. strictNullChecks, noImplicitAny, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, alwaysStrict, useUnknownInCatchVariables 같은 엄격한 타입 검사 옵션들이 한 번에 켜진다. strict 모드 없이 TypeScript를 쓰면 타입 시스템이 느슨하게 동작해서 JavaScript와 큰 차이가 없어질 수 있다. any 타입이 암묵적으로 추론되거나 null 체크를 빠뜨려도 컴파일이 통과되기 때문이다. strict 모드를 켜면 이런 잠재적 버그들이 컴파일 타임에 오류로 잡힌다. 런타임에서 Cannot read properties of null처럼 터지는 오류가 코드를 작성하는 단계에서 미리 발견된다. 처음 strict 모드를 켜면 기존 코드에서 수십, 수백 개의 오류가 쏟아질 수 있다. 이 오류들 각각이 런타임 버그의 씨앗이었다는 뜻이다. 새 프로젝트는 처음부터 strict: true로 시작하는 게 나중에 마이그레이션하는 것보다 훨씬 수월하다.

TypeScript strict 모드를 구성하는 주요 옵션

strict: true가 활성화하는 옵션 중 실제 코드에 가장 큰 영향을 주는 것은 strictNullChecks와 noImplicitAny다. strictNullChecks를 켜면 null과 undefined가 모든 타입에 암묵적으로 포함되지 않는다. string 타입 변수에 null을 할당하면 타입 오류가 난다. null이나 undefined가 될 수 있는 값은 string | null처럼 명시적으로 표현하거나, 사용 전에 null 체크를 해야 한다. noImplicitAny를 켜면 TypeScript가 타입을 any로 추론할 수밖에 없는 경우에 오류를 낸다. 함수 매개변수에 타입을 쓰지 않거나 JSON.parse 결과를 바로 쓰는 경우가 대표적이다. strictPropertyInitialization은 클래스의 프로퍼티가 생성자에서 초기화되지 않으면 오류를 낸다. 직접 겪어보면 이 옵션들이 처음에는 번거롭게 느껴지지만, 코드를 쓰면서 데이터 흐름을 더 명확하게 생각하게 되고 결과적으로 코드 품질이 올라가는 걸 체감하게 된다.

TypeScript tsconfig strict 설정하기

tsconfig.json에 TypeScript strict 모드를 설정하는 방법

tsconfig.json의 compilerOptions에 strict: true를 추가하면 된다. Next.js나 Create React App으로 만든 프로젝트는 기본 tsconfig에 이미 strict: true가 포함되어 있다. 직접 TypeScript 프로젝트를 설정하는 경우 npx tsc --init으로 기본 tsconfig를 생성하면 strict 옵션이 주석 처리된 상태로 있다. 주석을 지우고 true로 바꾸면 활성화된다. 기존 코드베이스에 strict를 켜면 한꺼번에 오류가 쏟아지는 경우가 있다. 이 경우 strict: true 대신 개별 옵션을 하나씩 켜는 방식으로 단계적으로 적용할 수 있다. strictNullChecks만 먼저 켜고 오류를 수정한 다음 noImplicitAny를 켜는 식이다. 또는 // @ts-nocheck 주석으로 파일 단위로 일시적으로 검사를 비활성화하면서 파일별로 마이그레이션하는 방법도 있다. noImplicitAny를 부분적으로 적용하고 싶다면 strict: false 상태에서 noImplicitAny: true만 단독으로 설정할 수도 있다. 장기적으로는 strict: true를 목표로 가져가는 게 TypeScript를 제대로 활용하는 방법이다.

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2020",
    "lib": ["ES2020", "DOM"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "preserve",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "paths": {
      "@/*": ["./*"]
    }
  }
}

TypeScript strict 모드에서 자주 만나는 오류와 해결 방법

strict 모드를 켰을 때 가장 자주 나타나는 오류 유형이 있다. 첫 번째는 'Object is possibly null or undefined'다. strictNullChecks로 발생하며, 값을 쓰기 전에 null 체크를 추가해야 한다. if (value !== null) { ... } 또는 옵셔널 체이닝 value?.property 형태로 처리한다. 확실히 null이 아니라는 것을 보장할 수 있는 상황에서는 Non-null assertion operator(!)를 쓸 수 있지만 남용하면 strict 모드의 이점이 사라진다. 두 번째는 'Parameter implicitly has an any type'이다. 함수 매개변수에 타입이 없을 때 noImplicitAny가 오류를 낸다. 매개변수에 타입을 명시하면 해결된다. 세 번째는 'Property has no initializer and is not definitely assigned in the constructor'다. strictPropertyInitialization으로 발생하며, 클래스 프로퍼티를 생성자에서 초기화하거나 !를 붙여 명시적으로 늦은 초기화를 선언한다. 네 번째는 API 응답처럼 unknown 타입으로 들어오는 값을 바로 쓰려 할 때 나타나는 오류다. 타입 가드나 타입 단언으로 타입을 좁혀야 한다.

TypeScript strict 모드 실용 패턴

TypeScript strict 환경에서 null과 undefined를 안전하게 처리하는 방법

strictNullChecks가 켜진 환경에서 null과 undefined를 다루는 패턴을 익혀두면 타입 오류를 빠르게 해결할 수 있다. 옵셔널 체이닝 연산자(?.)는 중간 값이 null이나 undefined이면 undefined를 반환하고 더 이상 접근하지 않는다. user?.profile?.avatar처럼 체이닝해서 중첩된 객체에 안전하게 접근할 수 있다. null 병합 연산자(??)는 왼쪽 값이 null이나 undefined일 때 오른쪽 기본값을 반환한다. value ?? 'default'처럼 쓴다. ||와 달리 0이나 빈 문자열을 falsy로 취급하지 않기 때문에 기본값 처리에 더 정확하다. 타입 가드(Type Guard)는 조건문 안에서 타입을 좁히는 방법이다. typeof value === 'string'처럼 조건을 쓰면 그 블록 안에서는 value가 string으로 추론된다. Array.isArray나 instanceof도 타입 가드로 동작한다. as를 사용한 타입 단언은 컴파일러에게 타입을 강제로 알려주는 방법이다. 정말 확신하는 경우에만 쓰고, 타입 단언이 많아진다면 타입 설계를 다시 검토하는 게 좋다.

TypeScript strict 모드에서 외부 데이터와 API 응답을 타입 안전하게 처리하는 방법

fetch나 axios로 받은 API 응답, JSON.parse 결과, localStorage에서 읽은 값은 TypeScript가 타입을 알 수 없다. 예전에는 이런 값에 any를 쓰거나 타입 단언으로 강제 변환하는 방식이 흔했는데, strict 환경에서는 이 값들을 unknown으로 받고 타입을 검증하는 패턴을 쓰는 게 안전하다. zod 같은 스키마 검증 라이브러리를 쓰면 런타임에 외부 데이터를 검증하면서 TypeScript 타입도 자동으로 추론해준다. API 응답 스키마를 zod로 정의하고 parse하면, 파싱이 성공한 값은 타입이 보장된다. Next.js App Router에서 서버 컴포넌트나 Server Action에서 외부 API를 호출할 때 zod로 응답을 검증하는 패턴이 현재 가장 많이 쓰인다. fetch 응답을 any로 단언하면 API 스키마가 바뀌어도 TypeScript가 알려주지 않는다. zod를 쓰면 스키마가 바뀐 순간 런타임 오류로 즉시 알 수 있어서 디버깅이 빠르다. 처음에는 검증 코드가 늘어난다는 느낌이 들지만, 실제 운영에서 타입 불일치로 생기는 버그가 사라지는 효과가 훨씬 크다.

자주 묻는 질문

strict: true를 켜면 기존 코드에 오류가 너무 많이 납니다. 어떻게 시작하나요?+

strict 대신 개별 옵션을 하나씩 켜는 방식을 추천합니다. strictNullChecks부터 시작해서 오류를 수정하고, 다음에 noImplicitAny를 켜는 식으로 단계적으로 적용하면 됩니다. 또는 // @ts-nocheck 주석으로 파일별로 마이그레이션하는 방법도 있습니다.

Non-null assertion operator(!)를 언제 써도 되나요?+

코드 흐름상 절대 null이 될 수 없다는 것을 100% 확신하는 상황에서만 쓰세요. 예를 들어 document.getElementById가 항상 존재하는 요소를 가져오는 경우입니다. 남용하면 strict 모드를 쓰는 의미가 없어집니다.

TypeScript strict 모드와 ESLint는 함께 써야 하나요?+

네, 서로 보완 관계입니다. TypeScript는 타입 오류를 잡고, ESLint는 코드 스타일과 잠재적 논리 오류를 잡습니다. @typescript-eslint/eslint-plugin을 설치하면 TypeScript 코드에 맞는 ESLint 규칙을 적용할 수 있습니다.

skipLibCheck: true를 쓰면 안 되나요?+

라이브러리 타입 선언 파일(.d.ts)의 오류를 건너뛰는 옵션입니다. 외부 패키지 타입 정의 문제로 빌드가 실패할 때 임시로 쓸 수 있습니다. 본인 코드의 타입 안전성에는 영향을 주지 않으므로 대부분의 프로젝트에서 true로 설정해도 됩니다.

관련 글