[Java 개념노트 27] 캡슐화와 getter/setter 이해하기
안녕하세요. Java 개념노트 시리즈 스물일곱 번째 글입니다.
지난 글에서는 클래스, 필드, 메서드, 생성자의 접근 범위를 정하는 접근 제어자에 대해 정리했습니다. 이번 글에서는 접근 제어자와 깊게 연결되는 객체지향 핵심 개념인 캡슐화와 getter/setter에 대해 알아보겠습니다.
자바에서 객체의 필드를 모두 public으로 열어두면 외부에서 값을 마음대로 바꿀 수 있습니다. 처음에는 편해 보이지만, 프로그램이 커질수록 데이터가 잘못 변경되는 문제가 생길 수 있습니다. 이런 문제를 줄이기 위해 사용하는 개념이 바로 캡슐화입니다.
1. 캡슐화란?
캡슐화는 객체의 데이터를 외부에서 직접 접근하지 못하게 숨기고, 정해진 메서드를 통해서만 사용하도록 만드는 객체지향 원칙입니다.
영어로는 Encapsulation이라고 합니다. 캡슐화는 객체 내부의 데이터를 보호하고, 외부에서는 필요한 기능만 사용할 수 있도록 도와줍니다.
쉽게 말하면
캡슐화는 중요한 데이터를 아무나 직접 만지지 못하게 숨겨두고, 안전한 문을 통해서만 접근하게 만드는 방식입니다.
2. 캡슐화가 필요한 이유
객체의 필드를 외부에 모두 공개하면 잘못된 값이 들어갈 수 있습니다. 예를 들어 회원의 나이는 음수가 될 수 없고, 상품 가격도 음수가 될 수 없습니다. 하지만 필드가 public이면 외부에서 잘못된 값을 바로 넣을 수 있습니다.
class Member {
public String name;
public int age;
}
public class Main {
public static void main(String[] args) {
Member member = new Member();
member.name = "건";
member.age = -100;
System.out.println(member.name);
System.out.println(member.age);
}
}
위 코드에서는 나이에 -100이라는 잘못된 값이 들어갔습니다. 필드가 public이기 때문에 외부에서 직접 값을 변경할 수 있기 때문입니다.
문제점
필드를 public으로 열어두면 외부에서 검증 없이 값을 바꿀 수 있습니다. 그러면 객체의 상태가 잘못될 수 있습니다.
3. 필드는 private으로 숨기기
캡슐화를 적용할 때 가장 기본적인 방식은 필드를 private으로 만드는 것입니다.
class Member {
private String name;
private int age;
}
필드를 private으로 만들면 클래스 밖에서 직접 접근할 수 없습니다.
Member member = new Member();
member.age = -100; // 오류 발생
이제 외부에서는 age 필드를 직접 바꿀 수 없습니다. 대신 값을 넣고 꺼내는 메서드를 따로 만들어야 합니다.
4. getter와 setter란?
private 필드는 외부에서 직접 접근할 수 없습니다. 그래서 필드 값을 저장하거나 꺼낼 수 있도록 public 메서드를 만들어 사용합니다. 이때 값을 꺼내는 메서드를 getter, 값을 저장하는 메서드를 setter라고 합니다.
| 구분 | 역할 | 예시 |
| getter | 필드 값을 꺼내는 메서드 | getName() |
| setter | 필드 값을 저장하거나 변경하는 메서드 | setName() |
핵심 포인트
필드는 private으로 숨기고, 필요한 경우 public getter/setter를 통해 접근하게 만드는 것이 캡슐화의 기본 형태입니다.
5. getter 작성하기
getter는 private 필드 값을 외부로 반환하는 메서드입니다. 보통 get필드명() 형태로 작성합니다.
class Member {
private String name;
public String getName() {
return name;
}
}
name 필드는 private이지만, getName()은 public이므로 외부에서 호출할 수 있습니다.
Member member = new Member();
System.out.println(member.getName());
6. setter 작성하기
setter는 private 필드에 값을 저장하거나 변경하는 메서드입니다. 보통 set필드명() 형태로 작성합니다.
class Member {
private String name;
public void setName(String name) {
this.name = name;
}
}
여기서 this.name은 객체의 필드이고, 오른쪽의 name은 setter로 전달받은 매개변수입니다.
Member member = new Member();
member.setName("건");
7. getter와 setter 전체 예제
이제 getter와 setter를 함께 사용하는 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
Member member = new Member();
member.setName("건");
member.setAge(20);
System.out.println("이름: " + member.getName());
System.out.println("나이: " + member.getAge());
}
}
class Member {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
실행 결과는 다음과 같습니다.
이름: 건
나이: 20
외부에서는 필드에 직접 접근하지 않고, setName(), setAge(), getName(), getAge() 메서드를 통해 값을 저장하고 꺼냈습니다.
8. setter에서 값 검증하기
setter의 가장 중요한 장점은 값을 저장하기 전에 검증할 수 있다는 것입니다. 예를 들어 나이는 0보다 작을 수 없도록 막을 수 있습니다.
class Member {
private int age;
public void setAge(int age) {
if (age < 0) {
System.out.println("나이는 0 이상이어야 합니다.");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
}
사용 예시는 다음과 같습니다.
public class Main {
public static void main(String[] args) {
Member member = new Member();
member.setAge(-100);
member.setAge(20);
System.out.println(member.getAge());
}
}
실행 결과는 다음과 같습니다.
나이는 0 이상이어야 합니다.
20
잘못된 값인 -100은 저장하지 않고, 올바른 값인 20만 저장했습니다.
9. 캡슐화 전과 후 비교
캡슐화를 적용하기 전과 후를 비교해보겠습니다.
캡슐화 전
class Product {
public String name;
public int price;
}
Product product = new Product();
product.name = "키보드";
product.price = -50000;
가격에 음수가 들어가도 막을 수 없습니다.
캡슐화 후
class Product {
private String name;
private int price;
public void setName(String name) {
this.name = name;
}
public void setPrice(int price) {
if (price < 0) {
System.out.println("가격은 0 이상이어야 합니다.");
return;
}
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
이제 가격을 저장하기 전에 검증할 수 있습니다. 외부에서 필드에 직접 접근하지 못하기 때문에 객체의 상태를 더 안전하게 관리할 수 있습니다.
10. getter만 있으면 읽기 전용
필드에 getter만 만들고 setter를 만들지 않으면 외부에서는 값을 읽을 수만 있고 변경할 수 없습니다. 이런 구조를 읽기 전용처럼 사용할 수 있습니다.
class Member {
private String id;
public Member(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
사용 예시는 다음과 같습니다.
Member member = new Member("user01");
System.out.println(member.getId());
id는 생성자에서 한 번 정해지고, 외부에서는 getId()로 읽을 수만 있습니다. setId()가 없기 때문에 외부에서 변경할 수 없습니다.
실무 감각
모든 필드에 무조건 setter를 만드는 것이 정답은 아닙니다. 변경되면 안 되는 값은 setter를 만들지 않는 것이 더 안전합니다.
11. setter만 있으면 쓰기 전용?
반대로 setter만 만들고 getter를 만들지 않으면 외부에서 값을 넣을 수는 있지만 읽을 수는 없습니다. 자주 쓰는 구조는 아니지만, 외부에 값을 공개하고 싶지 않을 때 사용할 수 있습니다.
class LoginForm {
private String password;
public void setPassword(String password) {
this.password = password;
}
}
비밀번호처럼 외부에 그대로 꺼내 보여주면 안 되는 값은 getter를 만들지 않는 방식도 생각할 수 있습니다.
12. boolean 필드의 getter 이름
boolean 필드는 getter 이름을 get 대신 is로 작성하는 경우가 많습니다.
class Member {
private boolean login;
public boolean isLogin() {
return login;
}
public void setLogin(boolean login) {
this.login = login;
}
}
isLogin()은 로그인 상태인지 true 또는 false로 반환합니다.
| 필드 타입 | getter 예시 |
String name |
getName() |
int age |
getAge() |
boolean login |
isLogin() |
13. getter/setter 이름 규칙
getter와 setter는 보통 정해진 이름 규칙을 따릅니다.
| 필드 | getter | setter |
name |
getName() |
setName(String name) |
age |
getAge() |
setAge(int age) |
price |
getPrice() |
setPrice(int price) |
login |
isLogin() |
setLogin(boolean login) |
14. 생성자와 setter 함께 사용하기
생성자 안에서 필드에 직접 값을 넣을 수도 있지만, setter에 검증 로직이 있다면 생성자에서도 setter를 호출할 수 있습니다.
class Product {
private String name;
private int price;
public Product(String name, int price) {
this.name = name;
setPrice(price);
}
public void setPrice(int price) {
if (price < 0) {
System.out.println("가격은 0 이상이어야 합니다.");
return;
}
this.price = price;
}
public int getPrice() {
return price;
}
}
이렇게 하면 객체 생성 시에도 가격 검증 로직을 적용할 수 있습니다.
15. 캡슐화와 데이터 보호
캡슐화의 핵심은 데이터를 안전하게 보호하는 것입니다. 객체 내부의 중요한 값을 외부에서 직접 바꾸지 못하게 하고, 메서드를 통해 올바른 방식으로만 변경하게 만듭니다.
class Account {
private int balance;
public void deposit(int amount) {
if (amount <= 0) {
System.out.println("입금액은 0보다 커야 합니다.");
return;
}
balance += amount;
}
public void withdraw(int amount) {
if (amount <= 0) {
System.out.println("출금액은 0보다 커야 합니다.");
return;
}
if (amount > balance) {
System.out.println("잔액이 부족합니다.");
return;
}
balance -= amount;
}
public int getBalance() {
return balance;
}
}
balance는 계좌 잔액이므로 외부에서 직접 바꾸게 두면 위험합니다. 대신 입금은 deposit(), 출금은 withdraw() 메서드를 통해 처리하도록 만들었습니다.
16. getter/setter를 무조건 만들어야 할까?
초보 단계에서는 private 필드를 만들고 getter/setter를 작성하는 연습이 중요합니다. 하지만 실제로는 모든 필드에 getter와 setter를 무조건 만드는 것이 항상 좋은 설계는 아닙니다.
- 외부에서 읽을 필요가 있는 값에는 getter를 만들 수 있습니다.
- 외부에서 변경할 필요가 있는 값에는 setter를 만들 수 있습니다.
- 변경되면 안 되는 값에는 setter를 만들지 않는 것이 좋습니다.
- 외부에 공개하면 안 되는 값에는 getter도 만들지 않을 수 있습니다.
- setter가 필요하다면 값 검증 로직을 함께 넣는 것이 좋습니다.
주의
getter/setter는 단순히 자동으로 만드는 코드가 아니라, 어떤 값을 외부에 공개하고 변경할 수 있게 할지 결정하는 설계 도구입니다.
17. 캡슐화 전체 예제
이번에는 회원 정보를 캡슐화하는 전체 예제를 보겠습니다.
public class Main {
public static void main(String[] args) {
Member member = new Member("user01", "건");
member.setAge(-10);
member.setAge(20);
member.setPassword("123");
member.setPassword("java1234");
System.out.println("아이디: " + member.getId());
System.out.println("이름: " + member.getName());
System.out.println("나이: " + member.getAge());
}
}
class Member {
private final String id;
private String name;
private int age;
private String password;
public Member(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0) {
System.out.println("나이는 0 이상이어야 합니다.");
return;
}
this.age = age;
}
public void setPassword(String password) {
if (password.length() < 8) {
System.out.println("비밀번호는 8자 이상이어야 합니다.");
return;
}
this.password = password;
}
}
실행 결과는 다음과 같습니다.
나이는 0 이상이어야 합니다.
비밀번호는 8자 이상이어야 합니다.
아이디: user01
이름: 건
나이: 20
id는 한 번 정해지면 바뀌지 않도록 final 필드로 만들고 getter만 제공했습니다. age와 password는 setter에서 검증 후 저장하도록 만들었습니다. 특히 password는 외부에 그대로 보여주면 안 되므로 getter를 만들지 않았습니다.
18. 캡슐화 사용 시 자주 하는 실수
캡슐화와 getter/setter를 처음 배울 때는 아래와 같은 실수를 자주 합니다.
| 실수 | 문제점 | 해결 방법 |
| 필드를 public으로 열어둠 | 외부에서 값이 마음대로 변경됨 | 필드는 기본적으로 private 고려 |
| setter에서 검증 없이 바로 저장 | 잘못된 값이 들어갈 수 있음 | 필요한 경우 조건 검사 추가 |
| 모든 필드에 setter 생성 | 변경되면 안 되는 값도 바뀔 수 있음 | 변경 필요 여부를 먼저 판단 |
| 비밀번호 같은 민감한 값에 getter 생성 | 외부에 값이 노출될 수 있음 | 공개하면 안 되는 값은 getter 생략 |
| getter/setter 이름 규칙을 지키지 않음 | 코드 이해와 도구 사용이 불편해질 수 있음 | getName(), setName() 규칙 사용 |
19. 직접 연습해보기
아래 코드를 직접 작성하고 실행해보세요.
public class Main {
public static void main(String[] args) {
Product product = new Product("P001", "키보드", 50000);
product.printInfo();
product.setPrice(-1000);
product.setPrice(45000);
product.setName("기계식 키보드");
product.printInfo();
}
}
class Product {
private final String productCode;
private String name;
private int price;
public Product(String productCode, String name, int price) {
this.productCode = productCode;
this.name = name;
setPrice(price);
}
public String getProductCode() {
return productCode;
}
public String getName() {
return name;
}
public void setName(String name) {
if (name == null || name.isEmpty()) {
System.out.println("상품명은 비어 있을 수 없습니다.");
return;
}
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
if (price < 0) {
System.out.println("가격은 0 이상이어야 합니다.");
return;
}
this.price = price;
}
public void printInfo() {
System.out.println("상품코드: " + productCode);
System.out.println("상품명: " + name);
System.out.println("가격: " + price);
}
}
실행 결과는 다음과 같습니다.
상품코드: P001
상품명: 키보드
가격: 50000
가격은 0 이상이어야 합니다.
상품코드: P001
상품명: 기계식 키보드
가격: 45000
20. 이번 글 정리
이번 글에서는 객체의 데이터를 안전하게 보호하는 캡슐화와 getter/setter에 대해 정리했습니다. 핵심 내용은 다음과 같습니다.
- 캡슐화는 객체의 데이터를 숨기고 정해진 메서드로만 접근하게 만드는 객체지향 원칙이다.
- 필드는 보통
private으로 숨긴다. - getter는 private 필드 값을 외부로 반환하는 메서드이다.
- setter는 private 필드 값을 저장하거나 변경하는 메서드이다.
- setter에서는 값을 저장하기 전에 검증 로직을 넣을 수 있다.
- getter만 제공하면 읽기 전용처럼 사용할 수 있다.
- setter를 만들지 않으면 외부에서 값을 변경하기 어렵다.
- boolean 필드는 getter 이름으로
is필드명()을 자주 사용한다. - 모든 필드에 getter/setter를 무조건 만드는 것이 항상 좋은 설계는 아니다.
- 공개할 값과 숨길 값을 구분하는 것이 중요하다.
한 줄 요약
캡슐화는 필드를 private으로 숨기고 getter/setter 같은 메서드를 통해 객체의 데이터를 안전하게 다루는 방식입니다.
다음 글 예고
다음 글에서는 [Java 개념노트 28] 패키지와 import 이해하기라는 주제로 클래스를 폴더처럼 분류하고 다른 클래스의 기능을 가져오는 방법을 정리해보겠습니다.
GWDEVELBlog Java 개념노트 시리즈

'Computer Science > Java' 카테고리의 다른 글
| [Java ] 개념노트29 상속이란 무엇인가? (0) | 2026.06.11 |
|---|---|
| [Java] 개념노트 28 패키지와 import 이해하기 (0) | 2026.06.10 |
| [Java] 개념노트 26 접근 제어자 이해하기 (0) | 2026.06.10 |
| [Java] 개념노트 25 final 키워드 이해하기 (0) | 2026.06.10 |
| [Java] 개념노트 24 static 키워드 이해하기 (0) | 2026.06.10 |