# 접근 제어자(Access Modifier)
본 장에서는 객체의 사용자에게 필요한 정보와 기능만을 노출시켜 사용을 보다 용이하도록 하고, 객체의 오류를 최소화 할수 있는 방법 중 하나인 캡슐화(Encapsulation)에 대하여 학습한 내용을 정리하였다.
캡슐화 방식을 채택하면, 결과적으로 객체를 프로그램의 좋은 부품으로 활용하는데 많은 도움이 된다. 객체지향 프로그래밍에서 접근제어자 또는 속성의 가시성은 캡슐화를 달성하는데 중요한 기능을 제공한다.
# 캡슐화(Encapsulation)
캡슐화의 핵심은 불필요한 정보를 감추는 것이다. 캡슐화의 본질은 객체를 사용하는 사람이 간단히 사용만 할 수 있도록 감싸는 패키징과 같다고 할 수 있다.
가령, 가전제품을 제작하는 사람과 사용하는 사람이 있다고 할 때, 제작하는 사람은 내부의 복잡한 기계부품 및 회로도를 이용하여 유지/보수를 행하지만, 그 가전제품을 이용하는 사람은, 기계 내부의 회로 혹은 부품에는 관심을 갖지 않고, 가전제품의 외부에 이용자가 이용할 수 있는 부분만 신경쓰면 되는 것과 같은 맥락이라고 볼 수 있다.
이는 사용자에게 사용성을 확대함과 동시에, 내부의 불필요한 접근(Access)을 차단함으로써 고장 혹은 오류의 가능성을 최소화 시킬 수 있는 장점 역시 보유하고 있는 것이다.
객체지향 프로그래밍에서도 상기와 같은 맥락의 기능 - 사용자의 불필요한 접근을 억제하고, 객체를 사용하고자 하는 사용자에게 인터페이스 혹은 패키징과 같은 것을 제공하는 -을 제공하고 있는데, 대표적인 것이 바로 Access modifier라고 하는 접근 제어자 이다.
접근제어자(Access Modifier)는 객체를 사용하는 사용자 쪽에서 접근을 할 수 있게 할 것인지 접근을 못하게 할 것인지를 결정할 수 있도록 설계되어있다.
접근제어자와 관련된 예제는 다음과 같다.
<?php
class MyFileObject{
## ==== 객체의 내부 ==== ##
function __construct($fname){
$this->filename = $fname;
if(!file_exists($this->filename)){
die('There is no file '.$this->filename);
}
## 객체 생성시에 상태를 주입받도록 처리하고, 만일 파일이 존재하지 않는다면 프로그램을
## 종료하는 코드를 생성자에 포함시켜 초기화를 하였기 때문에
## 하기에 명시한 오류는 발생하지 않게 된다.
}
function isFile(){
return is_file($this->filename);
}
## ==== 객체의 내부 ==== ##
}
$file = new MyFileObject('data.txt');
## ==== 사용자의 입장 ==== ##
// $file = new MyFileObject();
// $file->filename = 'data.txt';
## 객체 생성시 인자를 받지 않고, 객체를 생성한 다음에 상기와 같이
## 상태를 주입한다면, 파일이 존재하지 않을 때 오류가 발생하는 등의 문제가 발생할 수도 있다.
## 따라서 주입하려는 파일이 존재하지 않는다면 등의 조건을 걸기 위해서는, 객체 생성과 동시에
## 상태를 주입받는 조치를 취하여야 한다.
var_dump($file->isFile());
var_dump($file->filename);
## ==== 사용자의 입장 ==== ##
Access Modifier는 상기의 코드블럭에서 객체의 내부 부분을 객체의 사용자에게 노출할 것인지 안할 것인지를 결정할 수 있도록 하는 것이다.
예제와 같이 인스턴스가 생성되었을 때에, 본래 의도와는 다른, 존재하지 않는 상태가 주입되어 객체의 동작에 혼란을 야기할 수 있는 확률이 존재하게되는데, 객체 생성시 인자를 받도록 조치를 취하면, 이와 같은 오류를 억제할 수 있는 능력이 생기게 되는 것이다.
객체 생성시에 상태를 주어야지만 생성할 수 있도록 하고, 그 내부에서 주입된 상태가 의도한 것인지 아닌지를 판단하는 구문을 삽입한다면, 주입되는 생성자가 생성한 클래스에서 혼란의 야기 여부를 파악할 수 있도록 도와주는 것이다.
그러나 상기의 방법 역시 객체를 사용하는 사람이 강제로 상태를 주입하는 것이 가능하다.
$file = new MyFileObject();
$file->filename = 'forErrorCreatedVoidFile.txt';
상기와 같이 존재하지 않는 파일을 실수 혹은 고의로 주입하여도 클래스 내부에 filename이라는 변수를 사용하고 있기 때문에 주입이 가능하도록 설계가 되어있는 것이다.
이와같은 상황을 피하기 위한 장치 역시 마련되어있다. 이것이 바로 접근제어자 인것이다. 예제는 다음과 같다.
<?php
class MyFileObject{
## ==== 객체의 내부 ==== ##
private $filename;
## 이와같이 클래스 내부에서 private라고 선언한다면
## 외부에서 해당 변수명으로는 상태주입이 불가능해진다.
function __construct($fname){
$this->filename = $fname;
if(!file_exists($this->filename)){
die('There is no file '.$this->filename);
}
}
function isFile(){
return is_file($this->filename);
}
## ==== 객체의 내부 ==== ##
}
만일 상기와 같은 클래스의 인스턴스에 다음과 같이 상태를 주입하려고 한다면, cannot access private property라는 오류가 발생하고 프로그램은 종료되게 된다. 예제는 다음과 같다.
$file = mew MyFileObject('data.txt');
$file -> filename = `injectAnotherData.txt';
## Can not access private property ERROR
즉 접근 제어자는 객체의 사용자가 객체에 직접 접근할 수 없도록 규제할 수 있는 기능을 갖고 있는 것이다.
만일 사용자가 객체에 직접 접근해도 상관 없고, 직접 접근할 수 있도록 허가를 하고 싶다면 private가 아니라 public을 사용하면 된다. 예제는 다음과 같다.
<?php
class MyFileObject{
## ==== 객체의 내부 ==== ##
public $filename;
## 이와같이 클래스 내부에서 public이라고 선언한다면
## 외부에서 해당 변수명으로 상태 주입이 가능해진다.
function __construct($fname){
$this->filename = $fname;
if(!file_exists($this->filename)){
die('There is no file '.$this->filename);
}
}
function isFile(){
return is_file($this->filename);
}
## ==== 객체의 내부 ==== ##
}
다음의 예제는 학습 내용을 총 망라한 예제이다.
<?php
## 객체, 생성자, 메소드, 접근제어자 복습 예제
class Person {
# 사람 이라는 클래스를 생성
# 사람이라는 객체가 어떠한 기능이 있는지를 모아 놓은 것(클래스)
function greeting() {
# person 이라는 설계도로 만들어진 사람은 인사를 할 수 있는 기능이 생긴 것이다.
print("Hi, i am {$this -> name}.");
# 인사를 시키면, 이 인스턴스(this)의 변수(name)에 저장된 것을 출력해서
# Hi I am ~~. 라고 인사를 하게 된다.
}
}
=== 사용자 ===
$baby = new Person();
# baby 이라는 변수에 사람 이라는 객체를 생성
# !! baby 는 이제 내부에 사람이라는 인스턴스가 생긴다 !!
# 따라서 baby는 Person 클래스에 모아놓은 기능을 전부 사용할 수 있게되었다.
# 이는 Person이 제공하는(안에있는) 기능을 시킬수 있다.
$baby -> name = "tom";
# baby 인스턴스의 name변수는 이제 tom이라는 값을 갖게되었다 (상태를 주입)
$baby->greeting(); # Hi I am tom.
# baby라는 인스턴스는 greeting이라는 메소드(함수)를 이용하여 인사를 출력함.
# baby라는 사람은 이제 인사를 할 수 있다.
<?php
## 객체, 생성자, 메소드, 접근제어자 복습 예제
class Person {
# 사람 이라는 클래스를 생성
# 사람이라는 객체가 어떠한 기능이 있는지를 모아 놓은 것(클래스)
private $name;
function greeting() {
# person 이라는 설계도로 만들어진 사람은 인사를 할 수 있는 기능이 생긴 것이다.
print("Hi, i am {$this -> name}.");
# 인사를 시키면, 이 인스턴스(this)의 변수(name)에 저장된 것을 출력해서
# Hi I am ~~. 라고 인사를 하게 된다.
}
function setName($_name) {
# person 이라는 설계도로 만들어진 사람은 이름을 받을 수 있는 기능이 생기게 되었다.
$this -> name = $_name;
# 이 인스턴스(this)의 name(변수)에 setName메소드의 인자로 받은 값($_name)을
# name(변수)에 할당할 수 있도록 함.
}
}
=== 사용자 ===
$baby = new Person();
# $baby -> name = "tom";
// 클래스에 name을 접근제어자로서 금지시켰기 때문에, 이제 사용자는 baby이라는 인스턴스에
// name 에 값을 할당할 수가 없게 되었다.
// Person이라는 설계도를 수정하여 이름을 지어줄 수 없게 되었다.
# baby 이라는 인스턴스에 사용자가 name변수의 값을 지정하지 못하도록 하고,
# 클래스를 생성하는 사람이 name변수의 값을 클래스 내부에서 할당하도록 조치를 취하고 싶을 때,
$baby->setName('tom')
# private로 직접주입을 할 수 없도록 처리하고, name변수에 값을 할당할 수 있는 메소드를 마련하였으니,
# 해당 메소드를 통하여 private 변수에 값을 할당함.
# baby이라는 인스턴스에 'tom'이라는 이름을 지어줌
$baby->greeting(); # Hi I am tom.
# greeting 메소드 실행
# baby에게 인사를 시킴
<?php
## 객체, 생성자, 메소드, 접근제어자 복습 예제
class Person {
# 사람 이라는 클래스를 생성
# 사람이라는 객체가 어떠한 기능이 있는지를 모아 놓은 것(클래스)
private $name;
# private의 장점은 만일, person객체에 name이 반드시 필요할 때, 그것을 강제할 수 있고,
# 원하지 않는 값 혹은 혼란을 야기시키는 값을 미연에 방지할 수 있는 것이다. 하기와 같다.
function greeting() {
print("Hi, i am {$this -> name}.");
# 인사를 시키면, 이 인스턴스(this)의 변수(name)에 저장된 것을 출력해서
# Hi I am ~~. 라고 인사를 하게 된다.
}
function setName($_name) {
# person 이라는 설계도로 만들어진 사람은 이름을 받을 수 있는 기능이 생기게 되었다.
if(empty($_name)) {
# 만일 생성된 인스턴스가 값을 할당받지 못한다면($_name);
# Person 클래스로 생성된 객체가 이름을 받지 못한다면
die('please, give me a name');
# 'please, give me a name' 을 출력하고 프로그램이 종료된다.
}
$this -> name = $_name;
# 이 인스턴스(this)의 name(변수)에 setName메소드의 인자로 받은 값($_name)을
# name(변수)에 할당할 수 있도록 함.
}
function getName() {
# getName 메소드를 생성( 사용자에게 name의 값이 무엇인지 조회할 수 있도록 하는 기능을 보유 )
return $this -> name;
# 이 인스턴스(this)의 변수(name)의 값을 리턴
}
}
=== 사용자 ===
$baby = new Person();
# $baby -> name = "tom";
$baby->setName(''); # please give me a name
# baby 인스턴스에 $_name 인자를 부여하지 않았음으로 please give me a name을 출력하고
# 프로그램은 종료된다.
$baby->greeting();
# 상기와 같은 상황에서 $baby 객체의 name값을 알고싶을 때 다음과 같이 입력한다면 접근불가에러가 발생한다.
# name이 private이기 때문이다.
print($baby->name);
# baby인스턴스 안에 name변수를 출력
# baby의 이름은 무엇인가?
# name의 값을 조회할 수 있도록 하기 위해서는 값을 조회할 수 있는 새로운 메소드를 생성해야한다.
# => getName()메소드
print($baby->getName()); # tom
# getName()메소드를 출력
# baby에게 getName()을 시킴
상기 예제와 같이 사용자가 직접 클래스 내부의 변수에 접근할수 없도록 하고 메소드(set / get)로서만 접근하여 조회할 수 있도록 하는 것이 setget메소드이다. setget을 사용하면, 사용자로부터 혼란야기가 가능한 상태의 주입을 억제할 수 있게 된다. 이는 프로그램의 안정도에 기여를 한다.
== JetBrain 의 PhpStorm 을 사용하면 setget메소드를 만드는 데 도움을 받을 수 있다. (에디터 내에 기능제공)
상기와 같이 변수 뿐만 아니라, 메소드에서도 내부적으로만 사용해야하는 메소드가 있을 수 있고, 외부적으로 접근해도 상관없는 메소드가 존재할 수 있다. 이를 위한 기능 역시 제공하고 있는데 다음과 같다.
cf) 메소드 내에서 관련이 있는 기능을 묶어서 다시 함수로서 꺼내는 기법 : 메소드 추출 기법;
어떠한 로직이 있을때 서로 관련있는 로직을 묶어서 새로운 함수로 만드는 것을 뜻함.
<?php
## 객체, 생성자, 메소드, 접근제어자 복습 예제
class Person {
private $name;
function greeting() {
print("Hi, i am {$this -> name}.");
}
function setName($_name) {
$this -> ifEmptyDie($_name);
# 이 인스턴스(this)에서 외부에 들어온 값($_name)을 내부 메소드 ifEmptyDie();의 인자로
# 실행시킴
$this -> name = $_name;
}
function getName() {
return $this -> name;
}
# 클래스 내부에서 사용할 메소드를 선언
# 만일 값이 없다면 프로그램을 종료한다 라는 기능을 갖고있음.
# 해당 메소드는 외부에서 접근해서 사용할 필요가 없고,
# 그것을 제어하기 위해서 변수의 것과 마찬가지로
# private를 삽입하였다.
private function ifEmptyDie($value) {
# 인자로 $value를 받음
if(empty($value)) {
# 만일 인자로 받은 value가 비어있다면
die('please, give me a name');
# please, give me a name를 출력하고 프로그램을 종료시킨다.
}
}
}
=== 사용자 ===
$baby = new Person();
$baby->setName('tom');
$baby->greeting();
print($baby->name);
print($baby->getName());
class의 메소드 역시도 마찬가지이다. 특별히 지정해주지 않으면 기본값(default)으로 public이 붙어있고, 외부의 접근을 제어하기 위해서는 별도로 private(접근제어자) 를 붙여주어야 한다.
'Language > PHP' 카테고리의 다른 글
공부 내용 정리 PHP :: 초급 VI (0) | 2021.06.02 |
---|---|
공부 내용 정리 PHP :: 초급 V (0) | 2021.06.02 |
공부 내용 정리 PHP :: 초급 III (0) | 2021.06.01 |
공부 내용 정리 PHP :: 초급 II (0) | 2021.05.31 |
공부 내용 정리 PHP :: 초급 I (0) | 2021.05.26 |