Type vs Interface, 명확한 선택 기준이 필요할 때

by DD
2시간 전
조회수 22

type과 interface를 "그냥 익숙해서" 구분 없이 사용하다가 코드 리뷰에서Criteria 부재를 인정받으며 구분 기준(Clear Criteria) 부재 문제를 인식함

TypeScript 공식 문서에 따르면, type은 선언 후 속성을 추가할 수 없지만 interface는 언제든 확장 가능한 선언 병합(Declaration Merging) 지원 여부가 핵심 차이임

intersection(&) 연산자는 프로퍼티 타입 충돌 시 never 타입으로 암묵적 변환되지만, interface extends는 선언 단계에서 즉시 오류를 발생시켜 타입 충돌 조기 감지(Early Conflict Detection)를 가능하게 함

저자는 객체 contract/props/API 응답은 interface, union/tuple/conditional type은 type으로 구분 적용 중이며, 이제 "왜 이 상황에서 이 타입을 선택했는지"설명 가능하게 됨

선언 병합(Declaration Merging)이 만드는 근본적 차이

TypeScript 공식 문서가 강조하는 type과 interface의 핵심적 차이는 선언 병합(Declaration Merging) 지원 여부다. interface는 동일한 이름으로 다시 선언하면 기존 정의에 새 속성이 자동으로 병합되지만, type alias는 한 번 정의하면 닫혀서 재정의가 불가능하다.

이 동작은 라이브러리 타입 확장(Library Type Augmentation) 시나리오에서 특히 유용함. 서드파티 라이브러리의 타입을 별도 파일에서 `interface X`로 재선언하면 기존 타입에 필드를 추가할 수 있음

type의 경우 mapped type이나 utility type처럼 타입 표현 자체를 조합하는 데 적합하며, union, tuple, conditional type 등에서 더 높은 표현력을 발휘함

practical하게 보면, 외부 라이브러리를 확장해야 하는 상황이 아니라면 이 차이는 크게 체감되지 않으나, 타입 시스템의 설계 철학을 이해하는 데 중요한 포인트다

extends vs intersection(&) 충돌 처리 메커니즘

본문에서 가장 주목할 만한 기술적 분석은 interface extends와 type intersection의 충돌 처리 방식 차이다. type A & B에서 프로퍼티 타입이 서로 다를 경우, TypeScript는 `string & number`를 계산하여 `never` 타입을 도출한다. 이 과정은 선언 단계에서 오류를 발생시키지 않으며, 런타임에 실제로 해당 프로퍼티에 접근할 때 문제가 드러난다.

반면 interface B extends A에서 타입 충돌이 발생하면, TypeScript는 선언 단계에서 즉시 컴파일 에러를 발생시킨다. 이 동작 차이는 "타입 안전성(Type Safety)을 언제 검증할 것인가"라는 설계 관점의 차이에 기인함

저자의 경험담처럼, 객체 계층 구조에서는 interface extends가 잠재적 버그를 조기에 발견하게 해주므로 더 안전한 선택지가 된다

다만 type intersection의 동작이 항상 불필요한 것은 아니다. 의도적으로 `never`를 활용하여 불가능한 상태를 표현하는 패턴도 존재한다

실전 의사결정 매트릭스(Decision Matrix)

저자가 제시한 구분 기준은 다음과 같다.

interface 우선 상황: 객체 contract, API 응답, props, 객체 계층 확장. interface extends를 통해 선언 단계에서 타입 충돌을 즉시 감지할 수 있으며, IDE hover 시 일관된 타입 이름 표시가 가능함

type 우선 상황: union, tuple, conditional type, primitive alias. 타입 표현 자체를 조합하고 조합한 타입에 의미 있는 이름을 부여하는 데 더 유연함

핵심은 "익숙함"이 아닌 상황별 근거로 선택하는 것이다. 예를 들어 API 응답 타입이 외부 스키마에 의존하고 계층 확장이 필요하다면 interface, 상태값의 가능한 경우의 수를 나열해야 한다면 type이 각각 적합하다

프로젝트마다 컨벤션이 다를 수 있으나, 중요한 것은 팀 내에서 일관된 기준(Consistent Criteria)을 공유하고 그 근거를 설명할 수 있다는 점이다

IDE 개발 경험(Developer Experience)에서의 차이

본문에서 간과하기 쉬운 차이 중 하나는 IDE에서의 개발 경험(Developer Experience)이다. 복잡한 객체 타입을 재사용할 때, type alias는 때때로 익명 타입으로 표시되어 hover 시 가독성이 떨어지는 반면, interface는 선언된 이름이 그대로 유지되어 IDE에서 읽기 좋다.

type alias가 조건부 타입(Conditional Type)으로 감싸지면, 제네릭 인스턴스화 과정에서 익명 구조체(Anonymous Structure)로 확장되어 디버깅이 어려워진다

interface는 선언 병합 특성 덕분에 모듈 단위 분할 선언이 가능하여, 대규모 프로젝트에서 타입 정의를 여러 파일에 걸쳐 작성해도 자연스럽게 통합된다

이 차이는 대규모 팀에서 타입 문서화(Typed Documentation) 및 코드 가독성에 영향을 미치므로, API 계약 정의 시 interface를 선호하는 팀이 많은 이유 중 하나다

Type vs Interface, "그냥 익숙해서"에서 벗어나기