Java에서 클래스를 사용하여 프로그래밍을 작성하다보면, 클래스 내에 존재하는 멤버 변수에 대한 내용을 함부로 변경하거나 조회하는 것을 금지하고 싶은 경우가 있다. 아래의 예시를 보자. 필자가 어떤 사이트에 가입한 회원 정보와 관련된 클래스인 Customer를 아래와 같이 작성한 뒤, Main 함수에서 2명의 고객에 대해 인스턴스를 생성했다고 가정해보자. 두 클래스는 default package에 포함되어 있다.
Customer 클래스의 멤버변수 중 절대 타인에게 보여서는 안되는 가장 중요한 정보가 비밀번호(cust_pw)다. 따라서 고객 본인 외에 타인에게 이 비밀번호는 절대 노출되어서는 안된다. 하지만 인스턴스로 비밀번호 변수를 호출하면 아무런 방해없이 이 정보를 확인할 수 있게 된다.
더 심한 경우는 비밀번호를 변경하는 것도 가능하다.
이렇게 클래스 내부에 민감한 정보가 포함된 경우, 해당 내용에 대한 접근 권한을 부여하는 키워드(예약어)를 접근 제어자라고 한다. 접근 제어자는 멤버변수는 물론, 매서드와 클래스 심지어 생성자에도 적용할 수 있으며, 접근 제어자를 통해 클래스 내부의 민감한 정보 노출을 통제할 수 있다.
1. 접근 제어자의 종류
Java에서 사용하는 접근 제어자는 아래와 같다.
- public : 접근에 제한이 없음
- protected : 동일한 패키지 내 클래스 또는 타 패키지에 속하더라도 상속 관계로 지정된 하위 클래스에서 호출 가능
- (default) : 기본값. 동일 패키지 내 클래스에서만 호출 가능
- private : 클래스 내부에서만 호출 가능
각 접근 제어자 이름의 의미로 유추할 수 있겠지만, public은 접근에 제한이 없고, private으로 내려갈수록 클래스 내에 정의된 정보로의 접근이 어려워진다. 접근 제어자는 Java 자료형 선언 시, 자료형의 앞 부분에 명시해주는데 이는 예제를 통해 확인해보자.
사용 시 한 가지 예외가 있는데, 접근 제어자를 클래스에 적용하는 경우에는 단 두 가지 접근제어자 - public, (default) - 만 사용 가능하다는 것이다.
- 클래스: public, (default)만 사용 가능
- 나머지: 접근 제어자 네 가지 모두 사용 가능
(1) private, (default) 접근 제어자 적용
필자가 노출하기를 꺼려하는 고객의 패스워드에 private 접근 제어자를 적용해보자. Customer 클래스 내에 정의된 cust_pw 변수 앞에 private을 적용하는 순간, Main 클래스에 에러가 발생한다.
Main 클래스 에러 내용을 보면 패스워드를 저장하는 멤버변수인 cust_pw가 보이지 않는 상태(not visible)이라는 문구가 보인다. 이는 Customer 클래스가 cust_id를 아무에게도 노출하지 않도록 private으로 지정해놓았기 때문이다. 즉, cust_pw에 대한 내용은 Customer 클래스 내부에서만 사용과 변경이 가능한 상태이므로, 다른 클래스에서 이 값에 변경은 커녕 조회도 안된다는 것이다.
그럼, 사용자가 비밀번호를 조회, 변경해야 할 때는 어떻게 해야할까? cust_pw는 Customer 클래스 내부에서만 조회, 변경이 가능하므로, Customer 클래스에서 cust_pw를 조회, 변경할 수 있는 매서드를 정의해주면 된다.
cust_pw를 조회, 변경하는 매서드들은 접근 제어자를 기본값(default)으로 적용했기 때문에, 같은 패키지에 존재하는 Main 클래스에서 이 매서드들을 호출하는 것에 큰 문제가 없다. 따라서 새로 생성한 매서드를 인스턴스를 통해 호출하는 경우, 비밀번호 조회와 변경이 가능해진다.
당연한 이야기지만, 새로 생성한 매서드도 private 접근 제어자가 적용된다면, 인스턴스를 통해 비밀번호를 조회하거나 변경하는 일은 불가능해지게 된다.
(2) public 접근 제어자 적용
이제 필자는 Customer 클래스 소스 파일을 default package가 아닌 별도의 패키지로 이동시키려한다. 패키지를 만들어 이동시키는 순간, Customer 인스턴스 생성 시 에러가 발생함을 확인할 수 있다.
Customer 클래스의 기본 접근제어자인 (default)이기 때문에, 같은 패키지 내에 존재하는 클래스에서만 호출이 가능하다. 하지만, Main 클래스가 Customer와 별개의 클래스에 위치해있기 때문에, Main 클래스에서 Customer 클래스 자체를 확인할 수 없는 상태다.
(default)보다 넓은 범위의 접근을 허용하는 접근 제어자는 public과 protected가 있다. 이 중 protected는 다른 패키지에 있는 클래스를 참고하기 위해서는 반드시 클래스 사이 상속 관계가 설정되어 있어야하기 때문에 (default)와 마찬가지로 에러가 발생한다. 반면 접근에 제한이 없는 public 접근 제어자를 Customer 클래스, 생성자에 모두 적용하는 경우 인스턴스 생성 과정에서 에러는 발생하지 않는다. 하지만 Customer 클래스 내부의 매서드는 여전히 (default)로 설정되어 있기 때문에, 인스턴스를 통해 매서드를 호출하는 과정은 여전히 에러가 발생한다.
Customer 클래스 내부 매서드도 public 접근 제어자를 적용하면, 위의 에러는 더 이상 발생하지 않게 된다.
멤버변수의 경우 접근제어자가 public으로 제어된 것이 아무것도 없기 때문에, Main 클래스에서 이 변수들을 직접 호출하는 경우 에러가 발생한다. 만약 Main 클래스의 멤버변수도 직접 호출하기를 원한다면 모든 멤버변수의 자료형 앞에 public 접근 제어자를 적용해주어야 한다.
(3) protected 접근 제어자
이제 필자는 VIP 클래스를 하나 만들어 Customer 클래스를 상속해보려 한다. VIP 클래스는 vips라는 별도의 패키지에 저장하려고 한다. 상속을 위해 부모 클래스에는 기본 생성자가 반드시 포함되어야하기 때문에 Customer 클래스에 별도로 정의하지 않은 Customer() 기본 생성자를 추가한다.
VIP 클래스에서 부모 클래스인 Customer의 import 와 상속이 불가능하다. 우선 Customer 클래스가 다른 패키지에 소속되어 있기 때문에 public 접근 제어자가, 생성자도 protected와 public 접근 제어자 중 하나가 적용되어야 코드 에러가 사라진다.
하지만 필자는 Customer 클래스에 정의된 내용을 VIP 클래스가 아닌 다른 클래스에 보이고 싶지 않다면 public은 좋은 선택이 아니다. 대신에 Customer와 VIP 사이 상속 관계가 설정되어 있으므로, protected 접근 제어자를 적용하면 된다.
이제 VIP 클래스의 생성자를 생성해보자. 필자는 Customer와 동일하게 생성하되, Main 함수에서 VIP 인스턴스 형성이 가능하도록 public 접근 제어자를 생성자에 적용할 것이다.
이제 Main 함수에서 VIP 인스턴스를 생성하면 아무 문제 없이 생성되는 것을 확인할 수 있다. 하지만, 인스턴스를 통해 멤버변수나 매서드를 호출하는 것은 여전히 불가능하다. VIP가 상속받는 Customer 클래스의 매서드와 멤버변수에 기본 접근 제어자가 적용되어있기 때문이다.
만약 VIP 인스턴스를 통해 Customer 매서드를 호출하려면, 매서드 오버라이딩 진행 후 public 접근 제어자를 적용해주어야 한다. 당연히 서로 다른 패키지에 존재하는 상속 관계 클래스라면 자식 클래스에서 부모 클래스의 매서드를 호출할 수 있어야하므로 부모 클래스의 매서드는 protected 접근 제어자가 적용되어 있어야 한다.
다음 포스팅에서는 접근 제어자에 속하지 않는 접근자 중 하나인 final 에 대해 알아보려한다. 사실 기타 접근자의 종류가 상당히 다양하지만 지금까지 많이 사용한 final에 대해서 간략하게 정리하고 넘어가려한다.
Fin.
'Java > Java Basic' 카테고리의 다른 글
[Java Basic] 21. 클래스 다형성 (0) | 2022.07.27 |
---|---|
[Java Basic] 20. final 제어자 (0) | 2022.07.23 |
[Java Basic] 18. 패키지와 Import (0) | 2022.07.16 |
[Java Basic] 17. 상속 관계 클래스의 매서드 오버라이드 (0) | 2022.07.11 |
[Java Basic] 16. 클래스의 상속관계와 포함관계 (0) | 2022.07.11 |
댓글