icon안동민 개발노트

객체와 인터페이스


 타입스크립트에서 객체와 인터페이스는 복잡한 데이터 구조를 표현하고 타입을 정의하는 데 핵심적인 역할을 합니다.

 이 절에서는 객체 타입 정의 방법, 인터페이스의 특징과 활용, 그리고 관련된 고급 개념들을 살펴보겠습니다.

객체 타입 정의 방법

  1. 인라인 객체 타입
let person: { name: string; age: number } = { name: "Alice", age: 30 };
  1. 인터페이스
interface Person {
  name: string;
  age: number;
}
let person: Person = { name: "Bob", age: 25 };
  1. 타입 별칭
type Person = {
  name: string;
  age: number;
};
let person: Person = { name: "Charlie", age: 35 };

 각 방법의 장단점

  • 인라인 객체 타입 : 간단한 일회성 사용에 적합, 재사용성 낮음
  • 인터페이스 : 확장성과 재사용성 높음, 객체지향 설계에 적합
  • 타입 별칭 : 유니온 타입 등 복잡한 타입 표현에 유용, 인터페이스와 유사한 기능 제공

인터페이스 상세

 인터페이스는 객체의 구조를 정의하는 강력한 도구입니다.

interface Vehicle {
  brand: string;
  year: number;
  isElectric?: boolean; // 선택적 프로퍼티
  readonly vin: string; // 읽기 전용 프로퍼티
  start(): void; // 메서드
}
 
let car: Vehicle = {
  brand: "Tesla",
  year: 2023,
  vin: "1HGCM82633A004352",
  start() { console.log("Engine started"); }
};

 인터페이스의 주요 기능

  • 선택적 프로퍼티 (?)
  • 읽기 전용 프로퍼티 (readonly)
  • 메서드 정의
  • 함수 타입 정의 가능

인터페이스 확장과 병합

 확장 (extends)

interface ElectricVehicle extends Vehicle {
  batteryCapacity: number;
}

 병합 (declaration merging)

interface Car {
  wheels: number;
}
interface Car {
  doors: number;
}
// Car 인터페이스는 wheels와 doors 프로퍼티를 모두 가짐

 이들의 유연성

  • 확장 : 기존 인터페이스를 재사용하고 확장하여 새로운 타입 정의
  • 병합 : 같은 이름의 인터페이스를 여러 번 선언하여 점진적으로 확장 가능

인덱스 시그니처

 동적 프로퍼티 타입 정의

interface StringDictionary {
  [key: string]: string;
}
 
let dict: StringDictionary = {
  "key1": "value1",
  "key2": "value2"
};

 활용 사례

  • 동적 키를 가진 객체 (예 : API 응답 데이터)
  • 맵 또는 딕셔너리 형태의 데이터 구조

초과 프로퍼티 검사

 타입스크립트는 객체 리터럴을 변수에 직접 할당할 때 초과 프로퍼티 검사를 수행합니다.

interface User {
  name: string;
  age: number;
}
 
let user: User = { name: "Alice", age: 30, location: "New York" }; // 에러

 우회 방법

  1. 타입 단언 사용 : let user: User = { name: "Alice", age: 30, location: "New York" } as User;
  2. 인덱스 시그니처 추가 : interface User { [key: string]: any; ... }

함수 타입과 콜백

 인터페이스를 사용한 함수 타입 정의

interface SearchFunc {
  (source: string, subString: string): boolean;
}
 
let mySearch: SearchFunc = function(src, sub) {
  return src.search(sub) > -1;
};

 콜백 함수 타입 지정

interface AsyncCallback {
  (error: Error | null, result?: any): void;
}
 
function fetchData(callback: AsyncCallback) {
  // 비동기 작업 수행
}

타입 별칭 vs 인터페이스

 주요 차이점

  1. 확장 문법 (extends vs 교차 타입 &)
  2. 선언 병합 (인터페이스만 가능)
  3. 유니온 타입 표현 (타입 별칭에서 더 자연스러움)

 선택 기준

  • 객체 형태를 정의할 때는 주로 인터페이스 사용
  • 복잡한 타입 조합이나 유니온 타입에는 타입 별칭 사용

구조적 타이핑

 타입스크립트는 구조적 타이핑을 채택하고 있습니다.

interface Named {
  name: string;
}
 
let person = { name: "Alice", age: 30 };
let named: Named = person; // 정상 동작

 타입 호환성에 미치는 영향

  • 객체의 실제 구조만 중요, 선언된 타입 이름은 무관
  • 더 많은 프로퍼티를 가진 객체도 호환 가능

설계 원칙과 Best Practices

 1. 인터페이스 분리 원칙 (ISP) 적용

  • 큰 인터페이스보다 작고 특화된 여러 인터페이스 선호

 2. 공통 프로퍼티를 가진 기본 인터페이스 생성 및 확장

interface BaseEntity {
  id: number;
  createdAt: Date;
}
interface User extends BaseEntity {
  name: string;
}

 3. 읽기 전용 프로퍼티 활용으로 불변성 강화

interface Config {
  readonly apiKey: string;
}

 4. 선택적 프로퍼티 사용으로 유연성 확보

interface UserOptions {
  name: string;
  email?: string;
  age?: number;
}

 5. 인덱스 시그니처 사용 시 주의

  • 가능한 한 구체적인 타입 사용
  • [key: string]: any 대신 구체적인 타입 명시

 6. 함수 타입에 인터페이스 활용

  • 재사용 가능한 콜백 타입 정의

 7. 초과 프로퍼티 검사 활용

  • 타입 안정성 강화를 위해 의도적으로 사용

 8. 구조적 타이핑 이해와 활용

  • 유연한 코드 설계, 하지만 의도치 않은 타입 호환 주의

 객체와 인터페이스는 타입스크립트에서 복잡한 데이터 구조를 표현하고 타입을 정의하는 데 핵심적인 역할을 합니다. 인라인 객체 타입, 인터페이스, 타입 별칭 등 다양한 방법으로 객체 타입을 정의할 수 있으며, 각 방법은 상황에 따라 적절히 선택하여 사용할 수 있습니다.

 인터페이스는 객체의 구조를 정의하는 강력한 도구로, 선택적 프로퍼티, 읽기 전용 프로퍼티, 메서드 정의 등 다양한 기능을 제공합니다. 인터페이스 확장과 병합을 통해 유연하고 재사용 가능한 타입 정의가 가능하며, 이는 대규모 애플리케이션 개발에 특히 유용합니다.

 인덱스 시그니처를 사용하면 동적 프로퍼티를 가진 객체를 타입 안전하게 정의할 수 있습니다. 이는 API 응답 데이터나 딕셔너리 형태의 데이터 구조를 다룰 때 유용합니다.

 초과 프로퍼티 검사는 타입스크립트의 중요한 타입 안전성 기능 중 하나입니다. 이를 통해 객체 리터럴 할당 시 예기치 않은 프로퍼티를 방지할 수 있습니다. 필요에 따라 타입 단언이나 인덱스 시그니처를 사용하여 이 검사를 우회할 수 있습니다.

 인터페이스를 사용하여 함수 타입과 콜백을 정의하는 것은 코드의 재사용성과 가독성을 높이는 좋은 방법입니다. 특히 복잡한 콜백 구조를 가진 비동기 프로그래밍에서 유용합니다.

 타입 별칭과 인터페이스는 많은 경우 서로 대체 가능하지만, 각각의 특징과 장단점이 있습니다. 일반적으로 객체 형태를 정의할 때는 인터페이스를 사용하고, 복잡한 타입 조합이나 유니온 타입에는 타입 별칭을 사용하는 것이 권장됩니다.

 구조적 타이핑은 타입스크립트의 중요한 특징 중 하나로, 객체의 실제 구조에 기반한 타입 체킹을 가능하게 합니다. 이는 유연한 코드 설계를 가능하게 하지만, 때로는 의도치 않은 타입 호환성 문제를 야기할 수 있으므로 주의가 필요합니다.

 효과적인 객체와 인터페이스 사용을 위해서는 인터페이스 분리 원칙 적용, 공통 프로퍼티를 가진 기본 인터페이스 생성 및 확장, 읽기 전용 프로퍼티 활용, 선택적 프로퍼티 사용 등의 설계 원칙과 Best Practices를 따르는 것이 중요합니다. 이를 통해 더 유지보수가 용이하고, 타입 안전한 코드를 작성할 수 있습니다.

 결론적으로, 객체와 인터페이스는 타입스크립트에서 타입 안전성과 코드 구조화를 위한 핵심 도구입니다. 이들을 효과적으로 활용함으로써 더 견고하고 유지보수가 용이한 애플리케이션을 개발할 수 있습니다.