Kim WooSup

좋은 설계란 무엇일까?

2026-06-08

AI 요약

객체지향책을 다시 읽으면서 깨달은 건, 좋은 설계란 단순히 클래스를 예쁘게 나누는 게 아니라 변경이 생겼을 때 덜 흔들리는 구조를 만드는 일이라는 점이다. 그래서 이제는 데이터보다 먼저 객체들이 어떤 행동을 하고 어떤 책임을 질지, 메시지는 어떻게 오갈지를 고민해 변경이 퍼지는 방향을 좁히는 쪽으로 설계하려 한다.

1년 전쯤 객체지향 필독서라고 불리는 책들을 몇 권 읽었었다.

솔직히 말하면 그때는 필요성을 느껴서 읽었다기보다 “남들이 다 읽으니까”, “유명한 책이니까”라는 생각이 더 컸다. 그래서인지 책을 덮고 몇 주만 지나도 머릿속에 남는 내용이 거의 없었다.

 

최근에는 코드를 구현하는 방식이 크게 달라졌다.

AI의 도움을 받아 구현 속도는 빨라졌고, 단순히 코드를 작성하는 것 자체보다 어떤 구조로 나눌지, 책임을 어디에 둘 지에 더 관심이 생겼다.

 

그 상태로 다시 객체지향 책을 펼치니 예전과는 다르게 읽혔다.

 

예전에도 책이나 강의에서 본 대로 객체 안에 메서드를 두고, Service는 흐름만 통제하도록 노력하긴 했다. 하지만 왜 그렇게 해야 하는지 깊게 이해했다기보다는, 좋은 코드라고 하니까 따라 했던 것에 가까웠다.

 

이번에는 달랐다.

객체지향을 단순히 코드 스타일로 보는 것이 아니라, 변경을 다루기 위한 방법으로 다시 보게 됐다.

 

이 글은 책을 다시 읽으면서 지금 내가 이해한 좋은 설계와 객체지향에 대해 짧게 정리한 글이다.


 

좋은 설계란 무엇일까?

객체지향을 이야기하기 전에 먼저 생각해봐야 할 것이 있다.

 

좋은 설계란 무엇일까?

 

물론 가장 중요한 건 일단 동작하는 코드를 만드는 것이다. 요구사항을 만족하고, 문제없이 동작하는 게 먼저다. 아무리 구조가 좋아 보여도 제대로 동작하지 않으면 의미가 없다.

 

하지만 개발은 거기서 끝나지 않는다.

 

요구사항은 계속 바뀐다.
처음 만든 코드가 그대로 유지되는 경우는 거의 없다.

 

그래서 내가 생각하는 좋은 설계는, 변경이 생겼을 때 덜 흔들리는 구조다.

 

오늘 만든 코드를 내일 수정해야 할 때, 코드 이곳저곳을 같이 뜯어고쳐야 한다면 변경하기에 귀찮고 두려워진다. 귀찮다고 그대로 내버려두면 그게 기술부채를 발생시킨다고 생각한다. 반대로 변경해야 할 부분이 비교적 명확하고, 영향 범위가 좁다면 부담이 줄어든다.

 

좋은 설계는 처음부터 완벽한 구조를 만드는 것이 아니라, 앞으로의 변경을 감당할 수 있게 만드는 일에 가깝다고 생각한다.

 

 

책을 다시 보게 된 이유

예전에는 객체지향을 단순하게 생각했다.

 

“도메인에 관련 있는 데이터와 메서드를 모으면 되는 것 아닌가?”

 

물론 이것도 틀리다고는 생각하지 않지만, 좋은 설계가 되지는 않았다.

데이터와 메서드를 한 클래스에 모아도, 객체들이 서로 강하게 얽혀 있으면 변경은 여전히 어렵다. 하나를 수정했는데 예상하지 못한 다른 코드가 같이 깨질 수 있다.

 

책을 다시 읽으면서 느낀 건 객체지향의 핵심이 단순히 클래스를 만드는 데 있지 않다는 점이었다.

객체가 어떤 책임을 가져야 하는지, 다른 객체와 어떤 메시지를 주고받아야 하는지, 변경이 생겼을 때 그 변경을 누가 감당해야 하는지를 고민하는 것이 더 중요했다.

 

결국 객체지향은 변경이 퍼지는 방향을 조절하는 방법에 가깝다고 느꼈다.

 

 

객체보다 협력이 먼저였다

예전에는 객체를 설계할 때 먼저 데이터를 떠올렸다.

“이 객체는 어떤 필드를 가져야 하지?”
“어떤 값을 저장해야 하지?”

이런 식으로 생각했다.

 

하지만 책을 다시 읽으면서 관점이 조금 바뀌었다.
객체를 먼저 고립된 덩어리로 보는 것이 아니라, 객체들이 함께 일하는 흐름을 먼저 봐야 했다.

 

객체는 혼자 존재하지 않는다.

어떤 객체는 요청하고, 어떤 객체는 응답한다.
어떤 객체는 판단하고, 어떤 객체는 실행한다.
이 과정이 모여 하나의 기능이 된다.

 

그래서 객체를 설계할 때는 “이 객체가 어떤 데이터를 가져야 할까?”보다 먼저 이런 질문을 해야 한다고 느꼈다.

  • 이 기능을 수행하려면 어떤 객체들이 협력해야 할까?

  • 각 객체는 어떤 책임을 가져야 할까?

  • 객체들 사이에는 어떤 메시지가 오가야 할까?

 

객체지향은 객체 하나를 잘 만드는 것보다, 객체들이 적절하게 협력하도록 만드는 것에 더 가깝다.

 

 

이유의 부재

돌이켜보면 이전에도 객체에게 행동을 넣는 식으로 코드를 작성하긴 했다.

 

"getter를 사용하는건 도메인에게 정보를 강탈하는 거다, 예의 있게 물어봐라" 라고 들었다.

그래서 getter로 값을 꺼내서 처리하기 보단, 객체 안에 상태를 변경하는 메서드를 두는 방식을 사용하고 있었다.

 

OrderCommandServiceImpl
Copied
01Order order = Order.order(user, store, orderProducts, command.address(), clockHolder.nowDateTime());02Order acceptedOrder = order.accept();03Order rejectedOrder = order.reject();04Order cancelledOrder = order.cancel();05Order completedOrder = order.complete();

 

하지만 이 방식이 왜 좋은지 깊게 이해하기보다, 강의나 책에서 본 구조를 따라 하는 것에 가까웠다.

“Service는 비즈니스 흐름을 관리 하는거다.”
“객체가 스스로 상태를 바꾸게 해야 한다.”
“도메인 메서드를 만들어야 한다.”

 

이런 말들은 알고 있었지만, 왜 그래야 하는지 내 언어로 설명하기는 어려웠다.

 

공부하면서 객체 안에 메서드를 두는 것이 단순히 코드를 예쁘게 정리하기 위한 것이 아니라는 걸 느낀다. 객체가 자기 상태를 직접 다루게 하면, 외부 객체가 그 내부 구조를 자세히 알 필요가 줄어든다.

 

Copied
01order.cancle();

 

이 한 줄에는 많은 의미를 담을 수 있다.

  • 이미 배달된 주문은 취소할 수 없다.

  • 결제 완료 상태라면 환불 처리가 필요하다.

  • 취소 시각을 기록해야 한다.

  • 취소 후 상태를 변경해야 한다.

 

이런 규칙들이 Service에 흩어져 있으면, 주문 취소 정책이 바뀔 때 여러 코드를 같이 확인해야 한다.

 

반대로 Order가 취소 가능 여부와 상태 변경 책임을 갖고 있다면, 주문 취소와 관련된 규칙을 한곳에서 다룰 수 있다.

 

예전에는 이런 구조를 “객체지향스럽게 보이는 코드” 정도로 이해했다.

하지만 지금은 객체에게 행동을 맡긴다는 것은 단순히 메서드를 클래스 안에 넣는 것이 아니라, 변경이 생겼을 때 그 변경을 감당할 책임을 어디에 둘지 정하는 일에 가깝다고 생각한다.

 

 

데이터보다 행동을 먼저 생각하기

가장 크게 와닿았던 부분은 행동을 먼저 생각하라는 말이었다.

 

그동안은 객체를 만들 때 자연스럽게 필드부터 떠올렸다.
하지만 객체가 어떤 데이터를 가지는지는, 결국 그 객체가 어떤 행동을 해야 하는지에 따라 결정된다.

 

객체가 먼저 해야 할 일이 정해지고, 그 행동을 수행하기 위해 필요한 상태가 생긴다.

 

예를 들어 어떤 객체가 단순히 데이터를 들고만 있다면 그 객체는 스스로 판단하거나 행동하지 못한다. 결국 다른 객체가 그 데이터를 꺼내서 판단하게 되고, 로직은 점점 밖으로 새어나간다.

 

반대로 객체가 자신의 행동을 가지고 있으면, 외부에서는 세부 상태를 직접 알 필요가 줄어든다.
이 과정에서 캡슐화도 자연스럽게 따라온다.

 

상태를 숨기기 위해 억지로 캡슐화하는 것이 아니라, 객체가 자기 책임을 수행하도록 만들다 보면 외부에 드러낼 것이 줄어든다.

 

 

정리

예전에는 객체지향을 데이터와 메서드를 하나의 클래스로 묶는 것 정도로 이해했다.

 

중요한 건 클래스를 많이 나누는 것이 아니다.
객체들이 어떤 책임을 가지고, 어떤 메시지를 주고받으며, 변경이 생겼을 때 어디까지 영향을 받는지를 고민하는 것이다.

 

좋은 설계는 거창한 구조를 만드는 일이 아니라, 변경을 조금 덜 두렵게 만드는 일에 가깝다.

앞으로 코드를 작성할 때도 단순히 “어떻게 구현할까?”에서 끝내지 않고, “이 책임은 어디에 두는 게 좋을까?”, “변경이 생기면 어디가 흔들릴까?”를 같이 생각해보려고 한다.

 

  1. 좋은 설계란 무엇일까?
  2. 책을 다시 보게 된 이유
  3. 객체보다 협력이 먼저였다
  4. 이유의 부재
  5. 데이터보다 행동을 먼저 생각하기
  6. 정리

'개발' 카테고리의 다른 글

  • SQL Server Logical Reads 기반 조회 쿼리 개선기→
  • CQRS 작게 시작하기→
목록으로