카테고리 없음

객체 지향 설계 5원칙(SOLID) 자연스럽게 적용한 과정

개발 N년차 2022. 2. 21. 22:59
반응형

- 객체 지향 설계 5원칙 - SOLID -

 

아마 객체지향에 대해서 공부를 하다 보면 자주 듣게 되는 용어 중 하나가 바로 SOLID 원칙일 것이다.

 

처음에는 이 원칙을 잘 모른 채 코드를 작성했지만 리팩토링을 통해 개선해 나가는 과정에서 나도 모르게 이 원칙들을 적용하고 있었고 나중에 공부해 보니 그게 바로 SOLID 원칙이었다는 걸 알게 되었다.

 

SOLID 란?

객체 지향을 잘 사용 할 수 있게 도와주는 원칙으로 아래의 5개의 원칙들을 말한다.

  • SRP (Single Responsibility Principle) : 단일 책임 원칙
  • OCP (Open-Closed Principle) : 개방 폐쇄 원칙
  • LSP (Liskov Substitution Principle) : 리스코프 치환 원칙
  • ISP (Interface Segragation Principle) : 인터페이스 분리 원칙
  • DIP (Dependency Inversion Principle) : 의존관계 역전 원칙

5개의 원칙을 지키게 되면 재 사용성이 좋아지며 유지보수 하기 좋은 코드가 나오게 된다.

 

 

1. SRP - 단일 책임 원칙

한 클래스는 하나의 변경 이유만 가져야 한다는 뜻이다.

 

클래스에 책임이 여러 개일 경우 하나의 변경이 다른 기능에 영향을 줄 수 있어 유지보수가 어려워진다.

 

기존에 구현된 이용권 관리 서비스를 개선하는 과정에서 단일 책임 원칙(SRP)을 적용해서 기존에는 VoucherService라는 서비스 하나에 이용권 발급, 상태 변경(만료, 중지 등) 등의 책임이 모두 들어 있었다.

 

이로 인해 클래스가 점점 비대해지고 관리가 어려워졌는데 이를 다음과 같이 책임 단위로 분리했다.

  • VoucherIssueService: 이용권을 발급하는 책임만 담당
  • VoucherLifecycleService: 이용권의 라이프사이클(만료, 중지 등)을 관리하는 책임만 담당

이러한 구조 변경을 통해 각 서비스가 명확한 책임을 가지게 되었고 변경이 발생해도 영향 범위를 최소화할 수 있게 되었다.

 

2. OCP - 개방-폐쇄 원칙

확장에는 열려 있고, 변경에는 닫혀 있어야 한다.

 

나의 경우 상품 할인 정책을 구현할 때 이 원칙을 적용했다. 할인 정책에 대해서 명확하게 정의가 되지 않은 상태에서 다양한 옵션의  할인 정책을 적용하기 위해 전략 패턴을 사용해서 구현을 했었다.

그래서 새로운 할인 정책이 생기더라도 기존 코드를 건드리지 않고 클래스만 추가해서 확장할 수 있게 되었다.

 

 

3. LSP - 리스코프 치환 원칙

자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다.

 

이 부분은 어디에 딱 적용했다고 말하기가 애매한 부분인 것 같다. 

 

4. ISP - 인터페이스 분리 원칙

클라이언트는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다.

 

처음에 결제 수단 관련 인터페이스를 설계하면서 pay()와 refund() 메서드를 포함한 하나의 PaymentMethod로 정의했었다.

interface PaymentMethod {
    void pay(int amount);
    void refund(int amount);
}

 

이 구조에서는 처음에는 카드 결제만 지원했기에 문제가 없었는데 B2B 고객들이 들어오면서 세금계산서 발급 요청의 건이 생기면서 추가 개발을 하려니 세금계산서 결제는 정책상 환불이 불가능함에도 불구하고 refund() 메서드를 구현해야 해서 불필요한 코드가 생겼다.

 

 

그래서 인터페이스를 다음과 같은 방식으로 분리했다.

interface Payable {
    void pay(int amount);
}

interface Refundable {
    void refund(int amount);
}

 

이렇게 하니 각 결제 수단이 필요한 기능만 선택적으로 구현할 수 있게 되어 구조가 훨씬 깔끔해졌다.

 

5. DIP - 의존관계 역전 원칙

상위 모듈은 하위 모듈에 의존하면 안 된다.
모두 추상화(인터페이스)에 의존해야 한다.

 

이건 스프링을 사용하고 있어서 그런가 자연스럽게 DIP를 구현할 수 있다. 

@Service, @Repository 등등 그냥 자연스럽게 사용하고 있다.

 

 

블로그 글을 쓰다보니 느껴지는 게 SOLID 원칙은 단순히 외워서 적용할 수 있는 게 아니라 코드를 개선해 가는 과정에서 점차 자연스럽게 체득되는 것 같다.


처음부터 모든 원칙을 완벽하게 지키면서 개발하기는 오히려 비효율적인 부분도 많아서 꼭 지킬 필요는 없다고 생각하지만 서비스가 고도화되면서 이 원칙들이 왜 필요한지 그리고 어떤 문제를 해결하려는 목적인지 이해하고 개발한다면 진짜 좋은 코드가 나오는 것 같다.

반응형