[Java 기본] 6. 접근 제어자
by Hi.Claire🖥️ 김영한의 실전 자바 - 기본편 (김영한, 인프런)
섹션6. 접근 제어자
6-1. 접근 제어자가 필요한 이유
(문제) 스피커에 들어가는 소프트웨어 개발하기
요구사항
- 스피커의 음량은 절대 100을 넘으면 안된다. (100을 넘어가면 스피커의 부품들이 고장난다.)
스피커 객체 설계
- 데이터(필드) : 음량
- 기능(메서드) : 음량을 높이고, 내리고, 현재 음량을 확인할 수 있는 단순한 기능
- 요구사항대로 스피커의 음량은 100까지만 증가할 수 있다.
(예시1.1) Speaker 객체
package access;
public class Speaker {
int volume;
Speaker(int volume) {
this.volume = volume;
}
void volumeUp() {
if (volume >= 100) {
System.out.println("음량을 증가할 수 없습니다. 최대 음량입니다.");
return;
}
volume += 10;
System.out.println("음량을 10 증가합니다.");
}
void volumeDown() {
volume -= 10;
System.out.println("음량을 10 감소합니다.");
}
void showVolume() {
System.out.println("현재 음량: " + volume);
}
}
(예시1.2) Speaker 객체 사용
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
}
}
현재 음량: 90
음량을 10 증가합니다.
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다.
현재 음량: 100
오랜 시간이 흘러 다음 버전의 스피커를 출시하게 되었다.
이때는 새로운 개발자가 급하게 기존 코드를 이어받아서 개발을 하게 되었다.
참고로 새로운 개발자는 기존 요구사항을 잘 몰랐다.
코드를 실행해보니 음량이 100 이상으로 올라가지 않았는데, 소리를 더 올리면 좋겠다고 생각한 개발자는 다양한 방면으로 고민했다.
Speaker 클래스를 보니 volume 필드를 직접 사용할 수 있었다.
그래서 volume 필드의 값을 200으로 설정하고 이 코드를 실행한 순간 스피커의 부품들에 과부하가 걸리면서 고장났다.
(예시1.3) Speaker 객체 사용 - volume 필드에 직접 접근
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
// 필드에 직접 접근
System.out.println("volume 필드에 직접 접근");
speaker.volume = 200;
speaker.showVolume();
}
}
현재 음량: 90
음량을 10 증가합니다.
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다.
현재 음량: 100
volume 필드에 직접 접근
현재 음량: 200
현재 Speaker 객체를 사용하는 사용자는 Speaker 객체의 volume 필드에 직접 접근할 수 있다.
volumeUp() 메서드에서 음량이 100을 넘지 못하도록 하는 기능을 개발했지만 소용이 없다.
왜냐하면 Speaker 사용자가 volume 필드에 직접 접근해서 원하는 값을 설정할 수 있기 때문이다.
이런 문제를 근본적으로 해결하기 위해서는 volume 필드의 외부 접근을 막을 수 있는 방법이 필요하다.
(예시1.4) Speaker 객체 - volume 필드의 접근 제어자를 private으로 수정
package access;
public class Speaker {
// private 접근 제어자 사용
private int volume;
Speaker(int volume) {
this.volume = volume;
}
void volumeUp() {
if (volume >= 100) {
System.out.println("음량을 증가할 수 없습니다. 최대 음량입니다.");
return;
}
volume += 10;
System.out.println("음량을 10 증가합니다.");
}
void volumeDown() {
volume -= 10;
System.out.println("음량을 10 감소합니다.");
}
void showVolume() {
System.out.println("현재 음량: " + volume);
}
}
private 접근 제어자를 사용할 경우 해당 클래스 내부에서만 호출할 수 있다.
SpeakerMain 클래스를 다시 실행해보면 다음 코드에서 다음과 같은 컴파일 오류가 발생한다.
// 필드에 직접 접근
System.out.println("volume 필드에 직접 접근");
speaker.volume = 200; // 컴파일 오류 발생
speaker.showVolume();
java: volume has private access in access.Speaker
위의 오류는 volume 필드는 private으로 설정되어 있기 때문에 외부에서 접근할 수 없다는 오류다.
(예시1.5) Speaker 객체 사용 - volume 필드 직접 접근 부분을 주석 처리
package access;
public class SpeakerMain {
public static void main(String[] args) {
Speaker speaker = new Speaker(90);
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
speaker.volumeUp();
speaker.showVolume();
// 필드에 직접 접근
System.out.println("volume 필드에 직접 접근 주석 처리");
// speaker.volume = 200; // 컴파일 오류 발생
speaker.showVolume();
}
}
현재 음량: 90
음량을 10 증가합니다.
현재 음량: 100
음량을 증가할 수 없습니다. 최대 음량입니다.
현재 음량: 100
volume 필드에 직접 접근 주석 처리
현재 음량: 100
volume 필드를 private을 사용해서 Speaker 내부에 숨겼다.
외부에서 volume 필드에 직접 접근할 수 없게 막은 것이다.
이제 새로운 개발자도 volume 필드에 직접 접근하지 않고, volumeUp()과 같은 메서드를 통해서만 접근하게 된다.
결과적으로 Speaker가 고장나는 문제는 발생하지 않게 되었다.
좋은 프로그램은 무한한 자유도가 주어지는 프로그램이 아니라 적절한 제약을 제공하는 프로그램이다.
6-2. 접근 제어자의 종류
접근 제어자의 핵심은 속성과 기능을 외부로부터 숨기는 것이다.
접근 제어자의 종류
- private : 모든 외부 호출을 막는다. 같은 클래스 내부에서만 접근이 가능하다.
- default(package-private) : 같은 패키지 안에서의 접근만 가능하다.
- protected : 같은 패키지 안에서의 접근이나 상속 관계의 접근을 허용한다.
- public : 모든 외부 접근을 허용한다.
접근 제어자를 명시하지 않으면 같은 패키지 안에서의 호출을 허용하는 default 접근 제어자가 적용된다.
6-3. 접근 제어자 사용 - 필드, 메서드
필드, 메서드, 생성자 레벨의 접근 제어자는 private, default, protected, public 4가지 모두 사용된다.
객체의 메서드 내부에서는 자기 자신에게 접근하는 것이므로 private을 포함한 모든 곳에 접근할 수 있다.
6-4. 접근 제어자 사용 - 클래스 레벨
클래스 레벨의 접근 제어자는 public, default만 사용할 수 있다.
public 클래스는 반드시 파일명과 이름이 같아야 한다.
- 하나의 자바 파일에 public 클래스는 하나만 등장할 수 있다.
- 하나의 자바 파일에 default 접근 제어자를 사용하는 클래스는 무한정으로 만들 수 있다.
6-5. 캡슐화
캡슐화(Encapsulation)는 객체 지향 프로그래밍의 중요한 개념 중 하나다.
캡슐화는 데이터와 해당 데이터를 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것을 말한다.
즉, 속성과 기능을 하나로 묶고, 외부에 꼭 필요한 기능만 노출하고 나머지는 모두 내부로 숨기는 것이다.
캡슐화를 통해 데이터의 직접적인 변경을 방지하거나 제한할 수 있다.
이러한 캡슐화를 안전하게 완성할 수 있게 해주는 장치가 바로 접근 제어자다.
그렇다면 어떤 것을 숨기고 어떤 것을 노출해야 할까?
(1) 데이터를 숨겨라.
객체에는 속성(데이터)과 기능(메서드)이 있다.
캡슐화에서 가장 필수로 숨겨야 하는 것은 속성(데이터)이다.
객체 내부의 데이터를 외부에서 함부로 접근하게 두면 클래스 안에서 데이터를 다루는 모든 로직을 무시하고 데이터를 변경할 수 있다.
결국 모든 안전망을 빠져나가게 되고 캡슐화가 깨진다.
음악 플레이어를 떠올려보자.
우리는 음악 플레이어의 켜고, 끄고, 볼륨을 조절하는 버튼을 누를 뿐이다.
그 내부에 있는 전원부나 볼륨의 상태 데이터를 직접 수정하지 않는다.
그것은 음악 플레이어가 스스로 해야 하는 일이다.
우리는 음악 플레이어가 제공하는 기능을 통해서 음악 플레이어를 사용할 뿐이다.
객체의 데이터는 객체가 제공하는 기능인 메서드를 통해서 접근해야 한다.
(2) 기능을 숨겨라.
객체의 기능 중에서 외부에서 사용하지 않고 내부에서만 사용하는 기능들이 있다.
이런 기능도 모두 감추는 것이 좋다.
사용자 입장에서 꼭 필요한 기능만 외부에 노출하고, 나머지 기능은 모두 내부로 숨기자.
6-6. 문제와 풀이
(문제) 쇼핑 카트
ShoppingCartMain 코드가 작동하도록 Item, ShoppingCart 클래스를 완성해라.
요구사항
- 접근 제어자를 사용해서 데이터를 캡슐화한다.
- 해당 클래스는 다른 패키지에서도 사용할 수 있어야 한다.
- 장바구니에는 상품을 최대 10개만 담을 수 있다.
- 10개 초과 등록 시 "장바구니가 가득 찼습니다."를 출력한다.
(예시) ShoppingCartMain
package access.ex;
public class ShoppingCartMain {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("마늘", 2000, 2);
Item item2 = new Item("상추", 3000, 4);
cart.addItem(item1);
cart.addItem(item2);
cart.displayItems();
}
}
장바구니 상품 출력
상품명:마늘, 합계:4000
상품명:상추, 합계:12000
전체 가격 합:16000
(풀이1.1) Item
package access.ex;
public class Item {
private String name;
private int price;
private int quantity;
public Item(String name, int price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public String getName() {
return name;
}
public int getTotalPrice() {
return price * quantity;
}
}
price와 quantity를 외부에 반환한 다음 외부에서 둘을 곱해 상품별 합계를 구해도 되지만, getTotalPrice() 메서드를 제공하면 외부에서는 단순히 이 메서드를 호출하면 된다.
이 메서드의 핵심은 자신이 가진 데이터는 자신이 직접 사용(계산)한다는 점이다.
(풀이1.2) ShoppingCart
package access.ex;
public class ShoppingCart {
private Item[] items = new Item[10];
private int itemCount;
public void addItem(Item item) {
if (itemCount >= items.length) {
System.out.println("장바구니가 가득 찼습니다.");
return;
}
items[itemCount] = item;
itemCount++;
}
public void displayItems() {
System.out.println("장바구니 상품 출력");
for (int i = 0; i <itemCount; i++) {
System.out.println("상품명:" + items[i].getName() + ", 합계:" + items[i].getTotalPrice());
}
System.out.println("전체 가격 합:" + calculateTotalPrice());
}
private int calculateTotalPrice() {
int totalPrice = 0;
for (int i = 0; i < itemCount; i++) {
Item item = items[i];
totalPrice += item.getTotalPrice();
}
return totalPrice;
}
}
장바구니 안의 상품들의 가격 합계를 계산하는 메서드를 추출했다.
calculateTotalPrice() 메서드는 ShoppingCart 클래스 내부에서만 사용하므로 private 접근 제어자를 사용한다.
'☕️ Java > 김영한의 실전 자바 - 기본편' 카테고리의 다른 글
[Java 기본] 8. final (0) | 2025.05.25 |
---|---|
[Java 기본] 7. 자바 메모리 구조와 static (0) | 2025.05.21 |
[Java 기본] 5. 패키지 (0) | 2024.09.16 |
[Java 기본] 4. 생성자 (2) | 2024.09.16 |
[Java 기본] 3. 객체 지향 프로그래밍 (0) | 2024.08.26 |
블로그의 정보
Claire's Study Note
Hi.Claire