네이버 부캠을 하고 있는데 만나는 사람마다 책을 추천해달라고 요청을 해왔습니다.
많은 책을 추천받았기에 한권씩 읽으며 다양한 사람과 공유해보고자 새롭게 카테고리를 만들고 글을 시작해봅니다.
첫번재 책은 객체지향의 사실과 오해라는 토끼책이에요 🐰🐰🐰
자 시작해보겠습니ㅏ~
객체 = 실세계(?)?><???><?????
"객체지향이란 시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법이다."
객체지향을 대학교 수업에서 배울 때 아마 자동차나 동물 상속 예제들은 한번쯤 봤을겁니다.
책을 읽어보면 객체 ≠ 실생활이라는데 왜 자꾸 비유해왔을까 생각해봤었습니다.
답은 간단해요. 학습에 너무나 효과적이거든요!
하지만 정확한 객체지향에 대해서 책에서는
"객체지향 설계는 현실을 모방하는 것이 아니라, 현실 속 객체의 이름과 특징으로 새로운 세상을 만드는 것이다."
아... 모방이 아니라 창조였던 거구나!
소프트웨어 안의 전등은 사람의 도움 없이도 스스로 켜질 수 있고, 자동차는 알아서 주차할 수도 있잖아요.
현실과는 다른 새로운 능력을 가지는 거죠...!
협력하는 객체들의 세상 ☕☕
카페에서 커피를 주문하는 상황을 떠올려볼께요:
- 손님: "아메리카노 톨사이즈로 하나 주세요"
- 알바: "네, 3500원입니다" → POS기 조작 → "바리스타님, 아메리카노 톨 하나요!"
- 바리스타: 원두 갈기 → 추출 → "아메리카노 나왔습니다!"
이게 바로 협력이에요!
각자의 역할과 책임이 명확하고, 요청과 응답으로 목표를 달성하죠.
협력은 한 사람이 다른 사람에게 도움을 요청할 때 시작된다. 요청을 받은 사람은 일을 처리한 후, 요청한 사람에게 필요한 지식이나 서비스를 제공하는 것으로 요청에 응답한다."
책임과 역할의 힘
- 손님: 원하는 메뉴를 명확하게 주문할 책임
- 알바: 주문을 받고 결제를 처리하고 바리스타에게 전달할 책임
- 바리스타: 주문받은 음료를 정확하게 제조할 책임
알바가 바뀌어도 시스템이 돌아가야합니다. 왜냐하면 역할이 정의되어 있으니까요.
"역할은 협력 안에서 구체적인 객체로 대체될 수 있는 추상적인 협력자다."
protocol OrderTaker {
func takeOrder(_ order: String) -> Receipt
}
class PartTimer: OrderTaker {
private let barista: Barista
func takeOrder(_ order: String) -> Receipt {
let receipt = processPayment(order)
barista.makeCoffee(order)
return receipt
}
}
class Manager: OrderTaker {
private let barista: Barista
func takeOrder(_ order: String) -> Receipt {
// 매니저만의 특별한 처리 방식
let receipt = processPaymentWithDiscount(order)
barista.makeCoffee(order)
return receipt
}
}
알바든 매니저든 OrderTaker 역할만 할 수 있으면 대체 가능한 거죠!
자율성의 진짜 의미
"객체는 충분히 협력적이어야 하고, 동시에 충분히 자율적이어야 한다."
바리스타에게 "아메리카노 만들어주세요"라고 요청하면, 어떤 원두를 쓸지, 어떤 기계를 쓸지는 바리스타가 알아서 결정해요.
손님은 바리스타의 내부 구현을 몰라도 되고, 바리스타는 자신만의 방식으로 커피를 만들 수 있어요.
이게 바로 캡슐화~
책임이란 무엇인가? 🤔🤔🤔🤔🤔🤔
"객체지향 세계에서 어떤 객체가 어떤 요청에 대해 대답해 줄 수 있거나,
적절한 행동을 할 의무가 있는 경우 해당 객체가 책임을 가진다고 말한다."
책임은 크게 두 가지로 나뉘어요:
하는 것(doing)
- 객체를 생성하거나 계산을 하는 등의 스스로 하는 것
- 다른 객체의 행동을 시작시키는 것
- 다른 객체의 활동을 제어하고 조절하는 것
아는 것(knowing)
- 개인적인 정보에 관해 아는 것
- 관련된 객체에 관해 아는 것
- 자신이 유도하거나 계산할 수 있는 것에 관해 아는 것
결국 어떤 일을 할지와 어떤 것에 대해 아는 것이 책임이라는 의미입니다.
객체의 정체성: 상태, 행동, 식별자 👑
사실 이 책은 거의 모든 케이스가 앨리스로 설명을 해요.
앨리스가 "Drink Me" 물약을 마셔서 키가 작아지고, "Eat Me" 케이크를 먹어서 키가 커져도, 여전히 앨리스는 앨리스잖아요?
이게 바로:
- 상태: 앨리스의 키 (변할 수 있음)
- 행동: 물약을 마시고, 케이크를 먹는 것 (상태를 변화시킴)
- 식별자: 앨리스라는 정체성 (변하지 않음)
"객체의 다양한 특성을 효과적으로 설명하기 위해서는 객체를 상태(state), 행동(behavior), 식별자(identity)를 지닌 실체로 보이는 것이 가장 효과적이다."
상태가 필요한 진짜 이유
처음에 저는 "상태 없이 행동만 있으면 안 될까?"라고 생각했어요. 함수형 프로그래밍에 빠져있을 때였거든요 😅
그런데 현실을 생각해보니까 말이 안 되더라고요.
제가 감기에 걸린 상태에서 "노래해줘"라는 요청을 받으면? 당연히 "목이 아파서 못하겠어요"라고 대답할 수밖에 없잖아요.
상태가 행동의 결과를 좌우하는 거죠.
"상태를 이용하면 과거에 얽매이지 않고 현재를 기반으로 객체의 행동 방식을 이해할 수 있다."
상태와 행동의 관계
"상태와 행동 사이에는 다음과 같은 관계가 있다:
- 객체의 행동은 상태에 영향을 받는다.
- 객체의 행동은 상태를 변경시킨다."
상태가 행동을 결정하고, 행동이 상태를 변경하는 거죠!
캡슐화의 힘
앨리스가 물약을 마시는 장면에서, 우리는 앨리스가 작아진다는 결과만 봐요. 내부에서 어떤 생물학적 변화가 일어났는지는 모르죠.
이게 바로 캡슐화의 핵심이에요:
"상태를 잘 정의된 행동 집합 뒤로 캡슐화하는 것은 객체의 자율성을 높이고 협력을 단순하고 유연하게 만든다."
타입과 추상화의 힘
지하철 노선도의 지혜
책에 의하면 초기 지하철 노선도는 실제 거리와 지형에 비례하게 길을 묘사했었다고 해요.
하지만 너무 보기가 불편해서 그냥 타고 내리는 것에만 집중하도록 다른 명확성은 제외하고 표현한 것이 현대의 지하철 노선도라고 합니다.
이게 바로 추상화입니다! 복잡한 현실에서 정말 필요한 정보만 뽑아내서 단순하게 만드는 거예요.
앨리스 정원의 트럼프들
책에서 가장 인상깊었던 장면이 앨리스가 정원에서 여러 인물들을 보고 "모두 트럼프에 불과해!"라고 말하는 부분이었어요.
하트여왕, 하트잭, 병사들... 다들 생김새도 다르고 역할도 다른데, 앨리스는 이들을 공통점을 기준으로 묶어서 생각한 거죠.
"앨리스가 인물들의 차이점을 무시하고 공통점만을 취해 트럼프라는 개념으로 단순화한 것은 추상화의 일종이다."
개념의 구조: 심볼, 내연, 외연
- 심볼(symbol): 개념을 가리키는 간략한 이름이나 명칭
- 내연(intension): 개념의 완전한 정의 (예: "납작하고 네모난 몸을 가진 존재")
- 외연(extension): 개념에 속하는 모든 객체의 집합 (예: 하트여왕, 하트잭, 병사들...)
타입 == 개념이라는 깨달음
"타입은 개념과 동일하다. 따라서 타입이란 우리가 인식하고 있는 다양한 사물이나 객체에 적용할 수 있는 아이디어나 관념을 의미한다."
그동안 저는 타입을 그냥 "데이터를 분류하는 방법" 정도로만 생각했거든요. 근데 타입이 개념 그 자체였다니!
타입을 결정하는 건 데이터가 아니라 행동이에요.
"어떤 객체가 어떤 타입에 속하는지를 결정하는 것은 객체가 수행하는 행동이다."
이게 바로 다형성의 핵심이더라고요!
행동이 우선이다! ⚡⭐️👍✨
결론적으로 객체의 타입을 결정하는 것은 객체의 행동뿐이다. 객체가 어떤 데이터를 보유하고 있는지는 타입을 결정하는 데 아무런 영향도 미치지 않는다."
처음 개발할 때는 "어떤 데이터를 저장할까?"부터 생각했는데, 이 책을 읽고 나서는 "어떤 행동을 해야 할까?"를 먼저 생각하게 되었어요.
- 먼저 책임(행동) 결정
- 그 다음 적절한 상태 선택
- 마지막에 구현 방법 고민
왜 상태 중심은 함정일까?
- 캡슐화가 저해된다: 상태가 공용 인터페이스에 그대로 노출됨
- 고립된 섬을 만든다: 협력이라는 문맥에서 벗어남
- 재사용성이 저하된다: 다양한 협력에 참여하기 어려움
책임-주도 설계의 철학
"올바른 객체를 설계하기 위해서는 먼저 견고하고 깔끔한 협력을 설계해야 한다."
- 시스템이 사용자에게 제공해야 하는 기능인 시스템 책임을 파악한다.("사용자가 로그인할 수 있어야 한다")
- 시스템 책임을 더 작은 책임으로 분할한다.("사용자 인증", "로그인 이력 기록", "세션 관리")
- 분할된 책임을 수행할 수 있는 적절한 객체 또는 역할을 찾아 책임을 할당한다.(AuthService, LoginHistory, SessionManager)
- 객체가 책임을 수행하는 중에 다른 객체의 도움이 필요한 경우 이를 책임질 적절한 객체 또는 역할을 찾는다.
- 해당 객체 또는 역할에게 책임을 할당함으로써 두 객체가 협력하게 한다.
메시지가 먼저, 객체는 나중에 ~
"책임-주도 설계는 객체가 아니라 객체들이 주고받는 메세지에 초점을 맞추게 하는 것에 초점을 맞춘다. 어떤 객체가 필요한지를 먼저 생각하지 말고, 어떤 메세지가 필요한지를 먼저 고민하라는 의미"
예전: "UserService 클래스를 만들어야지"
지금: "사용자가 로그인한다는 메시지가 필요하네. 누가 이 메시지를 받을까?"
여기부터는 5강이다. 어느새 절반을 넘어섰습니다.
책임과 메시지(메시지가 모든 것을 결정한다)
"훌륭하고 성장 가능한 시스템을 만들기 위한 핵심은 모듈 내부의 속성과 행동이 어떤가보다는 모듈이 어떻게 커뮤니케이션하는가에 달려있다." - 앨런 케이
지금까지 저는 객체 안에 뭘 넣을지, 어떤 메서드를 만들지만 고민했는데, 정작 중요한 건 객체들이 어떻게 대화하는가였던 거죠.
자율적인 책임이란 무엇인가? 🤔
"자율적인 객체란 스스로 정한 원칙에 따라 판단하고 스스로의 의지를 기반으로 행동하는 객체이다."
그런데 여기서 핵심은 책임 자체가 자율적이어야 한다는 거예요.
나쁜 예: "증언석으로 걸어가서, 목격했던 장면을 떠올리고, 떠오르는 기억을 시간 순서대로 재구성하고, 말로 간결하게 표현하라"
좋은 예: "증언하라"
첫 번째는 '어떻게(HOW)'를 지시하는 거고, 두 번째는 '무엇을'(WHAT)만 요청하는 거예요.
// 나쁜 예: 세부 구현을 강요
class BadWitness {
func testify() {
walkToWitnessStand()
recallMemories()
organizeChronologically()
speakClearly()
}
}
// 좋은 예: 자율성 보장
protocol Witness {
func testify() -> String // '무엇을' 할지만 명시
}
class MadHatter: Witness {
func testify() -> String {
// 모자장수만의 독특한 방식으로 증언
return "차 파티에서 일어난 일을 말씀드리죠..."
}
}
나쁜 예시가 바로 객체의 자율성을 제한하는 것이다.
너무 추상적인 것도 문제
하지만 너무 추상적인 책임도 문제예요:
❌ 너무 추상적: "설명하라" ✅ 적절한 수준: "증언하라"
"책임은 협력에 참여하는 의도를 명확하게 설명할 수 있는 수준 안에서 추상적이어야 한다."
메시지와 메서드
"메시지는 '무엇을' 의미하며, 메서드를 선택하는 것은 수신받은 객체의 결정에 따라 바뀐다."
protocol Testifiable {
func testify(about event: String) -> String
}
class MadHatter: Testifiable {
func testify(about event: String) -> String {
return "차 파티에서 \(event)가 일어났어요!"
}
}
class WhiteRabbit: Testifiable {
func testify(about event: String) -> String {
return "시간에 쫓기느라 \(event)는 잘 못봤습니다..."
}
}
// 같은 메시지, 다른 메서드!
let witnesses: [Testifiable] = [MadHatter(), WhiteRabbit()]
witnesses.forEach { witness in
print(witness.testify(about: "타르트 도난"))
}
같은 메시지를 보내도 다른 메서드가 실행되는 거죠! 이게 바로 다형성의 본질이에요.
인터페이스와 구현의 분리 🛡️
"훌륭한 객체란 구현을 모른 채 인터페이스만 알면 쉽게 상호작용할 수 있는 객체를 의미한다."
송신자는 수신자가 어떻게 메시지를 처리할지 몰라도 돼요. 그냥 "응답해주겠지"라고 믿고 메시지를 보내면 되는 거예요.
이렇게 하면:
- 송신자: 구체적인 구현에 의존하지 않음
- 수신자: 자율적으로 메시지 처리 방법 선택
- 시스템: 유연하고 확장 가능한 구조
- 액셀러레이터: 속도 증가
- 브레이크: 속도 감소
- 핸들: 방향 조절
가솔린 엔진이든 전기 모터든, 내부가 바뀌어도 운전 방법은 동일하죠!
메시지가 인터페이스를 결정한다
"객체의 인터페이스는 객체가 수신할 수 있는 메시지의 목록으로 구성되며 객체가 어떤 메시지를 수신할 수 있는지가 객체가 제공하는 인터페이스의 모양을 빚는다."
인터페이스가 먼저가 아니라 메시지가 먼저예요
캡슐화의 두 가지 관점
"객체지향의 세계에서 캡슐화는 두 가지 관점에서 사용된다"
- 상태와 행위의 캡슐화: 데이터와 기능을 하나로 묶기
- 사적인 비밀의 캡슐화: 구현 세부사항 숨기기
메시지가 객체를 선택한다
패러다임의 전환
"객체가 메시지를 선택하는 것이 아니라 메시지가 객체를 선택하게 해야 한다."
이게 정말 핵심이에요! 🎯
전통적인 방식:
- 객체를 먼저 생각
- 그 객체에 어떤 메서드를 넣을지 고민
객체지향적인 방식:
- 어떤 메시지가 필요한지 먼저 생각
- 그 메시지를 처리할 객체를 찾거나 만들기
유연하고 확장 가능한 협력
"무엇이 유연하고 확장 가능하고 재사용성이 높은 협력을 가능하게 하는가?
이 모든 것은 다형성을 지탱하는 메시지가 존재하기 때문에 가능한 것이다."
메시지 중심 설계의 3가지 장점:
- 협력이 유연해진다: 수신자를 다른 타입으로 대체 가능
- 협력을 확장할 수 있다: 새로운 수신자 추가 가능
- 협력을 재사용할 수 있다: 다양한 문맥에서 활용 가능
// 메시지 중심으로 설계된 유연한 협력
protocol PaymentProcessor {
func processPayment(_ amount: Money) -> PaymentResult
}
class CreditCardProcessor: PaymentProcessor {
func processPayment(_ amount: Money) -> PaymentResult {
// 신용카드 결제 로직
return .success
}
}
class PayPalProcessor: PaymentProcessor {
func processPayment(_ amount: Money) -> PaymentResult {
// 페이팔 결제 로직
return .success
}
}
class CryptoProcessor: PaymentProcessor {
func processPayment(_ amount: Money) -> PaymentResult {
// 암호화폐 결제 로직
return .success
}
}
// 송신자는 구체적인 구현을 모름
class OrderService {
private let paymentProcessor: PaymentProcessor
func processOrder(_ order: Order) {
let result = paymentProcessor.processPayment(order.amount)
// PaymentProcessor가 어떤 구현체든 상관없음!
}
}
느슨한 결합의 힘
"메시지는 송신자와 수신자 사이의 결합도를 낮춤으로써 설계를 유연하고, 확장 가능하고, 재사용 가능하게 만든다."
결합도가 낮다 = 서로에 대해 적게 알아도 된다 = 변경에 유연하다!
개발하다 보면 이런 경험 있으실 거예요. 왜냐? 저는 있거든요 😅
"어? 이 기능을 어디에 추가해야 하지?"
"사용자가 요구사항을 바꿨는데... 어디서부터 손대야 할까?"
"코드가 점점 복잡해지는데, 뭔가 잘못된 것 같은데..."
우리가 일반적으로 길을 잃었을 때 할 수 있는 선택은 2개입니다.
1. 길을 물어보기 vs 2. 카카오맵 같은 지도 활용하기
1의 경우 구체적인 길 안내를 받을 수 있으나, 다른 목적지로 갈 때는 다시 "어? 어떻게 가지?" 물어봐야 합니다.
반면 2의 경우 목적지가 달라져도 지도로 새로운 경로를 찾을 수 있어요.
개발에서는 1을 기능중심개발이라 하고 2를 구조중심개발이라고 합니다.
- "주문 취소 시 포인트도 환불해주세요"
- "배송비 무료 이벤트를 적용해주세요"
- "VIP 고객은 특별 할인을 받을 수 있게 해주세요"
기능 vs 구조: 어느 것이 먼저일까?
기능을 먼저 한다고 가정해볼게요
"주문 기능이 필요하네 → OrderService 만들자"
"결제 기능도 필요해 → PaymentService 만들자"
"배송 기능도... → DeliveryService 만들자"
처음엔 문제없어 보였는데, 요구사항이 바뀐다면?
- "주문 취소 시 포인트도 환불해주세요"
- "배송비 무료 이벤트를 적용해주세요"
- "VIP 고객은 특별 할인을 받을 수 있게 해주세요"
기능별로 나눠놓은 서비스들이 서로 얽히면서 스파게티 코드가 되어버렸어요... 😱😱😱😱
구조가 먼저다!
"지도 은유의 핵심은 기능이 아니라 구조를 기반으로 모델을 구축하는 편이
좀 더 범용적이고 이해하기 쉬우며 변경에 안정적이라는 것이다."
기능은 자주 변하지만, 구조는 상대적으로 안정적이라는 거죠!
전자상거래에서 생각해보면:
- 기능: 할인 정책, 배송 방식, 결제 수단 (자주 변함)
- 구조: 고객, 상품, 주문, 결제 (상대적으로 안정적)
구조를 먼저 잡고 그 위에 기능을 올려야 유연한 시스템이 나온다는 걸 깨달았어요!
// 구조 중심 설계
class Customer {
private let customerId: CustomerId
private var orderHistory: [Order]
func placeOrder(_ items: [OrderItem]) -> Order {
// 고객의 주문 행동
}
}
class Order {
private let orderId: OrderId
private let customer: Customer
private var items: [OrderItem]
private var payment: Payment?
func addDiscount(_ discount: Discount) {
// 주문에 할인 적용
}
func processPayment(_ paymentMethod: PaymentMethod) -> PaymentResult {
// 결제 처리
}
}
도메인 모델이라는 든든한 지도
도메인 모델이 뭔데?
"도메인 모델은 이해관계자들이 바라보는 멘탈 모델이다."
멘탈모델은 UX를 공부하면서 처음 배웠던 용어인데, 사용자들이 어떻게 이해하는지를 나타내는 용어예요.
예를 들어, 아이폰에서 오른쪽 스와이핑은 이전(back)을 의미하는 것처럼요.
결국 사용자의 생각 = 코드의 구조가 되어야 한다는 거죠!
이해와 소통에 장점을 두기 위해 도메인 모델이 있는 거고, 도메인 구조의 중요성은 요구사항이 변경될 때 더 두드러집니다
안정적인 구조의 힘
"사용자 모델에 포함된 개념과 규칙은 비교적 변경될 확률이 적기 때문에
사용자 모델을 기반으로 설계와 코드를 만들면 변경에 쉽게 대처할 수 있을 가능성이 크다."
핵심 도메인 구조는 상대적으로 안정적이라는 거죠!
그동안은 요구사항을 읽고 그것에 초점을 두고 생각했다면, 이제는 관점을 바꿔봅시다:
Before: "이런 기능을 만들어주세요"
After: "사용자가 이런 목표를 달성하려고 하는데, 시스템이 어떻게 도와줄 수 있을까요?"
사용자 목표 중심으로 생각하기
사용자의 목표: "원하는 상품을 구매하고 싶다"
시스템과의 상호작용:
- 상품 검색 → 시스템이 관련 상품 목록 제공
- 상품 선택 → 시스템이 상세 정보 제공
- 장바구니 담기 → 시스템이 장바구니에 추가
- 주문하기 → 시스템이 주문 처리
- 결제하기 → 시스템이 결제 진행
이렇게 하면 정말 필요한 기능이 무엇인지 더 명확해집니다.
소통의 변화
가장 큰 변화는 동료들과의 소통이 훨씬 원활해진 거예요.
예전에는:
- "OrderService에 할인 로직을 추가해주세요"
지금은:
- "고객이 주문할 때 할인을 받을 수 있어야 해요"
- "결제 과정에서 문제가 있을 때 사용자가 알 수 있어야 해요"
코드 중심이 아니라 도메인 중심으로 대화하니까, 기획자나 디자이너와도 훨씬 쉽게 소통할 수 있게 되었어요!
6장의 핵심 키워드
- 예측하지 말고 대비하자
- 유연한 구조를 만들되, 과도한 일반화는 피하자
- 도메인의 본질적 구조를 파악하자
"도메인 모델은 기능을 구현할 때 참조할 수 있는 궁극적인 지도다."
드디어 마지막 장이에요! 🎉
파울러의 세 가지 관점
📋 개념 관점(Conceptual Perspective)
설계는 도메인 안에 존재하는 개념과 개념들 사이의 관계를 표현
사용자가 도메인을 바라보는 관점을 반영해요. 실제 도메인의 규칙과 제약을 최대한 유사하게 반영하는 것이 핵심!
📝 명세 관점(Specification Perspective)
소프트웨어 안에서 살아 숨쉬는 객체들의 책임에 초점
객체의 인터페이스를 바라보게 되죠. 객체가 협력을 위해 '무엇'을 할 수 있는가에 초점!
⚙️ 구현 관점(Implementation Perspective)
객체들이 책임을 수행하는 데 필요한 동작하는 코드를 작성
책임을 '어떻게' 수행할 것인가에 초점을 맞춰 인터페이스를 구현하는 데 필요한 속성과 메서드를 클래스에 추가!
커피 전문점 도메인으로 이해하기
책에서 커피 전문점 예시로 이 세 관점을 설명하는데 정말 명쾌했어요!
도메인 구성 요소들:
- 손님이 메뉴판을 보고
- 원하는 메뉴 항목을 선택해서
- 바리스타에게 주문하면
- 바리스타가 커피를 제조
이를 객체지향으로 표현하면: 손님, 메뉴판, 메뉴 항목, 바리스타, 커피 객체들의 협력!
협력에서 인터페이스까지
메시지가 먼저, 객체는 나중에라는 원칙에 따라:
- "커피를 주문하라" → 손님 객체가 담당
- "메뉴 항목을 찾아라" → 메뉴판 객체가 담당
- "커피를 제조하라" → 바리스타 객체가 담당
이전에 말한 관점으로 다시 돌아가면
🔹 개념 관점: Customer, Menu, MenuItem, Barista, Coffee 클래스들이 커피 전문점 도메인의 핵심 개념들을 반영
🔹 명세 관점: 각 클래스의 public 메서드들이 다른 객체들과 협력할 수 있는 인터페이스를 제공
🔹 구현 관점: 클래스 내부의 private 속성들과 메서드 구현이 외부에 숨겨진 채 책임을 수행
이 되겟네요
개인적인 리뷰
왜 이 책을 더 일찍 읽지 못했을까? 😅
저는 네이버 부스트캠프를 진행 중인데, 이 책을 프로젝트를 시작하기 전에 읽었으면 어땠을까? 라는 생각이 들었습니다.
제가 직접 삽질하며 깨달은 것들이 이미 이 책에 다 있었기 때문이죠.
나의 삽질 경험담
저도 객체가 도대체 SRP(단일 책임 원칙)를 위해서 뭘 분리해야 하고, 어떤 걸 기준으로 분리해야 하는가?에 대해 정말 모호했었습니다.
그러다 보니:
구조를 처음 설계한 것과 추후에 보면 또 다른 구조가 되어 클래스 다이어그램을 다시 그려야 했거든요...
특히 이런 고민들이 끊이지 않았엇어여
에 대해 모호했었습니다.
- "UserService와 UserRepository, 어디까지가 각각의 책임이지?"
- "이 기능은 어느 클래스에 넣어야 하지?"
- "왜 자꾸 하나의 클래스가 여러 가지 일을 하게 되지?"
결국 이 책의 핵심은
"행위에 집중하고, 어떻게 객체들끼리 메시지를 주고받을 것인가!"라는 것 같습니다.
Before (내가 했던 방식):
"User 테이블이 있으니까 User 클래스를 만들고..."
"주문 관련 기능들을 OrderService에 다 넣고..."
"나중에 필요하면 메서드 추가하면 되겠지?"
After (책에서 배운 방식):
"사용자가 로그인하려고 하는데, 누가 이 요청을 받아야 할까?"
"인증은 누가 담당하고, 세션 관리는 누가 할까?"
"각 객체는 어떤 메시지를 주고받아야 할까?"
멘탈 모델과 도메인의 중요성 🧠🧠
그러면서 멘탈 모델을 위해 도메인이 등장하고, 다른 사람들이 명확하게 이해할 수 있는 코드란
사용자가 생각하는 방식 그대로 코드에 녹아있는 것이더라고요.
그래서 이 책은 개발을 초기 시작하는 분들에게 강추하고 싶은 책이였던 것 같습니다
다시 책 추천이 들어오면 새로운 책으로 나타나겠습니다.
'도서관' 카테고리의 다른 글
함께 자라기[애자일로 가는 길] (5) | 2025.08.24 |
---|