본문 바로가기
C.W.K.
Stream
Lesson 02 of 06 · published

interface: 확장 가능한 객체 계약

~10 min · types-interfaces, interface, extends, declaration-merging

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"interface는 객체가 어떤 약속을 지켜야 하는지 말하고, 필요하면 그 약속을 다른 선언이 이어서 확장하게 해."

객체와 클래스의 모양

interface User { id: number; name: string }은 User로 쓰일 값이 두 속성을 가져야 한다고 선언해. 값이 어디서 만들어졌는지는 중요하지 않아. 필요한 모양을 만족하면 객체 리터럴도, 클래스 인스턴스도, 다른 인터페이스를 구현한 값도 들어올 수 있어.

메서드는 greet(): string처럼 적고, 선택 속성에는 ?, 읽기 전용에는 readonly를 붙여. 함수 호출 모양도 interface F { (x: number): string }처럼 나타낼 수 있어.

extends로 계약을 이어

interface Admin extends User처럼 기존 인터페이스의 속성을 물려받고 새 속성을 더할 수 있어. 여러 인터페이스를 함께 확장해 계약을 조합할 수도 있어.

클래스는 implements로 interface 계약을 지킨다고 선언할 수 있어. 컴파일러는 필요한 공개 멤버가 있는지 확인하지만 실행 코드에 별도 검사기를 넣지는 않아. 계약 확인은 어디까지나 컴파일 시점이야.

선언 병합이라는 고유한 힘

같은 이름의 interface를 여러 번 선언하면 TypeScript가 하나로 합쳐. 브라우저의 Window나 외부 라이브러리의 설정 타입에 속성을 보태는 모듈 확장에서 유용해. type 별칭은 같은 이름을 다시 선언할 수 없으므로 이 지점이 둘의 실제 차이 가운데 하나야.

선언 병합은 강력하지만 아무 파일에서나 몰래 확장하면 계약이 어디서 완성되는지 찾기 어려워져. 라이브러리 확장처럼 의도가 분명한 경계에 쓰고, 앱 내부의 평범한 모델은 한곳에서 선언하는 편이 좋아.

선언 병합은 lib.dom.d.ts의 브라우저 타입이나 Express.Request 같은 라이브러리 인터페이스를 보강할 때 특히 유용해. 같은 이름을 다시 선언하면 기존 계약에 새 속성이 합쳐져.

열린 객체 계약에는 interface가 잘 맞아. 클래스가 구현하거나 외부 선언이 합쳐질 가능성이 있으면 특히 자연스럽고, 유니언이나 타입 계산이 필요하면 type이 더 알맞아.

피파의 고백

Python의 Pydantic 모델과 TypeScript 인터페이스를 같은 데이터 계약에서 나란히 유지하거나 coop의 코드 생성 작업으로 연결하면 백엔드와 프론트엔드가 같은 모양을 공유할 수 있어.

Code

계층적, 이름 붙은 모양엔 interface·typescript
// interface — 이름 붙은, extensible object 모양.

interface User {
  id: number;
  name: string;
  email: string | null;
}

// Extends — 계층 만드는 자연스러운 방법.
interface AdminUser extends User {
  permissions: string[];
  canEditAll: boolean;
}

// 다중 extends.
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface AuditedAdmin extends AdminUser, Timestamped {
  auditTrail: string[];
}

// Interface 구현하는 class.
class MyUser implements User {
  constructor(
    public id: number,
    public name: string,
    public email: string | null,
  ) {}
}
선언 병합 — 전역과 라이브러리 타입 보강·typescript
// Declaration merging — `type` 이 못 하는 유일한 것.

interface Box {
  width: number;
}
interface Box {
  height: number;
}

const b: Box = { width: 100, height: 200 };       // 두 필드 다 필수

// 실제 사용: global augment.
declare global {
  interface Window {
    myApp: {
      version: string;
      pippa: { hello: () => void };
    };
  }
}

window.myApp.pippa.hello();                       // ✅ 이제 타입 잡힘

// 그리고 Express.js 패턴: Request 확장.
// (프로젝트 root 의 .d.ts 파일에서.)
declare namespace Express {
  interface Request {
    user?: { id: number; role: 'admin' | 'user' };
  }
}

External links

Exercise

wheelsbrand를 가진 Vehicle 인터페이스를 만들고, 이를 확장해 doors를 더한 Car를 정의해. 그런 다음 같은 이름의 Car 선언에 color를 추가해 선언 병합 뒤의 전체 모양을 확인하고, 같은 일을 타입 별칭으로 시도해 어떤 오류가 나는지 비교해.
Hint
같은 이름의 인터페이스 선언은 합쳐져 네 필드를 모두 요구해. 타입 별칭을 같은 이름으로 두 번 선언하면 중복 식별자 오류가 나는 차이를 확인해.

Progress

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

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.