[강의요약] 스프링부트 개념과 활용 - 3부 스프링 부트 원리 (2/2)

개인적인 학습을 위한 inflearn - 스프링부트 개념과 활용(백기선) 강의 요약입니다.

개념과 원리 위주로 요약합니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8/dashboard

이전 글 에서 이어집니다.

3부 - Springboot 원리


6. 내장 웹 서버 이해

6-1. 스프링 부트는 웹 서버가 아니다. (톰캣, 네티 등이 웹서버) application.setWebApplicationType(WebApplicationType.NONE); 만 봐도 알 수 있다

6-2. tomcat 을 띄우기

public class Application {
    public static void main(String[] args) throws LifecycleException {
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(8080);

        Context context = tomcat.addContext("", "/");

        HttpServlet servlet = new HttpServlet() {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                PrintWriter writer = resp.getWriter();
                writer.println("<html><head><title>aaa</title></head>");
                writer.println("<body><h1>Hello Tomcat!</h1></body>");
            }
        };
        String servletName = "helloServlet";
        tomcat.addServlet("", servletName, servlet);
        context.addServletMappingDecoded("/hello", servletName);

        tomcat.start();
        tomcat.getServer().await(); // 요청을 기다린다
    }
}

6-3. 내장 웹서버를 띄우는 것도 자동 설정의 기능

  • 톰캣 만들기, 서블릿 만들기, web mvc 등

spring-boot-autoconfiguration > META-INF > spring.factories

spring-boot-autoconfiguration > ServletWebServerFactoryAutoConfiguration : 서블릿 웹 서버 생성

DispatcherServletAutoConfiguration : http 서블릿을 상속해서 만든 스프링 서블릿. 얘는 소스에 따라 잘 안 바뀌어서 빼둠

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

자동 설정에 의해 톰캣용 자동 설정이 읽혀짐 -> 톰캣이 컨테이너로 올라간다.

7-1. 다른 서블릿을 쓰고 싶다면?

  1. 톰캣 의존성 제거
    • spring-boot-starter-web > org.springframework.boot:spring-boot-starter-tomcat exclusion
    • 제거 후 실행하면 웹 어플리케이션으로 올라가지 않음
  2. 다른 컨테이너 의존성 추가
    • org.springframework.boot:spring-boot-starter-jetty dependency 추가
    • jetty, undertow

7-2. 프로퍼티를 사용한 설정값 변경

  1. 어플리케이션 타입 변경 : spring.main.web-application = none
  2. 포트 변경 : server.port = 7070 (0을 주면 랜덤으로 사용할 수 있는 포트가 할당)
    1. 설정된 포트 확인 방법 : ApplicationListener로 ServletWebserverInitializedEvent를 받아와서 확인 가능
8. 내장 웹 서버 응용 2부 : HTTPS와 HTTP/2
HTTPS

키스토어 -> ssl -> https

8-1. 키스토어 생성

$ keytool -genkey -alias spring -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000                                                                    22:56:33
키 저장소 비밀번호 입력:  
새 비밀번호 다시 입력: 
이름과 성을 입력하십시오.
  [Unknown]:  Cherrue
조직 단위 이름을 입력하십시오.
  [Unknown]:  CHERRUE
조직 이름을 입력하십시오.
  [Unknown]:  CEO
구/군/시 이름을 입력하십시오?
  [Unknown]:  Seoul
시/도 이름을 입력하십시오.
  [Unknown]:  Seoul
이 조직의 두 자리 국가 코드를 입력하십시오.
  [Unknown]:  KR
CN=Cherrue, OU=CHERRUE, O=CEO, L=Seoul, ST=Seoul, C=KR이() 맞습니까?
  [아니오]:  Y

$ ls
keystore.p12 pom.xml      src

application.properties 에 정보 작성

# application.properties
server.ssl.key-store=keystore.p12
server.ssl.key-store-type=PKCS12
server.ssl.key-store-password=123456
server.ssl.key-alias=spring

8-2. 실행

https://localhost:8080 로 접속하면 비공개 설정이라고 빨간 화면을 띄움

브라우저가 공식 인증서가 아니라서 표시하는 위험 표시. 고급에 그래도 접속하기 버튼을 눌러서 들어감

$ curl -I -k https://localhost:8080/hello
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Date: Fri, 28 Jan 2022 14:18:08 GMT
8-3. http2가 무시되었네?

기본 커넥터가 하나라서 https를 설정하면 http/2를 주어도 https만 된다. 커넥터를 두 개 사용하도록 설정해보자

# application.properties
server.port = 8443
@SpringBootApplication
@RestController
public class Application {
    @GetMapping("/hello")
    public String hello() {
        return "Hello Spring!\n";
    }
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    @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;
    }
}

결과

❯ curl -I --http2 https://localhost:8443/hello -k                                                                                                                                   23:26:17
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Date: Fri, 28 Jan 2022 14:26:21 GMT

❯ curl -I --http2 http://localhost:8080/hello -k                                                                                                                                    23:26:21
HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 14
Date: Fri, 28 Jan 2022 14:26:31 GMT

HTTP/2

http/2를 쓰려면 ssl은 적용이 꼭 되어있어야 함

8-4. connector 설정 bean은 제거하고 application.properties에 설정 추가

# application.properties
server.http2.enabled=true

Undertow는 위 설정만 해주면 바로 됨. tomcat8.X대는 복잡하다.(java9 + tomcat9 이상이면 괜찮다)

8-5. undertow로 내장 웹서버 변경

// pom.xml
		<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
    </dependencies>

결과

❯ curl -I --http2 https://localhost:8443/hello -k                                                                                                                                   23:39:55
HTTP/2 200 
content-type: text/plain;charset=UTF-8
content-length: 14
date: Fri, 28 Jan 2022 14:39:57 GMT
9. 톰캣 + HTTP2 적용

9-1. pom.xml

  • java.version = 9
  • tomcat.version = 9.0.10 (9.X 이상 버전)

9-2. intellij 설정

  • Project Structure > Project Settings > Project > Project SDK : 9(java version “9”)
  • Project Structure > Project Settings > Modules > Dependencies 탭 > Module SDK : 9(java version “9”)
10. 독립적으로 실행 가능한 JAR

mvn clean : 메이븐 하위의 파일 삭제

mvn package : 패키징 (jar로 만드는 것)

  • mvn package -DskipTests : 테스트 스킵
  • java -jar 만들어진 파일.jar
  • dependencies가 하나의 jar에 다 들어간다 그 jar를 읽을 수 있는 loader가 같이 말린다 경로 : org/spring/framework/boot/loader/*
  • main을 실행하는 것 = JarLauncher. WarLauncher, PropertyLauncher 도 있음 MANIFEST.MF > Main-Class 가 Start-Class 를 읽어서 실행 MANIFEST.MF > Main-Class를 읽는 것은 Java의 스펙. 그 뒤를 스프링이 추가한 것
11. 스프링 부트 원리 정리

11-1. 의존성 관리

  • spring-boot-starter-web : 스프링 부트 의존성 관리
  • Parent > spring-boot-parent > spring-boot-dependencies : 진짜 의존성 관리 내역
  • parent VS pom.xml 에서 dependency 로 추가하는 것은 완전히 다르다.

11-2. 자동 설정

  • @SpringBootApplication
    • @EnableAutoConfiguration : 2단계. spring.factories (@ConditionalOnMissingBean 등의 조건을 보고 설정)
    • @ComponentScan : 1단계. 소스코드의 @Component 등

11-3. 내장 웹서버

  • 스프링 부트는 독립적으로 실행되는 것이 목표 : 내장 웹서버(default Tomcat) 을 가진다.
  • 스프링부트는 서버가 아니다. Auto Configuration으로 설정한 웹 서버를 이용함

댓글남기기