IMG-LOGO
공지사항 :

PHP의 접근 제어자(access modifier)

lmkfox - 2025-08-28 07:09:27 39 Views 0 Comment

1. 개요

접근 제어자는 클래스 내부 멤버(프로퍼티·메소드·상수)가 어디에서 접근 가능한지를 결정합니다. 주로 세 가지가 있습니다.

  • public : 어디서든 접근 가능 (클래스 내부, 자식, 외부 코드)

  • protected : 클래스 자신과 자식 클래스에서만 접근 가능

  • private : 해당 클래스 내부에서만 접근 가능 (자식도 접근 불가)

(참고) final, abstract, static, readonly 등은 접근 제어자와 별개입니다. readonly는 가시성은 아니고 쓰기 금지 속성입니다(PHP 8.1+).

2. 적용 대상

  • 프로퍼티(property) : 인스턴스 변수(또는 static) 선언 시 가시성 사용

  • 메소드(method) : 인스턴스/정적 메소드 가시성

  • 클래스 상수(const) : PHP 7.1부터 가시성(public/protected/private) 선언 가능

3. 기본 문법 예제

<?php
class A {
    public    int $pub   = 1;
    protected int $prot  = 2;
    private   int $priv  = 3;

    public function publicMethod() { return $this->priv; }        // OK
    protected function protectedMethod() { return $this->prot; } // 내부/자식용
    private function privateMethod() { return $this->priv; }      // 오직 이 클래스 내부
}

$a = new A();
echo $a->pub;           // OK
// echo $a->prot;       // 오류: protected 접근 불가
// echo $a->priv;       // 오류: private 접근 불가

4. 상속(override) 규칙

  • 오버라이드 시 접근성: 부모 메소드보다 더 제한적(더 숨김) 으로 바꿀 수 없습니다. 즉,

    • 부모가 public이면 자식은 public만 가능(더 좁힐 수 없음).

    • 부모가 protected이면 자식은 protected 또는 public으로 바꿀 수 있습니다(더 넓힐 수는 있음).

  • 이유: Liskov Substitution Principle(리스코프 치환 원칙) — 서브타입은 슈퍼타입으로 대체 가능해야 합니다.

예:

class Base {
    public function foo() {}
}
class ChildBad extends Base {
    // public -> protected 로 변경하면 fatal error: access level must be public
    // protected function foo() {} // 오류
}

5. 

private

의 특성 — 

클래스 단독 범위

  • private 프로퍼티/메소드는 정의된 클래스 내부에서만 접근 가능. 같은 명칭의 private 멤버를 부모/자식 클래스가 각각 선언하면 서로 다른 멤버로 취급됩니다(완전 분리).

  • 때문에 부모 클래스의 private 메소드나 프로퍼티는 자식에서 덮어써도 부모의 것을 가리키지 않습니다.

6. static 멤버와 가시성

  • public static, protected static, private static 동일하게 작동.

  • 접근: ClassName::$prop 또는 self::$prop, static::$prop 등으로 접근.

  • self::은 정의된 클래스의 멤버를 가리키고, static::(늦은 정적 바인딩)은 호출한 클래스를 가리킵니다. 가시성은 동일 규칙 적용.

7. 클래스 상수의 가시성 (PHP 7.1+)

class X {
    public const PUBLIC_C = 'pub';
    protected const PROT_C = 'prot';
    private const PRIV_C  = 'priv';
}

  • public 상수는 클래스 외부에서 X::PUBLIC_C로 접근 가능.

  • protected/private 상수는 그 가시성 규칙대로 제한됩니다.

8. 인터페이스와 트레이트 관련

  • 인터페이스: 메소드는 항상 public입니다. (인터페이스에 protected/private 메소드는 선언할 수 없습니다.)

  • 트레이트: 트레이트에 선언된 멤버도 가시성을 가질 수 있으며, 클래스로 use할 때 insteadof, as로 충돌 해결 및 가시성 변경(별칭) 가능:

trait T {
    protected function tMethod() {}
}
class C {
    use T {
        tMethod as public; // 가시성 변경 예
    }
}

9. 생성자와 가시성의 실무적 사용

  • 생성자를 private로 선언하면 외부에서 new로 만들 수 없고 정적 팩토리를 통해 인스턴스 생성 제어 가능:

class OnlyFactory {
    private function __construct() {}
    public static function create(): self { return new self(); }
}

10. Closure / 바인딩과 스코프

  • Closure::bind 또는 $closure->call($obj)를 사용하면 클로저의 실행 스코프를 바꿔 private/protected 멤버에 접근할 수 있도록 할 수 있습니다(강력하지만 남용 위험).

  • 예: Closure::bind(function(){ return $this->priv; }, $obj, $obj::class) — 객체의 private에 접근 가능.

11. 마법 메소드(

__get

__set

)와 가시성 우회

  • 선언되지 않은(또는 접근 불가) 프로퍼티 접근 시 __get/__set이 호출되므로 외부에서 private 멤버 접근을 흉내낼 수 있으나 이는 캡슐화 규칙을 우회하는 것이므로 신중히 사용.

12. 흔한 실수와 주의사항

  • public 프로퍼티를 남발하면 캡슐화·불변성·검증 책임이 깨집니다. 가능한 private + 게터/메소드 사용 권장.

  • 부모의 private 멤버를 자식에서 동일 이름으로 사용하면 “덮어쓴다”기보다 서로 다른 멤버가 됩니다 — 혼동 주의.

  • 생성자에서 오버라이드 가능한 메소드 호출(즉, 자식에서 재정의된 메소드 호출)은 위험. 자식 필드 초기화가 끝나기 전에 자식 메소드가 호출되어 부분 상태를 다룰 수 있음.

  • protected는 테스트/확장을 위해 유용하지만 남용 시 구현 세부 사항 노출(캡슐화 파손)로 이어질 수 있음. 가능한 private + 공용 API 권장.

13. 디버깅 팁

  • 접근 오류(예: Cannot access protected property)가 나오면 호출 위치를 먼저 확인 — 클래스 외부인지, 자식인지, 같은 클래스인지 확인.

  • 리플렉션(Reflection)으로 접근 가능성 확인 가능(단, Reflection API 사용은 고급 디버깅/툴 용도).

14. 실무 권장 관행(요약)

  • 프로퍼티는 기본 private. 필요한 경우에만 protected/public 사용.

  • 외부 접근은 명확한 게터/메소드로 제공(검증/불변성 유지).

  • 상속 API(자식이 사용할 것)는 protected로 설계하고 명확히 문서화.

  • 불변 값은 readonly(PHP 8.1+)로 표현.

  • 팩토리/DI를 활용하여 생성자 가시성(예: private 생성자 + static factory)로 인스턴스화 제어.

15. 실습 예제 모음

<?php
class ParentClass {
    private function secret() { return "parent secret"; }
    protected function doProtected() { return "ok"; }
    public function publicAPI() { return $this->doProtected(); }
}

class ChildClass extends ParentClass {
    // secret()는 부모의 private라서 여기서 다시 정의해도 부모 것과 분리된다
    private function secret() { return "child secret"; }

    // 부모 protected 메소드를 공개로 확장(허용)
    public function doProtected() { return "child override"; }

    public function show() {
        // 부모의 private secret() 접근 불가: $this->secret() 호출 시 ChildClass의 secret() 호출
        return $this->secret();
    }
}

$c = new ChildClass();
echo $c->publicAPI(); // "child override"
echo $c->show();      // "child secret"

마무리 요약

  • public / protected / private는 클래스 설계의 핵심 도구입니다.

  • 원칙: 멤버는 가능한 한 더 좁은 범위(private) 로 만들고, 외부와의 계약은 메소드(혹은 readonly 게터)로 노출하세요.

  • 상속·트레이트·팩토리 패턴과 결합해 올바른 캡슐화와 확장성을 확보하는 것이 중요합니다.


댓글