SOLID

god1hyuk·2022년 11월 9일
0

Spring / Spring Boot

목록 보기
4/4
post-thumbnail

해당 포스트는 <김영한>님의 인프런 강의 '스프링 핵심 원리 - 기본편'을 토대로 공부한 내용을 정리하였습니다.
(!) 강의 자료에서 캡쳐한 이미지는 출처를 기재하였습니다.

이번 포스트에서는 객체 지향 프로그래밍의 중요한 개념인 "SOLID 원칙"에 대해 공부한 내용을 정리해보았다.

SOLID?

‘클린코드’의 저자 로버트 마틴(Robert Martin)이 정의한 좋은 객체 지향 설계의 5가지 원칙

이미지 출처 : https://search.shopping.naver.com/book


1. SRP (Single Responsibility Principle, 단일 책임 원칙)

  • 하나의 클래스는 하나의 책임(역할)만 가져야 함
  • 클래스 내부에서 변경이 있을 시 다른 부분에 파급 효과가 적다면 단일 책임 원칙을 잘 따른 것 (UI 변경, 객체의 생성과 사용을 분리, …)

2. OCP (Open/Closed Principle, 개방 폐쇄 원칙)

  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 함
  • 확장 시에는 인터페이스를 구현한 새로운 클래스를 생성하여 새로운 기능 구현 (다형성의 활용)
    • 그러나 구현 객체를 변경하기 위해 클라이언트 코드를 변경해야 함 (OCP 원칙 위반)
  • 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요 (Spring Container의 역할)

3. LSP (Liskov Substitution Principle, 리스코프 치환 원칙)

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 변경이 가능해야 함
  • 다형성에서 인터페이스의 하위 클래스(구현체)는 인터페이스의 규약을 모두 지켜야 함

4. ISP (Interface Segregation Principle, 인터페이스 분리 원칙)

  • 인터페이스 설계 시 기능 별로 분리하여 설계하는 것
  • 인터페이스의 역할이 분명해지고 대체 가능성이 높아짐

5. DIP (Dependency Inversion Principle, 의존관계 역전 원칙)

  • 구체화에 의존하지 않고 추상화에 의존하는 것 (구현체가 아닌 인터페이스에 의존)
  • 클라이언트가 역할에 의존하지 않고 구현체에 의존하게 되면 변경이 어려움
    • 그러나 service에서 인터페이스를 의존하지만 구현체인 repository도 함께 의존하고 있음 (DIP 원칙 위반)

(!) 문제점

객체 지향의 핵심은 다형성이지만 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 변경해야 상황이 발생. 즉, 다형성 만으로는 OCP, DIP를 지킬 수 없음


객체 지향 설계와 Spring

앞서 다뤘던 SOLID 원칙에서 다형성의 한계로 인한 OCP, DIP 원칙이 위반되는 문제점이 있었다. 바로 이 문제점의 해결책은 Spring이다.

Spring에서는 DI(Dependency Injection, 의존성 또는 의존관계 주입)와 DI Container를 제공한다.

이 기술로 다형성과 OCP, DIP를 가능케 하여 클라이언트 코드 변경 없이 기능을 확장할 수 있다.

옛날 어떤 개발자가 좋은 객체 지향 개발을 하기 위해 OCP, DIP 원칙을 지키며 개발을 해보니 해야 할 일이 너무 많았다. 그래서 spring 프레임워크가 만들어 졌다고 한다. 더 정확하게는 DI Container 라는 개념이 탄생하게 된 것이다.


정리

정리하자면 가장 중요한 핵심은 “모든 설계에 역할과 구현을 분리”. 이상적으로는 모든 설계에 인터페이스를 부여를 하는 것이다. 하지만 모든 설계에 인터페이스를 도입(무분별하게 남발)하게 되면 추상화라는 비용이 발생한다. 직접 구현한 개발자가 아닌 다른 개발자는 추상화된 구현 클래스에 대해 알기가 어렵다. 그래서 코드를 일일이 타고 들어가 파악을 해야 하는 비용이 발생한다는 의미이다. 코드가 추상화 됨으로써 장점만 있는 것이 아니라 단점도 존재한다.

강의에서 김영한님은 “장점이 단점을 넘어설 때 선택해야 한다.” 라는 명언을 남기셨다.

기능을 확장할 가능성이 없다면 구체 클래스를 바로 직접 사용하고, 이후 확장이 필요한 상황이 발생한다면 그 때 리팩토링 하여 인터페이스를 도입하는 것도 방법이다.

0개의 댓글