Menu
Coddy logo textTech

트레이트 vs 상속

Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 55번째.

이제 트레이트(traits)가 어떻게 작동하는지 이해하셨으니, 트레이트와 상속(inheritance)을 각각 언제 사용해야 하는지 명확히 해보겠습니다. 둘 다 코드 재사용을 가능하게 하지만, 서로 다른 목적을 수행하며 뚜렷한 특징을 가지고 있습니다.

상속은 "is-a" 관계를 설정합니다. Animal을 확장하는 Dog 클래스는 개 동물임을 의미합니다. 자식 클래스는 부모의 정체성을 상속받으며 부모 타입이 요구되는 곳 어디에서나 사용될 수 있습니다.

트레이트(Traits)는 "has-a" 기능을 제공합니다. 클래스가 Loggable 트레이트를 사용할 때, 해당 클래스는 로깅 기능을 가집니다. 하지만 클래스 자체가 로거(logger)가 되는 것은 아닙니다. 트레이트는 클래스의 본질을 바꾸지 않으면서 동작을 추가합니다.

<?php
// 상속: Dog는 Animal입니다
class Animal {
    public function breathe(): void {
        echo "Breathing";
    }
}

class Dog extends Animal {
    public function bark(): void {
        echo "Woof!";
    }
}

// 트레이트: Dog는 로깅 기능을 가집니다
trait Loggable {
    public function log(string $msg): void {
        echo "Log: $msg";
    }
}

class Cat extends Animal {
    use Loggable;
}

기억해야 할 주요 차이점:

측면상속 (Inheritance)트레이트 (Traits)
관계is-ahas-a 기능
제한단일 부모만 허용다중 트레이트 허용
타입 검사instanceof와 함께 작동타입 정체성 없음
가장 적합한 경우계층적 관계관련 없는 클래스 간의 공유된 동작

클래스가 공통된 정체성을 공유할 때 상속을 사용하세요. 서로 관련 없는 클래스들이 동일한 계층 구조에 속하지 않으면서 동일한 기능이 필요할 때 트레이트(traits)를 사용하세요.

challenge icon

챌린지

쉬움

상속과 트레이트(trait)를 언제 사용해야 하는지 보여주는 차량 시스템을 구축해 보겠습니다. 상속을 통해 공통된 정체성을 공유하는 차량 계층 구조를 만들고, 트레이트를 통해 횡단 관심사(cross-cutting capabilities) 기능을 추가할 것입니다.

코드는 다음 네 개의 파일로 구성됩니다:

  • Trackable.php — GPS 추적 기능을 제공하는 Trackable이라는 트레이트를 생성합니다. 이 트레이트는 다음을 포함해야 합니다:
    • 프라이빗(private) 속성 $location (문자열)
    • 현재 위치를 저장하는 setLocation(string $location) 메서드
    • "Location: [location]"을 반환하는 getLocation() 메서드
    이 트레이트는 "has-a" 능력을 나타냅니다. 어떤 차량이든 추적 기능을 가질 수 있지만, 추적 기능이 있다고 해서 그것이 차량이 되는 것은 아닙니다.
  • Vehicle.php — "is-a" 관계를 설정하는 기본 Vehicle 클래스를 생성합니다. 트레이트 파일을 포함하세요. 생성자 프로모션(constructor promotion)을 사용하여 보호된(protected) $brand 속성을 정의합니다. "Vehicle: [brand]"를 반환하는 describe() 메서드를 추가합니다. 우리 함대의 모든 차량은 GPS를 가질 것이므로 이 클래스는 Trackable 트레이트를 사용해야 합니다.
  • Car.phpVehicle을 상속받는 Car 클래스를 생성합니다. Vehicle 파일을 포함하세요. 생성자 프로모션을 사용하여 브랜드와 프라이빗 $doors (정수) 속성을 받습니다. 브랜드와 함께 부모 생성자를 호출합니다. "Car: [brand] with [doors] doors"를 반환하도록 describe() 메서드를 오버라이드합니다.
  • main.php — Car 파일을 포함하세요. 브랜드 이름, 문 개수, 위치 문자열의 세 가지 입력을 받게 됩니다. Car 인스턴스를 생성하고 위치를 설정한 후 다음 두 줄을 출력합니다:
    • describe() 호출 결과
    • getLocation() 호출 결과

여기서 상속과 트레이트가 어떻게 함께 작동하는지 주목하세요: CarVehicle의 일종(is a)이며(상속은 정체성을 확립함), Vehicle은 추적 기능(has)을 가집니다(트레이트는 동작을 추가함). instanceof Vehicle을 사용하여 어떤 객체가 차량인지 확인할 수 있지만, 확인할 수 있는 "Trackable" 타입은 없습니다. 이것이 이 두 방식의 핵심적인 차이점입니다.

치트 시트

"is-a" 관계에는 상속(inheritance)을 사용하고, "has-a" 기능에는 트레이트(traits)를 사용하세요.

상속은 정체성을 확립합니다. 자식 클래스는 부모 클래스의 한 유형(is a)이며, 부모 타입이 필요한 모든 곳에서 사용될 수 있습니다.

트레이트는 클래스의 정체성을 바꾸지 않고 동작을 추가합니다. 클래스는 기능을 가지고(has) 있지만, 그 자체가 되지는 않습니다.

<?php
// 상속: Dog는 Animal입니다 (IS an Animal)
class Animal {
    public function breathe(): void {
        echo "Breathing";
    }
}

class Dog extends Animal {
    public function bark(): void {
        echo "Woof!";
    }
}

// 트레이트: Cat은 로깅 기능을 가지고 있습니다 (HAS logging capability)
trait Loggable {
    public function log(string $msg): void {
        echo "Log: $msg";
    }
}

class Cat extends Animal {
    use Loggable;
}
측면상속트레이트
관계is-ahas-a 기능
제한단일 부모만 가능여러 트레이트 허용
타입 체크instanceof와 함께 작동타입 정체성 없음
용도계층적 관계서로 관련 없는 클래스 간의 공유 동작

클래스가 공통된 정체성을 공유하는 계층적 관계에는 상속을 사용하세요. 서로 관련 없는 클래스 간에 공유되는 동작에는 트레이트를 사용하세요.

직접 해보기

<?php
// 메인 파일 - Car 인스턴스 생성 및 테스트

// TODO: Car.php 파일 포함

// 입력 읽기
$brand = trim(fgets(STDIN));
$doors = intval(trim(fgets(STDIN)));
$location = trim(fgets(STDIN));

// TODO: brand와 doors를 사용하여 Car 인스턴스 생성

// TODO: 자동차의 위치 설정

// TODO: describe() 결과 출력

// TODO: getLocation() 결과 출력
?>
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨