실제 내용
요즘 여러 지식을 돌아보며,
그동안 헷갈렸던 개념이나 알고 있다고 착각했던 내용들이 많았다는 걸 새삼 느끼고 있습니다.
그래서 이를 정리하고 되짚기 위한 개인 리런 프로젝트 – 다시 뛰기(re-learn) 를 시작합니다.
잘못 이해했던 개념, 개념 간의 경계, 실무에서의 적용까지 하나씩 다시 정의하고 기록해보려 합니다.
일주일에 두세 편씩 올리는 것을 목표로 하고 있고,
얼마나 꾸준히 이어갈 수 있을지는 나중에 다시 돌아보며 평가해보겠습니다.
다시 뛰기 프로젝트 첫번째 타입과 인터페이스의 차이점입니다.

Intro

타입스크립트를 공부하면서 가장 먼저 부딪히는 질문 중 하나는 **type과 interface의 차이점은 뭔가요?**입니다. 이 두 문법은 겉보기엔 비슷하지만, 의외로 세세한 차이점과 철학적인 배경이 존재합니다.
저도 처음에는 아무거나 써도 되겠지 하고 넘어갔지만, 프로젝트 규모가 커지고 협업이 늘어날수록 선택의 기준이 필요하다는 것을 절감하게 되었습니다.
이 글은 타입스크립트를 막 공부하기 시작했거나, 실무에서 좀 더 명확한 기준을 갖고 싶은 분들을 위해 다음과 같은 내용을 다룹니다:
  • 타입스크립트에서의 타입과 인터페이스의 개념과 문법
  • 두 문법 간의 핵심 차이점
  • 실무에서 어떤 기준으로 선택하면 좋은지
  • 실제 코드 예제를 통한 실전 감각
이 글을 다 읽고 나면, 면접에서 type과 interface의 차이점은요…라고 자신 있게 설명하고, 실제로 사용할 때도 기준을 세울 수 있는 상태가 되는 것이 목표입니다.

타입스크립트란 무엇인가?

타입스크립트는 자바스크립트의 슈퍼셋 언어입니다.
쉽게 말해, 자바스크립트에 타입 시스템을 추가한 언어라고 생각하면 됩니다.

왜 타입스크립트가 필요할까?

자바스크립트는 동적 타입 언어입니다. 변수의 타입이 런타임에 결정되죠.
function greet(name) {
  return "Hello, " + name.toUpperCase();
}

greet(123); // 런타임 에러! 123에는 toUpperCase() 메서드가 없음
타입스크립트는 이런 문제를 컴파일 시점에 미리 잡아줍니다.
function greet(name: string) {
  return "Hello, " + name.toUpperCase();
}

greet(123); // 컴파일 에러! string 타입이어야 하는데 number를 넣었음

핵심 포인트

  • 기존 자바스크립트 코드는 그대로 동작합니다 (슈퍼셋이니까!)
  • 타입 정보는 컴파일 후 제거되어 순수한 자바스크립트가 됩니다
  • 개발 시점에 오류를 미리 발견할 수 있어 안전한 코드 작성이 가능합니다
이제 이 타입 시스템에서 핵심인 타입과 인터페이스가 무엇인지 알아보겠습니다.

타입(Type)이란 무엇인가

타입은 값의 형태와 성질을 정의하는 방법입니다.
쉽게 말해, 이 변수는 어떤 종류의 데이터인가?를 명시하는 것이죠.

기본 타입들

타입스크립트는 자바스크립트의 기본 타입들을 그대로 사용합니다.
let name: string = "김개발";
let age: number = 25;
let isStudent: boolean = true;
let skills: string[] = ["JavaScript", "React"];

사용자 정의 타입

더 복잡한 구조는 직접 타입을 만들어 정의할 수 있습니다.
*// 객체 타입 정의*
type User = {
  id: number;
  name: string;
  email: string;
};

*// 함수 타입 정의*
type Calculator = (a: number, b: number) => number;

*// Union 타입 (여러 타입 중 하나)*
type Status = "loading" | "success" | "error";

타입의 특징

1. 컴파일 시점에만 존재
type User = { name: string };
*// 컴파일 후에는 이 타입 정의가 사라집니다*
2. 값을 직접 생성하지 않음
type Point = { x: number; y: number };
*// Point는 타입일 뿐, 실제 객체를 만들지는 않습니다*
3. 매우 유연한 표현 가능
*// 조건부 타입*
type IsString<T> = T extends string ? true : false;

*// 템플릿 리터럴 타입*
type EventName = `on${Capitalize<string>}`;

핵심 포인트

  • 타입은 데이터의 형태를 설명하는 도구입니다
  • 컴파일 타임에만 존재하고 런타임에는 사라집니다
  • 매우 유연하고 강력한 표현이 가능합니다
그렇다면 인터페이스는 타입과 어떻게 다를까요?

인터페이스(Interface)란 무엇인가

인터페이스는 객체의 구조를 정의하는 계약서입니다.
쉽게 말해, 이 객체는 어떤 속성들을 가져야 한다를 명시하는 방법이죠.

기본 인터페이스

interface User {
  id: number;
  name: string;
  email: string;
}

*// 사용하기*
const user: User = {
  id: 1,
  name: "김개발",
  email: "kim@example.com"
};

인터페이스의 특징

1. 확장(상속)이 쉽다
interface User {
  id: number;
  name: string;
}

*// User를 확장해서 새로운 인터페이스 만들기*
interface Admin extends User {
  role: string;
  permissions: string[];
}
2. 선언 병합(Declaration Merging)
interface User {
  id: number;
  name: string;
}

*// 같은 이름으로 다시 선언하면 자동으로 합쳐집니다*
interface User {
  email: string;
}

*// 결과: User는 id, name, email을 모두 가짐*
3. 클래스와 함께 사용
interface Flyable {
  fly(): void;
}

class Bird implements Flyable {
  fly() {
    console.log("새가 날아갑니다!");
  }
}

주로 언제 사용할까?

객체의 형태를 정의할 때:
interface ApiResponse {
  status: number;
  message: string;
  data: any;
}

interface ButtonProps {
  text: string;
  onClick: () => void;
  disabled?: boolean; *// 선택적 속성*
}

핵심 포인트

  • 인터페이스는 객체의 구조를 정의하는 데 특화되어 있습니다
  • 확장과 구현이 매우 직관적입니다
  • 선언 병합으로 나중에 속성을 추가할 수 있습니다
  • 주로 API 응답, 컴포넌트 Props 등에 많이 사용됩니다
이제 타입과 인터페이스의 차이점을 자세히 살펴보겠습니다.

타입 vs 인터페이스: 핵심 차이점

이제 둘의 차이점을 명확히 알아보겠습니다. 면접에서도 자주 나오는 내용이니 잘 기억해두세요!

1. 확장 방식의 차이

인터페이스: extends 키워드 사용
interface User {
  name: string;
}

interface Admin extends User {
  role: string;
}
타입: 교집합(&) 사용
type User = {
  name: string;
}

type Admin = User & {
  role: string;
}

2. 선언 병합 (Declaration Merging)

인터페이스: 가능 🟢
interface User {
  name: string;
}

interface User {
  age: number;
}

*// 자동으로 합쳐짐: { name: string; age: number; }*
타입: 불가능 🔴
type User = {
  name: string;
}

type User = {  *// ❌ 에러! 중복 선언*
  age: number;
}

3. 표현할 수 있는 타입의 범위

인터페이스: 객체 타입만 정의 가능
interface User {
  name: string;
}

*// ❌ 이런 건 안됨*
interface StringOrNumber = string | number;
타입: 모든 타입 표현 가능
*// 객체 타입*
type User = {
  name: string;
}

*// Union 타입*
type Status = "loading" | "success" | "error";

*// 함수 타입*
type Handler = (event: Event) => void;

*// 조건부 타입*
type IsArray<T> = T extends any[] ? true : false;

4. 계산된 속성 (Computed Properties)

인터페이스: 제한적
interface User {
  name: string;
  *// [key: string]: any; // 이 정도만 가능*
}
타입: 매우 유연
type EventHandlers = {
  [K in 'click' | 'focus' | 'blur']: (event: Event) => void;
}

*// 결과: { click: ..., focus: ..., blur: ... }*

5. 성능 차이

컴파일 속도 측면에서 인터페이스가 약간 더 빠릅니다.
  • 인터페이스: 단순한 구조, 캐싱 최적화
  • 타입: 복잡한 연산이 필요할 수 있음
하지만 실무에서 체감할 정도는 아닙니다.

6. 클래스와의 관계

인터페이스: implements 사용
interface Flyable {
  fly(): void;
}

class Bird implements Flyable {
  fly() { ... }
}
타입: implements 사용 가능하지만 제한적
type Flyable = {
  fly(): void;
}

class Bird implements Flyable {  *// 가능하지만...*
  fly() { ... }
}

*// Union 타입은 implements 불가*
type StringOrNumber = string | number;
class Test implements StringOrNumber { } *// ❌ 에러*

요약표

type vs interface
다음에는 그래서 언제 뭘 써야 하는가?에 대해 알아보겠습니다.

언제 타입을, 언제 인터페이스를?

이론은 충분히 알았으니, 이제 실제로 어떻게 선택해야 하는지 알아보겠습니다.

인터페이스를 사용하는 경우

1. 객체의 구조를 정의할 때

*// ✅ 좋은 예: API 응답 구조*
interface UserResponse {
  id: number;
  name: string;
  email: string;
  createdAt: string;
}

*// ✅ 좋은 예: React 컴포넌트 Props*
interface ButtonProps {
  text: string;
  onClick: () => void;
  disabled?: boolean;
  variant?: 'primary' | 'secondary';
}

2. 라이브러리 API를 정의할 때

*// 라이브러리 사용자가 확장할 수 있도록*
interface DatabaseConfig {
  host: string;
  port: number;
  database: string;
}

*// 사용자가 이렇게 확장 가능*
interface MyDatabaseConfig extends DatabaseConfig {
  ssl: boolean;
  timeout: number;
}

3. 클래스가 구현해야 할 계약을 정의할 때

interface Logger {
  info(message: string): void;
  error(message: string): void;
  warn(message: string): void;
}

class ConsoleLogger implements Logger {
  info(message: string) { console.log(message); }
  error(message: string) { console.error(message); }
  warn(message: string) { console.warn(message); }
}

타입을 사용하는 경우

1. Union 타입이 필요할 때

*// ✅ 상태 관리*
type LoadingState = 'idle' | 'loading' | 'success' | 'error';

*// ✅ 다양한 데이터 타입*
type ApiData = string | number | boolean | object;

*// ✅ 함수 오버로딩*
type CreateUser = {
  (name: string): User;
  (name: string, email: string): User;
  (userData: UserData): User;
}

2. 조건부 타입이나 유틸리티 타입을 만들 때

*// 조건부 타입*
type NonNullable<T> = T extends null | undefined ? never : T;

*// 매핑된 타입*
type Partial<T> = {
  [P in keyof T]?: T[P];
}

*// 템플릿 리터럴 타입*
type EventName<T extends string> = `on${Capitalize<T>}`;

3. 함수 타입을 정의할 때

*// ✅ 함수 시그니처*
type EventHandler = (event: Event) => void;
type Calculator = (a: number, b: number) => number;
type AsyncFetcher<T> = (url: string) => Promise<T>;

4. 기존 타입을 조합할 때

type User = {
  id: number;
  name: string;
}

type UserWithTimestamp = User & {
  createdAt: Date;
  updatedAt: Date;
}

type UserPermissions = User & {
  role: 'admin' | 'user' | 'guest';
  permissions: string[];
}

실제 프로젝트 컨벤션 예시

실제 프로젝트 할 때 사용했던 컨벤션은 다음과 같습니다.
*// 1. 객체 구조 정의 → interface*
interface User {
  id: number;
  name: string;
}

*// 2. Union, 조건부 타입 → type*
type Status = 'loading' | 'success' | 'error';
type ApiResponse<T> = T extends string ? string : object;

*// 3. React Props → interface*
interface ComponentProps {
  title: string;
  onClose: () => void;
}

*// 4. 함수 타입 → type*
type EventHandler = (event: Event) => void;

*// 5. 라이브러리 공개 API → interface (확장 가능성 고려)*
interface ConfigOptions {
  timeout: number;
  retries: number;
}

헷갈릴 때는?

간단한 판별법:
type에서만 쓸 수 있는 기능을 사용한다면 → type그렇지 않다면 → interface
type Status = 'loading' | 'success' | 'error';        // Union 타입
type Handler = (event: Event) => void;                 // 함수 타입
type Conditional<T> = T extends string ? true : false; // 조건부 타입
type Mapped = { [K in keyof T]: string };
왜 이렇게 하는가?
  • interface확장성, 선언 병합, 성능 면에서 더 유리
  • 팀원들이 나중에 쉽게 확장하고 수정할 수 있음
  • 대부분의 객체 타입 정의는 interface로 충분

Outro..

저는 이 글을 작성하면서 딱 네 가지 질문에 자신 있게 답할 수 있는 상태가 되기를 바랐습니다.
그리고 이 글을 읽으신 분들도 딱 네 가지만 얻고 가셨으면 좋겠습니다.
  • [ ] type과 interface는 무엇인가?
  • [ ] 문법적으로 어떤 차이가 있는가?
  • [ ] 어떤 상황에 type과 interface를 채택하는 자신만의 기준을 세웠는가?
  • [ ] 그렇다면 면접관의 질문에 답을하고 꼬리질문까지 완벽하게 커버 가능한가?