IMG-LOGO
공지사항 :

PHP 인스턴스 변수

lmkfox - 2025-08-26 06:37:00 25 Views 0 Comment

1) 인스턴스 변수란?

  • 클래스의 각 객체가 개별적으로 보유하는 상태입니다.

  • 문법적으로는 프로퍼티(property) 라고 부르며, static이 아닌 프로퍼티가 인스턴스 변수입니다.

  • 반대로 static 프로퍼티는 클래스 전체가 공유합니다(인스턴스 변수 아님).

class Counter {
    // 인스턴스 변수(객체마다 따로 있음)
    private int $value = 0;

    public function inc(int $step = 1): void { $this->value += $step; }
    public function value(): int { return $this->value; }
}

$a = new Counter();
$b = new Counter();
$a->inc(2);
echo $a->value(); // 2
echo $b->value(); // 0  ← 서로 다른 상태

2) 선언 방법과 가시성(접근 제어자)

class User {
    public int $id;          // 어디서나 접근 가능(권장 X)
    protected string $role;  // 자기 자신 + 자식 클래스에서 접근
    private string $name;    // 해당 클래스 내부에서만 접근

    public function __construct(int $id, string $name, string $role = 'user') {
        $this->id = $id;
        $this->name = $name;
        $this->role = $role;
    }

    public function name(): string { return $this->name; } // 게터
}

  • 기본 권장: 프로퍼티는 private, 읽기/변경은 메소드로 캡슐화.

  • 외부 직접 접근이 꼭 필요할 때만 public을 신중히 사용.

3) 타입 선언(필수 습관)

PHP 7.4+부터 타입드 프로퍼티를 지원합니다.

class Product {
    public int $id;
    public string $name;
    public ?string $description = null; // nullable
    public int|float $price;            // union 타입(8.0+)

    public function __construct(int $id, string $name, int|float $price) {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
    }
}

  • 타입이 있는 프로퍼티는 초기화 전에 접근하면 오류가 납니다. 생성자에서 반드시 채우세요.

  • 프로퍼티 기본값은 상수 표현식만 가능(객체 생성 등은 안 됨) → 생성자에서 처리.

4) 초기화 패턴

4.1 일반적인 방식

class Point {
    private int $x;
    private int $y;

    public function __construct(int $x, int $y) {
        $this->x = $x;
        $this->y = $y;
    }
}

4.2 생성자 프로퍼티 승격(PHP 8.0+)

생성자 매개변수에 접근 제어자와 타입을 붙여 선언+초기화를 한 줄로 처리합니다.

class Point {
    public function __construct(
        private int $x,
        private int $y,
    ) {}

    public function x(): int { return $this->x; }
    public function y(): int { return $this->y; }
}

4.3 지연 초기화/검증

class Session {
    private ?string $token = null;

    public function token(): string {
        if ($this->token === null) {
            $this->token = bin2hex(random_bytes(16));
        }
        return $this->token;
    }
}

5) 읽기 전용(Readonly) 인스턴스 변수(PHP 8.1+)

생성 이후 수정 불가한 불변 상태가 필요할 때 사용합니다.

final class Money {
    public function __construct(
        public readonly int $amount,
        public readonly string $currency
    ) {}
}

$m = new Money(1000, 'KRW');
// $m->amount = 2000;  // 오류: readonly는 재할당 불가

6) 접근/수정 방법

  • 클래스 내부: $this->prop

  • 외부: $obj->prop (단, public이어야 함)

  • 보통은 메소드로 캡슐화합니다.

class Profile {
    private string $nickname;

    public function __construct(string $nickname) {
        $this->nickname = $nickname;
    }
    public function rename(string $new): void {
        if ($new === '') throw new InvalidArgumentException('empty');
        $this->nickname = $new;
    }
    public function nickname(): string { return $this->nickname; }
}

$p = new Profile('kim');
$p->rename('lee');
echo $p->nickname(); // lee

7) nullsafe 연산자와 체이닝(PHP 8.0+)

객체가 null일 수 있을 때 안전하게 프로퍼티/메소드에 접근합니다.

$city = $user?->address?->city; // 중간이 null이면 전체가 null

8) 동적 프로퍼티(지양)

  • 예전엔 선언하지 않은 프로퍼티를 런타임에 추가해도 됐지만, PHP 8.2+에서 폐지(Deprecated) 경고가 발생합니다.

  • 꼭 필요하면 #[\AllowDynamicProperties] 또는 stdClass 활용, 권장 대안은 명시적 선언 혹은 __get/__set 매직 메소드.

class Box {
    private array $data = [];

    public function __get(string $name) { return $this->data[$name] ?? null; }
    public function __set(string $name, $value) { $this->data[$name] = $value; }
}

9) static 프로퍼티와의 차이

class Example {
    public static int $count = 0; // 모든 인스턴스가 공유

    public function __construct(private int $id) {
        self::$count++;
    }
}
$a = new Example(1);
$b = new Example(2);
echo Example::$count; // 2

  • 인스턴스 변수: 각 객체가 독립 보유

  • static 변수: 클래스 단위로 공유

  • 상태를 공유해야 하는 특수한 경우가 아니라면 static 남용 금지.

10) 상속과 인스턴스 변수

  • 부모의 protected 프로퍼티는 자식에서 사용 가능.

  • 같은 이름의 프로퍼티를 재선언하는 일은 피하세요(혼동과 호환성 이슈 유발).

  • 공통 상태는 부모에 protected로 두고, 동작은 자식에서 확장하는 구조가 안전합니다.

11) 컬렉션/객체 타입, 제네릭 표기 관례

PHP 언어 차원의 제네릭은 없지만, DocBlock으로 의도를 명시합니다.

/** @var list<User> */
private array $members = [];

/** @param list<User> $members */
public function __construct(array $members = []) { $this->members = $members; }

정적 분석기(phpstan/psalm)와 IDE가 이 정보를 활용합니다.

12) 실무 예제: 불변 값 + 가변 엔티티

final class Email {
    public function __construct(public readonly string $value) {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('invalid email');
        }
    }
}

final class Account {
    public function __construct(
        private int $id,
        private Email $email,
        private DateTimeImmutable $createdAt = new DateTimeImmutable()
    ) {}

    public function changeEmail(Email $email): void {
        $this->email = $email; // 도메인 규칙 검증은 Email에서 담당
    }

    public function id(): int { return $this->id; }
    public function email(): Email { return $this->email; }
    public function createdAt(): DateTimeImmutable { return $this->createdAt; }
}

포인트

  • **값 객체(Email)**는 readonly로 불변.

  • **엔티티(Account)**는 도메인 규칙을 따르며 인스턴스 변수를 통해 상태 변화를 관리.

13) 흔한 실수와 체크리스트

  • 타입드 프로퍼티를 초기화 전에 접근 → 오류. 생성자/팩토리에서 반드시 채우기.

  • 선언 없이 $obj->foo = ... 추가(동적 프로퍼티) → 8.2+에서 경고. 반드시 선언.

  • 외부에서 public 프로퍼티를 직접 바꿈 → 불변식 깨짐. 메소드로 검증/수정.

  • 프로퍼티 기본값에 new나 함수 호출 사용 → 불가. 생성자에서 처리.

  • 공유 상태가 필요한 게 아닌데 static 사용 → 테스트/동시성 문제 유발.

14) 요약

  • 인스턴스 변수는 객체마다 독립된 상태를 담는 프로퍼티입니다.

  • private + 타입 선언 + 생성자 초기화가 기본기.

  • 변경을 통제하고 싶다면 게터/세터 대신 의미 있는 도메인 메소드를 제공하세요.

  • 불변이 적합하면 readonly를, 동적 필드가 필요하면 명시적 선언 또는 매직 메소드로.


댓글