Junjangsee's Blog

SpringBoot - 내장 웹 서버

2019-04-17

images

내장 웹 서버 이해하기

  • SpringBoot는 Spring을 보다 쉽게 사용할 수 있도록 내장 웹 서버를 제공합니다.
  • 기본적으로 Tomcat이 내장되어 있지만 어떻게 사용되고 있는지 이해하기 위해 직접 구현해보며 이해합시다.


Tomcat 라이브러리

auto 라이브러리에 위 세개가 내장되어있는 Tomcat입니다.
이 라이브러리를 사용하지 않고 직접 Tomcat을 구동시켜보겠습니다.

새로운 프로젝트를 생성하고 Application에는 SpringBoot에 관련된 어노테이션을 설정하지 않은 상태로 둔 가정하에 진행합니다.

auto Tomcat을 new 연산자로 생성하고 포트 번호를 지정합니다.
그리고 경로를 지정한 후에 start 명령을 선언합니다.

정상적으로 구동된다면 오류 없이 Tomcat이 실행된 것을 확인할 수 있습니다.


서버 충돌시 조치방법

하지만! 서버 충돌 오류가 난다면 포트가 기존에 사용되고 있을 확률이 높습니다.

1
2
ps ax | grep tomcat
kill -9 번호

위 명령어를 통해서 실행된 포트를 지우시고 다시 시도해봅니다.


Servlet 추가하기

이제 Servlet을 추가하여 구동된 Tomcat을 확인합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Application {

public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();

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>");
writer.println("Hey, Tomcat");
writer.println("</title></head>");
writer.println("<body><h1>Hello Tomcat</h1></body>");
writer.println("</html>");
}
};

String servletName = "helloServlet";
tomcat.addServlet("/", servletName, servlet);
context.addServletMappingDecoded("/hello", servletName);

tomcat.start();
tomcat.getServer().await();

}

}

auto Get메소드를 생성하여 Hello Tomcat이 정상적으로 출력되는지 확인합니다.

auto 정상적으로 출력되는 것을 확인할 수 있습니다.

이처럼 자동으로 설정하지 않더라도 직접 Tomcat을 구동할 수도 있습니다. 하지만 이런 역할을 SpringBoot에서 자동으로 해주는데 우리는 @SpringBootApplication이 실행될 때 AutoConfiguration을 통해 자동으로 설정된다는 것을 인지해야합니다.


spring.factories 자동설정은 ?

auto spring.factories에서 Servlet에 관련된 것을 확인할 수 있습니다.
여기서 DispatcherServlet과 Servlet이 나뉜 이유는 ServletContainer는 매번 달라지지만 DispatcherServlet은 만들어진 Container에 등록만 하면 되기 때문에 별도로 나뉘게 된 것입니다.


내장 웹 서버 응용 - 컨테이너, 포트

내장 웹 서버를 이해했다면 이제 활용하는 법을 알아봅시다.

auto 현재 의존성에는 tomcat이 자동 설정을 통해 자동으로 웹 애플리케이션을 만들어주고 있습니다.
그렇다면 자동 설정되는 WAS(Tomcat)을 바꾸려면 어떻게 해야할까요?


WAS 바꾸는 방법

그냥 의존성을 추가하는 것이 아니라 exclusion을 통해 실행시 제외 시키고 WAS(jetty, undertow, etc…)를 추가해야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

auto web이 시작할 때 제외를 시켜야 하므로 spring-boot-starter-web에서 tomcat을 제외시킵니다.

auto jetty가 추가된 것을 확인할 수 있습니다.

auto 물론 결과도 jetty로 웹 애플리케이션이 실행됩니다.


웹 서버 사용하지 않는 @SpringBootApplication 설정

스프링 레퍼런스 홈페이지 - 웹서버
아래의 두 방법이 있습니다.

1
2
3
4
5
6
7
8
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
}
}

@SpringBootApplication에 타입을 NONE으로 설정하는 방법


웹 서버 사용하지 않는 application.properties 설정

1
spring.main.web-application-type=none

auto properties에 타입을 NONE으로 설정하는 방법


포트 변경 방법

스프링 레퍼런스 홈페이지 - 포트변경방법

1
2
server.port=포트번호
server.port=0

auto properties를 통해 원하는 포트를 선언할 수 있습니다.
그리고 포트번호를 0으로 주게 되면 사용가능한 포트번호를 알아서 찾아 구동합니다.


포트 리스너를 통해 포트번호 알아내기

웹서버가 생성이 되면 servletWebServer가 실행되고 이 이벤트 리스너 핸들러가 호출이 되고 onApplicationEvent 콜백이 실행됩니다.
먼저 포트리스너를 구현하기 위해 클래스를 생성합니다.

1
2
3
4
5
6
7
8
@Component
public class PortListener implements ApplicationListener<ServletWebServerInitializedEvent> {
@Override
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
ServletWebServerApplicationContext applicationContext = event.getApplicationContext();
applicationContext.getWebServer().getPort();
}
}

auto 포트번호를 출력한 모습입니다.


응답을 압축해서 보내기

스프링 레퍼런스 홈페이지 - 응답압축

1
server.compression.enabled=true

위 코드를 통해 응답을 압축해서 보낼 수 있습니다.


내장 웹 서버 응용 - HTTPS, HTTP2

실습하기 위해 새로운 프로젝트를 생성합니다.

Key-Store 생성하기

HTTPS를 사용하기 위해서는 Key-Store를 사용해야 합니다.

1
2
3
4
5
6
7
keytool -genkey
-alias tomcat
-storetype PKCS12
-keyalg RSA
-keysize 2048
-keystore keystore.p12
-validity 4000

auto
위 명령어 terminal을 통해 입력합니다.
생성할 때 나오는 항목들은 본인 임의로 작성해도 됩니다.
최종으로 맞는지 물어보면 y를 눌러 생성하면 됩니다.

auto 위 처럼 생성된 것을 볼 수 있습니다.


Key-Store(HTTPS) 실행하기

생성한 Key-Store는 application.properties에서 설정할 수 있습니다.

1
2
3
4
server.ssl.key-store=keystore.p12
server.ssl.key-store-password=123456
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=spring

auto server.ssl.key-store=classpath:keystore.p12는 keystore의 위치를 찾아낼 때 사용하는 명령어입니다. 지금은 초기폴더에 있어 따로 설정하지 않았지만 다른 곳에 생성하였다면 루트를 설정해주어야 합니다.

auto
auto 앞으로 모두 HTTPS 요청을 붙여야합니다.
그런데 여기서 빨간색으로 오류가 뜨는 것을 볼 수 있습니다.
이유는 만든 인증서가 공인된 인증서가 아니기 때문입니다. 하지만 무시하고 진행하면 값은 출력이 됩니다.

이렇게 HTTPS를 설정하면 커넥터(Connector)가 하나이기 때문에 다른 설정이 불가능합니다. 이제부터 이를 극복하는 설정법을 알아보도록 하겠습니다.


Connector(HTTP) 설정하기

1
2
3
4
5
6
7
8
9
10
11
12
@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;
}

auto 위 코드를 Application에 작성합니다.
그리고 기존 HTTPS가 적용되어있는 포트는 8443으로 변경해줍니다.

이렇게 되면 HTTPS도 받고 HTTP도 받게 됩니다.


HTTP2 사용하기

시작 전 HTTP를 활성화 하기 위한 코드는 삭제합니다.

1
server.http2.enabled=true

위 코드로 HTTP2를 활성화 할 수 있습니다.

하지만 이건 undertow 서버에서만 해당되는 것이고 tomcat에서는 다릅니다. 각자 사용하는 서버별 설정방법을 확인하시려면 서버별 HTTP2 설정방법 페이지로 이동하셔서 실습하시면 되겠습니다.
혹시 Intelli J를 사용하신다면

  • File - Project Structure - Project
  • File - Project Structure - Modules - Dependencies

위 설정들을 통해 Java 버전을 바꾸어야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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>

위 코드는 pom.xml에서 undertow서버를 설정하는 방법입니다.

http2로 실행시키면 결과가 출력될 것입니다.