Spring🌱 vs SpringBoot🌼

2020. 6. 8. 21:11개발/Java, Spring


Spring의 개념을 알 수 있다 : Spring이 왜 등장했는가?

SpringBoot의 개념을 알 수 있다 : SpringBoot이 왜 등장했는가?

Spring과 SpringBoot 차이를 알 수 있다 : 어떤 것을 해결하기 위해 등장했으며 Spring Boot의 특징은 무엇인가?

Spring과 Spring Boot 중 어떤 것을 선택해야 할지 알 수 있다 : 현재 추세는 어떻게 되는가? 


1. 🌱Spring

스프링 공식 문서에서는 Spring Framework를 아래와 같이 설명합니다.

The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform.

Spring Framework는 모든 종류의 배포 플랫폼에서 최신 Java 기반 엔터프라이즈 애플리케이션을 위한 포괄적인 프로그래밍 및 구성 모델을 제공합니다.
A key element of Spring is infrastructural support at the application level: Spring focuses on the "plumbing" of enterprise applications so that teams can focus on application-level business logic, without unnecessary ties to specific deployment environments.

Spring의 핵심 요소는 애플리케이션 레벨에서의 인프라 지원입니다. Spring은 특정 배포 환경에 불필요한 연결 없이 팀이 애플리케이션 레벨 비즈니스 로직에 집중할 수 있도록 엔터프라이즈 애플리케이션 plumbing(규약)에 초점을 맞춥니다.

이 말은 곧 Spring Framework는 개발자가 비즈니스 로직에 집중할 수 있도록, Java 기반 엔터프라이즈 애플리케이션을 위한 종합적인 기능들을 지원한다는 뜻입니다.

 

더 쉽게 말해, 우리는 Spring Framework를 이용해서 기업용 애플리케이션을 보다 쉽게 만들 수 있다 는 뜻이죠.

 

여기서 기업용(엔터프라이즈) 애플리케이션이 의미하는 것은 무엇일까요?

대규모 데이터 처리와 트랜잭션이 동시에 여러 사용자로부터 행해지는 매우 큰 규모의 환경을 엔터프라이즈 환경이라 일컫습니다.

 

그럼 왜 스프링을 통해 기업용 애플리케이션을 보다 쉽게 만들 수 있는 걸까요?

스프링의 가장 중요한 특징인 DI, IoC를 통해 재사용 및 유지보수가 용이한 코드를 작성할 수 있고, 확장성을 가진 코드를 설계할 수 있습니다. 또한 스프링이 제공해주는 기능들을 통해 개발자는 비즈니스 로직에만 집중할 수 있기 때문에 생산성을 증가시킬 수 있습니다.

2. 🌼Spring Boot

스프링부트 공식 문서에서는 Spring Boot를 아래와 같이 설명합니다.

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run". We take an opinionated view of the Spring platform and third-party libraries so you can get started with minimum fuss. Most Spring Boot applications need minimal Spring configuration.

Spring Boot는 단독 실행되는, 상용화 가능한 수준의 스프링 기반 애플리케이션을 쉽게 만들어 낼 수 있습니다. 최소한의 설정으로 스프링 플랫폼과 서드파티 라이브러리들을 사용할 수 있도록 하고 있습니다.

즉,  우리는  Spring Boot를 이용해서 스프링 기반 애플리케이션을 쉽게 만들 수 있다 는 뜻입니다.

3. 🔎 Spring과 Spring Boot의 차이 

Spring과 Spring Boot는 해결하고자 하는 문제가 다릅니다.

 

 

1) Spring이 해결하고자 하는 것

1. Dependency Injection(DI) - 의존성 주입

의존성 주입을 통해 객체간의 결합도를 낮추어 코드 재사용성을 향상시키고, 단위테스트를 용이하게 할 수 있도록 해줍니다.

 

2.  Duplictation/Plumbing Code - 중복된 코드 제거

 반복되는 코드들(Boilerplate code/ Plumbing code)을 제거함으로써 개발자가 비즈니스 로직에만 집중할 수 있도록 합니다.

 

3. Intergration with Other Frameworks - 다른 프레임워크와의 통합

Mockito와 같은 다른 프레임워크(해당 기능에서의 최선의 방법으로 구현된 프레임워크나 모듈들)와 통합하여, 개발자가 비즈니스 로직외에 신경 써야 할 부분을 덜어줍니다. 이는 생산성 향상에도 큰 도움을 줍니다.

2) Spring Boot가 해결하고자 하는 것 

Spring Boot aims to make it easy to create Spring-powered, production-grade applications and services with minimum fuss.

Spring Boot는 최소한의 번거로움으로 스프링 기반의 프로덕션 급 응용 프로그램 및 서비스를 쉽게 만들 수 있도록하는 것을 목표로합니다. 

따라서, Spring Boot는 아래와 같은 기능을 지원합니다.

  • Auto Configuration - 자동 설정
  • Easy dependency Management - 쉬운 의존성 관리
  • Embedded Servlet Container Support - 내장 서블릿 컨테이너(내장 서버)

1️⃣  Auto Configuration - 자동 설정

스프링부트 공식 문서에서는 auto configuration에 대한 내용을 아래와 같이 설명하고 있습니다.

Automatic config for spring functionality
스프링 기능을 위한 자동 설정을 지원한다. 
Opinionated ‘starter' dependencies to simplify build and application configuration
starter 의존성을 통해 간단히 설정할 수 있다.

이를 좀 더 자세히 알아보겠습니다. 

 

(1) 스프링 기능을 위한 자동 설정을 지원한다.

 

우선, Spring Boot 프로젝트를 생성하여 main에 가게 되면  @SpringBootApplication 어노테이션을 볼 수 있고,

그 속에 들어가보면 아래와 같은 어노테이션들을 확인할 수 있습니다.

  • @SpringBootConfiguration : Spring boot의 설정을 나타내는 어노테이션으로, Spring의 @Configuration을 대체합니다.
  • @EnableAutoConfiguration : 자동 설정의 핵심 어노테이션으로, 클래스 경로에 지정된 내용을 기반으로 설정 자동화를 수행합니다.
  • @ComponentScan : basePackages 프로퍼티 값에 별도의 경로를 설정하지 않으면 해당 어노테이션이 위치한 패키지가 루트 경로가 되어 빈으로 등록할 클래스들을 탐색합니다.

Spring Boot는 Bean을 두 번에 걸쳐 등록합니다.

 

1. @ComponentScan

처음에는 Spring과 마찬가지로 component-scan을 통해 component를 찾고 bean 생성을 진행합니다. 그 과정에서 우리가 설정한 bean들이 생성됩니다. (@component 어노테이션을 선언한 클래스들 ex. @Controller, @RestController, @Service, @Repository ... 그리고 @Configuration에 등록한 @Bean과 같은 설정들)

 

2. @EnableAutoConfiguration

그 후에는 @EnableAutoConfiguration으로 추가적인 Bean들을 읽어서 등록하게 됩니다. 메인 클래스(@SpringBootApplication)를 실행하면, @EnableAutoConfiguration에 의해 spring.factories 안에 들어있는 수많은 자동 설정들이 조건에 따라 적용이 되어 수 많은 Bean들이 생성되고, 스프링 부트 어플리케이션이 실행되는 것입니다.

 

즉, Spring Boot는 Component scan을 통해서 모은 component들의 정보와 spring.factories 파일에 사전 정의한 auto-configuration 내용에 의해 bean 생성을 진행합니다. 

 

@EnableAutoConfiguration 을 자세히 살펴보겠습니다.

 

공식 문서에는 다음과 같이 설명합니다.

Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. 

당신이 필요할 것 같은 빈들을 추측하고 설정하여 스프링 application context의 auto-configuration을 가능하게 한다.  
auto-configuration 클래스들은 대게 classpath와 당신이 정의한 빈을 바탕으로 적용된다. 

전체 내용은 여기를 클릭해주세요 😊

더보기
For example, if you have tomcat-embedded.jar on your classpath you are likely to want a TomcatServletWebServerFactory (unless you have defined your own ServletWebServerFactory bean)

예를 들어, 클래스 경로에 tomcat-embedded.jar 파일이 있다면, 당신은 TomcatServletWebServerFactory를 원할 수 있다. (자체적으로 ServletWebServerFactory빈을 정의하지 않았다면)
When using @SpringBootApplication, the auto-configuration of the context is automatically enabled and adding this annotation has therefore no additional effect.

@SpringBootApplication을 사용할 때, context의 auto-configuration이 자동으로 활성화된다.(@EnableAutoConfiguration을 선언해도 추가적인 효과는 없다. 이미 @SpringBootApplication안에 @EnableAutoConfiguration이 선언되어 있으므로)
Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration. You can always manually exclude() any configuration that you never want to apply (use excludeName() if you don't have access to them). You can also exclude them via the spring.autoconfigure.exclude property. Auto-configuration is always applied after user-defined beans have been registered.

auto-configuration은 가능한 한 지능적이 되려고 시도하며, 사용자가 자체적인 설정을 더 많이 정의할수록 뒤로 물러난다. 당신은 언제나 수동으로 exclude()할 수 있다. 네가 적용되지 않기를 바라는 어떤 설정이든. 당신은 또한 spring.autoconfigure.exclude 프로퍼티를 통해 그들을 제외시킬 수 있다. auto-configuration은 항상 사용자 정의 빈이 등록된 후에 적용된다. 
The package of the class that is annotated with @EnableAutoConfiguration, usually via @SpringBootApplication, has specific significance and is often used as a 'default'. For example, it will be used when scanning for @Entity classes. It is generally recommended that you place @EnableAutoConfiguration (if you're not using @SpringBootApplication) in a root package so that all sub-packages and classes can be searched.

@EnableAutoConfiguration 어노테이션이 붙은 클래스의 패키지(보통 @SpringBootApplication을 통해)는 특정한 의미를 가지고 있고, 기본으로 종종 사용된다. 예를 들어 @entity 클래스를 검색할 때 사용된다.  
일반적으로 모든 하위 패키지 및 클래스를 검색할 수 있도록 루트 패키지에 @enableAutoConfiguration을 위치시키는 것이 좋다.   
Auto-configuration classes are regular Spring @Configuration beans. They are located using the SpringFactoriesLoader mechanism (keyed against this class). Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBean annotations).

auto-configuration 클래스들은 일반적인 spring @configuration 빈들이다. 그들은 SpringFactoriesLoader 메카니즘을 사용하여 위치합니다. 일반적으로 auto-configuration 빈들은 @Conditioanl 이다.

그럼 @EnableAutoConfiguration은 어떻게 우리가 필요한 빈들을 추측하고 설정할 수 있는 걸까요? 지금부터 @EnableAutoConfiguration이 어떻게 동작하는지 살펴보겠습니다.

 

우선 @EnableAutoConfiguration 어노테이션 내부에 들어가면 아래와 같은 어노테이션들이 나옵니다.

@EnableAutoConfiguration 어노테이션 내부

여기서 주목할 부분은 @Import(AutoConfigurationImportSelector.class)입니다. AutoConfigurationImportSelector라는 이름만 봐도 자동 설정을 위한 파일 같네요! 이 내부로 들어가게 되면 아래와 같은 메서드들을 확인할 수 있습니다.

 

AutoConfigurationImportSelector#selectImports

우선,  selectImports() 를 통해 자동으로 설정할 빈을 결정합니다.

AutoConfigurationImportSelector#getAutoConfigurationEntry

이 때 getAutoConfigurationEntry()에서  getCandidateConfigurations()로 타고 들어가 모든 후보 빈을 불러옵니다.

그럼 후보 빈은 어떻게 정의되는 걸까요?

AutoConfigurationImportSelector#getCandidateConfigurations

 SpringFactoriesLoader 에 의해 결정이 되는데, 이 클래스는  META-INF/spring.factories  정의된 자동 설정할 클래스들을 먼저 불러오도록 합니다. 

SpringFactoriesLoader
Spring Boot가 미리 정의해둔 AutoConfiguration 정보는 spring-boot-autoconfigure/META-INF/spring.factories에서 확인 가능합니다.

편해서 좋긴 한데.. 저렇게나 많은 설정이 미리 정의되어 있다면 "우리가 정의한 bean과 Spring Boot가 정의한 bean이 충돌하는 경우가 발생하지 않을까?" "우리가 필요하지 않은 bean을 Spring Boot가 자동으로 등록하는 것은 오히려 비효율적이지 않을까?" 라는 생각이 들 수도 있습니다. 이런 경우, SpringBoot의 @Condition과 @Conditional(Spring 4.0 버전부터 추가됨)을 이용해서 문제를 해결할 수 있습니다. 위 @EnableAutoConfiguration 설명에서도 언급되었듯, Auto-configuration 빈들은 필요한 상황에만 자신이 실행될 수 있도록 @Condition, @Conditional로 생성되어있기 때문입니다. (getAutoConfigurationEntry()에서  중복된 설정(removeDuplicates)과 제외할 설정(getExcludsions())을 제외시켜줍니다.) 그렇게 되면 이 프로젝트에서 사용하는 빈만 임포트할 자동 설정 대상으로 선택되겠죠?

 

다시 한 번 정리를 하자면, @EnableAutoConfiguration에 의해 spring.factories 안에 들어있는 수많은 자동 설정들이 조건에 따라 적용이 되어 수 많은 Bean들이 생성되고, 자동 설정이 되는 것입니다. 

 

그렇다면 빈의 등록과 자동 설정에 필요한 파일은 무엇일까요? 

  • META-INF/spring.factories : 자동 설정 타깃 클래스 목록, 이곳에 선언되어 있는 클래스들이 @EnableAutoConfiguration 사용 시 자동 설정 타깃이 됩니다.
  • META-INF/spring-configuration-metadata.json : 자동 설정에 사용할 프로퍼티 정의 파일. 미리 구현되어 있는 자동 설정에 프로퍼티만 주입시켜주면 됨. 별도의 환경설정 필요 없음
  • org/springframework/boot/autoconfigure : 미리 구현해놓은 자동 설정 리스트. 이름은 '{특정 설정의 이름}AutoConfiguration' 형식으로 지정되어 있으며 모두 자바 설정 방식을 따르고 있습니다.

위 파일 모두 Spring-boot-AutoConfiguration에 미리 정의되어 있으며, 지정된 프로퍼티 값을 사용하여 설정 클래스 내부의 값들을 변경할 수 있습니다. 

 

Spring을 사용했다면 이 수 많은 bean과 설정 파일을 개발자가 직접 하나하나 등록해줘야 했을 것입니다. 하지만 우리는 SpringBoot의 Auto-configuration 기능을 이용하여, 프로젝트에서 필요한 bean들을 자동으로 등록 및 설정해줄 수 있게 되었습니다! 

 

(2) starter 의존성을 통해 간단히 설정할 수 있다.

 

Spring boot에서  starter란 의존성과 설정을 자동화해주는 모듈 을 뜻합니다.

 

예를 들어 Spring-boot-starter-jpa를 의존성 추가했을 때 Spring Boot는 아래와 같은 일들을 해줍니다.

 

1. spring-aop, spring-jdbc 등의 의존성을 걸어준다. easy dependency

2. classpath를 뒤져서 어떤 database를 사용하는지 파악하고, 자동으로 entityManager를 구성해준다. auto-configuration

3. 해당 모듈들 설정에 필요한 properties 설정을 제공한다. auto-configuration

 

auto-configuration 관점에서 2, 3번에 대한 내용을 여기서 설명합니다. (1번에 대한 내용은 아래 easy dependency에서 설명하도록 하겠습니다.)

 

자, 이번엔 web의 예시를 들어보겠습니다.

 

스프링을 이용해 웹 애플리케이션을 개발하려고 합니다. 이 때 우리는 Spring 프로젝트를 생성하고 웹 애플리케이션을 만들기 위해 Spring MVC를 추가하고 웹과 관련된 여러 설정 파일들(xml 파일들)을 직접 생성 및 관리해왔습니다.  

 

applicationContext.xml - 스프링 빈 설정 파일

 

web.xml - 웹 애플리케이션의 설정을 위한 배포와 관련된 것들의 설정 파일

 

 

실제로 web.xml을 보면 DispatcherServlet 을 명시해주고 요청을 처리할 url을 명시해주는 모습을 확인할 수 있습니다.

web.xml

문제는, 이러한 비슷한 기술들을 가지고 springMVC 애플리케이션을 또 개발할 일이 생긴다면, 우리는 복사 붙여넣기를 통해 위와 같은 설정을 구축할 것입니다. 이렇게 같은 작업이 반복된다는 것은 자동화해야한다는 여지를 남겨주었고 이를 스프링 부트가 해결해줍니다!

 

이걸 Spring Boot는 starter의존성 한줄로 해결합니다.

compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.1.RELEASE'

이렇게 gradle에 spring-boot-starter-web 의존성을 추가해주면 관련 모듈(jar)들도 함께 자동으로 classpath에 추가됩니다. 

classpath에 추가된 jar 파일들에 의해 spring.factories 파일에 있는 관련 설정이 자동으로 수행됩니다.

아래는 web과 관련된 설정 파일입니다. 이 역시도 factories 파일에 정의되어 있습니다.

우리는 이렇게 stater를 이용해 관련 의존성들을 추가하고, 해당 모듈에 맞게 자동으로 설정까지 할 수 있게 되었습니다! 의존성 관련해서는 아래에서 더 자세히 설명하겠습니다.

2️⃣ Easy dependency Management - 쉬운 의존성 관리

(1) Spring-boot-starter

 

웹 어플리케이션을 개발 할 때, 우리는  spring-web,  spring-webmvc ,  jackson-databind 등과 같은 의존성들을 필요로 합니다. 즉, Spring으로 웹 어플리케이션 개발 시 우리는 위와 같은 의존성들을 일일이 찾고, 호환되는 버전에 맞춰 의존성을 추가하는 번거로움이 존재했습니다. Spring Boot는 이런 번거로움을 줄이기 위해 Spring boot Starter 라고 불리는 것을 도입했습니다. 

 

Spring-boot-starter-web

사진에서 볼 수 있듯 starter-web은  spring-web,  spring-webmvc ,  jackson-databind로 이루어져 있고, 이제 우린 의존성에   spring-boot-starter-web 이 한 줄만 적어주면 web과 관련된 의존성들(위에 명시된 의존성들)을 추가할 수 있게 됩니다.

compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.1.RELEASE'

이것이 Spring Boot가 지원하는 쉬운 의존성 관리입니다. 

 

(2) io.spring.dependency-management

 

스프링부트의 의존성을 관리해주는 플러그인으로 dependency manager가 관리하는 프로젝트들은 알아서 버전관리를 해줍니다. 따라서 build 설정에 dependency에 대한 버전을 명시할 필요가 없습니다. Spring Boot 자체를 업그레이드 하면 하위 종속성들도 일관된 방식으로 업그레이드 됩니다. (단, 직접 버전을 명시하는 경우 해당 버전으로 오버라이딩 됩니다.)

plugins {
  id "io.spring.dependency-management" version "1.0.9.RELEASE"
}

 

즉, 우리는  Spring Boot의 dependency-management를 이용하여 단 한줄의 의존성으로 수 많은 프로젝트들의 버전을 충돌 없이 관리할 수 있게 됩니다.  

 

dependency manger가 관리하는 프로젝트 목록은 여기서 살펴볼 수 있습니다.

 

3️⃣ Embedded Servlet Container Support  - 내장 서블릿 컨테이너

Embedded server to avoid complexity in application deployment

어플리케이션 배포의 복잡성을 피할 수 있는 내장 서버
Not War, Just Jar

 

마지막으로 알아볼 차이점은  Spring Boot는 Servlet Contaioner를 내장 하고 있다는 것입니다. 내장 서버를 통해 애플리케이션 배포 시 복잡함을 피할 수 있다고 하는데, 조금 더 자세히 살펴볼까요?

 

기존, Spring을 이용한 프로젝트는 war(Web application ARchive)파일로 배포할 수 있었습니다. (war파일은 웹 어플리케이션을 압축해 저장해놓은 파일로, tomcat과 같은 was에서 돌아갈 수 있는 구조를 담고 있습니다.) 따라서 Spring으로 개발한 프로젝트를 배포하기 위해서는 (웹 어플리케이션이 압축된)war파일(프로그램을 실행시킬)WAS가 필요했죠.

war파일을 실행시킬 WAS를 직접 설정해주어야 합니다. 정말 귀찮은 작업입니다.

하지만 Spring Boot는 Tomcat이나 Jetty같은 내장 서버를 가지고 있기 때문에 jar파일로 배포할 수 있게 됩니다. 따라서 Spring Boot로 개발한 프로젝트를 배포하기 위해서는 단순히 jar파일만 필요합니다. 

 

즉, 스프링부트가 tomcat을 내장하면서, Servlet Container에 종속되던 WebApplication이 역으로 WebApplication에 Servlet Container이 종속할 수 있도록해주게 됩니다.

 

4. 언제 무엇을 사용해야 할까?

위 그림은 Jetbrain사에서 매년 진행하는 Java와 관련된 설문 결과를 그래프로 도식화 해놓은 것입니다.

(위 그래프는 What web frameworks do you use? (어떤 웹 프레임워크를 사용하냐)라는 질문에 대한 답변입니다.)

 

2017년까지만해도 Spring Boot보다 Spring MVC의 비중이 더 높았지만 시간이 흐를수록 Spring Boot 사용 비중이 커지고 있음을 확인할 수 있습니다. 이는 새로 시작되는 프로젝트의 대부분이 Spring Boot로 만들어짐을 의미하며, 대부분의 개발자들이 Spring Boot를 선호하고 있음을 뜻합니다. 저 역시도 편리성을 제공해주는데 굳이 마다할 필요는 없다고 생각되어 Spring Boot를 더 선호하는 편입니다!

 

자 그럼, 위에서 Spring Boot가 어떻게 사용자에게 편리함을 제공하는지도 살펴보았고, 실제 시장 점유율도 Spring Boot가 높게 차지하고 있음을 알아보았습니다. 그럼 왜 아직 몇몇의 개발자들은 Spring을 사용할까요? 사실 이건 제가 궁금한 점이기도 합니다. 아무리 찾아봐도 답을 찾기가 어렵더라구요.. 다만, 추측가는 한 가지는 이전에 Spring으로 진행해놓은 프로젝트를 계속 Spring으로 개발하면서 차지하는 비중도 상당할 것이라 생각이 드네요. 하지만 이 또한 저의 추측일 뿐이니, 혹시 다른 의견 있으시다면 댓글 부탁드립니다. (ㅎㅎ! 마무리가 어색하네요)

 

+ 추가로 처음 배우는 스프링부트2 베타 리더의 말에 우아한형제들 서버 개발자 이동욱님은

기존 스프링 환경을 사용하시는 분들께서 스프링 부트로 넘어가지 않는 이유로 2가지를 얘기합니다. 하나는 대규모 트래픽에서 버틸 수 있는가 하는 것이고, 다른 하나는 내가 모르는 설정이 되어 있는 것 아닌지 불안하다는 겁니다.

라고 말씀을 하셨습니다. 해당 책에서 하루 2천만 이상의 PV가 발생하는 서비스에서 스프링 부트로 개발/운영한 경험을 다루고 있다고 하니 첫 번째와 같은 의문에 대해서는 해결할 수 있을 것 같아요. (실제로 Netflix에서도 Spring Boot를 사용한다고 하니, 웬만한 서비스는 Spring Boot를 이용해도 큰 문제는 없을 것이라 생각됩니다. )

 


 References