본문 바로가기

백엔드 잡학사전

[스프링 핵심] 스프링과 객체 지향 프로그래밍

IoC,

DI,

객체 지향 프로그래밍 SOLID,

다형성 polymorphism,

컨테이너,

SRP,

OCP/ DIP,,,,

 

이런 개념들을 모두 다뤄볼 예정이다.

 

스프링의 핵심가치는, "객체 지향 프로그래밍"이다

옛날에는 EJB라는 기술이 있었다. 이게 표준이었다.

그런데 이게, 제공하는 기능은 많은데 한 번 쓰기가 너무 복잡하고 느리고 힘들었다.

 

소위 "EJB 지옥".

EJB에 지친 우리 선배 개발자들은 하나 둘 반기를 들기 시작했고, 차라리 기능이 적더라도 쓰기 간편하고 빠른 걸 만들고 싶다는 열망이 뻗치기 시작했다.

 

이 흐름의 가장 선봉장은 두명이 있었는데, 그게 오픈소스로 만들어졌고, 결국 각각 Spring과 Hibernate로 발전했다. 이후 JPA가 만들어졌다. 특이하게도 인터페이스 -> 구현체가 아니라, 구현체 -> 인터페이스 방향으로 개발이 되었다.

ORM 시장은 JPA - 이건 인터페이스라 이 인터페이스를 바탕으로 만든 구현체로는 Hibernate 등이 있다.

 

EJB라는 극한의 추운 겨울에서 벗어나 서버 개발자에게 따뜻한 봄이 찾아왔다는 의미에서 프로그램 명을 spring으로 명명.

 

스프링이란?

 

스프링은 EJB와 뭐가 다를까? - 핵심 컨셉

스프링은 JAVA의 프레임워크잖아, JAVA는 가장 대표적인 객체 지향 언어야. 즉, 스프링은 개발자로 하여금 객체 지향 언어를 잘할 수 있게 도와주는 도구라는 점이 가장 핵심적이다. 그 방법론은 스프링이 DI가 가능하게끔 한다는 점이다.

 

백엔드 개발자 공고를 보면, 내 입장에서는 다른 개념보다 특별할 것 없어 보이고 그리 대단한 내용같아 보이지도 않는 OOP에 대한 이해를 자-알 하고 있는 개발자를 원한다고 한다. 그 이유가 여기 있는 거다. 자바와 스프링을 쓰기 때문에, 그 가장 핵심 가치가 객체 지향 프로그래밍이기 때문에 자바와 스프링을 메인 기술 스택으로 하는 한국의 서버 개발자들은 이 중요한 가치를 잘 알고 있어야 하는 것이다.

 

객체 지향 프로그래밍 때려잡기

좋은 객체 지향 프로그래밍이란? 프로그래밍을 '객체'들의 모임으로 파악하는 것.

각각의 객체가 주체가 되어 메시지를 주고받고, 데이터를 처리하는 방식이다.

마치 레고블럭 조립하는 것처럼, 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.

 

그런데 이제, 레고블럭 조립한다는 측면에만 주목하지 말고, 인터페이스와 구현체에 대해 주목해야 한다!!!!!!

 

추상화, 캡슐화, 상속, 다형성 <- 이런 특징들이 있음.

 

* 다형성 polymorphism: 역할(인터페이스)과 구현(구현체)으로 세상을 바라보기.

인터페이스는 로미오, 줄리엣. 그 역을 수행하는 진짜 사람을 구현체라고 생각하면 그만.

로미오 역할을 하는 사람은, 줄리엣 역할을 하는 사람이 누구인지는 관계없이, 줄리엣이라는 역할을 염두에 두고 공연을 준비하고 진행하면 된다. 김태희 송혜교보다는 정유미 고윤정이라고 하면, 상대가 정유미였다가 고윤정으로 바뀐 경우, 고윤정에 맞춰서 뭔가 새로 준비할 게 전혀 없다는 것이다. => 그래서 유연하고 변경 용이하다는 것이다.

 

다르게 표현하면, 객체지향 프로그래밍은 결국 역할과 구현을 분리하는 것이다.

 

장점:

  1. 클라이언트는 대상의 역할 (인터페이스) 만 알고, 내부 구조를 몰라도 된다.
  2. 내부 구조 뿐 아니라, 대상 자체를 변경해도 영향을 받지 않는다.

구현체보다는, 인터페이스가 더 중요하다는 개념이다. 구현체는 대체 가능하지만, 인터페이스는 대체 불가능함.

 

자바에서의 다형성

 

Overriding. 인터페이스는 그대로 있고, 그걸 구현한 서로 다른 객체를 만들면, 그에 맞는 녀석이 불러진다는 것.

                     부모는 마음이 넓어서 여러 자식들 아무나 와도 다 품어준다.

                     클라이언트는 서버의 "인터페이스"와 만나고,

                     그 인터페이스에서 파생된 어떤 형태의 서버가 들어가 그 자리를 대체하든 상관 없다는 것.

 

좋은 점이, 클라이언트와 서버의 관계라고 할 때, 서버를 인터페이스-구현체로 생각하면 관계대상인 클라이언트, 서버 인터페이스, 서버 구현체 이렇게 세개의 주체가 있는데 그 중 클라이언트와 인터페이스는 거의 그대로 두고 수정이 필요한 경우 거의 서버 구현체만 수정을 하면 된다는 점이다. 즉 객체지향의 프로그래밍에서는, 클라이언트를 최대한 보호하고 서버 인터페이스를 잘 짜는 것을 중요한 미덕으로 생각한다.

 

다형성만큼 중요한, SOLID 원칙

 

▶ SRP: 한 클래스는 하나의 책임만 가져야 한다. 변경이 있을 때 파급 효과가 크지 않도록 설계해야 한다는 것.

            OOP가 수정에 유리하다는 장점인데, 수정을 하기 어렵다고 하면 OOP를 전혀 못 살리는 거니까.

 

 OCP: 확장에는 열려있으나 변경에는 닫혀있어야 한다. 클라이언트를 수정하면 안 된다는 거다.

보면, Service가 클라이언트인데 클라이언트의 코드를 수정하잖아? 그러니까 OCP의 닫혀야한다는 원칙을 위반한 것이다. 그렇기 때문에 이때는, 이걸 조정해주는 설정자가 필요하다. DI, IoC 등의 개념이 이때 들어가는데, 어쨌든 스프링이 이거를 진행해준대.

 

 LSP: 프로그램의 객체는 하위 타입의 인스턴스로 바꿀 수 있어야 한다. 그러니까 어느 다른 객체로 치환 시에는 정확성을 깨뜨리면 안 된다는 것. 예를 들어 액셀을 대체하는 녀석은 느리더라도 앞으로 가야하지, 뒤로 가는 기능은 LSP를 위반한다는 것이다.

 

ISP: 인터페이스가 너무 크면 안된다는 것. SRP가 클래스에 대한 이야기였다면 ISP는 인터페이스에 대한 이야기.

 

DIP: 의존관계 역전 원칙, 구현체가 아닌 인터페이스에 의존하라는 것. 이 의존관계를 역전하지 말라는 말인 듯하다.

         => MemberService는, MemberRepository에만 의존하도록 설계를 해야 한다는 말! 앞으로 다룬다고 한다.

 

스프링에서의 객체 지향은?

아까 언급했지만, 스프링에서 객체 지향을 가능하게 하는 기술적 기반은 Dependency Injection이다.

한마디로, 순수 JAVA로 OOP를 가능하게 하려면 결국에는 이것저것 살을 잔뜩 붙여야 하고, 그 결과물은 결국 현재 스프링이 된다. 그러니까 객체지향이 가능하게끔 하는 지원을 스프링이 해준다고 그냥 생각하면 편하다.

 

그러면 인터페이스가 중요한 거고, 인터페이스에 의존해야 한다면, 뭘 만들 때 무조건 구현체가 아닌 인터페이스를 만드는 게 좋겠네? 그게 아니다. 왜냐면 인터페이스를 만들면 추상화라는 비용이 발생하기 때문이다. 그렇기 때문에, 인터페이스가 필요한 상황 - a.k.a. 기능을 확장할 가능성이 있는 상황 - 이 아니라면 구체 클래스를 직접 사용하고, 나중에 리팩터링해서 인터페이스를 도입하는 것이 현실적인 방법이다.