LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

이펙티브 타입스크립트 2판 Day 9: 제네릭은 관계를 보존할 때만 강하다

2026년 7월 4일·5분 읽기
범위: Effective TypeScript 2판 Item 49–54
오늘의 질문: “제네릭을 넣으면 타입 설계가 항상 더 좋아질까?”

먼저 보는 코드

ts
type ApiResponse<T> = {
  ok: boolean;
  data?: T;
  error?: string;
};

async function getJson<T>(url: string): Promise<ApiResponse<T>> {
  const response = await fetch(url);
  return response.json();
}

겉으로는 좋아 보입니다. 호출자는 getJson("/api/user")처럼 쓸 수 있고, 결과도 User로 보입니다. 하지만 여기에는 질문이 남습니다.

런타임 JSON이 정말 User인지 누가 확인했을까?

Item 49–54 구간을 코드 리뷰 관점으로 읽으면 핵심은 “복잡한 타입을 만들자”가 아닙니다. 타입이 실제 코드의 관계를 보존하는지, 아니면 검증되지 않은 믿음을 예쁘게 포장하는지를 보는 일입니다.

제네릭과 조건부 타입의 경계

type coverage는 숫자보다 경계가 중요하다

type coverage를 올리는 일은 중요합니다. any가 넓게 퍼진 코드베이스는 타입스크립트의 질문을 잃습니다. 하지만 coverage 수치만 보고 안전하다고 말하기도 어렵습니다.

ts
const user = (await response.json()) as User;

이 한 줄은 coverage를 좋아 보이게 만들 수 있습니다. 하지만 외부 입력 검증이 없다면 런타임 안전성은 올라가지 않습니다. 리뷰에서는 any가 줄었는지보다 “검증되지 않은 경계가 타입 단언으로만 닫혔는지”를 봐야 합니다.

좋은 coverage 개선은 보통 이런 모양입니다.

ts
const value: unknown = await response.json();

if (!isUser(value)) {
  throw new Error("Invalid user payload");
}

renderUser(value);

타입이 늘어난 것이 아니라, 검증 위치가 생기고 내부 타입이 좁아진 것이 중요합니다.

제네릭은 “모든 타입을 받을 수 있음”이 아니다

제네릭의 장점은 타입을 아무거나 받는 데 있지 않습니다. 입력과 출력의 관계를 보존하는 데 있습니다.

ts
function first<T>(items: T[]): T | undefined {
  return items[0];
}

이 함수는 string[]을 넣으면 string | undefined, User[]를 넣으면 User | undefined를 돌려줍니다. T가 단순히 멋있어서가 아니라, 호출자가 준 원소 타입과 반환 타입이 연결되어 있기 때문에 의미가 있습니다.

반대로 이런 제네릭은 의심해야 합니다.

ts
function parse<T>(text: string): T {
  return JSON.parse(text);
}

호출자가 parse(text)라고 적으면 타입은 User가 되지만, 실제 검증은 없습니다. 이 경우 제네릭은 관계를 보존한 것이 아니라 호출자의 희망을 받아 적은 것에 가깝습니다.

코드 리뷰에서는 이렇게 묻습니다.

T가 입력과 출력 사이의 실제 관계를 표현하는가? T가 런타임 검증 없이 외부 데이터를 믿게 만드는가? 제네릭을 빼고 더 구체적인 타입을 쓰는 편이 더 명확하지 않은가?

조건부 타입은 API 규칙을 담을 때 강하다

조건부 타입은 타입 안에서 분기합니다.

ts
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

이런 타입은 유틸리티로 가치가 있습니다. Promise로 감싸진 타입이면 내부 타입을 꺼내고, 아니면 그대로 둔다는 규칙이 명확하기 때문입니다.

하지만 조건부 타입이 길어질수록 리뷰 비용도 늘어납니다. 타입 에러가 났을 때 팀원이 “어느 분기에서 왜 이 타입이 나왔는지” 설명할 수 있어야 합니다. 구현을 간단히 만드는 타입이면 좋고, 구현보다 타입 퍼즐이 어려워지면 위험 신호입니다.

템플릿 리터럴 타입은 문자열 규칙을 좁힐 때 쓴다

프론트엔드 코드에는 문자열 규약이 많습니다. 라우트, 이벤트 이름, cache key, analytics key가 대표적입니다.

ts
type UserRoute = `/users/${string}`;
type MetricEvent = `article:${"view" | "click" | "share"}`;

이런 타입은 오타를 줄이고 규칙을 코드에 남깁니다.

ts
function track(event: MetricEvent) {
  // ...
}

track("article:view");
track("article:delete"); // 타입 에러

다만 템플릿 리터럴 타입도 런타임 검증을 대신하지 않습니다. URL 파라미터나 서버 응답처럼 외부에서 온 문자열은 여전히 검증이 필요합니다. 타입은 개발 중 규칙을 좁혀 주고, 런타임 경계는 별도로 닫아야 합니다.

프론트엔드 코드 리뷰 체크리스트

코드 변화좋은 신호위험 신호
any 제거unknown + 검증 뒤 내부 타입으로 좁힘as SomeType만 늘어남
제네릭 추가입력/출력/콜백 사이 관계가 보존됨호출자가 원하는 타입을 임의로 주입함
조건부 타입 추가반복되는 타입 규칙을 짧고 설명 가능하게 표현함분기가 길고 에러 메시지를 해석하기 힘듦
템플릿 리터럴 타입으로 key 제한route/event/cache key 규칙이 드러남외부 문자열 검증까지 해결했다고 착각함
type coverage 수치 개선검증 경계와 테스트가 같이 생김숫자만 좋아지고 런타임 위험은 그대로

이번 범위의 결론은 “타입스크립트 고급 기능을 많이 쓰자”가 아닙니다. 제네릭, 조건부 타입, 템플릿 리터럴 타입은 모두 관계와 규칙을 표현하는 도구입니다. 관계가 없다면 제네릭은 장식이고, 검증이 없다면 타입 단언은 약속일 뿐입니다.

Day 9 타입 설계 점검

Quiz1 / 4
Q.다음 제네릭 함수의 가장 큰 문제는 무엇일까요?
js
"function parse<T>(text: string): T {\n  return JSON.parse(text);\n

Post Q&A

오케이징에게 물어보기

이펙티브 타입스크립트 2판 Day 9: 제네릭은 관계를 보존할 때만 강하다 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/study/effective-typescript
파일 9개, 폴더 0개
이펙티브 타입스크립트 2판 Day 1: 타입스크립트를 믿기 전에 알아야 할 경계이펙티브 타입스크립트 2판 Day 2: 타입을 값의 집합으로 보기이펙티브 타입스크립트 2판 Day 3: 타입 반복을 줄이는 리뷰 감각이펙티브 타입스크립트 2판 Day 4: 추론을 살리는 값 생성 패턴이펙티브 타입스크립트 2판 Day 5: 타입 추론을 방해하지 않는 API 설계이펙티브 타입스크립트 2판 Day 6: 유효한 상태만 표현하는 타입 설계이펙티브 타입스크립트 2판 Day 7: 문자열보다 도메인 의미를 좁히기이펙티브 타입스크립트 2판 Day 8: any를 밖에 세우고 unknown으로 검증하기이펙티브 타입스크립트 2판 Day 9: 제네릭은 관계를 보존할 때만 강하다

같은 섹션의 대표 이미지

9 posts · latest first
외부 경계, 제네릭, 조건부 타입, 템플릿 리터럴 타입의 역할과 과한 타입 계산의 위험을 보여주는 다이어그램
Study26. 07. 05.

이펙티브 타입스크립트 2판 Day 9: 제네릭은 관계를 보존할.

Item 49–54 범위를 바탕으로 type coverage, 제네릭, 조건부 타입, 템플릿 리터럴 타입을 코드 리뷰에서 어떻게 판단할지 정리합니다.

26. 07. 05.SEOJing
외부 JSON을 unknown으로 받고 검증 뒤 도메인 타입으로 넘기는 경계와 any가 내부로 번지는 위험을 보여주는 다이어그램
Study26. 07. 04.

이펙티브 타입스크립트 2판 Day 8: any를 밖에 세우고.

Item 42–48 범위를 바탕으로 any, unknown, 타입 단언, monkey patching, soundness 함정을 외부 입력 경계와 코드 리뷰 관점에서 정리합니다.

26. 07. 04.SEOJing
넓은 문자열과 선택적 필드를 도메인 이름과 유효 상태 유니온으로 좁히는 다이어그램
Study26. 06. 29.

이펙티브 타입스크립트 2판 Day 7: 문자열보다 도메인.

Item 35–41 범위를 바탕으로 string 남용, optional 필드, 특수 값, 도메인 이름 설계를 코드 리뷰 관점에서 정리합니다.

26. 06. 29.SEOJing
API 응답을 유효한 상태 유니온으로 변환하는 흐름 다이어그램
Study26. 06. 28.

이펙티브 타입스크립트 2판 Day 6: 유효한 상태만 표현하는.

Item 28–34 범위를 바탕으로 추론 위치, API 경계, null 처리, union 설계를 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 28.SEOJing
타입스크립트 추론이 값에서 API 경계로 흐르는 과정을 보여주는 다이어그램
Study26. 06. 27.

이펙티브 타입스크립트 2판 Day 5: 타입 추론을 방해하지 않는.

Effective TypeScript 2판 Item 19–22를 바탕으로 타입 추론, 타입 넓히기, 함수형 기법, 유니온 설계를 코드 리뷰 관점에서 정리합니다.

26. 06. 27.SEOJing
TypeScript에서 넓은 타입으로 먼저 담으면 key와 literal 정보가 사라지고 리뷰 체크가 약해지는 흐름 다이어그램
Study26. 06. 26.

이펙티브 타입스크립트 2판 Day 4: 추론을 살리는 값 생성.

Effective TypeScript 2판의 Item 16–21을 바탕으로 index signature, 타입 추론 기본, 변수/객체 생성 패턴을 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 26.SEOJing
반복된 타입 선언을 원본 타입에서 파생한 타입으로 바꿔 리뷰 체크를 강하게 만드는 흐름 다이어그램
Study26. 06. 25.

이펙티브 타입스크립트 2판 Day 3: 타입 반복을 줄이는 리뷰.

Effective TypeScript 2판의 Item 11–15를 바탕으로 excess property check, 함수식 타입, type vs interface, readonly, 타입 반복 제거를 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 25.SEOJing
TypeScript 타입을 값의 집합, type space, value space, assertion 경계로 정리한 다이어그램
Study26. 06. 24.

이펙티브 타입스크립트 2판 Day 2: 타입을 값의 집합으로.

Effective TypeScript 2판의 Item 6–10을 바탕으로 타입 시스템을 탐색하는 법, 타입을 값의 집합으로 보는 관점, type/value space, 타입 단언의 경계를 코드 리뷰 관점에서 정리합니다.

26. 06. 24.SEOJing
TypeScript를 JavaScript 위의 정적 타입 계층, 타입 제거, 구조적 타이핑, any의 구멍으로 정리한 다이어그램
Study26. 06. 23.

이펙티브 타입스크립트 2판 Day 1: 타입스크립트를 믿기.

Effective TypeScript 2판의 Item 1–5를 바탕으로 TypeScript와 JavaScript의 관계, tsconfig, 타입 제거, 구조적 타이핑, any의 위험을 프론트엔드 코드 리뷰 관점에서 정리합니다.

26. 06. 23.SEOJing