데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      타입스크립트에서 type 과 interface 더 알아보기

      chamming2 23.08.29
      14,249 17 4
      DEVOTEE 요약
      이 블로그에서는 타입스크립트에서 객체 타입을 정의하는 데 사용되는 type과 interface의 차이점을 설명하고 있습니다. interface는 객체 상속을 지원하며 캐싱의 장점이 있지만, type은 모든 유형의 타입을 정의할 수 있고 중복 정의 문제가 발생하지 않는다는 특징이 있습니다. 그리고 인터페이스는 인덱스 시그니처를 암묵적으로 갖지만 타입은 그렇지 않다는 차이점도 있습니다. 타입스크립트 팀은 기본적으로 interface를 사용하는 것을 권장하지만, Declaration Merging과 인덱스 시그니처 기능이 필요한 경우에만 type을 사용하도록 권장하고 있습니다.

      프론트엔드 개발자들의 언어인 타입스크립트에서는 type과 interface 라는 두 가지 키워드로 객체의 타입을 정의할 수 있는데요,

      이번 블로그에서 두 키워드의 사용 방법이 다른 것 외에도 기능적으로 어떤 차이가 있는지를 소개 합니다.

      • 타입스크립트는 한 객체가 다른 객체를 상속할 수 있게 해주는 interface 라는 일급 클래스 원시 타입(first-class primitive)을 제공한다.

      interface WithId {
        id: string
      }
      
      interface User extends WithId {
        name: string
      }
      
      const user: User = {
        id: '0623',
        name: 'chanmin',
        wrongProperty: 123, // Error!
      }

      인터페이스를 대체하기 위한 type 키워드 역시 존재한다.

      type 키워드는 객체 타입뿐만 아닌 모든 유형의 타입을 정의할 수 있다.

      type StringOrNumber = string | number
      
      const func = (arg: StringOrNumber) => {}
      
      func('hello')
      
      func(123)
      
      func(true) // Error!

      여기서 논란이 발생하는데, 객체 타입을 정의할 때는 과연 type 을 사용해야 할까 interface 를 사용해야 할까?


      1. 객체 상속을 사용할 때는 interface를 활용하자

      서로 상속 관계에 있는 객체를 정의할 때는 interface 를 활용한다.

      위의 코드 중 interface 를 type 으로 고쳐 보자.

      type WithId {
        id: string;
      }
      
      type User & WithId {
        name: string;
      }
      
      const user: User = {
        id: "0623",
        name: "chanmin",
        // Error
        // `Type '{ id: string; name: string; wrongProperty: number; }' is not assignable to type 'User'.
        // Object literal may only specify known properties, and 'wrongProperty' does not exist in type 'User'.
        wrongProperty: 123,
      };

      타입스크립트는 인터페이스 이름을 기반으로 인터페이스의 정보를 내부 레지스트리에 캐싱할 수 있어, extends를 사용할 때는 캐싱의 장점을 활용할 수 있다.

      그러나 유니온(&) 연산자를 활용하는 경우에는 이름을 기반으로 캐싱할 수 없고, 항상 연산을 수행하기 때문에 성능상 불리하다.

      레퍼런스 : https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections

      이런 장점에도 불구하고 인터페이스를 기본으로 사용하는 것은 추천되지 않는다.


      2. 인터페이스는 선언된 인터페이스들을 합쳐버린다.

      인터페이스는 스코프 내에 정의된 같은 이름의 서로 다른 인터페이스를 하나로 병합한다.

      잠깐 변수를 할당하는 과정을 떠올려 보자.

      let user = {
        name: 'chanmin',
      }
      
      user = {
        age: 26,
      }
      
      // user 객체는 { age: 26 } 이 된다.

      변수에 할당이 중복으로 이루어진다면 일반적으로는 나중에 선언된 할당이 이루어져야 한다.

      (const 로 정의되었다면 재할당을 방지할 수도 있다.)

      그러나 인터페이스를 중복으로 정의할 때는 이와 다르게 동작한다.

      interface User {
        name: string
      }
      
      interface User {
        id: string
      }
      
      // 1. 스코프 내에 User 인터페이스가 중복으로 선언되었음에도, 이를 오류로 판단하지 않는다.
      // (* 마치 var 처럼 동작한다.)
      
      const user: User = {
        // Error
        // "Property 'name' is missing in type '{ id: string; }' but required in type 'User'."
        id: '123',
      }

      먼저 한 스코프 안에 User 라는 인터페이스를 중복으로 정의했음에도 var 키워드처럼 중복 할당을 허용하고 있다.

      그런데 더 문제가 되는 것은, 나중에 할당한 인터페이스 정의가 User 인터페이스를 덮어쓰는 것이 아니라 같은 이름의 서로 다른 인터페이스들의 속성을 모두 병합한다는 것이다.

      반면 interface 대신 type 을 활용하면 이런 문제가 발생하지 않는다.

      // Error: Duplicate identifier 'User'.
      type User = {
        name: string;
      }
      
      // Error: Duplicate identifier 'User'.
      type User = {
        id: string;
      }
      
      const user: User = {
        id: string;
      }

      객체를 타입으로 정의하면 동일 스코프에서 동일한 이름의 타입에 중복으로 할당하는 것을 방지해, const 키워드를 사용한 것처럼 동작한다.

      TIP. 인터페이스 병합 문제를 차단하기 위해서는 no-redeclare ESLint 룰을 적용할 수 있다.

      https://typescript-eslint.io/rules/no-redeclare/


      3. 인덱스 시그니처

      타입은 암묵적인 인덱스 시그니처를 갖는 반면 인터페이스는 그렇지 않다.

      즉 타입은 인덱스 시그니처를 갖는 타입에 할당할 수 있지만, 인터페이스는 할당할 수 없다.

      interface KnownAttributes {
        x: number
        y: number
      }
      
      const knownAttributes: KnownAttributes = {
        x: 1,
        y: 2,
      }
      
      type RecordType = Record<string, number>
      
      // Error
      // `Type 'KnownAttributes' is not assignable to type 'RecordType'.
      // `Index signature for type 'string' is missing in type 'KnownAttributes'.`
      
      // KnownAttributes 인터페이스에 <string, number> 외에 다른 타입이 확장될 수 있을 것으로 추론한다.
      const oi: RecordType = knownAttributes

      Type 'KnownAttributes' is not assignable to type 'RecordType'. Index signature for type 'string' is missing in type 'KnownAttributes'.`

      인터페이스에서 이를 지원하지 않는 까닭은 인터페이스는 추후 확장될 수 있는 가능성이 있기 때문이다. 인터페이스에 인덱스 시그니처를 활용하려면 명시적으로 정의해줘야 한다.

      interface KnownAttributes {
        x: number
        y: number
        [index: string]: unknown
      }

      또는 단순히 type 으로 변환해 사용할 수 있다.

      type KnownAttributes = {
        x: number
        y: number
      }
      
      const knownAttributes: KnownAttributes = {
        x: 1,
        y: 2,
      }
      
      type RecordType = Record<string, number>
      
      const oi: RecordType = knownAttributes


      결론 : 기본적으로는 type을 사용하자.

      TS 팀에서는 interface를 기본으로, type을 필요할 때만 사용하도록 권장한다. 그러나 Declaration Merging 및 인덱스 시그니처는 기본적으로 interface를 사용하기 꺼려지도록 한다.

      type의 장점

      • 암묵적인 인덱스 시그니처

      • 재선언 불가 (const 키워드의 역할)

      interface의 장점

      • 상속이 필요한 경우 캐싱을 활용할 수 있다.

      • interface 라는 이름의 특성 상, 보다 휴리스틱한 사용이 가능하다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      chamming2 님의 최신 블로그

      더보기
      동영상 기고하기