Skip to main content

2. 스프링 부트 원리

About 2 minJavaSpringSpring Bootcrashcoursejavajdkjdk8streamspringspringframeworkspringboot

2. 스프링 부트 원리 관련


namjunemy/TIL - [스프링 부트 개념과 활용] 2. 스프링 부트 원리

[스프링 부트 개념과 활용] 2. 스프링 부트 원리

1. 의존성 관리 이해

Spring Boot Reference Documentation

6.1.1. Dependency Management
  • spring-boot-stater-* 의 부모인 spring-boot-stater-parent, 그리고 다시 그 parent의 부모인 spring-boot-dependencies 에 정의되어 있는 pom.xmldependencyManegement 영역 안에 해당 릴리즈 버전에서 관리하는 의존성들이 정의 되어 있다.
    • 그렇기 때문에 우리는 각 스타터의 버전을 명시하지 않아도 되고, parent가 관리하는 버전을 사용하게 된다.
  • gradle을 사용한다면 intellij의 gradle 탭에서 참조하고 있는 각 의존성들의 버전들과 하위 의존성들의 상세 버전까지 확인 할 수 있다.
*스프링 부트의 dependencies에서 관리하는 의존성의 경우 버전 명시를 하지 않고 stater 위주로 추가해서 사용하면 된다. 별도의 버전 호환을 체크 하지 않고 사용할 수 있다. 하지만, 특별히 버전을 명시해서 사용해야 하는 경우는 명시해서 사용하면 설정 값이 해당 버전으로 오버라이딩 되기 때문에 그 버전을 사용 할 수 있다.
*스프링 부트의 dependencies에서 관리하는 의존성의 경우 버전 명시를 하지 않고 stater 위주로 추가해서 사용하면 된다. 별도의 버전 호환을 체크 하지 않고 사용할 수 있다. 하지만, 특별히 버전을 명시해서 사용해야 하는 경우는 명시해서 사용하면 설정 값이 해당 버전으로 오버라이딩 되기 때문에 그 버전을 사용 할 수 있다.

2. 의존성 관리 응용

버전 관리 해주는 의존성 추가

사용하고 싶은 dependency stater 등록

dependencies {
  // ...
  implementation("org.springframework.boot:spring-boot-starter-data-jpa")
  // ...    
}
스타터만 추가했을뿐인데, 해당 스프링 부트 릴리즈 버전에서 관리되고 있는 의존성의 하위 의존성들까지 추가가 된다.
스타터만 추가했을뿐인데, 해당 스프링 부트 릴리즈 버전에서 관리되고 있는 의존성의 하위 의존성들까지 추가가 된다.

버전 관리 안해주는 의존성 추가

  • 별도로 추가해야 하는 의존성들은 https://mvnrepository.comopen in new window 를 통해서 검색해서 직접 추가하면 된다. 스타터의 parent에서 버전관리가 되지 않으므로 직접 버전까지 명시해줘야 한다.

기존 의존성 버전 변경하기

추가적으로 부트에서 관리하고 있는 의존성의 버전을 개발자가 직접 수정해서 사용할 수 있다.

implementation("org.springframework.boot:spring-boot-starter-data-jpa:2.0.3.RELEASE")

3. 자동 설정 이해

  • @SpringBootApplication 이 선언되어 있는 메인 클래스에서 저 애노테이션을 따라 들어가보면,
  • @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan 등이 선언되어 있다.
  • 애플리케이션에서 빈은 사실 두 단계로 나눠서 읽힌다.
    • 1단계: @ComponentScan
    • 2단계: @EnableAutoConfiguration
  • @ComponentScan 에서는
    • @Component
    • @Configuration, @Repository, @Service, @Controller, @RestController
    • 위의 애노테이션
  • @EnableAutoConfiguration 에서는
    • spring.factories
      • org.springframework.boot.autoconfigure.EnableAutoConfiguration
    • @Configuration
    • @ConditionalOnXxxYyyZzz

4. 자동 설정 만들기 1부: Starter와 AutoConfigure

  • Xxx-Spring-Boot-Autoconfigure 모듈: 자동 설정
  • Xxx-Spring-Boot-Starter 모듈 : 필요한 의존성 정의
  • 그냥 하나로 만들고 싶을 때는?
    • Xxx-Spring-Boot-Starter
  • 본인이 만든 의존성에 의해 만들어지는 빈이 @ComponentScan에 의해서 먼저 만들어지고,
  • 2번째 단계의 @EnableAutoConfiguration에 의해 @Configuration으로 설정되어있는 빈이 이것을 덮어 쓰는 경우도 생긴다.

5. 자동 설정 만들기 2부: @ConfigurationProperties

  • 정리해서 설명하자면, 본인이 애플리케이션에서 직접 @Bean 으로 정의한 holoman 이라는 빈이 AutoConfigure로 인해 만들어진 @Configuration으로 선언된 빈으로 덮어쓰여지는 문제가 생긴다. @SpringBootApplication이 1단계, 2단계로 나눠서 빈을 스캔하기 때문이다.
    • 이 경우에는 @AutoConfigure로 선언한 @Bean@ConditionalOnMissingBean 애노테이션을 추가해주면, 해당 타입 의 빈이 비어있을 경우에만 생성한다.
    • 결과적으로 1단계로 @ComponentScan이 이루어질때 빈이 만들어졌으면, 2단계 @EnableAutoConfiguration 에서는 만들어지지 않는다.
    • 따라서, 본인이 애플리케이션에 직접 생성한 @Bean의 우선순위가 높아지게 된다.
  • 추가적으로 application.yaml에 설정한 값을 @ConfigurationProperties를 통해서 빈에 주입할 수 있다.
    • 해당 커밋 내용 참조
    • https://github.com/namjunemy/spring-boot-concept-and-utilization/commit/53b92ad92935a0568bb28c2a7d95298c995ebfdb

6. 내장 웹 서버 이해

  • 부트는 서버가 아니다.
    • 톰캣 객체 생성
    • 포트 설정
    • 톰캣에 컨텍스트 추가
    • 서블릿 만들기
    • 톰캣에 서블릿 추가
    • 컨텍스트에 서블릿 맵핑
    • 톰캣 실행 및 대기
  • 이 모든 과정을 보다 상세히 또 유연하게 설정하고 실행해주는게 바로 스프링 부트의 자동 설정
    • ServletWebServerFactoryAutoConfiguration(서블릿 웹 서버 생성)
      • TomcatServletWebServerFactoryCustomizer(서버 커스터마이징)
    • DispatcherServletAutoConfiguration
      • 서블릿 만들고 등록

내장 웹 서버 응용 1부: 컨테이너와 포트

  • spring-boot-starter-webspring-boot-starter-tomcat을 가져온다.
  • 만약 tomcat이 아닌, 다른 tomcat이 아닌 jetty를 내장 웹서버로 사용하고 싶다면 아래와 같이 gradle 설정 파일을 작성해주면 된다.
// ...
configurations {
    compile.exclude module: 'spring-boot-starter-tomcat'
}

dependencies {
    // ...
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-jetty')
    // ...
}

부트를 웹 애플리케이션으로 띄우지 않으려면, 프로퍼티 설정을 통해서 일반 애플리케이션으로 사용할 수 있다. 서버의 포트도 간단하게 설정할 수 있다.

.application.yaml

  spring:
    main:
      web-application-type: none
      
  server:
    port: 9090

서버 포트 정보를 0으로 할당하면, 사용 가능한 포트를 랜덤으로 선택한다.

server:
  port: 0

위와 같이 할당된 포트를 애플리케이션 코드에서 사용하는 best way를 spring boot 도큐먼트에서 설명하고 있다.

package io.namjune.springbootconceptandutilization;

import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class PortListener implements ApplicationListener<ServletWebServerInitializedEvent> {

    @Override
    public void onApplicationEvent(
        ServletWebServerInitializedEvent servletWebServerInitializedEvent) {
        ServletWebServerApplicationContext applicationContext =
            servletWebServerInitializedEvent.getApplicationContext();
        System.out.println(applicationContext.getWebServer().getPort());
    }
}

내장 웹 서버 응용 2부: HTTPS와 HTTP2

  • Spring Boot SSL key generate
    • 명령어 수행한 위치에 키스토어가 생성된다.
keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000
application.yaml
server:
  ssl:
    key-store: keystore.p12
    key-store-type: PKCS12
    key-store-password: 123456
    key-alias: tomcat
  • 이렇게 SSL 키를 등록하고 스프링부트 애플리케이션을 실행하면, localhost:8080으로 접근이 불가하다. 앞으로 애플리케이션으로의 모든 접근은 https로 해야한다.

  • 추가적으로 http 접근도 가능하게 설정하려면 아래와 같이 애플리케이션 코드에 http 요청을 받기 위한 커넥터를 추가해주면 된다. 대신 application.yaml에서 https의 포트를 변경해준다.

    // ...
    @Bean
    public ServletWebServerFactory serverFactory() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addAdditionalTomcatConnectors(createStandardConnector());
        return tomcat;
    }

    private Connector createStandardConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        connector.setPort(8080);
        return connector;
    }
    // ...

로그 확인

# ...
# 2018-11-13 11:23:41.187  INFO 12432 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8443 (https) 8080 (http) with context path ''
# ...
  • HTTP2 설정은 SSL이 기본적으로 적용되어있는 상태에서 http2.enabledtrue로 할당해주면 된다.
    • 추가적으로 해줘야하는 작업은 각 웹서버마다 다르다(undertow는 https 설정이 되어있으면 추가적인 설정 없이 http2 enable만 true로 할당하면되고, tomcat은 9.X버전과 JDK9 이상을 쓰면 추가적인 설정없이 http2를 적용할 수 있다.)
server:
  http2:
    enabled: true

이찬희 (MarkiiimarK)
Never Stop Learning.