프로그래밍 언어 활용/SPRING

[SPRING] 2. 객체 지향 설계와 스프링

프린이8549 2024. 8. 30. 16:16

 

https://yangpro8549.tistory.com/58

0. 들어가기에 앞서

 

우리는 앞서 스프링의 핵심은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 것이라고 정리했다.

 

 

그렇다면 좋은 객체 지향 프로그래밍이란 것은 무엇일까?

 

본 고에서는 위 물음에 답하기 위해 객체 지향 프로그래밍의 4가지 특징과 객체 지향 설계를 위한 5가지 원칙(SOILD)를 살펴보고자 한다.

 

그리고 SOILD의 관점에서 스프링이 왜 만들어졌는가?에 대해 답해보고자 한다.

 

 

1. 객체 지향 프로그래밍이란?

객체 지향 프로그래밍의 4가지 특징에 대해 살펴보기 전에

 

객체 지향 프로그래밍이 무엇인지에 대해 간단하게 설명하자면 아래와 같다.

1.1. 객체 지향 프로그래밍(OOP)

 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다.

 

 각각의 객체메시지를 주고 받고, 데이터를 처리할 수 있다(협력).

 

 또한 객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.

 

이런 객체 지향 프로그래밍에는 대표적인 특징 4가지가 있다.

 

1.2. 객체 지향 프로그래밍의 특징

OOP의 4가지 특징은 아래와 같다.

  • 추상화
  • 캡슐화
  • 상속
  • 다형성

위 특징 중 OOP의 유연하고 변경이 용이한 개발을 담당하는 특징이 바로 "다형성"이라고 할 수 있다.

 

그리고 스프링을 통한 객체 지향 설계에 가장 중요한 역할을 수행하는 것이 바로 "다형성"이다.

 

그렇다면 다형성이란 무엇인가?

 

 

2. 다형성

다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다.

 

그리고 이를 쉽게 설명하는 단어들이 있는데 바로 "역할"과 "구현"이다.

 

  • 역할 = 인터페이스
  • 구현 = 인터페이스를 구현한 클래스, 구현 객체

위 관점을 로미오와 줄리엣 공연 무대로 비유하자면, 장동건과 원빈이 로미오 역할을 수행하고 김태희와 송혜고가 줄리엣 역할을 수행한다고 할 때, 러프하게 말하자면 장동건이 로미오를 하든, 원빈이 로미오를 하든 로미오와 줄리엣 공연의 관람객은 공연 관람에 큰 영향을 받지 않는다. 그러나 로미오 역할이 없어진다면 그 자체로 로미오와 줄리엣 공연은 성사될 수 없다.

 

즉, 중요한 것은 역할(인터페이스)이지 배우(구현 객체)가 아니라는 것이다.

 

자바에서는 인터페이스와 오버라이딩을 통해 다형성을 적용할 수 있다.

 

예컨대, 아래 예시처럼  구현 객체를 실행 시점에 상황에 따라 유연하게 변경할 수 있는 것이다.

 

따라서 다형성에 충실한 객체 지향 설계가 이루어졌다면 클라이언트에 영향을 주지 않고 서버의 구현 기능을 유연하게 변경할 수 있게 될 것이다. (인터페이스를 안정적이으로 설계했다는 전제 하에)

 

...

 

과연 그럴까??

 

정말 다형성만으로 "유연하고 변경이 용이"하게 개발할 수 있을까?

 

답은 "아니다"이다.

 

왜 다형성만으로 쉽게 부품을 갈아 끼우듯 개발할 수 없는지 증명하기 위해 우리는 먼저 좋은 객체 지향 설계의 5가지 원칙을 탐구해보고자 한다.

 

 

3. 좋은 객체 지향 설계의 5가지 원칙

<<Clean code(2012)>>로 유명한 로버트 마틴(Robert C. Martin)은 아래와 같이 좋은 객체 지향 설계를 위한 5가지 원칙을 제시한 바 있다.

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

각각의 원칙들을 살펴보자면,

 

3.1. SRP : 단일 책임 원칙

SRP는 한 마디로 하나의 클래스는 하나의 책임만 가져야 한다는 것이다.

 

그러나 책임이라는 단어가 의미하는 바는 문맥과 상황에 따라 모호할 수 있다.

 

SRP 에서 중요한 기준은 변경이라고 할 수 있다.(ex. UI 변경, 객체의 생성과 사용을 분리하는 등)

 

변경이 있을 때 그 파급 효과가 적다면 SRP를 충실하게 이행한 것이라고 볼 수 있다.

 

3.2. OCP : 개방 폐쇄 원칙

 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다는 것이다.

 

모듈의 소스 코드를 수정하지 않아도 모듈의 기능을 확장하거나 변경할 수 있다는 것이다.

 

그리고 객체 지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성을 위해서 OCP는 필수적으로 지켜져야할 핵심 원칙이라고 할 수 있다.

 

3.3. LSP : 리스코프 치환 원칙

프로그램의 객체는 프로그램의 정확성을 해치지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함을 의미한다.

 

단순히 컴파일에 성공하는 것을 넘어서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것으로써, 인터페이스를 구현한 구현체를 믿고 사용하기 위해서는 LSP가 반드시 지켜져야 한다. 

 

즉, LSP는 다형성을 지원하기 위한 원칙이라고 할 수 있다.

 

예컨대 자동차 인터페이스를 예로 들면 기어는 D일 때 엑셀은 앞으로 가라는 기능을 수행해야 한다. 뒤로 갈 경우 LSP 위반이라고 할 수 있다. 

 

3.4. ISP : 인터페이스 분리 원칙

특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 의미이다. 

 

이를 통해 인터페이스는 명확해지고 대체 가능성이 높아진다.

 

3.5. DIP : 의존관계 역전 원칙

프로그램은 구체화가 아니라 추상화에 의존해야함을 의미한다.

 

쉽게 말해, 구현 클래스에 의존하지 말고 인터페이스에 의존해야 한다는 의미이다.

 

이는 전술한 역할과 구현의 관점에서 역할에 의존하게 해야한다는 것과 같다.

 

로미오와 줄리엣 공연을 하는데 공연이 로미오 역할이 아니라 장동건 배우에 의존하게 된다면(대본이 없이 장동건 배우의 즉흥으로 상연됐다면), 유사시에 원빈 배우가 로미오 역할을 하게 된다면 공연은 진행될 수 없을 것이다.

 

즉, 구현체에 의존하게 되면 변경이 아주 어려워지는 것이다. 따라서 변경이 용이해지기 위해서는 필수적으로 인터페이스에 의존해야 한다.

 

3.6. 다형성 만으로 좋은 객체 지향 설계는 왜 안되는가?

이상의 5가지 원칙의 내용만 얼핏 보기에는 왜 다형성만으로 쉽게 부품을 갈아 끼우듯 개발할 수 없는지 이해하기 어려울 수 있다.

 

그렇다면 5가지 원칙에서 범위를 추려서 OCP, DIP를 중심으로 다형성에 대해서 살펴보자

 

3.6.1. OCP

 

 앞서 잠깐 제시했던 예시를 다시 꺼내어, Member 객체를 메모리에 저장해야할지, db에 저장해야할지 확정되지 않은 상태에서 그때 그때 클라이언트의 요구에 따라 저장 방식을 적용해야 한다고 가정해보자.

그리하여 요구에 따라 메모리에 저장했으나 

 

갑작스레 이후 DB에 저장하도록 요구사항이 변경된다면 아래와 같이 코드의 변경이 수반될 것이다.

 

위 사례는 다형성을 충실히 이행하고 있다.

 

그러나 구현 객체를 변경하기 위해 JdbcMemberRepository(); 로 코드가 변경됨에 따라 OCP 원칙을 위배하게 되었다.

 

즉, 다형성을 활용한 객체 지향 설계가 OCP 원칙 준수를 보장하지 않는 것이다.

 

3.6.2. DIP

 

그리고 MemberService는 인터페이스에 의존하지만 동시에 해당 클라이언트가  JdbcMemberRepository를 직접 선택함으로써 구현 클래스를 의존하고 있기 때문에 DIP도 위배됨에 따라

 

다형성을 활용한 객체 지향 설계가 DIP 원칙 준수를 보장하지 않음을 알 수 있다.

 

3.7. 소결론

상기한 내용을 통해 유연하고 변경이 용이한 객체 지향 프로그래밍의 핵심은 다형성이지만,

 

다형성 만으로는 OCP, DIP를 준수할 수 없음을 알 수 있었다.

 

그렇다면 OCP, DIP를 준수하기 위해서 다형성에 필요한 플러스 알파는 무엇일까?

 

 

4. 결론 : 객체 지향 설계와 스프링

 결론부터 말하자면 OCP, DIP를 준수하기 위해서는 다형성에 추가적으로 DI(Dependency Injection; 의존성 주입) 과정이 필요했다.

 

개발자들은 좋은 객체 지향 애플리케이션을 개발하기 위해서 OCP, DIP 원칙을 지키면서 개발을 하는 과정에서 오히려 배보다 배꼽이 커지는 상황을 겪게 되었고, DI 컨테이너를 만드는 등의 작업이 모여 종국에는 프레임워크를 만들게 되었으니, 그것이 바로 SPRING인 것이다.

 

스프링은 DI 컨테이너를 제공함으로써 클라이언트 코드의 변경 없이 기능을 확장할 수 있게 해줌으로써 좋은 객체 지향 애플리케이션을 개발하기 위함이라는 그 목적을 충실히 이행하게 된 것이다.

 

스프링의 핵심이라고 할 수 있는 DI에 대해서는 다음 글을 통해 소개해보고자 한다.

 

 

<참고문헌>

인프런 김영한님의 강의 <스프링 핵심 원리 - 기본편>