WeniVooks

검색

타입스크립트 베이스캠프

인터페이스

1. 인터페이스

1.1 interface 키워드

객체의 구조를 정의하기 위해 interface 키워드를 사용할 수 있습니다.

interface User {
  id: number;
  name: string;
  info: string;
}
interface User {
  id: number;
  name: string;
  info: string;
}
1.2 선택적 속성

필수가 아닌 속성은 ?를 사용하여 선택적으로 정의할 수 있습니다.

interface User {
  id: number;
  name: string;
  info?: string;
}
 
const user: User = {
  id: 1,
  name: "홍길동"
  // info는 선택사항이므로 생략 가능
};
interface User {
  id: number;
  name: string;
  info?: string;
}
 
const user: User = {
  id: 1,
  name: "홍길동"
  // info는 선택사항이므로 생략 가능
};
1.3 인터페이스 확장

extends 키워드를 사용하여 기존 인터페이스를 확장할 수 있습니다.

interface Character {
  nickName: string;
}
 
interface Bird {
  fly: number;
}
 
interface BirdCharacter extends Character, Bird {
  level: number;
}
 
const birdChar: BirdCharacter = {
  nickName: "Gary",
  fly: 10,
  level: 1
};
interface Character {
  nickName: string;
}
 
interface Bird {
  fly: number;
}
 
interface BirdCharacter extends Character, Bird {
  level: number;
}
 
const birdChar: BirdCharacter = {
  nickName: "Gary",
  fly: 10,
  level: 1
};
1.4 인터페이스 병합

같은 이름의 인터페이스가 같은 스코프에 선언되면 자동으로 병합됩니다.

interface Character {
  nickName: string;
}
 
interface Character {
  level: number;
}
 
// 다음과 동일합니다:
// interface Character {
//   nickName: string;
//   level: number;
// }
 
const char: Character = {
  nickName: "Gary",
  level: 1
};
interface Character {
  nickName: string;
}
 
interface Character {
  level: number;
}
 
// 다음과 동일합니다:
// interface Character {
//   nickName: string;
//   level: number;
// }
 
const char: Character = {
  nickName: "Gary",
  level: 1
};

인터페이스 병합은 전역 인터페이스를 확장할 때 유용하지만, 코드의 명확성을 위해 과도한 사용은 피하는 것이 좋습니다.

1.5 특정 인터페이스 강제

implements는 클래스가 특정 인터페이스의 구조를 따르도록 강제하는 기능입니다.

interface Vehicle {
  start(): void;
  stop(): void;
}
 
// Car 클래스는 Vehicle 인터페이스를 구현해야 함
class Car implements Vehicle {
  start() {
    console.log("차가 출발합니다");
  }
  
  // stop 메서드를 구현하지 않으면 에러 발생!
  // 에러: 'Car' 클래스가 'Vehicle' 인터페이스를 
  // 올바르게 구현하지 않았습니다.
  // 'stop' 속성이 'Car' 형식에 없습니다.
}
interface Vehicle {
  start(): void;
  stop(): void;
}
 
// Car 클래스는 Vehicle 인터페이스를 구현해야 함
class Car implements Vehicle {
  start() {
    console.log("차가 출발합니다");
  }
  
  // stop 메서드를 구현하지 않으면 에러 발생!
  // 에러: 'Car' 클래스가 'Vehicle' 인터페이스를 
  // 올바르게 구현하지 않았습니다.
  // 'stop' 속성이 'Car' 형식에 없습니다.
}
// 올바른 구현
interface Vehicle {
  start(): void;
  stop(): void;
}
 
class Bicycle implements Vehicle {
  start() {
    console.log("자전거가 출발합니다");
  }
  
  stop() {
    console.log("자전거가 멈춥니다");
  }
}
 
// 인스턴스 생성
const bicycle = new Bicycle();
bicycle.start();
// 올바른 구현
interface Vehicle {
  start(): void;
  stop(): void;
}
 
class Bicycle implements Vehicle {
  start() {
    console.log("자전거가 출발합니다");
  }
  
  stop() {
    console.log("자전거가 멈춥니다");
  }
}
 
// 인스턴스 생성
const bicycle = new Bicycle();
bicycle.start();

2. interface와 type의 차이

2.1 확장 방식
// Interface - extends 사용
interface Animal {
  name: string;
}
interface Bear extends Animal {
  honey: boolean;
}
 
// Type - & 사용
type Animal = {
  name: string;
}
type Bear = Animal & {
  honey: boolean;
}
// Interface - extends 사용
interface Animal {
  name: string;
}
interface Bear extends Animal {
  honey: boolean;
}
 
// Type - & 사용
type Animal = {
  name: string;
}
type Bear = Animal & {
  honey: boolean;
}
2.2 선언 병합
  • interface는 같은 이름으로 여러번 선언 가능하며 자동으로 병합됩니다.
  • type은 같은 이름으로 재선언할 수 없습니다.
2.3 사용 권장 사례

타입은 주로 변수에 사용이 됩니다. 함수의 아규먼트, 반환 값, JSON과 같은 형태를 정의할 때 사용합니다. 인터페이스는 객체의 구조를 정의하고 이를 통한 확장을 도모하고자 할 때 사용합니다. JAVA와 같은 객체지향 프로그래밍 언어에서 사용합니다. 요약하자면 아래와 같습니다.

  • interface: 객체의 구조를 정의할 때
  • type: 일반적 변수로 타입을 정의할 때

3. 연습문제

  1. 다음 조건을 만족하는 인터페이스들을 작성해보세요.

    • BaseItem 인터페이스: id(number), name(string) 속성을 가짐
    • Book 인터페이스: BaseItem을 확장하고 author(string), pages(number) 속성을 추가
    • Movie 인터페이스: BaseItem을 확장하고 director(string), duration(number) 속성을 추가
  2. 아래 코드가 정상적으로 동작하도록 필요한 인터페이스를 정의해보세요.

function processUser(user: User): UserDetails {
  return {
    displayName: user.firstName + " " + user.lastName,
    age: user.age,
    isAdmin: user.role === "admin"
  };
}
 
const user = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  role: "admin"
};
 
const details = processUser(user);
function processUser(user: User): UserDetails {
  return {
    displayName: user.firstName + " " + user.lastName,
    age: user.age,
    isAdmin: user.role === "admin"
  };
}
 
const user = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  role: "admin"
};
 
const details = processUser(user);
  1. (리엑트 실전 문제) 입력1, 연산자, 입력2를 받아서 계산 결과를 반환하는 컴포넌트를 작성해보세요. 입력1과 입력2는 숫자로 입력받고, 연산자는 문자열 타입으로 4칙 연산을 지원합니다. 결과는 숫자 타입으로 반환되어야 합니다. 아래 명령어를 사용하여 리엑트 프로젝트를 생성하고, App 컴포넌트를 작성해보세요.
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

4. 연습문제 정답

  1. 다음과 같이 인터페이스를 작성할 수 있습니다.
// BaseItem 인터페이스 정의
interface BaseItem {
  id: number;
  name: string;
}
 
// Book 인터페이스 정의 - BaseItem을 확장
interface Book extends BaseItem {
  author: string;
  pages: number;
}
 
// Movie 인터페이스 정의 - BaseItem을 확장
interface Movie extends BaseItem {
  director: string;
  duration: number;
}
 
// 사용 예시
const book: Book = {
  id: 1,
  name: "TypeScript 완벽 가이드",
  author: "위니브",
  pages: 300
};
 
const movie: Movie = {
  id: 2,
  name: "인셉션",
  director: "크리스토퍼 놀란",
  duration: 148
};
// BaseItem 인터페이스 정의
interface BaseItem {
  id: number;
  name: string;
}
 
// Book 인터페이스 정의 - BaseItem을 확장
interface Book extends BaseItem {
  author: string;
  pages: number;
}
 
// Movie 인터페이스 정의 - BaseItem을 확장
interface Movie extends BaseItem {
  director: string;
  duration: number;
}
 
// 사용 예시
const book: Book = {
  id: 1,
  name: "TypeScript 완벽 가이드",
  author: "위니브",
  pages: 300
};
 
const movie: Movie = {
  id: 2,
  name: "인셉션",
  director: "크리스토퍼 놀란",
  duration: 148
};
  1. 다음과 같이 인터페이스를 작성할 수 있습니다.
// User 인터페이스 정의
interface User {
  firstName: string;
  lastName: string;
  age: number;
  role: string;
}
 
// UserDetails 인터페이스 정의
interface UserDetails {
  displayName: string;
  age: number;
  isAdmin: boolean;
}
 
// processUser 함수 사용을 위한 인터페이스들이 모두 정의됨
function processUser(user: User): UserDetails {
  return {
    displayName: user.firstName + " " + user.lastName,
    age: user.age,
    isAdmin: user.role === "admin"
  };
}
 
const user = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  role: "admin"
};
 
const details = processUser(user);
// User 인터페이스 정의
interface User {
  firstName: string;
  lastName: string;
  age: number;
  role: string;
}
 
// UserDetails 인터페이스 정의
interface UserDetails {
  displayName: string;
  age: number;
  isAdmin: boolean;
}
 
// processUser 함수 사용을 위한 인터페이스들이 모두 정의됨
function processUser(user: User): UserDetails {
  return {
    displayName: user.firstName + " " + user.lastName,
    age: user.age,
    isAdmin: user.role === "admin"
  };
}
 
const user = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  role: "admin"
};
 
const details = processUser(user);
  1. App 컴포넌트 입니다.
import { useState } from 'react'
 
function App() {
  // typescript이니 useState<string>('')와 같이 제네릭을 사용하여 타입을 명시할 수 있습니다.
  const [input1, setInput1] = useState('')
  const [input2, setInput2] = useState('')
  const [operation, setOperation] = useState('')
  const [result, setResult] = useState('')
 
  function calculate(): string {
    const num1: number = parseFloat(input1)
    const num2: number = parseFloat(input2)
    let res: string = ''
    switch (operation) {
      case '+':
        res = (num1 + num2).toString()
        break
      case '-':
        res = (num1 - num2).toString()
        break
      case '*':
        res = (num1 * num2).toString()
        break
      case '/':
        res = (num1 / num2).toString()
        break
      default:
        res = '잘못된 연산자입니다.'
    }
    setResult(res)
    return res
  }
  return (
    <>
      <h1>계산기</h1>
      <input 
        type="text" 
        value={input1}
        onChange={(e) => setInput1(e.target.value)}
      />
      <input type="text"
        value={operation}
        onChange={(e) => setOperation(e.target.value)}
      />
      <input type="text"
        value={input2}
        onChange={(e) => setInput2(e.target.value)}
      />
      <button
       onClick={calculate}
      >계산하기</button>
      <div>
        <h2>결과: {result}</h2>
      </div>
    </>
  )
}
 
export default App
import { useState } from 'react'
 
function App() {
  // typescript이니 useState<string>('')와 같이 제네릭을 사용하여 타입을 명시할 수 있습니다.
  const [input1, setInput1] = useState('')
  const [input2, setInput2] = useState('')
  const [operation, setOperation] = useState('')
  const [result, setResult] = useState('')
 
  function calculate(): string {
    const num1: number = parseFloat(input1)
    const num2: number = parseFloat(input2)
    let res: string = ''
    switch (operation) {
      case '+':
        res = (num1 + num2).toString()
        break
      case '-':
        res = (num1 - num2).toString()
        break
      case '*':
        res = (num1 * num2).toString()
        break
      case '/':
        res = (num1 / num2).toString()
        break
      default:
        res = '잘못된 연산자입니다.'
    }
    setResult(res)
    return res
  }
  return (
    <>
      <h1>계산기</h1>
      <input 
        type="text" 
        value={input1}
        onChange={(e) => setInput1(e.target.value)}
      />
      <input type="text"
        value={operation}
        onChange={(e) => setOperation(e.target.value)}
      />
      <input type="text"
        value={input2}
        onChange={(e) => setInput2(e.target.value)}
      />
      <button
       onClick={calculate}
      >계산하기</button>
      <div>
        <h2>결과: {result}</h2>
      </div>
    </>
  )
}
 
export default App
2.3 객체와 타입 정의3장 타입스크립트 추가 문법