Adventure Time - Finn 3
본문 바로가기
프론트

TypeScript 인터페이스(Interface)

by hyun9_9 2026. 4. 11.

객체의 타입을 정의하는 핵심 도구, 인터페이스의 기본부터 확장까지 단계별로 정리합니다.


1. 왜 interface가 필요한가?

object 타입으로 변수를 선언하면 프로퍼티에 접근할 수 없습니다.

let user: object;

user = {
  name: 'xx',
  age: 30
}

console.log(user.name); // ❌ Error — object 타입에는 name이 없음

이 문제를 해결하기 위해 interface로 객체의 구조를 명확히 정의합니다.

interface User {
  name: string;
  age: number;
}

let user: User = {
  name: 'xx',
  age: 30
}

user.age = 10;           // ✅ 정상 동작
console.log(user.age);   // 10

💡 interface는 객체가 어떤 프로퍼티와 타입을 가져야 하는지 계약(contract)을 정의하는 것입니다. 정의되지 않은 프로퍼티를 추가하려고 하면 에러가 발생합니다.

 


2. 옵셔널 프로퍼티 (?)

모든 프로퍼티가 항상 필요한 건 아닙니다. ?를 붙이면 해당 프로퍼티는 있어도 되고 없어도 됩니다.

interface User {
  name: string;
  age: number;
  gender?: string;  // 있어도 되고 없어도 됨
}

let user: User = {
  name: 'xx',
  age: 30
  // gender 없이도 ✅ OK
}

user.gender = "male";  // ✅ 나중에 추가도 OK

?가 없으면 객체 생성 시 해당 프로퍼티를 반드시 포함해야 하므로, 선택적 데이터에는 옵셔널을 활용하세요.


3. 읽기 전용 프로퍼티 (readonly)

생성 시 값을 할당하면 이후 수정할 수 없는 프로퍼티입니다.

interface User {
  name: string;
  age: number;
  gender?: string;
  readonly birthYear: number;
}

let user: User = {
  name: 'xx',
  age: 30,
  birthYear: 2000,
}

user.age = 10;           // ✅ OK
user.gender = "male";    // ✅ OK
user.birthYear = 1990;   // ❌ Error — 읽기전용 속성이라 할당 불가

💡 생년월일, ID 같은 한번 정해지면 변하지 않는 값에 readonly를 사용하면 실수로 변경하는 것을 방지할 수 있습니다.


4. 인덱스 서명 (Index Signature)

프로퍼티의 이름을 미리 알 수 없지만, 키와 값의 타입 패턴이 일정할 때 사용합니다.

interface User {
  name: string;
  age: number;
  readonly birthYear: number;
  [grade: number]: string;  // 숫자 키에 문자열 값
}

let user: User = {
  name: 'xx',
  age: 30,
  birthYear: 2000,
  1: 'A',
  2: 'B'
}

문자열 리터럴 타입과 조합

인덱스 서명의 값을 특정 값으로 제한할 수도 있습니다.

type Score = 'A' | 'B' | 'C' | 'F';

interface User {
  name: string;
  age: number;
  readonly birthYear: number;
  [grade: number]: Score;  // Score에 정의된 값만 허용
}

let user: User = {
  name: 'xx',
  age: 30,
  birthYear: 2000,
  1: 'A',
  2: 'G'  // ❌ Error — 'G'는 Score 타입에 없음
}

5. 인터페이스로 함수 타입 정의

인터페이스는 객체뿐 아니라 함수의 형태도 정의할 수 있습니다.

interface Add {
  (num1: number, num2: number): number;
}

const add: Add = function(x, y) {
  return x + y;
}

add(10, 20);  // 30
interface IsAdult {
  (age: number): boolean;
}

const a: IsAdult = (age) => {
  return age > 19;
}

a(33);  // true

💡 함수 인터페이스를 사용하면 매개변수 이름은 달라도 됩니다. 중요한 건 매개변수의 타입과 반환 타입이 일치하는 것입니다.


6. 인터페이스로 클래스 정의 (implements)

implements 키워드를 사용하면 클래스가 특정 인터페이스의 구조를 반드시 따르도록 강제할 수 있습니다.

interface Car {
  color: string;
  wheels: number;
  start(): void;
}

class Bmw implements Car {
  color;
  wheels = 4;

  constructor(c: string) {
    this.color = c;
  }

  start() {
    console.log('go...');
  }
}

const b = new Bmw('green');
console.log(b);   // Bmw { wheels: 4, color: 'green' }
b.start();         // "go..."

Car 인터페이스에 정의된 color, wheels, start()를 모두 구현하지 않으면 컴파일 에러가 발생합니다.


7. 인터페이스 확장 (extends)

기존 인터페이스를 상속받아 새로운 프로퍼티를 추가할 수 있습니다.

interface Car {
  color: string;
  wheels: number;
  start(): void;
}

interface Benz extends Car {
  door: number;
  stop(): void;
}

const benz: Benz = {
  // Car에서 상속받은 프로퍼티
  color: 'g',
  wheels: 4,
  start() {
    console.log('go...');
  },
  // Benz에서 추가된 프로퍼티
  door: 5,
  stop() {
    console.log('stop..');
  }
}

여러 인터페이스 동시 확장

쉼표(,)로 구분하여 여러 인터페이스를 동시에 상속받을 수 있습니다.

interface Car {
  color: string;
  wheels: number;
  start(): void;
}

interface Toy {
  name: string;
}

interface ToyCar extends Car, Toy {
  price: number;
}

ToyCar는 Car의 color, wheels, start()와 Toy의 name, 그리고 자체 프로퍼티인 price를 모두 가져야 합니다.


한눈에 보기

기능 문법 설명

기본 정의 interface User { name: string; } 객체 구조 정의
옵셔널 gender?: string 선택적 프로퍼티
읽기 전용 readonly birthYear: number 수정 불가
인덱스 서명 [key: number]: string 동적 키-값 패턴
함수 타입 (x: number): boolean 함수 형태 정의
클래스 구현 class Bmw implements Car 클래스에 인터페이스 적용
확장 interface Benz extends Car 인터페이스 상속
다중 확장 interface ToyCar extends Car, Toy 여러 인터페이스 상속