스프링과 스프링부트 무엇이 다를까?

Jake Seo·2021년 6월 30일
2

자바 잡지식

목록 보기
6/17
post-custom-banner

먼저, 스프링이란 무엇일까?

위키피디아에 적힌 설명을 보면 아래와 같다.

The Spring Framework is an application framework and inversion of control container for the Java platform. The framework's core features can be used by any Java application, but there are extensions for building web applications on top of the Java EE (Enterprise Edition) platform. Although the framework does not impose any specific programming model, it has become popular in the Java community as an addition to the Enterprise JavaBeans (EJB) model. The Spring Framework is open source. From wikipedia

스프링 프레임워크는 자바 플랫폼을 위한 애플리케이션 프레임워크이며 IOC 컨테이너입니다. 프레임워크의 핵심 기능은 어떤 자바 애플리케이션에 의해서도 사용될 수 있지만, 자바 EE 플랫폼 위에서 웹 애플리케이션을 빌드하기 위한 확장 기능들이 특히 많습니다. 프레임워크가 어떠한 특정한 프로그래밍 모델을 강요하지는 않지만, 엔터프라이즈 자바 빈즈(EJB) 모델에 추가하는 것이 자바 커뮤니티에서는 인기입니다. 스프링 프레임워크는 오픈소스입니다.

위 설명만 듣고 스프링이 무엇인지 감이 오는 분은 자바 & 스프링에 대한 상당한 이해가 있는 분일 것이다. 사실 이미 스프링을 알아야 저 정의가 이해가 된다. 밑에서 다시 스프링에 대해 더 상세한 예제로 설명해보겠다.

일단 위의 스프링의 정의를 이해하기 위해 모르는 용어를 하나씩 배워보자.

IOC 컨테이너란 무엇일까?

스프링을 접하다보면 가장 흔히 듣는 축약어가 DIIoC이다.

스프링에서 가장 중요한 개념이 IOC 컨테이너이다. reflection이라는 기술을 통해 자바 오브젝트를 설정하고 관리하는 수단을 제공하는 기술이다. 컨테이너는 특정한 오브젝트의 오브젝트 라이프사이클을 관리하는데 책임이 있다. 오브젝트를 생성하고, 초기화 메소드를 호출하고, 오브젝트를 서로 연결하면서 환경 설정을 해준다.

컨테이너에 의해 생성된 오브젝트는 managed objects 혹은 managed beans 라고 불린다. 컨테이너는 XML 파일 또는 자바 애노테이션 또는 설정 클래스를 검출하여 환경 설정이 된다. 이러한 데이터 소스는 빈을 생성하기 위해 필요한 정보들을 제공하기 위한 빈 정의가 포함된다.

오브젝트들은 Dependency Lookup(DL) 혹은 Dependency Injection(DI)와 같은 방식에 의해 얻어진다. Dependency Lookup은 호출자가 컨테이너 오브젝트에 특정 타입의 특정 이름의 오브젝트를 요청하는 것이고, Dependency Injection은 생성자, 프로퍼티, 팩토리 메소드와 같은 패턴을 통해 이름에 맞는 오브젝트를 넘겨주는 것이다.

많은 경우에, 스프링 프레임워크의 다른 파트를 사용할 때는 이러한 컨테이너를 사용하면 애플리케이션을 만들면서 더욱 쉽게 환경 설정을 하고 커스터마이징을 할 수 있음에도 컨테이너를 사용하지 않는다. 스프링 컨테이너는 작은 스케일의 애플리케이션부터 큰 기업용 애플리케이션까지 거의 모든 자바 환경에서 애플리케이션을 설정하고 통합하기 위한 지속적인 매커니즘을 제공한다.

컨테이너는 Pitchfork project의 방법으로 부분적으로 EJB 3.0을 따르는 컨테이너가 될 수 있다. 스프링 프레임워크가 표준을 따르지 않는다고 비난하는 사람도 있다. 하지만, SpringSource는 EJB 3.0 을 주 목표로 보지 않고 스프링 프레임워크와 컨테이너를 더 강력한 프로그래밍 모델로 만드는 것이라 주장한다. 프로그래머는 직접적으로 오브젝트를 만들지 않지만, 스프링 설정 파일에 이를 정의함으로써 어떻게 만들어져야 하는지에 대해 설명한다. 비슷하게 서비스와 컴포넌트들도 직접 호출되지 않는다. 유지보수와 테스팅의 용이성을 증가시키기 위해 IoC가 의도한 방향이다.

DI는 간단히 설명하면 스프링 내부 애플리케이션 컨텍스트에서 필요한 타입의 빈이 꺼내져서 자동으로 주입되는 것이고, DL은 스프링 내부 애플리케이션 컨텍스트에서 해당 타입의 빈을 직접 찾아서 쓰는 방식을 말한다. 스프링 핵심 원리 - 기본편 #58 / 관련 링크는 저작권 때문에 비공개 김영한님의 스프링 핵심 원리 강의에 나옵니다.

자바 EE 플랫폼이란?

Java Platform, Enterprice Edition 을 축약해서 Java EE라고 불렀었고, Java 2 Platform, Enterprise Edition을 축약해서 J2EE라고 부른다. 결국에는 스펙들의 집합이다. Java Standard EditionJava SE를 상속받은 형태이며, Java SE는 분산 컴퓨팅, 웹 서비스와 같은 엔터프라이즈 기능들에 대한 스펙을 담고 있다.

스펙으로는 웹 스펙, 웹서비스 스펙, 엔터프라이즈 스펙, 기타 스펙 등이 존재하는데 웹 스펙으로는 우리가 익히 알고 있는 자바 서블릿이나, 자바 EL, 자바 웹소켓 등이 있고, 웹 서비스 스펙으로는 Java Restful Web Services 등이 있다.

스프링의 다양한 모듈들 (스프링이 편리하게 만들 수 있도록 지원하는 것들)

  • Spring Core Container: 스프링의 기반이 되는 모듈이며, 스프링 컨테이너를 제공한다. - BeanFactoryApplicationContext가 있다.
    • BeanFactoryApplicationContext의 상위 인터페이스로 프레임워크 환경 설정과 기본 기능을 제공하고, ApplicationContextBeanFactory를 상속하여 AOP 기능, 메세지 리소스 핸들러, 이벤트 발행 등 추가적인 엔터프라이즈 기능을 더 제공한다.
  • Aspect-Oriented Programming: AOP라 불리며, 공통 관심사(Cross-cutting concerns)구현을 가능하게 한다.
  • Authentication / Authorization: Spring Security라는 스프링 서브 프로젝트를 통해 보안 설정을 간편하게 할 수 있도록 지원함
  • Convention over configuration: Spring Roo 모듈 내부에서 스프링을 기반으로 한 엔터프라이즈 앱을 빠르게 개발할 수 있는 솔루션을 제공
  • Data access: Java Database Coinnectivity(JDBC) 관계형 디비 관리 시스템과 ORM 도구, NoSQL 모두 제공
  • Inversion of Control: 자바 오브젝트들의 라이프사이클 관리와 애플리케이션 컴포넌트 관리 환경 설정을 해준다. 주로 의존성 주입을 통해 이루어진다.

BeanFactoryApplicationContext에 대한 설명 스프링 핵심 원리 - 기본편 #29 BeanFactory와 ApplicationContext

스프링 핵심 기술 설명 공식문서에 기술에 대해 매우 자세히 나와있다.

결국 스프링 프레임워크를 다시 정리하면?

자바 웹 애플리케이션을 만들 때, DI, IoC와 같은 기술을 통해 설계된 아키텍처를 이용해 유지보수성, 확장성 등을 지키면서도 개발자는 최대한 비즈니스 로직을 만드는데만 집중할 수 있게 만들어주는 프레임워크이다. 또 DI, IoC 와 같은 기술은 객체간 결합도를 낮추어 코드 재사용성을 향상시키고, 단위 테스트를 용이하게 할 수 있도록 해준다.

한 마디로 정리하면 웹 개발에 주로 이용되는 다양한 모듈과 함께 좋은 객체지향 웹 어플리케이션을 개발할 수 있도록 도와준다.

스프링 프레임워크가 가졌던 문제들

설정할 게 너무나 많다.

스프링을 처음 배울 때, 스프링은 초기 설정만 잘해도 고수라는 소리를 들었다. 그만큼 설정을 어디서 어떻게 할 수 있는지에 대해 알기가 어려웠고, 막상 설정 파일을 봐도 무슨 소리가 적혀 있는 건지 알기가 너무 어려웠다.

의존성 관리가 힘들다

스프링 프로젝트는 보통 독립적으로 돌아가는 게 아니라, 다양한 의존성과 함께 돌아가는데 스프링 버전이 계속 올라감에 따라 호환이 되는 의존성과 그렇지 않은 의존성을 반드시 구분해서 의존성을 추가해야 했다. 이러한 작업들이 개발자를 매우 피곤하게 했다.

배포가 어렵다

스프링 프로젝트는 .war 파일을 추출하여 해당 파일을 톰캣 안의 특정 폴더로 넣어서 배포했다. 스프링 프로젝트에서 추출한 .war 파일은 WAS 없이 독립적으로 실행이 불가능해서 항상 WAS를 먼저 설치하고 그 안에 .war 파일을 넣어 배포했었다.

위의 3가지 문제를 해결하려 나온 것이 스프링 부트이다.

스프링 부트의 등장

Spring Boot helps you to create stand-alone, production-grade Spring-based applications that you can run. We take an opinionated view of the Spring platform and third-party libraries, so that you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.

You can use Spring Boot to create Java applications that can be started by using java -jar or more traditional war deployments. We also provide a command line tool that runs “spring scripts”.

Our primary goals are:

Provide a radically faster and widely accessible getting-started experience for all Spring development.

Be opinionated out of the box but get out of the way quickly as requirements start to diverge from the defaults.

Provide a range of non-functional features that are common to large classes of projects (such as embedded servers, security, metrics, health checks, and externalized configuration).

Absolutely no code generation and no requirement for XML configuration.

스프링 부트는 자체적으로 구동 가능한 프로덕션 수준의 스프링 기반 애플리케이션을 만드는 것을 도와줍니다. 스프링 플랫폼과 서브파티 라이브러리에 대한 독선적인 관점을 취함으로써, 최소한의 수고로 스프링 프로젝트를 실행할 수 있게 해줍니다. 대부분의 스프링 부트 애플리케이션은 매우 작은 스프링 설정만을 요구합니다.

스프링부트는 java -jar 명령어 혹은 war파일을 배포하는 전통적인 방식으로 실행될 수 있는 자바 애플리케이션을 쉽게 만들 수 있게 해줍니다. 또, "spring script"를 실행하는 커멘드 라인 도구도 제공합니다.

스프링 부트의 주요 목적은 다음과 같습니다.

  • 스프링 개발에서 빠르고 폭 넓게 접근 가능한 시작(getting-started) 경험을 제공합니다.
  • 기본으로 고정적인 환경 설정 값을 제공하지만, 설정 값을 변경하고 싶다면 유연하고 신속하게 변경할 수 있습니다.
  • 프로젝트의 커다란 클래스들에 공통적인 비-함수적(non-functional) 기능의 범위를 제공합니다.
    • 임베디드 서버, 시큐리티, 메트릭스, 모니터링, 외부로 노출된 환경 설정 값들이 이에 해당합니다.
  • XML 설정이 전혀 필요 없으며 어떠한 코드도 생성하지 않아도 됩니다.

짧게 요약하자면, 많은 설정을 고정된 값으로 자동으로 먹여주어서 빠른 스프링 애플리케이션을 개발할 수 있도록 해준다.

스프링 부트 공식문서가 생각보다 잘 되어 있어서 엄청나게 많은 정보를 얻을 수 있다.

어려운 설정 해결: 스프링 부트의 AutoConfiguration (자동 설정) 애노테이션들

@SpringBootApplication

스프링 부트 프로젝트를 생성하면, 가장 메인이 되는 자바 클래스 위에 @SpringBootApplication이라는 애노테이션이 붙어있다. 이 곳에 자동 설정의 비밀이 있다.

@SpringBootApplication 애노테이션 내부 구현을 보면, 위와 같이 @SpringBootConfiguration@EnableAutoConfiguration을 볼 수 있다.

아래의 애노테이션 설명은 굳이 안 읽어봐도 된다. 간단히 설명하면 자주쓰이는 빈을 자동으로 주입해서 자동설정 한다는 얘기다.

@SpringBootConfiguration

스프링 부트 애플리케이션에 @Configuration 애노테이션을 제공하는 클래스이다. 스프링의 표준 @Configuration 애노테이션에 대한 대안으로 사용되어, Configuration을 자동으로 찾을 수 있다.

@EnableAutoConfiguration

스프링 애플리케이션 컨텍스트의 Auto-configuration을 가능하게 한다. 사용자가 필요할만한 빈들을 추측하고 설정해준다. Auto-configuration 클래스들은 주로 클래스 패스와 빈을 정의한 곳을 기반으로 적용된다. 이를테면, 사용자가 클래스 패스에 tomcat-embedded.jar를 갖고 있다면, TomcatServletWebServerFactory가 필요할 것이라고 가정하는 식이다.

@SpringBootApplication 애노테이션을 사용하면, 해당 컨텍스트의 Auto-configuration은 자동으로 활성화되며 이 애노테이션을 사용한다고 추가적인 효과는 없다.

Auto-configuration은 최대한 지능적으로 작동하려고 한다. 사용자가 직접 configuration에 많이 관여할수록 Auto-configuration이 관여하는 부분은 더 적어진다. 애노테이션에 exclude 엘리먼트나 excludeName 엘리먼트 등을 통해 Auto-configuration에서 제외할 클래스 등을 설정할 수 있다.

@EnableAutoConfiguration 애노테이션이 있는 클래스의 패키지는 주로 @SpringBootApplication을 통해 명확한 중요성을 갖는다. 이를테면, @Entity 클래스들을 찾아다니는데 사용될 수 있다. (SpringBootApplication 애노테이션을 쓰지 않는다면,) @EnableAutoConfiguration은 루트 패키지에 위치하는 것이 일반적으로 권장된다. 그래야 모든 서브 패키지와 클래스들이 정상적으로 검출될(searched) 수 있다.

Auto-configuration 클래스들은 일반적인 스프링 @Configuration 빈들이다. SpringFactoriesLoader 메커니즘에 의해 위치된다. Auto-configuration 빈들은 일반적으로 @Conditional 애노테이션이 붙어있다. (대부분은 @ConditionalOnClass@ConditionalOnMissingBean 애노테이션과 같은 것이 붙어있다.)

3가지 주요 애노테이션 요약

  • @SpringBootConfiguration: 스프링 부트의 설정을 나타내는 애노테이션이며, Spring의 @Configuration 애노테이션을 대체한다.
  • @EnableAutoConfiguration: 자동 설정의 핵심 애노테이션으로 클래스 경로에 지정된 내용을 기반으로 설정 자동화를 수행한다.
  • @ComponentScan: 해당 애노테이션이 위치한 패키지를 루트 경로로 빈이 등록된 클래스가 있는지 탐색한다.

스프링 부트가 Bean을 등록하는 방법

  1. @ComponentScan: 일반 Spring 프로젝트처럼 컴포넌트 스캔을 통해 컴포넌트를 찾고 빈을 생성한다. 이 과정에서 사용자가 정의한 Bean들이 생성된다. ex) @Controller, @RestController, @Service, @Repository, @Component, @Configuration@Bean, ...

  2. @EnableAutoConfiguration: spring.factories 내부에 들어있는 수많은 자동 설정이 위에 설명했듯, @Conditional 조건에 따라 생성되고, 스프링 부트 애플리케이션이 시작된다.

spring.factories의 내용은 이 링크에서 볼 수 있다.

spring-boot-autoconfigure 의존성에서 다양한 자동 설정들을 구경할 수 있다.

정리

정리하면 컴포넌트 스캔에 검출된 컴포넌트 정보와 spring.factories 파일에 사전 정의한 Auto-configuration 내용에 의해 bean 생성을 진행한다.

spring-boot-autoconfigure 의존성 내부에 있는 파일들은 각 Auto-configuration에 필요한 정보들을 가지고 있다.

AutoConfiguration 애노테이션들 코드로 살펴보기

@EnableAutoConfiguration

ENABLED_OVERRIDE_PROPERTY: auto-configuration이 활성화됐을 때, 오버라이드를 위해 사용되는 환경 프로퍼티

@AutoConfigurationPackages: auto-configuration 패키지임을 나타내는 애노테이션, base packagesbase package classes가 명시되지 않았다면 애노테이트된 클래스를 등록함

@Import(AutoConfigurationImportSelector.class): 핵심적인 애노테이션으로, 자동설정을 위한 클래스를 임포트하는 것을 확인할 수 있다. @Import 애노테이션은 주로 @Configuration을 import 하려고 사용한다. 스프링 설정 XML에서의 <import /> 태그와 동일한 기능을 제공한다.

AutoConfigurationImportSelector.class

.selectImports(): AutoConfigurationSelector.class에서는 .selectImports()라는 메소드를 통해 애노테이션 메타 데이터를 받아서 자동으로 설정해줄 빈들을 결정한다.

.getAutoConfigurationEntry(): .getCandidateConfiguration()을 타고 들어가 모든 후보 빈을 불러온다. 이후에 중복된 것을 제거하고, auto-configuration에서 exclude한 것들을 제거한다.

.getCandidateConfigurations(): SpringFactoriesLoader.loadFactoryNames() 메소드를 이용해서 필요한 configuration들을 불러온다.

SpringFactoriesLoader: 내부의 .loadFactoryNames() 메소드를 이용해서 필요한 configuration들을 불러오는데 불러오는 위치는 결국 FACTORIES_RESOURCE_LOCATION이고, 내부에 정의된 문자열을 보면 "META-INF/spring.factories"에서 가져오는 것을 알 수 있다.

위는 spring.factories의 내용이다. 수많은 빈들이 정의되어 있다. 위에 정의된 빈들은 스프링부트의 @Condition, @Conditional 과 같은 애노테이션을 이용해 우리가 직접 정의한 빈과 충돌하지 않도록 잘 설정되어 있다.

빈 등록 및 자동설정에 필요한 파일들

  • META-INF/spring.factories: 자동 설정 타겟 클래스 목록을 제공하며, 이 파일에 선언된 클래스들이 @EnableAutoConfiguration 애노테이션을 사용할 때 자동 설정 타겟이 된다.
  • META-INF/spring-configuration-metadata.json: 자동 설정에 사용할 프로퍼티 정의 파일이다. 미리 구현된 자동 설정에 프로퍼티만 주입시키면 된다. 별도의 환경 설정이 필요 없다.
  • org/springframework/boot/autoconfigure: 미리 구현해놓은 자동 설정 리스트이다. {설정이름}AutoConfiguration과 같은 네이밍의 파일들로 존재한다. 모두 자바 설정 방식을 따른다.

자동 설정이 없다면? xml에서 java 파일로 변경 예제

BEFORE: web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
  
  <display-name>ABC System</display-name>
  
  <context-param>
    <param-name>logbackConfigLocation</param-name>
    <param-value>classpath:logback/logback.xml</param-value>
  </context-param>
  
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/application-context/*-context.xml</param-value>
  </context-param>
  
  <listener>
  	<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>
  
  <listener>
  	<listener-class>abc.login.SessionAttributeListener</listener-class>
  </listener>
  
  <filter>
  	<filter-name>characterEncodingFilter</filter-name>
  	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    
  	<init-param>
  		<param-name>encoding</param-name>
  		<param-value>UTF-8</param-value>
  	</init-param>
  </filter>
  
  <filter-mapping>
  	<filter-name>characterEncodingFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter>
  	<filter-name>requestContextFilter</filter-name>
  	<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>requestContextFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter>
  	<filter-name>EnvironmentSetupFilter</filter-name>
  	<filter-class>abc.filter.EnvironmentSetupFilter</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>EnvironmentSetupFilter</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <filter>
  	<filter-name>springSecurityFilterChain</filter-name>
  	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  
  <filter-mapping>
  	<filter-name>springSecurityFilterChain</filter-name>
  	<url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <servlet>
  	<servlet-name>dispatcher</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:spring/web-application-context/dispatcher-context.xml</param-value>
  	</init-param>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>dispatcher</servlet-name>
  </servlet-mapping>
  
  <session-config>
  	<session-timeout>60</session-timeout>
  </session-config>
  
  <jsp-config>
  	<jsp-property-group>
  		<url-pattern>*.jsp</url-pattern>
  		<page-encoding>UTF-8</page-encoding>
  		<scripting-invalid>false</scripting-invalid>
  		<include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
    	</jsp-property-group>
    
  	<jsp-property-group>
  		<url-pattern>*.jspf</url-pattern>
  		<page-encoding>UTF-8</page-encoding>
  		<scripting-invalid>false</scripting-invalid>
  		<include-prelude>/WEB-INF/jsp/commonDefinition.jspf</include-prelude>
  	</jsp-property-group>
  </jsp-config>
  
  <welcome-file-list>
  	<welcome-file>index.html</welcome-file>
  	<welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <error-page>
  	<error-code>404</error-code>
  	<location>/404.html</location>
  </error-page>
  
  <error-page>
  	<error-code>500</error-code>
  	<location>/500.html</location>
  </error-page>
  
</web-app>

웹 애플리케이션 설정 부분 중 상당히 많은 부분을 담당한다.

AFTER: application.properties + WebConfig

context-param 변환

<context-param>
	<param-name>logbackConfigLocation</param-name>
	<param-value>classpath:logback/logback.xml</param-value>
</context-param>

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:spring/application-context/*-context.xml</param-value>
</context-param>

위의 설정부는 스프링부트 내부 application.properties에서

logging.config=classpath:logback/logback.xml

위와 같은 설정을 함으로써 변환할 수 있다.

listener 변환

<listener>
	<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
	<listener-class>abc.login.SessionAttributeListener</listener-class>
</listener>
@Configuration
public class WebConfig implements WebMvcConfigurer {
	@Bean
	public RequestContextListener requestContextListener(){
		return new RequestContextListener();
	}
}

DispatcherServletRequestContextListener, RequestContextFilter는 모두 같은 일을 한다고 한다.

@Component
public class SessionAttributeListener implements HttpSessionAttributeListener {
public static ConcurrentHashMap<HttpSession, String> sessionMap = new ConcurrentHashMap<>();
...

filter 변환

<filter>
	<filter-name>characterEncodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
	<param-name>encoding</param-name>
	<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
	<filter-name>characterEncodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
	<filter-name>requestContextFilter</filter-name>
	<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>requestContextFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
	<filter-name>EnvironmentSetupFilter</filter-name>
	<filter-class>abc.filter.EnvironmentSetupFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>EnvironmentSetupFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

스프링에서 기본으로 제공하는 필터들 (CharacterEncodingFilter, HiddenHttpMethodFilter, FormContentFilter, RequestContextFilter)은 따로 정의하지 않더라도 자동으로 동작한다고 한다. CharcterEncodingFilter에 대한 설정은 아래와 같다.

application.properties

spring.http.encoding.charset=UTF-8
spring.http.encoding.enable=true
spring.http.encoding.force=true

스프링 시큐리티 관련 필터도 spring-security-config 의존성을 갖고 있으면, 스프링 시큐리티 3.2 버전 이후부터 직접 설정을 할 필요강 ㅓㅄ다고 한다. 그래서 SpringSecurityFilterChain도 굳이 수동으로 해줄 필요가 없다.

커스텀 필터에 대해서는 아래와 같이 작성해준다.

@Component
public class EnvironmentSetupFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
  ServletException {
	  HttpServletRequest hReq = (HttpServletRequest)request;
  }
}

위와 같이 컴포넌트 스캔만 가능하도록 만들어주면 된다. 필터간의 순서는 다음과 같이 정의 가능하다.

@Component
@Order(1)
public class AFilter implement Filter {
	...
}

@Component
@Order(2)
public class BFilter implement Filter {
	...
}

error-page

스프링부트에서는 그냥 resource 디렉토리 밑 error 디렉토리 밑에 404.html, 500.html을 넣어놓으면 자동으로 인식해서 보여준다.

스프링부트에서 자동 설정을 하는 방식

WebMvcAutoConfiguration과 같은 클래스를 보면, 스프링 부트에서 web.xml로 직접 설정해주던 복잡한 설정을 자동으로 하는 것을 볼 수 있다.

auto-configuration 결론

만일 스프링 부트의 의존성 관리가 없었다면 위와 같은 .xml 파일들로 직접 빈 설정을 하나하나 해주어야 했다. 하지만 스프링부트에서는 WebMvcAutoConfiguration과 같이 미리 작성된 클래스를 이용하여 자동 설정을 해준다. 단적으로 web.xml 파일만 예를 든 것이지 실제로 설정해야 할 .xml 파일들이 매우 많다.

복잡한 의존성 관리 해결: starter

스프링부트 프로젝트를 만들면 의존성에 spring-boot-starter...과 같은 의존성이 매우 많이 추가되는 것을 볼 수 있다.

starter 모듈이란 의존성과 설정을 자동화해주는 모듈을 말한다. 이를테면 spring-boot-starter-jpa를 의존성에 추가했을 때, 스프링 부트는 아래와 같은 일을 한다.

  • spring-aop, spring-jdbc 등의 의존성을 걸어준다. (easy depedency)
  • classpath를 뒤져 어떤 database를 사용하는지 파악하고 자동으로 entityManager를 구성해준다. (auto-configuration)
  • 해당 모듈 설정에 필요한 properties 설정을 제공한다. (auto-configuration)

spring-boot-starter 의존성 구경하기

위는 메이븐 리포지토리의 Spring Boot Starter이다. 아래쪽에 보면 Compile Dependencies라는 연관 의존성이 걸려있다.

위에서는 Spring Boot Starter 2.5.2 버전을 사용하였을 때, 어떤 스프링 버전이 가장 알맞는지, 어떤 starter-autuconfigure을 사용해야 하는지 어떤 starter-logging을 사용해야 하는지 자동으로 버전 의존성을 걸어준다.

spring-boot-starter-web 의존성 구경하기

starter-web은 웹에 관련된 의존성 json, tomcat등의 의존성을 어떤 버전을 사용해야 하는지 모두 같이 관리되고 있는 것을 볼 수 있다.

spring-boot-starter 의존성 적용방법

gradle 기준으로 아래 한줄만 추가하면 된다.

implementation group: 'org.springframework.boot', name: 'spring-boot-starter', version: '2.5.2'

이로써 매우 편리하게 의존성을 관리할 수 있다. 이전의 길디 긴 pom.xml은 이제 필요 없다.

전체 의존성을 커스텀하는 방법

스프링부트 의존성을 관리해주는 플러그인이 있다.

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

스프링부트의 dependency-management 를 활용하여 프로젝트 내부에 있는 수 많은 의존성들의 버전을 한번에 관리할 수 있다.

세부 내용은 공식문서 링크에서 확인할 수 있다.

내장 서블릿 컨테이너 관련 정보

스프링 부트는 Servlet Container를 내장하고 있다. 내장 서버를 통해 애플리케이션 배포 시 수행해야 하던 복잡한 프로세스를 비교적 단순하게 만들었다.

기존의 방식

기존에는 .war 파일을 export 후에 이 .war 파일을 WAS(서블릿 컨테이너)가 있는 곳에 위치시켜야 했다.

스프링 부트에서 변경된 방식

이제는 .jar 파일을 export 후에 이 .jar 파일을 직접 java -jar 명령어로 실행할 수 있게 되었다. 내부적으로 내장 톰캣을 가지고 있기 때문에 가능한 일이다.

정리

스프링 프로젝트보다는 스프링 부트 프로젝트를 만들어서 사용하는 것이 더욱 합리적으로 보인다. 좋은 개발 프레임워크를 판단하는 기준은 여러 개가 있겠지만, 그 중 큰 것은 개발자가 비즈니스 로직에 더욱 집중할 수 있게 만들 수 있냐 없냐의 차이 같다.

원래의 스프링 프로젝트의 환경 설정은 너무 까다롭고 어려웠다. 스프링 부트를 통해 내가 필요한 설정만 차근차근 배워가며 필요 없는 설정은 일단 생각하지 않고 개발을 할 수 있다.

물론 이렇게 편리하게 개발할 수 있다는 것이 오히려 사람들이 스프링 프레임워크 자체에 대한 지식 없이 스프링 개발을 하게 만들 수도 있겠지만, 그건 따로 공부해야 맞는 것 같고, 어찌됐든 스프링 부트는 혁신이라고 생각한다.

레퍼런스

또링 스프링 vs 스프링 부트
10분 테코톡 - 또링
pom.xml, web.xml
web.xml 설정 내용 등
web.xml 부트로 마이그레이션
web.xml 부트로 마이그레이션2

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.
post-custom-banner

0개의 댓글