codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›languages

TypeScript 와 strict 모드

2026-04-28 게시· 2026-05-18 갱신·0회 조회

TypeScript 와 strict 모드

TypeScript 는 JavaScript 에 정적 타입을 더한 언어. 이 글은 TypeScript 의 출자 · 동작 방식과 strict: true 옵션이 실제로 켜는 것들을 사실 기준으로 정리. 런타임 검증의 빈자리와 비슷한 도구의 위치도 함께.

1. TypeScript 에 대한 이야기

Microsoft 의 Anders Hejlsberg (Turbo Pascal · Delphi · C# 의 설계자로 알려진 인물) 가 주도해 만든 언어. 첫 공개는 2012 년 10 월이고, 1.0 은 2014 년 4 월. 저장소는 microsoft/TypeScript, 라이선스는 Apache-2.0.

언어 자체는 JavaScript 의 상위 집합 (superset) 으로 설계. 모든 유효한 JS 는 유효한 TS 이며, 컴파일러는 타입 정보를 제거한 JS 로 출력. 즉 TS 의 타입은 컴파일 타임에만 존재하고 런타임에는 흔적이 거의 남지 않습니다 (예외: enum · namespace 같은 일부 산출물).

배포물은 두 가지 — 컴파일러 tsc 와 언어 서버 tsserver. 후자가 VS Code · WebStorm 등에서 자동완성 · 오류표시를 담당.

2. 구조적 타이핑

TypeScript 의 타입 시스템은 structural typing (구조적 타이핑). Java · C# 의 명목적 타이핑 (nominal typing) 과 달리 두 타입의 호환은 이름이 아니라 모양 으로 판단:

interface Point { x: number; y: number }
function len(p: Point) { return Math.hypot(p.x, p.y) }

const p = { x: 3, y: 4, label: "a" }   // Point 라 선언 안 했지만
len(p)                                  // 모양이 맞으면 통과

3. strict: true 가 켜는 것

tsconfig.json 에 "strict": true 한 줄을 켜면 다음 옵션 묶음이 한꺼번에 (공식 문서 Strict Mode Family):

옵션 의미
noImplicitAny 추론이 실패해 암묵적 any 가 되는 경우를 오류로 봄.
strictNullChecks null 과 undefined 가 모든 타입에 자동 포함되지 않음. 의도한 곳만 T | null.
strictFunctionTypes 함수 매개변수의 반공변 (contravariant) 검사를 엄격히.
strictBindCallApply bind · call · apply 의 인자 타입을 검사.
strictPropertyInitialization 클래스 필드가 생성자에서 반드시 초기화되도록 강제 (strictNullChecks 필요).
alwaysStrict 출력 JS 파일 상단에 "use strict" 를 자동으로 붙이고, 분석도 strict 모드.
useUnknownInCatchVariables try { } catch (e) 의 e 기본 타입이 any 가 아닌 unknown.

이 가운데 strictNullChecks 한 가지가 가장 큰 가치를 만든다는 평이 흔함. null / undefined 관련 런타임 오류를 컴파일 시점으로 옮김.

4. 제네릭 · 조건부 · 매핑 타입

// 제네릭
function first<T>(arr: T[]): T | undefined { return arr[0] }

// 매핑 타입
type Partial<T> = { [K in keyof T]?: T[K] }

// 조건부 타입
type NonNull<T> = T extends null | undefined ? never : T

infer · keyof · 템플릿 리터럴 타입까지 결합하면 타입 수준에서 작은 함수형 언어 가 동작한다고 표현.

5. 다른 길

도구 위치 비고
TypeScript Microsoft, 2012 사실상 표준. tsc 가 검사 + 트랜스파일.
Flow Meta, 2014 비슷한 시도. 현재 Meta 내부 위주. Hermes 엔진과 결합.
JSDoc + // @ts-check 1995 (JSDoc) 빌드 도구 없이 JS 파일에 타입을. tsc 가 읽을 수 있음.
esbuild · swc · Babel 별도 타입 검사 안 함. 타입 정보를 제거 만 (빠름). 검사는 tsc --noEmit 별도.

esbuild · swc 는 의도적으로 타입을 검사하지 않고 지움. 대규모 코드베이스에서 빌드와 검사를 분리해 빌드는 빠른 도구로, 검사는 tsc --noEmit 으로 돌리는 구성이 흔합니다.

6. tsconfig 출발점

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "skipLibCheck": true,
    "esModuleInterop": true
  }
}

noUncheckedIndexedAccess 와 exactOptionalPropertyTypes 는 strict 묶음에 들어가지 않지만 별도로 켜는 편이 흔함.

7. 실무 타입 패턴

문법을 넘어 매일 쓰는 자리의 선택지. 큰 기능이 아니라 작은 결정들이 코드베이스의 결을 만듦.

interface vs type — 둘 다 객체 모양을 적을 수 있지만 잘하는 일이 다름. 객체 모양 · 확장 · 선언 병합 은 interface, 유니온 · 인터섹션 · 조건부 타입 · 원시 타입 별칭 은 type:

// interface — 객체 모양, extends 로 확장, 같은 이름 재선언이 병합됨
interface Animal { name: string }
interface Dog extends Animal { bark(): void }

// type — 유니온·인터섹션·별칭은 type 만 가능
type Id = string | number
type Result = { ok: true; value: number } | { ok: false; error: string }

객체 하나라면 어느 쪽이든 동작하므로 팀 규칙을 따르면 됨. 유니온이 필요해지는 순간 type 으로 자연히 갈림.

유틸리티 타입 — 이미 있는 타입에서 새 타입을 파생. 부분 필드 인터페이스를 손으로 다시 적는 대신 표준 도구를 재사용:

interface User { id: string; name: string; email: string; createdAt: Date }

type UserCard = Pick<User, "id" | "name">      // 일부 필드만
type PublicUser = Omit<User, "email">          // 일부 필드 제외
type UserDraft = Partial<User>                 // 전 필드 선택적
type UserInput = Required<UserDraft>           // 전 필드 필수
type UsersById = Record<string, User>          // 키-값 맵
type Make = ReturnType<typeof createUser>      // 함수 반환 타입 추출

원본 User 가 바뀌면 파생 타입이 함께 따라옴 — 단일 출처가 유지됨.

enum 대신 as const + 유니온 — TS 의 enum 은 런타임 객체를 만들어 번들에 코드가 남고 트리 셰이킹이 어려움. as const 객체와 거기서 뽑은 유니온이 권장 대안:

const Role = {
  Admin: "admin",
  Editor: "editor",
  Viewer: "viewer",
} as const;

type Role = typeof Role[keyof typeof Role];   // "admin" | "editor" | "viewer"

function grant(r: Role) { /* ... */ }
grant(Role.Editor);                            // 값으로도, 리터럴로도 사용

런타임에는 평범한 객체 하나뿐이고, 타입은 좁은 유니온이라 자동완성과 누락 검사가 모두 작동.

as 캐스트 절제 — as 는 "내가 컴파일러보다 잘 안다"는 선언이라 검사를 끔. 정당한 자리는 좁음 — 검증을 마친 직후, DOM 이벤트 타깃, 제네릭 기본값 정도:

// 정당 — 런타임 검증을 통과한 값에 타입을 붙임
const data = JSON.parse(raw) as Config;        // 다음 줄에서 zod 등으로 검증 전제

// 정당 — DOM 은 일반 타입만 주므로 좁혀 줌
const input = e.target as HTMLInputElement;

// 부적절 — 타입 가드가 정답인 자리
function isUser(v: unknown): v is User {
  return typeof v === "object" && v !== null && "id" in v;
}
if (isUser(value)) value.id;                   // as User 대신 가드로 좁힘

"좁힐 수 있는데 단언했다"면 대개 타입 가드가 맞음. as 는 컴파일러의 안전망을 끄므로, 쓸 때마다 "왜 검사보다 단언인가" 를 답할 수 있어야 함.

8. 런타임 검증과 zod

타입은 컴파일 시점에만 살아 있음. 외부에서 들어온 JSON · 폼 입력 · 환경변수는 런타임에 따로 검증해야 함. 이 자리에 자주 쓰이는 라이브러리:

import { z } from "zod"

const User = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  age: z.number().int().nonnegative()
})
type User = z.infer<typeof User>      // 정적 타입 자동 추출

const u = User.parse(jsonFromApi)     // 실패 시 throw

zod (Colin McDonnell, 2020) 외에도 valibot · ArkType · yup · joi 가 같은 자리.

라이브러리 첫 공개 비고
zod 2020 TS-first. 가장 넓게 채택.
yup 2015 Form 검증으로 시작. Formik 과 함께 자주.
joi 2012 본래 hapi 서버용. 노드 백엔드에서 자리 잡음.
valibot 2023 트리 셰이킹 친화. 함수 단위 가져오기.
ArkType 2023 TS 표현식을 그대로 파싱해 런타임 타입을 만듦.

9. 자주 걸리는 자리

any 와 unknown 의 차이 — any 는 검사 우회, unknown 은 "정체를 모름". catch 변수가 any 였던 옛 동작은 useUnknownInCatchVariables 로 unknown 이 됨. 좁히지 않으면 사용할 수 없게 만들어 안전.

as 단언의 남용 — 단언은 검사를 끄는 도구. 검증 함수 (parse) 로 바꿀 수 있는 자리에 as 가 자라기 쉬움.

enum 의 함정 — TS 의 enum 은 런타임 객체를 만들고 트리 셰이킹이 어려움. 자주 권장되는 대안은 as const 객체 + 유니온 타입 추출.

타입과 런타임의 분리 — 타입은 빌드 시 사라짐. 런타임 분기는 zod 같은 검증 또는 typeof · in 으로 직접 만들어야.

타입 정의 패키지 — JS 라이브러리는 타입이 없을 수 있음. @types/<pkg> (DefinitelyTyped) 가 별도 패키지.

하고픈 말

TypeScript strict + zod 의 결합이 컴파일 타임 + 런타임 두 자리를 모두 잡는 표준 짝. any 와 as 의 사용을 의도적으로 적게 두면 타입 시스템의 가치가 거의 그대로. 외부에서 들어오는 모든 데이터는 zod parse 한 번을 거치는 습관이 운영의 안정성을 크게.

Next

  • java21-modern
  • python-async

TypeScript 공식 사이트 · TypeScript GitHub · TSConfig Reference · Strict Mode Family 문서 · DefinitelyTyped · TC39 ECMAScript proposals · zod · valibot · ArkType 을 참고합니다.

languages 카테고리의 다른 글

카테고리 전체 보기 →
  • Rust — Tauri 가 Rust 인 이유
  • Python 과 비동기 (asyncio)
  • Java 21 의 모습