ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [03] 내장 Web Application Server
    Spring/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과 관련된 의존성이 추가되어있는 것을 확인해 볼 수 있습니다.

    project 생성시 추가된 tomcat관련 의존성

     

    Spring code를 사용하지 않고도 아래 그림의 순서대로 java code를 작성하면 tomcat을 구동할 수 있습니다. 

    Tomcat 구동을 위해 구현해야 할 code 순서

     

    하지만 위에 언급한 것 외에도 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가 추가되었습니다.

     

    Container 의존성 변경 확인

    project를 실행해보면 jetty web server가 8080 port에서 실행됩니다.

     

     

    project를 web application으로 실행하지 않는 방법 세 가지

    1. main method에서 SpringApplication 객체의 setWebApplicationType() method에 매개변수로 WebApplicationType.NONE을 넘겨줍니다.
    2. 의존성에서 기본 WAS인 tomcat을 제외해줍니다.
    3. /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가 시작되었습니다.

    server port 변경 후 실행 결과

     

     

    Ramdom port에서 WAS를 실행하려면 /resources/application.properties file의 server.port property value를 0 으로 지정합니다.

     

    random port에서 container 기동

    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를 생성합니다.

    keytool을 사용한 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

    댓글

Designed by Tistory.