Skip to content

필사 모드: TypeScript 완전 가이드 — 타입 레벨 프로그래밍·Generics·Zod·tRPC·TanStack (Season 2 Ep 4, 2025)

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며 — TypeScript가 정복한 것들

2024~2025년, TypeScript는 단순히 "많이 쓰이는 언어"를 넘어 **사실상의 표준**이 되었다:

- **Stack Overflow Survey 2024**: 웹 개발 3대 언어 (JS, HTML, TS)

- **State of JS 2024**: 78%의 프로젝트가 TypeScript 사용

- **npm 다운로드**: TS 관련 패키지가 상위 100위 중 60% 차지

- **Next.js·Remix·Astro·Nuxt·Svelte**: 모두 TS 퍼스트

- **Node.js 22+**: `--experimental-strip-types` 플래그로 TS 직접 실행

- **Deno·Bun**: TS를 변환 없이 네이티브 실행

> TypeScript 팀의 책임자 Anders Hejlsberg는 Turbo Pascal → Delphi → C# → TypeScript를 만든 인물. 그 궤적을 이해하면 TS의 설계 철학이 보인다.

이 글은 TS를 **타입 시스템으로서** 심도 있게 다룬다.

1부 — TypeScript의 설계 철학

1.1 Gradual Typing

**"JavaScript를 점진적으로 강하게 타입 지정할 수 있게"**.

`any`를 허용하고, 점진 마이그레이션을 가능하게. 이것이 Flow·Elm이 망한 이유이자 TS가 이긴 이유.

1.2 Structural Typing

interface Duck { quack: () => void }

const obj = { quack: () => console.log('quack') };

const d: Duck = obj; // OK — 구조적 호환

"오리처럼 울면 오리다" — Name-based (Java) 아닌 **구조 기반**.

1.3 Type Erasure

// 컴파일 전

function greet<T extends string>(name: T): T { return name; }

// 컴파일 후 (JS)

function greet(name) { return name; }

**타입은 런타임에 존재하지 않는다.** 런타임 검증이 필요하면 Zod 같은 별도 도구.

2부 — TypeScript 5.x 핵심 기능 10가지

2.1 `const` Type Parameters (5.0)

type Hello = <const T>(x: T) => T;

const result = Hello(['a', 'b']); // readonly ['a', 'b']

2.2 Decorators (5.0 표준)

function logged(target: any, ctx: ClassMethodDecoratorContext) {

return function (this: any, ...args: any[]) {

console.log(`calling ${String(ctx.name)}`);

return target.apply(this, args);

};

}

class Foo {

@logged

bar() {}

}

2.3 `using` 선언 (5.2) — Explicit Resource Management

{

using file = await fs.open('log');

// 스코프 벗어나면 자동으로 [Symbol.dispose]() 호출

}

2.4 `satisfies` 연산자 (4.9)

const config = {

port: 3000,

host: 'localhost',

} satisfies Record<string, string | number>;

// config.port는 여전히 number로 좁혀짐

// satisfies는 검증만, 타입 넓히지 않음

2.5 Variadic Tuple Types (4.0)

type Concat<T extends any[], U extends any[]> = [...T, ...U];

type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]

2.6 Template Literal Types (4.1)

type Event = `on${Capitalize<'click' | 'hover'>}`; // 'onClick' | 'onHover'

type Route = `/users/${string}`;

2.7 `infer` 추론

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

2.8 Discriminated Union

type Result =

| { ok: true; value: string }

| { ok: false; error: Error };

function handle(r: Result) {

if (r.ok) { r.value } // 타입 좁혀짐

else { r.error }

}

2.9 `as const` + enum 대체

const Color = { Red: 'red', Blue: 'blue' } as const;

type Color = typeof Color[keyof typeof Color]; // 'red' | 'blue'

**Enum은 쓰지 말자 (2025 커뮤니티 합의)**. Union + `as const`가 우수.

2.10 NoInfer (5.4)

function createStreetLight<C extends string>(

colors: C[],

default_?: NoInfer<C>,

): void {}

createStreetLight(['red', 'yellow'], 'red'); // OK

createStreetLight(['red', 'yellow'], 'blue'); // Error — NoInfer로 막힘

3부 — Generics 실전 패턴 10

3.1 기본

function identity<T>(x: T): T { return x; }

const a = identity(42); // a: number

const b = identity('hello'); // b: string

3.2 Constraint

function getLength<T extends { length: number }>(x: T): number {

return x.length;

}

3.3 Default Type

type Ref<T = string> = { current: T };

const r: Ref = { current: 'hi' };

3.4 Keyof + Lookup

function pluck<T, K extends keyof T>(obj: T, key: K): T[K] {

return obj[key];

}

const name = pluck({ name: 'Alice', age: 30 }, 'name'); // name: string

3.5 Mapped Types

type Readonly<T> = { readonly [K in keyof T]: T[K] };

type Partial<T> = { [K in keyof T]?: T[K] };

type Pick<T, K extends keyof T> = { [P in K]: T[P] };

3.6 Conditional Type

type IsString<T> = T extends string ? true : false;

type A = IsString<'hello'>; // true

type B = IsString<42>; // false

3.7 Distributive Conditional

type ToArray<T> = T extends any ? T[] : never;

type X = ToArray<string | number>; // string[] | number[]

3.8 `infer`로 추출

type First<T extends any[]> = T extends [infer H, ...any[]] ? H : never;

type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;

3.9 Recursive Type

type DeepPartial<T> = T extends object

? { [K in keyof T]?: DeepPartial<T[K]> }

: T;

3.10 Branded Type (타입 레벨 다른 ID 구분)

type Brand<T, B> = T & { __brand: B };

type UserID = Brand<string, 'UserID'>;

type OrderID = Brand<string, 'OrderID'>;

function fetchUser(id: UserID) {}

const uid = 'u_123' as UserID;

fetchUser(uid); // OK

// fetchUser('raw string'); // Error

4부 — 타입 레벨 프로그래밍 실전

4.1 String 조작

type CamelCase<S extends string> =

S extends `${infer Head}_${infer Tail}`

? `${Head}${Capitalize<CamelCase<Tail>>}`

: S;

type A = CamelCase<'user_name_age'>; // 'userNameAge'

4.2 Route 파라미터 추출

type ExtractParams<S extends string> =

S extends `${string}:${infer Param}/${infer Rest}`

? Param | ExtractParams<`/${Rest}`>

: S extends `${string}:${infer Param}`

? Param

: never;

type R = ExtractParams<'/users/:id/posts/:postId'>;

// 'id' | 'postId'

4.3 SQL 타입 추론 (간이)

type ParseSelect<S extends string> =

S extends `SELECT ${infer Cols} FROM ${string}`

? Cols extends `${infer First}, ${infer Rest}`

? First | ParseSelect<`SELECT ${Rest} FROM X`>

: Cols

: never;

**타입 레벨 프로그래밍이 유용한 순간:**

- API 응답 스키마 자동 추론

- URL/라우트 타입 안전성

- SQL·GraphQL 쿼리 검증

**주의**: 재귀 깊이 제한(~50), 컴파일 시간 증가.

5부 — Zod: 런타임 검증 + 타입 추론

5.1 왜 Zod인가

TS는 런타임에 타입이 없다. API 응답, 폼 입력, LocalStorage 등 **신뢰할 수 없는 데이터**는 런타임에 검증해야 함.

5.2 기본 사용

const UserSchema = z.object({

id: z.string().uuid(),

email: z.string().email(),

age: z.number().int().positive(),

role: z.enum(['admin', 'user']),

});

type User = z.infer<typeof UserSchema>; // 자동 타입 추론

// 검증

const user = UserSchema.parse(untrustedData);

// 실패 시 ZodError throw, 또는 .safeParse()로 result 반환

5.3 복잡한 스키마

const PostSchema = z.object({

title: z.string().min(1).max(100),

tags: z.array(z.string()).min(1).max(5),

publishedAt: z.coerce.date(), // string -> Date 자동 변환

author: UserSchema, // 중첩

}).strict(); // 추가 필드 금지

5.4 2025 대안들

- **Valibot**: Zod보다 ~90% 작은 번들 (tree-shakable)

- **ArkType**: TS 문법 그대로 런타임 스키마

- **TypeBox**: JSON Schema 호환

**추천**:

- 서버·복잡한 로직: **Zod** (생태계 압도적)

- 번들 크기 민감: **Valibot**

- OpenAPI 통합: **TypeBox**

6부 — tRPC: 타입 안전한 API

6.1 기본 철학

"서버에서 정의한 API 타입이 클라이언트에 **자동으로** 전파된다."

6.2 서버

const t = initTRPC.create();

export const appRouter = t.router({

userById: t.procedure

.input(z.object({ id: z.string() }))

.query(async ({ input }) => {

return await db.user.findUnique({ where: { id: input.id } });

}),

createUser: t.procedure

.input(z.object({ email: z.string().email() }))

.mutation(async ({ input }) => {

return await db.user.create({ data: input });

}),

});

export type AppRouter = typeof appRouter;

6.3 클라이언트

export const trpc = createTRPCReact<AppRouter>();

// 컴포넌트

const { data, isLoading } = trpc.userById.useQuery({ id: 'u_1' });

// data 타입이 서버 return 타입으로 자동 추론됨

**장점**: 스키마 언어 필요 없음. 코드젠 없음. 그냥 TS.

**한계**: 모노레포·Node.js 환경 전제. 다른 언어 클라이언트 ❌.

**다른 언어 필요하면 → gRPC / GraphQL / OpenAPI**.

7부 — TanStack: 현대 React의 표준 스택

7.1 TanStack의 구성

| 라이브러리 | 역할 |

|-----------|------|

| **TanStack Query** (React Query) | 서버 상태 관리·캐시 |

| **TanStack Router** | 타입 안전 라우팅 |

| **TanStack Table** | 헤드리스 테이블 |

| **TanStack Form** | 폼 상태 관리 |

| **TanStack Start** | 풀스택 메타 프레임워크 (2024~) |

7.2 TanStack Query

const { data, isLoading, error } = useQuery({

queryKey: ['user', userId],

queryFn: () => fetch(`/api/users/${userId}`).then(r => r.json()),

staleTime: 5 * 60 * 1000,

refetchOnWindowFocus: true,

});

**핵심 개념**:

- **staleTime**: 캐시가 신선한 시간 (이 시간 내엔 네트워크 호출 ❌)

- **cacheTime/gcTime**: 사용자가 컴포넌트 버린 후 캐시 유지 시간

- **Mutation**: POST/PUT/DELETE + Optimistic Update

- **Query Invalidation**: 변경 후 관련 쿼리 재검증

7.3 TanStack Router

const rootRoute = createRootRoute();

const usersRoute = createRoute({

getParentRoute: () => rootRoute,

path: '/users/$userId',

component: UserComponent,

loader: ({ params }) => fetchUser(params.userId),

});

// URL → 파라미터 → 컴포넌트 props까지 타입 안전

Next.js App Router와의 차이: **완전 타입 안전**, 하지만 SSR·빌드 인프라 부족.

7.4 TanStack Start (2024~)

Remix·Next.js와 경쟁하는 풀스택 프레임워크. Vite 기반. 아직 beta지만 급성장 중.

8부 — 2024~2025 런타임 혁명: Bun·Deno·Node

8.1 Node.js 22+ (2024~)

- `--experimental-strip-types`: .ts 직접 실행

- 내장 Test Runner (`node --test`)

- 내장 Watch Mode (`node --watch`)

- `require(esm)` 지원

8.2 Bun (1.1+ Production Ready)

- TS/JSX 네이티브 실행

- 번들러·테스트러너·패키지 매니저 내장

- Node.js 호환 99%

- 속도: npm install ~25배 빠름, 런타임 3~5배

bun install

bun run dev

bun test

bun build ./src/index.ts

8.3 Deno (2.0+)

- 보안 퍼스트 (`--allow-net`, `--allow-read`)

- TS 네이티브, 표준 라이브러리 풍부

- `deno.json` 단일 설정

- Node.js 호환성 급성장

deno run --allow-net main.ts

deno task dev

8.4 선택 기준

| 상황 | 추천 |

|-----|-----|

| 기존 Node.js 프로젝트 | Node 22+ |

| 새 풀스택 | Bun (속도) |

| 보안·표준 중시 | Deno |

| Serverless | Node (Lambda 지원) |

9부 — Monorepo와 빌드 도구

9.1 Turborepo vs Nx

| 항목 | Turborepo | Nx |

|-----|-----------|-----|

| 학습 곡선 | 낮음 | 높음 |

| 기능 | 간결 | 풍부 (generator 등) |

| Vercel 통합 | 최상 | 부분 |

| 추천 | 소~중형 | 대형·복잡 |

9.2 Turborepo 기본

// turbo.json

{

"tasks": {

"build": { "dependsOn": ["^build"], "outputs": [".next/**"] },

"test": { "dependsOn": ["build"] },

"dev": { "cache": false, "persistent": true }

}

}

`turbo build`가 워크스페이스 전체를 의존성 그래프대로 병렬 빌드 + 리모트 캐시.

9.3 Biome (2024~, ESLint/Prettier 대체)

- 10~100배 빠름 (Rust 작성)

- Lint + Format 통합

- 단일 설정 파일 (`biome.json`)

- 아직 ESLint 룰셋 전체 커버는 아님 (80%+)

bunx @biomejs/biome init

bunx @biomejs/biome check --write ./src

**2025 권장 조합**:

- 새 프로젝트 → **Biome**

- 복잡한 린트 룰 필요 → **ESLint Flat Config**

- 대형 모노레포 → **Biome + Turborepo 리모트 캐시**

10부 — AI 시대의 TypeScript

10.1 AI와 TS가 찰떡인 이유

- **타입이 AI의 컨텍스트**: Copilot·Cursor는 타입을 읽고 더 정확한 코드 생성

- **Zod 스키마 = AI 입출력 명세**: Function calling에 이상적

- **tRPC 라우터 = AI 엔드포인트**: 타입 안전 AI API

10.2 Vercel AI SDK 예제

const result = await streamText({

model: openai('gpt-4o'),

messages,

tools: {

weather: {

description: 'Get weather',

parameters: z.object({ city: z.string() }),

execute: async ({ city }) => fetchWeather(city),

},

},

});

for await (const chunk of result.textStream) {

process.stdout.write(chunk);

}

10.3 Mastra·LangChain.js (2025)

- **Mastra**: TS 퍼스트 에이전트 프레임워크 (Zod 네이티브)

- **LangChain.js**: 성숙·범용, 하지만 무거움

- **Vercel AI SDK**: UI 스트리밍까지 통합

11부 — TypeScript 마스터 로드맵 6개월

Month 1: 기본 + strict

- strict mode 기본값 이해

- Narrowing, Type Guard

- Utility Types (Pick, Omit, Partial, Record 등)

Month 2: Generics

- Constraint, Default, Keyof

- Conditional Type, infer

- Distributive Conditional

Month 3: 고급 타입

- Template Literal

- Recursive Type

- Branded Type, Phantom Type

Month 4: 런타임 검증

- Zod 마스터

- API 레이어 타입 안전

- tRPC로 풀스택 만들기

Month 5: 실전 패턴

- Domain-Driven Design

- Result Monad 패턴

- 에러 타입 설계

Month 6: 타입 레벨 프로그래밍

- TypeScript 타입 체조 (typeChallenge)

- 라이브러리 타입 정의 기여

- Turborepo 모노레포 구축

12부 — TS 체크리스트 12

1. **structural typing** vs nominal typing 차이를 안다

2. **`any`, `unknown`, `never`** 차이를 안다

3. **Type vs Interface** 선택 기준을 안다

4. **Union 좁히기 (Narrowing)** 방법 5가지 이상을 안다

5. **Conditional Type + `infer`** 예시를 쓸 수 있다

6. **Mapped Type으로 자체 유틸리티** 만들 수 있다

7. **`satisfies` vs type annotation** 차이를 안다

8. **Branded Type**으로 ID 구분 방법을 안다

9. **Zod 스키마 → 타입 추론** 흐름을 안다

10. **tRPC 또는 GraphQL Codegen**으로 타입 안전 API 만들 수 있다

11. **Turborepo 또는 Nx**로 모노레포를 구성할 수 있다

12. **TS 5.x의 주요 기능 5개**를 말할 수 있다

13부 — TS 안티패턴 10

1. **`any` 남발**: 타입 안전성 포기. `unknown` + narrowing

2. **Enum 사용**: 번들 크기 증가, tree-shake 안 됨. Union + `as const`

3. **`as Type` 강제 캐스팅**: 타입 거짓말. `satisfies` 또는 Guard 사용

4. **`@ts-ignore`로 해결**: `@ts-expect-error`라도 써서 언젠간 지우게

5. **타입 대신 주석으로 의도 표현**: 타입으로 표현 가능한 건 타입으로

6. **Generics 과용**: Container/Factory 외엔 대부분 불필요

7. **`Function` 타입 사용**: 구체적 시그니처 명시

8. **`Object` 타입**: `Record<string, unknown>` 또는 구체 타입

9. **거대한 interface**: 10개 이상은 분할 신호

10. **TS 설정 `strict: false`**: `strict: true` 필수. 예외는 마이그레이션 중일 때만

마치며 — TypeScript는 "천천히 강해지는" 언어

TypeScript의 가장 큰 매력은 **쓰면 쓸수록 강해진다**는 점이다.

처음엔 그냥 JS에 타입 붙이는 도구. 6개월 뒤엔 Generics와 Conditional Type으로 설계. 1년 뒤엔 타입으로 프로그램 자체를 표현. 2년 뒤엔 타입 레벨 프로그래밍으로 불가능해 보이던 것을 가능하게.

그리고 **Zod·tRPC·TanStack·Vercel AI SDK**가 모두 TypeScript 퍼스트로 설계되어 있어서, TS를 잘할수록 생태계 전체가 강력해진다.

2025년 웹 엔지니어에게 TS는 **선택이 아니라 호흡**이다.

다음 글 예고 — "Python 완전 가이드: FastAPI·AsyncIO·Pydantic·uv·Polars·AI 엔지니어링 시대"

Season 2 Ep 5는 AI 엔지니어링의 공용어, **Python**. 다음 글은:

- Python 3.13의 변화 (free-threading, JIT)

- FastAPI + Pydantic v2로 타입 안전 API

- AsyncIO 실전 (TaskGroup, asyncio.timeout)

- uv: pip/poetry/pyenv 대체의 진짜 등장

- Polars vs Pandas 2025

- LangChain·LangGraph·Pydantic AI

- AI 엔지니어링 스택 총정리

스크립트 언어에서 AI 시대의 주역까지, 다음 글에서 이어진다.

현재 단락 (1/314)

2024~2025년, TypeScript는 단순히 "많이 쓰이는 언어"를 넘어 **사실상의 표준**이 되었다:

작성 글자: 0원문 글자: 10,547작성 단락: 0/314