IMG-LOGO
공지사항 :

PHP의 클래스 멤버 static

lmkfox - 2025-08-30 06:49:06 36 Views 0 Comment

1) 한눈에 요약

  • static 멤버는 클래스 자체에 속하는 값/함수로, 인스턴스 없이 ClassName::$prop 또는 ClassName::method()로 접근합니다. $this는 정적 메서드 내부에서 사용할 수 없습니다. 

2) 문법(정의·접근)

class Foo {
    public static int $count = 0;      // 정적(클래스) 프로퍼티 (타입 선언 가능)
    protected static string $name = 'app';

    public static function inc(): void {
        self::$count++;
    }

    public static function info(): string {
        // $this 불가 (인스턴스 컨텍스트 없음)
        return self::$name . '(' . self::$count . ')';
    }
}

// 접근
Foo::inc();
echo Foo::$count;       // 1
echo Foo::info();

3) 클래스 상수와의 차이

  • const(또는 public const)는 변경 불가능한 상수. static은 변경 가능한 클래스 레벨 값.

class C {
    public const VERSION = '1.0';
    public static int $counter = 0;
}

4) 타입 선언·초기화 규칙(중요)

  • 타입이 붙은 정적 프로퍼티(public static int $x;)는 초기화되지 않은 상태가 될 수 있고, 초기화 전에 읽으면 런타임 오류가 납니다. 생성자 또는 초기화 루틴에서 값을 설정하세요. 

5) 인스턴스 vs 정적의 차이(상태 보유 방식)

  • 인스턴스 프로퍼티: 객체마다 별도 값.

  • 정적 프로퍼티: 클래스(또는 선언된 클래스) 단위로 값 보유. 같은 클래스 내의 모든 인스턴스에서 동일한 정적 값을 참조합니다.

실용 예: 요청당 카운터나 전역 설정(읽기 전용)·유틸성 함수에 자주 사용됩니다.

6) 상속에서의 동작(공유 vs 재선언)

  • 부모 클래스에만 정적 프로퍼티가 선언되어 있다면, 부모가 선언한 “한 번의” 값이 공유되는 동작을 보일 수 있습니다(상황에 따라 공유처럼 보임).

  • 부모와 자식 모두 같은 이름으로 static을 다시 선언하면 각 클래스는 서로 독립적인 정적 저장소를 가집니다(즉, 자식에 재선언하면 자식 클래스용 별도 정적 값이 됨). 

예시(동작 차이):

class A {
    public static int $x = 0;
}

class B extends A { /* 재선언 없음 */ }
class C extends A { public static int $x = 100; } // C는 별도 선언

A::$x = 1;
echo B::$x; // 1  (A에 선언된 값 참조)
echo C::$x; // 100 (C에서 재선언했으므로 C 전용)
핵심: “공유”처럼 보이는 경우(부모만 선언)와 “클래스별 고유 저장소”(자식에서 재선언) 동작을 구분해서 설계하세요. 

7) 정적 메서드와 Late Static Binding (

self::

 vs 

static::

)

  • self::는 정의된 클래스의 멤버를 가리키고, static::은 호출한(런타임) 클래스를 가리키는 늦은 정적 바인딩(Late Static Binding)입니다. 팩토리/템플릿 패턴에서 유용합니다. 

예:

class Base {
    public static function factory() { return new self(); }   // self -> 항상 Base
    public static function lateFactory() { return new static(); } // static -> 호출 클래스
}

class Child extends Base {}

var_dump(get_class(Base::factory()));      // "Base"
var_dump(get_class(Child::factory()));     // "Base"  (self 바인딩)
var_dump(get_class(Child::lateFactory())); // "Child" (late static binding)

8) static 변수(함수 내부) vs static 프로퍼티

function counter() {
    static $i = 0; // 함수 스코프에 영속적(해당 함수 내부 고정)
    return ++$i;
}

  • 이 static $i(함수 내부)는 그 함수 호출 횟수 등을 기억할 때 유용합니다. 클래스 static 프로퍼티는 클래스 범위의 값입니다. 둘은 개념은 비슷하지만 스코프가 다릅니다.

9) 싱글턴 구현 예제(전통적 패턴)

final class Singleton {
    private static ?Singleton $instance = null;
    private function __construct() {}
    public static function instance(): Singleton {
        return self::$instance ??= new self();
    }
}
$s = Singleton::instance();

  • 싱글턴은 전역 상태를 만드는 쉬운 방법이지만 테스트·유연성·의존성 관리에 문제를 일으킵니다. DI 컨테이너 사용을 권장합니다.

10) 정적 멤버 사용 시 주의사항 (실무 팁)

  • 전역 상태는 최소화: 정적 프로퍼티로 전역 상태를 만들면 테스트와 재현성이 어려워집니다.

  • 테스트성 저하: 모킹이 어렵고, 실행 순서/사이드 이펙트가 테스트에 영향을 줍니다.

  • 장기 실행 프로세스 주의(워커/서버): PHP-FPM/Apache의 경우 프로세스가 재사용될 수 있으므로 정적 값이 요청 간에 남을 수 있습니다(특히 데몬/AMQP 워커 등에서는 주의).

  • 초기화 방식: 정적 값이 복잡한 계산(파일 읽기, DB 조회 등)으로 초기화되어야 하면 “명시적 초기화 메서드”나 지연 초기화(lazy init) 패턴을 써서 예측 가능하게 만드세요.

    예: getConfig() 내부에서 if (self::$cfg === null) self::$cfg = loadFromFile(); 처럼.

  • 대안: 순수 유틸은 네임스페이스 함수(또는 final 클래스로 감싼 정적 메서드)로 만들고, 상태가 필요한 로직은 객체(DI)로 분리하세요.

11) 트레이트에서 

static

 사용

  • 트레이트는 정적 멤버를 가질 수 있습니다. 트레이트를 use한 클래스에 해당 멤버가 포함됩니다. 충돌 해결은 insteadof/as로 해야 합니다.

12) 성능 관련 한마디

  • static 접근은 인스턴스 메서드 호출보다 미세한 차이는 있을 수 있으나, 일반적인 애플리케이션에서는 설계(아키텍처)가 성능보다 훨씬 중요합니다. 미세 최적화 목적만으로 static을 남용하지 마세요.

13) 요약 체크리스트(권장)

  • 전역·가변 상태는 static로 만들지 말고 DI로 주입하라.

  • 유틸·순수 함수는 static(혹은 namespaced function) 적절.

  • 상속 환경에서 클래스별 고유 정적 값을 원하면 자식에서 재선언하라. 

  • 타입이 있는 static 프로퍼티는 초기화 확인(또는 nullable로 선언)하라. 

  • 팩토리용 정적 메서드와 static::(late static binding)을 함께 쓰면 하위 클래스에 맞는 인스턴스 생성에 편리하다. 


원하시면 다음 중 한 가지를 바로 만들어 드리겠습니다.

  1. static 기반의 설정 캐시 클래스(lazy init 포함) 예제

  2. 싱글턴 대체로 DI 컨테이너와 결합한 구현 예제

  3. 상속 상황에서 static 프로퍼티 동작을 보여주는 실습 코드 + 실행 결과


댓글