캡슐화(Encapsulation)
모듈과 가시성에 대해 기본적으로 이해했으니, 이제 다시 캡슐화(Encapsulation) 이야기로 돌아와 봅시다. 캡슐화란 객체의 내부 구현이나 데이터를 외부로부터 숨기는 기법입니다. 주로 객체의 상태가 항상 올바른 규칙(불변성, Invariants)을 따르도록 강제할 때 사용하죠.
우리의 Ticket 구조체를 다시 봅시다:
struct Ticket {
title: String,
description: String,
status: String,
}
만약 모든 필드가 공개(pub)되어 있다면 캡슐화가 전혀 이루어지지 않은 상태입니다. 외부에서 언제든 필드 값을 바꿀 수 있고, 그 타입이 허용하는 어떤 값(예: 빈 문자열)이든 들어올 수 있다고 가정해야 하니까요. 결국 제목이 비어 있거나 상태값이 엉망인 티켓이 만들어지는 걸 막을 길이 없습니다.
더 엄격한 규칙을 적용하고 싶다면 필드를 **비공개(Private)**로 유지해야 합니다1. 대신 Ticket 인스턴스와 상호작용할 수 있는 공개 메서드를 제공하는 거죠. 이 메서드들은 “제목은 절대 비워둘 수 없다” 같은 규칙(불변성)을 철저히 검증하고 지켜낼 책임이 있습니다.
필드 중 하나라도 비공개라면, 외부 모듈에서는 구조체 인스턴스화 구문을 사용해 직접 Ticket을 만들 수 없게 됩니다:
// 외부 모듈에서는 작동하지 않습니다!
let ticket = Ticket {
title: "Build a ticket system".into(),
description: "A Kanban board".into(),
status: "Open".into()
};
우리는 이제 하나 이상의 **공개 생성자(Constructor)**를 제공해야 합니다. 모듈 외부에서 구조체의 새 인스턴스를 안전하게 생성할 수 있도록 도와주는 정적 메서드나 함수를 말하죠. 다행히 우리는 이미 하나 준비해 두었습니다. 바로 앞선 연습 문제에서 구현했던 Ticket::new 메서드입니다.
접근자 메서드(Getter)
정리하자면 다음과 같습니다:
Ticket의 모든 필드는 비공개입니다.- 객체 생성 시 유효성 검사를 수행하는 공개 생성자
Ticket::new를 제공합니다.
좋은 시작이네요! 하지만 이것만으로는 부족합니다. 티켓을 만들기만 하는 게 아니라, 그 안에 든 내용을 읽거나 활용해야 하니까요. 그런데 필드가 비공개라면 어떻게 그 값에 접근할 수 있을까요?
이럴 때 바로 접근자 메서드(Accessor Method), 흔히 말하는 **게터(Getter)**가 필요합니다. 게터는 비공개 필드의 값을 외부에서 읽을 수 있게 해주는 공개 메서드입니다. Rust에는 다른 언어들처럼 게터를 자동으로 만들어주는 내장 기능은 없습니다. 하지만 걱정 마세요, 그냥 평범한 메서드를 직접 작성하면 되니까요.
Exercise
The exercise for this section is located in 03_ticket_v1/05_encapsulation