빠른 비교
interface User {
id: string;
name: string;
}
type UserWithRole = User & {
role: "admin" | "member";
};타입 선언
먼저 자주 보는 형태를 나누면 아래와 같습니다.
- 객체 계약 이름 붙이기:
interface User { ... } - union 이름 붙이기:
type Status = "idle" | "done" - 튜플 이름 붙이기:
type Point = [number, number] - intersection 조합:
type A = B & C - 구현 계약 연결:
class Service implements Api
1) 객체 계약을 드러내면 interface
객체 shape를 읽기 좋게 이름 붙이고, 확장하거나 구현 계약으로 쓰고 싶다면 interface가 자연스럽습니다.
interface User {
id: string;
name: string;
}특히 공개 API, props shape, 클래스 구현 계약에서 interface는 "객체 계약"이라는 느낌이 잘 드러납니다.
2) 타입 조합이 필요하면 type
union, tuple, intersection, 조건부 타입처럼 객체를 넘어서는 조합 표현은 type이 더 넓게 다룹니다.
type Status = "idle" | "loading" | "done";
type Point = [number, number];
type UserWithRole = User & { role: string };즉 type은 객체 선언 도구라기보다 더 일반적인 타입 이름 붙이기 도구에 가깝습니다.
interface User {
id: string;
name: string;
}
type UserId = User["id"];3) 선언 병합과 확장은 interface 쪽이 더 자연스럽다
같은 이름 선언을 병합하거나 extends로 객체 계약을 잇는 흐름은 interface가 읽기 좋은 경우가 많습니다.
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}interface Service {
run(): void;
}
class JobService implements Service {
run() {}
}4) 둘 중 하나만 강박적으로 고집할 필요는 없다
프로젝트 전체를 type만 또는 interface만으로 밀어붙이는 것보다, 역할을 분리해 쓰는 편이 오히려 자연스러운 경우가 많습니다.
중요한 건 "언제 무엇을 쓰는지 팀 안에서 읽히는가"입니다.
5) 혼용 자체보다 일관성 부족이 더 큰 문제다
같은 종류의 객체 계약을 어떤 파일에서는 type, 다른 파일에서는 interface로 뒤섞어 쓰면 독자가 규칙을 추론해야 합니다.
그래서 팀 기준이 있으면 그 기준을 유지하는 편이 좋습니다.
언제 interface를 쓸까
type과 interface를 고를 때 볼 점
- 객체 계약과 확장 중심이다:
interface - union, tuple, 조합 타입 표현이다:
type - 선언 병합이 필요하다:
interface - intersection으로 구조를 합친다:
type - 중요한 건 우열보다 팀 일관성이다
주의할 점
type과 interface를 섞어 쓰는 것 자체는 문제가 아니지만, 같은 역할을 들쭉날쭉하게 쓰면 읽기 비용이 커집니다.
// 예: 객체 공개 계약은 interface,
// union/조합 타입은 type 으로 정하는 식의 팀 규칙
interface User {
id: string;
}
type Status = "idle" | "done";
// ❌ union 표현은 interface로 바로 쓸 수 없다
type Result = "ok" | "error";참고 링크
2 sources