객체지향 설계 - SOLID 원칙을 실무 코드에 적용하는 구체적인 예시

목차

  1. 서론: 기능 구현 그 너머, 기술 부채와의 싸움
  2. SRP (단일 책임 원칙): 클래스의 역할을 한 문장으로 정의하기
  3. OCP (개방-폐쇄 원칙): if-else 지옥에서 탈출하는 법
  4. LSP와 ISP: 거대한 인터페이스를 쪼개면 생기는 변화
  5. DIP (의존 역전 원칙): 테스트가 즐거워지는 마법
  6. 마치며: 완벽함보다는 '방향성'이 중요한 이유
  7. Q&A: SOLID 원칙, 이것이 궁금해요!



OOP-객체지형-공부-모습


1. 서론: 기능 구현 그 너머, 기술 부채와의 싸움

개발을 처음 시작했을 때는 그저 기능이 돌아가기만 하면 된다고 생각했어요. 그런데 코드가 쌓이고 서비스가 커지다 보니, 어제 짠 코드를 오늘 수정하는 게 두려워지는 순간이 오더라고요.

이른바 기술 부채가 쌓인다는 것을 몸소 체험하게 된 거죠. 그때 가장 먼저 찾아보게 된 것이 바로 객체지향 설계의 기본인 SOLID 원칙이었어요.

오늘은 제가 실무 코드를 작성하면서 SOLID 원칙을 어떻게 이해하고 적용하려 노력했는지, 그 경험을 나눠보려고 합니다.


2. SRP (단일 책임 원칙): 클래스의 역할을 한 문장으로 정의하기

단일 책임 원칙, 즉 SRP는 하나의 클래스는 하나의 기능만 가져야 한다는 원칙이에요. 처음에는 이게 참 당연한 소리처럼 들렸어요.

하지만 막상 코드를 짜다 보면, 이 클래스 저 클래스에서 호출하는 기능을 한곳에 몰아넣는 편이 개발 속도는 훨씬 빠르거든요. 저도 예전에는 결제 처리와 로그 저장, 이메일 발송까지 한 클래스에 다 넣은 적이 있었어요.

결국 유지보수 과정에서 큰 코다치게 되었죠. 특정 로직을 수정하면 연관 없는 기능까지 에러가 나는 상황이 반복되더라고요. 요즘은 무조건 클래스의 역할이 무엇인지 한 문장으로 정의해보려고 노력해요.

클래스 이름 옆에 어떤 일을 하는지 주석을 달았을 때, 그리고라는 접속사가 들어간다면 그건 이미 책임이 나뉘어야 한다는 신호더라고요.


3. OCP (개방-폐쇄 원칙): if-else 지옥에서 탈출하는 법

확장에는 열려 있고 수정에는 닫혀 있어야 한다는 OCP 원칙은 제가 가장 좋아하는 원칙이에요. 실무에서 새로운 기능을 추가할 때 기존 코드를 건드리는 건 정말 위험한 일이거든요.

저는 이 원칙을 지키기 위해 인터페이스를 적극적으로 활용하기 시작했어요. 예를 들어, 다양한 결제 수단을 처리하는 로직을 짤 때 처음에는 if-else 문을 떡칠했었어요.

그런데 이제는 결제 방식을 인터페이스로 정의하고, 각 결제 수단별로 클래스를 따로 구현해요. 이렇게 하면 새로운 결제 수단이 추가되어도 기존 결제 로직을 건드릴 필요가 없죠. 제가 짠 코드지만 나중에 수정할 때 정말 마음이 편해지더라고요.


4. LSP와 ISP: 거대한 인터페이스를 쪼개면 생기는 변화

리스코프 치환 원칙과 인터페이스 분리 원칙은 사실 인터페이스를 얼마나 잘 설계하느냐의 문제와 맞닿아 있어요. 예전에는 모든 기능을 다 담은 거대한 인터페이스를 하나 만들어두고, 필요할 때마다 가져다 썼거든요.

그런데 이 방식은 사용하지 않는 메서드까지 억지로 구현해야 하는 불필요함을 만들더라고요. 그래서 요즘은 필요한 기능만 골라서 인터페이스를 정의하려고 해요.

ISP를 적용해서 인터페이스를 잘게 쪼개다 보니, 코드가 더 명확해지고 객체 간의 결합도도 확실히 낮아지더군요. 작은 차이지만 이런 디테일이 나중에 엄청난 생산성 차이를 만들어내는 것 같아요.


5. DIP (의존 역전 원칙): 테스트가 즐거워지는 마법

마지막으로 의존 역전 원칙, DIP는 추상화에 의존하라는 원칙이죠. 쉽게 말해 구체적인 클래스가 아니라 인터페이스나 추상 클래스를 바라보게 하라는 거예요.

처음에는 의존성 주입(DI) 개념이 참 낯설었는데, 막상 적용해보니 테스트 코드를 작성할 때 신세계가 열리더라고요. 실제 DB나 외부 API 없이도 Mock 객체를 만들어 기능을 검증할 수 있게 되니까요.


6. 마치며: 완벽함보다는 '방향성'이 중요한 이유

물론 모든 코드에 SOLID 원칙을 완벽하게 적용하는 것은 현실적으로 어려워요. 때로는 빠른 배포를 위해 타협해야 하는 순간도 분명히 있죠. 하지만 중요한 건 이런 원칙들을 기준으로 삼고 코드를 바라보는 시각을 가지는 것이라고 생각해요.

저도 아직 갈 길이 먼 주니어 개발자이지만, SOLID 원칙을 고민하며 짠 코드들이 확실히 버그도 적고 나중에 다시 볼 때도 훨씬 읽기 편하더라고요. 비슷한 고민을 하고 계신 분들이 있다면, 거창한 리팩토링보다는 오늘 작성하는 클래스 하나에 이 원칙들을 살짝만 녹여보는 건 어떨까요? 사소한 습관이 모여서 정말 좋은 개발자가 되는 길이라고 믿습니다.


Q&A: SOLID 원칙, 이것이 궁금해요!

Q1. 모든 클래스를 무조건 SOLID 원칙에 맞춰 쪼개야 할까요?
A. 아닙니다! 모든 코드에 완벽하게 적용하려다 보면 오히려 설계가 복잡해지는 '오버엔지니어링'에 빠질 수 있습니다. 처음부터 너무 욕심내기보다, 수정할 때마다 반복해서 고통받는 로직이나 확장 가능성이 큰 기능부터 조금씩 원칙을 적용해 보세요.

Q2. 인터페이스를 너무 잘게 쪼개면 관리가 힘들지 않나요?
A. 적절한 균형이 필요합니다. 다만, 사용하지 않는 메서드까지 억지로 구현해야 하는 '거대한 인터페이스'보다는, 필요한 기능별로 명확히 분리된 인터페이스가 장기적으로는 훨씬 관리하기 쉽고 테스트 코드 작성도 용이합니다.

Q3. 테스트 코드를 잘 작성하려면 DIP(의존 역전 원칙)가 왜 필수인가요?
A. DIP를 적용해 구체적인 클래스 대신 인터페이스에 의존하게 만들면, 실제 DB나 외부 API 연결 없이도 '가짜(Mock) 객체'를 쉽게 주입할 수 있습니다. 덕분에 서버를 띄우지 않아도 로직 단위의 빠른 테스트가 가능해지죠.

Q4. 주니어 개발자가 이 원칙들을 익히기 가장 좋은 실천법은 무엇인가요?
A. "클래스 이름 옆에 '그리고'라는 접속사가 들어가는가?"를 자문해보는 습관을 추천합니다. 클래스의 이름을 명확히 짓는 과정만으로도 SRP를 실천하는 첫걸음을 떼실 수 있을 거예요.

Post a Comment

다음 이전