☁️ EJB → Spring → Spring Boot
스프링이란 기존의 EJB
(J2EE)에 의존적인 개발 방식의 불편한 점을 개선한 프레임워크이다.
EJB에 의존적인 개발은 객체지향적인 설계가 힘들고 서버 구축 비용이 비싼데 성능조차 좋지 않은 단점이 있었다. 이러한 단점을 보완하여 스프링 IoC 컨테이너
, DI
(의존성 주입), AOP
등 객체지향의 장점을 살린 기능을 가지는 프레임워크가 스프링
프레임워크이다.
하지만 스프링으로 만들어진 프로젝트도 불편한 점이 여전히 존재했다. 별도의 WAS 서버를 설치하고 배포해야 하는 작업, XML의 사용, 외부 라이브러리들과의 버전 호환성 등의 불편한 점을 개선하여 스프링 프레임워크를 더 쉽고 편리하게 사용할 수 있게 하는 프로젝트가 스프링 부트
이다.
스프링 부트는 아파치 톰캣을 내장하고 있어 별도의 WAS 설치 없이 서버를 구축하고 JAR
파일을 통해 빌드, 배포할 수 있다. 또한 스프링 버전과 라이브러리들의 버전 호환성을 맞추어 한번에 종속성을 추가해주는 starter
기능과 같은 서드 파티 라이브러리 자동 구성 기능등을 제공한다.
정리하자면, 스프링은 EJB 방식에서 벗어난 프레임워크의 코어이고, 스프링 부트는 스프링 코어(IoC/DI, AOP .. )의 기능과 내장 톰캣, 라이브러리 버전 통합등의 기능을 추가하여 스프링의 기능을 더욱 쉽고 편리하게 사용하게 해주는 스프링 기반 프레임워크이다.
☁️ 스프링과 객체지향
내가 생각하는 객체지향의 핵심은 각 객체를 분리하고 추상화시키고 추상화에 의존하여 변경에 유연하고 확장에 용이한 설계를 하는 것이다. 스프링의 핵심 기능들은 이런 객체지향의 핵심인 인터페이스(추상화)와 컴포지션(추상화와의 의존) 그리고 디자인패턴(인터페이스와 컴포지션으로 구현)으로 구현되어 있다. 따라서 스프링을 객체지향 프레임워크라고 부르기도 한다.
스프링의 핵심 기능 중 하나인 DI
로 예시를 들어보겠다.
먼저 아래와 같은 추상화에 의존하고 외부로 부터 의존성을 주입받아 OCP를 지키는 코드가 있다.
public interface Driveable {
public void 운전하기();
}
public class 정현수 {
public void drive(Driveable 운전가능한것) {
운전가능한것.운전하기();
}
}
이 코드에는 OCP를 지키기 위해 필요한 부분이 빠져있다. 의존성을 주입해주는 코드이다. OCP를 지키기 위해서는 외부에서 의존성을 주입해주는 아래와 같은 코드가 필요하다.
Driveable car = new Car();
new 정현수().drive(car);
이렇게 의존성을 주입해주는 것을 DI가 수행해준다.
DI
와 코드의 조합을 통해 다형성과 OCP
, DIP
를 지킬 수 있다. 다형성(추상화)만으로는 OCP와 DIP를 만족할 수 없다. 클라이언트가 추상화를 의존하고 클라이언트의 변경없이 서버의 기능을 변경하거나 갈아 끼우려면 외부에서 구현체를 주입해주어야 한다. 클라이언트에게 서버의 구현체를 주입해주는 행위를 DI라고 한다.
DI는 전략 패턴과 같은 형태를 띈다. 외부(스프링 컨테이너)로부터 의존성(객체)를 주입받고 객체에 따라 상이한 행위를 수행한다. DI는 의존성을 주입하는 클라이언트에 해당하는 스프링 컨테이너, 전략에 해당하는 스프링 빈으로 등록된 객체, 전략을 주입받아 전략에 따른 행위를 수행하는 컨텍스트는 빈 의존성을 주입받는 객체로 이루어진다.
☁️ 역할과 구현의 분리
모든 설계에서 역할
과 구현
을 분리하자. 클라이언트는 역할을 의존하도록 설계하고, 클라이언트의 외부에서 구현을 주입해줌으로써 OCP와 DIP를 만족하게 설계하자. 이러한 설계를 통해 클라이언트는 구현의 변경에 유연하고 확장에는 용이한 구조를 가질 수 있다.
개발에서 모든 상황에서 무조건적으로 좋은 설계란 존재하지 않는다. 모든 것은 트레이드 오프이다. 물론 역할과 구현을 인터페이스와 구체 클래스로 분리함으로써 오는 단점도 존재한다.
먼저 인터페이스를 생성하는데 필요한 비용이 소모된다. 또한 클라이언트가 인터페이스를 의존하게 되면 개발자 입장에서는 클라이언트가 어떤 구체 클래스와 의존하는지 파악하는데 시간이 걸린다. 따라서 추후 기능을 확장해야 할 가능성이 보이지 않는다면 인터페이스를 생성하지 말고 나중에 리팩터링 하자.
☁️ 프레임워크 VS 라이브러리
프레임워크와 라이브러리는 코드 실행의 주도권을 관점으로 나눌 수 있다.
프레임워크의 경우에는 프레임워크가 가지는 제어 흐름,
예를 들어서 스프링이면 애플리케이션이 시작되고, 컴포넌트들을 읽어서 빈으로 등록하고, 의존성을 주입해주고 이러한 일련의 실행 플로우에 맞추어 내가 코드를 작성하면 프레임워크가 가지는 실행 플로우에 따라 실행이 되는 것이고, 라이브러리는 개발자가 작성한 프로그램의 제어 흐름에 외부의 기능을 가져다 쓰는 것이다.
간단하게 내가 작성한 코드에서 사용된다면 라이브러리, 내가 작성한 코드를 실행해주는 것은 프레임워크이다.
- JUnit5는 내가 작성한 테스트 코드를 실행해준다. - 프레임워크
- Assersions는 내가 가져다 코드로 작성한다. - JUnit의 라이브러리
'Spring > 스프링 이해' 카테고리의 다른 글
[Spring] 제어의 역전 (IoC) (0) | 2023.06.06 |
---|