-
[03] 내장 Web Application ServerSpring/Spring boot 2020. 5. 20. 08:55반응형
Spring boot 내장 WAS에 대해 살펴보기 전에 정확한 용어를 확인하도록 하겠습니다.
정적(static) page
- client에서 request가 있을 때 항상 정해진 내용으로만 response 하는 page를 말합니다.
- 정적인 요소는 html, css, 그 밖의 image를 포함한 resource들이 있습니다.
동적(dynamic) page
- client의 request에 대해 server에서 데이터를 가공하여 client에 맞는 내용을 response 하는 page를 말합니다.
Web Server
- client의 request에 대해 정적인 page를 http protocol을 사용하여 제공합니다.
- client로 부터 동적 page에 대한 request를 받으면 이를 처리할 수 있는 container로 전달하는 역할도 합니다.
Container
- 동적 page에 대한 request를 받아 적절하게 가공한 후 정적인 page로 제공하는 software module입니다.
Web Application Server(WAS) 또는 Application Server(AS)
- Web Server와 Container의 기능을 동시에 할 수 있는 server입니다. 동적 page request를 처리합니다.
- WAS의 종류
- Tomcat (Apache project)
- Sun Application Server
- JBOSS (RedHat)
- Jeus (TMAX)
- Web Spere (IBM)
- Web Logic (BEA)
[Spring boot의 Web Application Server(WAS)]
Spring boot는 default 내장 Web Application Server(WAS)로 tomcat을 사용합니다. 처음 project를 설정하고 의존성을 확인해보면 tomcat과 관련된 의존성이 추가되어있는 것을 확인해 볼 수 있습니다.
Spring code를 사용하지 않고도 아래 그림의 순서대로 java code를 작성하면 tomcat을 구동할 수 있습니다.
하지만 위에 언급한 것 외에도 Tomcat 설정을 비롯한 많은 부분을 구현해야 하는 수고가 따르게 됩니다. 이런 불편함을 덜기 위해 Spring boot는 자동 설정을 통해 내장 WAS를 사용할 수 있게 해 줍니다.
Spring boot의 spring-boot-autoconfigure project의 META-INF directory 아래 위치한 spring.factories file을 열어보면 spring boot 자동 설정과 관련된 내용이 들어있습니다.
1. ServletWebServerFactoryAutoConfiguration
-Servlet web server에 대한 자동 설정입니다.
-TomcatWebServerFactoryCustomizer class에서 Tomcat server를 Customizing 합니다.
2. DispatcherServletAutoConfiguration
-DispatcherServletAutoConfiguration class에서 HttpServlet을 상속하여 DispatcherServlet을 만들고 등록합니다.
Servelt Web Server는 설정에 따라 달라질 수 있으나 Servlet은 항상 그대로이기 때문에 Servlet Web Server의 설정과 Servlet 생성 및 등록 작업을 나눠서 관리하고 있습니다.
[내장 WAS의 변경]
Spring boot는 내장 was로 tomcat을 default로 사용하도록 되어있습니다.
Servlet 기반의 Web MVC application을 개발을 할 때 의존성으로 tomcat이 들어가 있습니다. 그래서 자동 설정(@condtionalonclass)에 의해 tomcat용 자동 설정 file로 tomcat을 만들어 사용하게 됩니다.
다른 containter를 사용하기 위해 Spring boot 의존성 설정에서 tomcat을 빼고 원하는 container를 추가해 줍니다.
<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-jetty</artifactId> </dependency> </dependencies>
① spring-boot-starter-tomcat을 org.springframework.boot에서 관리하고 있는 의존성 목록에서부터 제외합니다.
② 의존성에 spring-boot-starter-jetty를 추가하여 새로운 container로 jetty를 사용하도록 합니다.
의존성을 확인해보면 tomcat이 빠지고 jetty가 추가되었습니다.
project를 실행해보면 jetty web server가 8080 port에서 실행됩니다.
project를 web application으로 실행하지 않는 방법 세 가지
- main method에서 SpringApplication 객체의 setWebApplicationType() method에 매개변수로 WebApplicationType.NONE을 넘겨줍니다.
- 의존성에서 기본 WAS인 tomcat을 제외해줍니다.
- /resources/application.properties file의 내용에 spring.main.web-application-type=none 을 추가합니다.
특정 port를 지정해서 Web Application Server를 실행하기 원하면 /resources/application.properties file의 내용에 server.port=port번호를 추가합니다. port를 지정하고 application을 실행한 결과는 다음과 같습니다. 정상적으로 지정한 port에서 WAS가 시작되었습니다.
Ramdom port에서 WAS를 실행하려면 /resources/application.properties file의 server.port property value를 0 으로 지정합니다.
WAS와 WAS가 실행되고 있는 port 정보를 확인하는 방법은 ServletWebServerInitializedEvent 객체의 getApplicationContext() method를 통해 ServletWebServerApplicationContext type의 객체를 얻어온 후 그 객체의 getWebServer() method와 getPort() method를 chaining 하여 WAS객체에 접근하여 WAS가 실행되고 있는 port 번호를 알 수 있습니다.
@Component public class PortListener implements ApplicationListener<ServletWebServerInitializedEvent> { // ① @Override public void onApplicationEvent(ServletWebServerInitializedEvent servletWebServerInitializedEvent) { // ② ServletWebServerApplicationContext applicationContext = servletWebServerInitializedEvent.getApplicationContext(); // ③ System.out.println(applicationContext.getWebServer().getPort()); } }
① Web application server가 초기화(생성) 되면 event listener인 onApplicationEvent()가 호출됩니다.
② Event에서 web application context 정보를 꺼냅니다.
③ Application context는 ServletWebServerApplicationContext이기 때문에 web application server(getWebServer() method)와 그 port(getPort() method)를 알 수 있습니다.
[HTTPS를 사용하여 Service 하기]
Server는 client의 request가 변조된 것이 아닌지 확인할 수 있고 client는 server의 request가 신뢰할 수 있는 request임을 보증받을 수 있기 때문에 HTTPS를 사용합니다. (보다 자세한 내용은 https://opentutorials.org/course/228/4894 를 참고해주세요.)
HTTPS 적용 예제를 진행하기 앞서 새로운 project를 생성합니다. (Project 생성 방법)
HTTPS를 사용하려면 우선 keystore를 만들어야 합니다. Terminal 창을 열어 다음 명령어를 입력해주세요.
keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000
① -genkey : key를 생성할 때 사용하는 option입니다.
② -alias : key의 별명을 지정하는 option입니다.
③ -storetype : store type을 설정하는 option입니다. Servlet container마다 지원하는 store type이 다릅니다. 주로 지원되는 store type에는 PKCS, JKS 등이 있습니다.
④ validity : key의 유효기간을 설정하는 option입니다. 설정 단위는 일입니다.
keytool이 실행되고 몇 가지 입력 사항을 요구합니다. Test를 위해 임의로 생성하는 key이기 때문에 가짜 정보로 대충 입력해 줍니다. 정보 입력이 완료되면 입력한 정보를 묻는데 마지막으로 Y 또는 yes를 입력해주면 key를 생성합니다.
정상적으로 keystore가 만들어진 것을 볼 수 있습니다.
생성된 keystore를 spring boot project에서 사용하기 위해 application.properties file에 ssl과 관련된 key/value pair를 입력해야 하는데, key 생성 시 입력했던 option 값을 기준으로 입력합니다.
#keystore가 root에 있는 경우 server.ssl.key-store=keystore.p12 #keystore가 class path에 있는 경우 #server.ssl.key-store=classpath:keystore.p12 server.ssl.key-store-type=PKCS12 server.ssl.key-store-password=123456 server.ssl.key-alias=tomcat
application.properties file의 편집을 완료하고 project를 실행합니다.
browser를 열어 http://localhost:8080/로 접근하면 server로의 연결이 거부됩니다.
protocol을 https로 변경하여 https://localhost:8080/로 다시 접속을 시도합니다. (browser가 알고 있는 public key provider가 아니기 때문에 안전하지 않음으로 뜨지만 무시하고 진행하면 됩니다.)
https로 request를 보내면 정상적으로 response 됩니다.
[HTTPS와 HTTP를 모두 사용하는 방법]
Tomcat의 connector는 기본적으로 하나만 등록되는데 그 connector에 위와 같이 SSL을 적용한 경우 server로 전송하는 모든 request는 https를 통해 보내야 합니다.
만약 http, https reqeust를 모두 받을 수 있도록 하려면 각각의 protocol마다 port를 달리해서 connector를 하나 더 추가해야 합니다. connector 추가를 위해 main class를 다음과 같이 변경합니다.
@SpringBootApplication @RestController public class Application { @GetMapping("/hello") public String hello() { return "Hello Spring"; } public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.run(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; } }
8080 port는 http가 사용하게 되었으므로 https는 다른 port를 사용하도록 설정합니다. 여기서는 8443을 사용하도록 하겠습니다. 이를 위해 application.properties file의 내용을 다음과 같이 변경합니다.
server.ssl.key-store=keystore.p12 server.ssl.key-store-type=PKCS12 server.ssl.key-store-password=123456 server.ssl.key-alias=tomcat server.port=8443
두 protocol에 대해 모두 request를 받을 수 있는지 확인해보기 위해 curl 명령을 사용할 수 있습니다.
위와 같이 protocol이 변경돼도 protocol이 사용하는 port를 통해 request를 날리면 HTTP 응답 code가 200이 떠서 request가 성공적으로 수행되었음을 확인할 수 있습니다.
[HTTP2 활성화 방법 with Undertow]
Http2 사용은 web application server마다 그 설정이 차이가 납니다. Undertow를 사용하는 경우는 https를 적용한 상태에서 application.properties file의 내용에 http2 사용하도록 설정만 하면 http2 request를 처리할 수 있게 됩니다.
pom.xml file을 열어 의존성에서 tomcat을 빼주고 undertow를 넣어줍니다.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.dave</groupId> <artifactId>spring-boot-example_0523</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.7.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--① begin--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> <!--① end--> </dependency> <!--② begin--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <!--② end--> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
① 의존성을 받을 때 tomcat을 제외합니다.
② undertow 의존성을 추가해 줍니다.
main class의 내용은 다음과 같이 수정합니다. (Spring boot 기동에 필요한 code + /hello request를 처리할 수 있는 cotroller가 추가된 구조입니다.)
@SpringBootApplication @RestController public class Application { @GetMapping("/hello") public String hello() { return "Hello Spring"; } public static void main(String[] args) { SpringApplication app = new SpringApplication(Application.class); app.run(args); } }
http2 사용 설정이 추가된 application.properties file의 내용은 다음과 같습니다.
server.ssl.key-store=keystore.p12 server.ssl.key-store-type=PKCS12 server.ssl.key-store-password=123456 server.ssl.key-alias=tomcat server.port=8443 server.http2.enabled=true #http2를 활성화 합니다.
linux나 macOS의 경우 curl로 server에 request를 날려 protocol을 확인할 수 있습니다.(curl -I -k --http2 https://localhost:8443/hello) Windows에서는 browser의 개발자 도구로 protocol을 확인하도록 합니다. 먼저 chrome을 실행하고 F12를 눌러 개발자 도구를 실행하고 Network menu를 클릭합니다.
Ctrl+R로 Network log를 기록하도록 하고 log 영역에서 우 click 합니다.
Header Options를 선택하고 Protocol을 click 하여 check 하면 Log 정보에 protocol이 보입니다.
이제 browser에서 https://localhost:8443/hello로 접속해 봅니다. 개발자 도구를 확인해보면 protocol column에 h2(http2)를 사용했음을 확인할 수 있습니다.
[HTTP2 활성화 방법 with Tomcat]
Tomcat에서 http2를 사용하려면 tomcat 9 이상, JDK 9이상 version을 사용하길 권장합니다. (tomcat version은 mvnrepository.com에서 확인할 수 있습니다.)
pom.xml에서 <properties>안에 <java.version>과 <tomcat.version>을 각각 9와 9.0.35으로 지정합니다. 전체 pom.xml code는 다음과 같습니다.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.dave</groupId> <artifactId>spring-boot-example_0523</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.7.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <!--http2를 사용할 수 있는 Java와 tomcat version으로 지정합니다. --> <java.version>9</java.version> <tomcat.version>9.0.35</tomcat.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Ctrl+Shift+A 또는 Shift key를 연속으로 두 번 눌러 검색창을 열고 project structure를 입력하거나 Ctrl+Alt+Shfit+S로 project structure menu를 열어줍니다.
Project Settings > Project에서 Project SDK를 Java9로 변경합니다.
Project Settings > Modules에서 Dependencies tab의 Module SDK를 Java 9로 변경합니다.
Project를 실행한 뒤 undertow로 http2를 사용했을 때와 동일하게 test를 진행하면 http2 protocol을 사용하여 통신하고 있음을 확인할 수 있습니다.
'Spring > Spring boot' 카테고리의 다른 글
[05] Spring Application (0) 2020.05.28 [04] 독립으로 실행가능한 JAR file (0) 2020.05.26 [02] Spring boot 자동 설정 (0) 2020.05.15 [01] Spring boot 의존성 관리 (0) 2020.05.12 [00] Maven으로 Spring Boot Project 생성 (0) 2020.05.11