본문 바로가기
C.W.K.
Stream
Lesson 04 of 07 · published

tsconfig strict가 켜는 검사

~16 min · typescript, tsconfig, strict, react

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
TypeScript의 strict는 boolean 하나처럼 보이지만 여러 검사를 한꺼번에 켜. 오류 메시지가 가리키는 검사를 알면 타입 체커와 싸우지 않고 버그를 고칠 수 있어.

strict: true가 켜는 검사

strict를 켜면 서로 관련된 엄격한 검사가 함께 적용돼. 전부 외울 필요는 없지만 오류가 어느 규칙에서 왔는지 알면 해결 방향을 잡기 쉬워.

  • noImplicitAny는 추론할 수 없는 값이 조용히 any가 되지 않게 해.
  • strictNullChecksnullundefined를 다른 값과 구분하게 해.
  • strictFunctionTypes는 함수 인자의 호환성을 더 안전하게 검사해.
  • strictBindCallApplybind, call, apply에 넘긴 인자도 확인해.
  • strictPropertyInitialization는 클래스 필드가 생성 과정에서 초기화되는지 검사해.
  • noImplicitThis는 타입을 알 수 없는 this를 막아.
  • alwaysStrict는 strict mode로 파일을 해석하고 출력하게 해.
  • useUnknownInCatchVariables는 catch의 오류 값을 any 대신 unknown으로 다루게 해.
새 설정에 strict: true가 없어도 먼저 버전을 확인해. TypeScript 6.0부터 strict가 기본값이어서 최신 Vite 템플릿에는 중복된 줄이 없을 수 있어. 예전 프로젝트에 명시된 설정은 그대로 두면 돼.

React 프로젝트에서 함께 볼 설정

jsx: react-jsx는 최신 JSX transform을 사용하게 해. moduleResolution: bundler는 Vite와 같은 방식으로 import를 해석해. target: ES2022 이상이면 top-level await와 비공개 클래스 필드 같은 현대 문법을 불필요하게 낮추지 않아.

설정 파일이 나뉘는 이유

tsconfig.json은 다른 설정을 가리키는 시작점이고, tsconfig.app.json은 브라우저에서 실행할 src/ 코드를 검사해. tsconfig.node.json은 Node에서 실행되는 Vite 설정을 맡아. 실행 환경이 다르므로 필요한 라이브러리 타입도 달라.

오류 메시지를 해결하는 순서

string | undefinedstring 자리에 넘길 수 없다는 오류가 보이면 먼저 값이 있는지 검사해 타입을 좁혀. 기본값을 주거나 함수가 undefined를 받도록 계약을 넓히는 방법도 있어. 실제 검증 없이 non-null assertion인 !로 숨기면 런타임 오류 가능성은 그대로 남아.

각 strict 검사가 실제로 막는 버그

noImplicitAny는 타입을 잃은 이벤트 인자가 코드 전체로 퍼지는 일을 막아. strictNullChecks는 아직 오지 않은 API 응답을 이미 있는 값처럼 쓰지 못하게 하고, strictFunctionTypes는 더 좁은 입력만 처리하는 함수를 넓은 콜백 자리에 잘못 넘기는 일을 잡아. useUnknownInCatchVariables 덕분에 오류를 문자열이라고 가정하기 전에 instanceof Error 같은 검사가 필요해.

strictPropertyInitialization은 생성자가 끝난 뒤에도 비어 있을 수 있는 클래스 필드를 알려 줘. strictBindCallApply는 함수의 원래 시그니처와 다른 인자를 bind·call·apply에 넘기는 실수를 막고, noImplicitThis는 콜백 안의 this가 어느 객체인지 모호한 코드를 거부해.

브라우저 코드와 Node 설정을 나누는 이점

앱 설정에는 DOM 타입이 필요하지만 vite.config.ts에는 Node 타입이 필요해. 두 환경을 한 tsconfig에 섞으면 브라우저 코드에서 Node 전역을 실수로 사용해도 컴파일이 통과하거나, 반대로 Vite 설정이 DOM 계약을 물려받을 수 있어. Project reference로 나누면 각 파일이 실제 실행 환경에 맞는 타입만 보게 돼.

Code

Vite 기본 tsconfig.app.json 설정 읽기·json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",

    /* strict 체크 여덟 개를 플래그 하나로 */
    "strict": true,

    /* 린팅 (strict 아닌 경고들) */
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    /* Bundler용 설정: TypeScript는 JavaScript를 만들지 않고 Vite가 변환해. */
    "noEmit": true,
    "isolatedModules": true,
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true
  },
  "include": ["src"]
}
각 strict 체크가 잡는 실제 React 버그·tsx
// strictNullChecks — 없으면, undefined 일 수 있는 user의 .name 접근이
// 조용히 컴파일되고 런타임에 터져.
function Greet({ user }: { user?: { name: string } }) {
  // 컴파일러 거부: user가 possibly undefined.
  // return <p>Hello, {user.name}</p>;

  // 올바른 패턴: 먼저 narrow.
  if (!user) return <p>Hello, friend</p>;
  return <p>Hello, {user.name}</p>;
}

// useUnknownInCatchVariables — catch (e)가 any 아니라 unknown 줌.
async function loadOrLog() {
  try {
    await fetch("/api/data");
  } catch (e) {
    // 'e'는 unknown. 바로 .message 못 함.
    const message = e instanceof Error ? e.message : String(e);
    console.error(message);
  }
}

External links

Exercise

부트스트랩 프로젝트에서 strict를 잠깐 끄고 타입이 없는 shout(msg)shout(null) 호출이 통과하는지 확인해. Strict를 다시 켠 뒤 암시적 any, null 가능 값, 초기화되지 않은 클래스 필드가 각각 어떤 오류를 내는지 기록해. 타입 선언, null 검사, 생성자 초기화로 고치고 non-null assertion으로 숨긴 버전과 런타임 안전성도 비교해.
Hint
최종 상태에서는 strict를 켜고 tsc --noEmit가 성공해야 해. 오류를 없애는 위치가 아니라 어떤 값이 들어올 수 있다는 계약을 실제로 좁혔는지 설명해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 2

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.
  1. 이 thread는 leaf-bound Issue로 옮겨졌어요
    Chan
    Chan

    For anyone wondering why strict is missing from tsconfig.app.json, this is expected with TypeScript 6.0+. strict is now enabled by default, so Vite removed the explicit "strict": true from new project templates. If you were looking for it and couldn't find it, these references explain the change:

    TypeScript 6.0 announcement: https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/#simple-default-changes

    Vite PR: https://github.com/vitejs/vite/pull/22110

    💛 by 피파warm
    1. 피파
      피파· warmChanChan

      Thank you, Chan — this is a genuinely useful catch. You’re right: with TypeScript 6.0+, strict is enabled by default, so the newest Vite React TS template may not show an explicit "strict": true line anymore. I’m promoting this into a quest update request so the lesson can explain both worlds: older templates where learners can find the explicit flag, and newer TS 6.0+ templates where the default changed.