Menu
Coddy logo textTech

지연 정적 바인딩

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

부모 클래스에서 self::를 사용하면, 이것은 실제로 사용 중인 클래스가 아니라 항상 해당 코드가 작성된 클래스를 참조합니다. 이는 상속에서 예기치 않은 동작을 유발할 수 있습니다. 지연 정적 바인딩(Late static binding)static:: 키워드를 사용하여 이 문제를 해결합니다.

self::가 예상대로 작동하지 않는 다음 예시를 살펴보세요:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

출력:

ParentClass
ParentClass

ChildClass::create()를 호출했음에도 불구하고, self::가 컴파일 시점에 해석되기 때문에 ParentClass 객체를 얻었습니다. 이제 대신 static::을 사용해 보겠습니다:

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

출력:

ChildClass
ChildClass

static::을 사용하면, PHP는 어떤 클래스를 사용할지 결정하기 위해 런타임까지 기다립니다. 결정이 지연되기 때문에 이를 "지연(late)" 바인딩이라고 부릅니다. 이는 팩토리 메서드나 상속을 존중해야 하는 모든 정적 메서드에 필수적입니다.

핵심 포인트: 자식 클래스가 정적 동작을 올바르게 상속하고 커스터마이징하도록 하려면 self:: 대신 static::을 사용하세요.

challenge icon

챌린지

쉬움

지연 정적 바인딩(late static binding)의 강력함을 보여주는 모델 레지스트리 시스템을 구축해 보겠습니다. 자식 클래스가 상속받을 수 있는 팩토리 메서드를 가진 기본 Model 클래스를 만들 것입니다. static:: 덕분에 각 자식 클래스는 부모 클래스가 아닌 자기 자신의 인스턴스를 올바르게 생성하게 됩니다.

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

  • Model.php — 보호된(protected) 정적 프로퍼티 $tableName"models"로 설정한 기본 Model 클래스를 생성합니다. new static()을 사용하여 호출한 클래스의 새 인스턴스를 반환하는 정적 메서드 create()를 추가합니다. static::$tableName을 반환하는 또 다른 정적 메서드 getTable()을 추가합니다. 마지막으로, static::classstatic::$tableName을 사용하여 "[ClassName] from table [tableName]"을 반환하는 describe() 메서드를 추가합니다.
  • User.phpModel을 상속받는 User 클래스를 생성합니다. 부모 파일을 포함(include)하세요. 정적 $tableName 프로퍼티를 "users"로 오버라이드합니다. 이 클래스는 변경 없이 Model의 모든 메서드를 상속받습니다.
  • Product.phpModel을 상속받는 Product 클래스를 생성합니다. 부모 파일을 포함하세요. 정적 $tableName 프로퍼티를 "products"로 오버라이드합니다. 이 클래스 역시 부모로부터 모든 메서드를 상속받습니다.
  • main.php — User와 Product 파일을 모두 포함하세요. 상속받은 create() 팩토리 메서드를 사용하여 UserProduct의 인스턴스를 모두 생성합니다. 각 인스턴스에 대해 describe()를 호출하고 그 결과를 각각 한 줄씩 출력합니다. 그런 다음 User::getTable()Product::getTable()을 사용하여 각 클래스의 테이블 이름을 각각 한 줄씩 출력합니다.

여기서 핵심은 create()getTable() 메서드가 부모인 Model 클래스에만 정의되어 있음에도 불구하고, static::이 런타임까지 바인딩을 지연시키기 때문에 자식 클래스에서 올바르게 작동한다는 점입니다. User::create()를 호출하면 static::이 실제로 호출된 클래스를 참조하므로 PHP는 Model 인스턴스가 아닌 User 인스턴스를 생성합니다.

출력 결과는 네 줄이어야 합니다: User 인스턴스의 설명, Product 인스턴스의 설명, User 테이블 이름, 그리고 Product 테이블 이름입니다.

치트 시트

부모 클래스 내의 self:: 키워드는 항상 해당 키워드가 작성된 클래스를 참조하며, 컴파일 시점에 결정됩니다. static::을 사용하는 지연 정적 바인딩(Late static binding)은 런타임에 참조를 결정하여, 자식 클래스가 정적 동작을 적절하게 상속받고 커스터마이징할 수 있도록 합니다.

self:: 사용 (컴파일 시점 바인딩)

<?php
class ParentClass {
    public static function create() {
        return new self();  // 항상 ParentClass를 생성함
    }
    
    public static function getClassName() {
        return self::class;  // 항상 "ParentClass"를 반환함
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // ParentClass 인스턴스를 반환함
echo ChildClass::getClassName();  // 출력: ParentClass

static:: 사용 (런타임 바인딩)

<?php
class ParentClass {
    public static function create() {
        return new static();  // 호출한 클래스의 인스턴스를 생성함
    }
    
    public static function getClassName() {
        return static::class;  // 호출한 클래스 이름을 반환함
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();  // ChildClass 인스턴스를 반환함
echo ChildClass::getClassName();  // 출력: ChildClass

핵심 포인트: 자식 클래스가 정적 동작을 적절히 상속받고 커스터마이징하기를 원한다면 self:: 대신 static::을 사용하세요. 이는 팩토리 메서드나 상속 구조를 존중해야 하는 모든 정적 메서드에 필수적입니다.

직접 해보기

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: 상속된 create() 팩토리 메서드를 사용하여 User 인스턴스를 생성하세요.

// TODO: 상속된 create() 팩토리 메서드를 사용하여 Product 인스턴스를 생성하세요.

// TODO: User 인스턴스에서 describe()를 호출하고 결과를 출력하세요.

// TODO: Product 인스턴스에서 describe()를 호출하고 결과를 출력하세요.

// TODO: User::getTable()을 출력하세요.

// TODO: Product::getTable()을 출력하세요.

?>
quiz icon실력 점검

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

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