본문 바로가기
Java, Kotlin, Spring/Spring, Spring Boot

Springboot - 내장 웹 서버, HTTPS, HTTP2

by Wordbe 2021. 1. 14.
728x90

 

 

스프링부트는 내장 웹 서버로 톰캣이 자동 설정되어있다.

spring-boot-starter-web이 톰캣을 가져온다.

톰캣은 아래와 같이 직접 구현하여 실행시킬 수도 있다. (대부분은 스프링부트로 바로 실행시킬 것이다..)

public class Application {

    public static void main(String[] args) throws LifecycleException {
        // SpringApplication.run(Application.class, args);

        // 1 Tomcat 객체 생성
        Tomcat tomcat = new Tomcat();

        // 2 포트 설정, 커넥터 연결
        tomcat.setPort(8080);
        tomcat.getConnector();

        // 3 컨텍스트 추가
        Context context = tomcat.addContext("/", "/");

        // 4 서블릿 생성 (웹 페이지)
        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("My Tomcat Server");
                writer.println("</title></head>");
                writer.println("<body><p>Hello Tomcat</p></body>");
                writer.println("</html>");
            }
        };

          // 5 톰캣에 서블릿 추가
        String servletName = "helloServlet";
        tomcat.addServlet("/", servletName, servlet);

          // 6 컨텍스트에 서블릿 매핑 (url 에 해당 서블릿 매핑)
        context.addServletMappingDecoded("/hello", servletName);

          // 7 톰캣 실행 및 서버가동 유지
        tomcat.start();
        tomcat.getServer().await();
    }

}

스프링부트는 위 일련의 과정을 상세하고 유연하게 자동설정해준다.

ServletWebServerFactoryAutoConfiguration 은 서블릿 웹 서버를 생성하고, 여기서 TomcatServletWebServerFactoryCustomizer 를 통해 서버를 커스터마이징 한다. (1 ~ 2 과정)

또한 DispatcherServletAutoConfiguration 에서 서블릿을 만들고 등록한다. (3 ~ 6 과정)

 

 

 


컨테이너, 서버 포트

tomcat 대신 jetty, undertow, netty 등의 다른 웹서버로 바꿀 수 있다. (단, netty 는 webflux)

메이븐에서 exclusion 태그에 tomcat 을 담아 톰캣을 제외시키고, 다른 디펜던시로 (예를들면) undertow 로 교체할 수 있다.

pom.xml

<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>

 

 

또한 간단한 프로퍼티 등록으로 다양한 웹서버 기능을 사용할 수 있다.

application.properties

# 스프링부트 실행 시 웹애플레키이션을 실행시키지 않음.
spring.main.web-application-type=none

# 포트번호를 지정
server.port=7070

# 포트번호를 임의로 지정 (Random Port)
server.port=0

 

 

또한 프로퍼티 파일말고도, 빈 설정을 통해 포트 정보를 확인하고 설정할 수 있다.

ApplicationListener<ServletWebServerInitializedEvent> 의 구현체를 만들고 메소드를 이용하면 된다.

@Component
public class PortListener implements ApplicationListener<ServletWebServerInitializedEvent> {

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

 

 

 

 


HTTP, HTTP2

 

https 서버 접근

스프링부트에서 웹서버를 실행할 때 localhost:8080 으로 보통 실행시킨다.

여기서 https 를 사용하도록 해보자.

우선 로컬에서 키스토어(인증서) 를 하나 만들자.

keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000

그리고 프로퍼티 설정에 방금 만들어준 키스토어 정보를 입력해준다. 그러면 스프링부트는 이 속성을 읽어서 https 인증서를 브라우저에게 전송할 것이다.

 

application.properties

server.ssl.key-store=keystore.p12
server.ssl.key-store-password=123456
server.ssl.keyStoreType=PKCS12
server.ssl.keyAlias=tomcat
server.port=8443
server.http2.enabled=true

일반적인 인증기관에서 발급하는 pub key는 대부분의 브라우저가 알고 있다.

하지만 로컬에서 인증서를 만들면 브라우저는 무슨 인증서인지 알지 못한다. 그래서 브라우저 주소창에 url을 치면 https 이지만 인증서를 모르기 때문에 접근할 지 되묻는다.

https 에서 인증서 오류

(크롬 브라우저 기준) 밑에 고급을 눌러서 localhost(안전하지 않음)로 이동을 눌러주면 된다.

혹시 그런 링크가 없을 시에는 브라우저를 다시 시작하고, 오른쪽 세로 점 3개 아이콘 클릭 > 설정 > 안전확인 > 보안 > 인증서 관리 > 인증서 이름 검색 > 신뢰 > 이 인증서 사용 시 "항상 신뢰" 로 변경해 준다. (mac 기준)

이제 웹사이트에 접속된 것이 보인다. 물론 코딩해놓은 것이 없어서 Whitelabel Error Page 가 보일 것이다. 하지만 서버는 연결된 것이다.

 

curl 명령어로 콘솔에서 서버 정보를 받아올 수 도 있다.

$ curl -I -k https://localhost:8443/

curl 명령어 옵션

  • -k : https 사이트를 SSL certificate 검증 없이 연결한다.
  • -I : Information 으로 HTTP 응답 유형, 결과, content-type, content-length, Date 를 출력한다.

 

 

 


새로운 커넥터 활성화

프로퍼티 파일에 https 설정을 했는데, http 서버도 다른 포트로 열고 싶다면, 아래처럼 코딩을 해서 http 커넥터를 만들 수 있다.

@SpringBootApplication
public class Application {

    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;
    }
}

이렇게 하고 애플리케이션을 실행시키면, 포트 8443 에서는 https 서버가, 포트 8080에서는 http 서버가 열린 것이 보일 것이다.

 


HTTPS2 활성화

application.properties

# http2 활성화 추가
server.http2.enabled=true

이 조건만 추가하면 http2 통신도 사용할 수 있다.

$ curl -I -k https://localhost:8443/
>> HTTP/2 404 
>> ...

 


실행가능한 jar 파일

애플리케이션을 실행시킬 때 주로 사용하는 메이븐 명령어이다.

# target 폴더 및 하위 파일 모두 삭제
$ mvn clean

# target 폴더 생성, jar 파일 생성
$ mvn package

# maven 라이프사이클 중 테스트 건너뛰기(시간단축)
$ mvn package -DskipTests

# 스프링부트 실행
$ java -jar target/springgoman-0.0.1-SNAPSHOT.jar

mvn package 명령 실행 시 jar 하나를 만들어 주는데, 이 파일 하나로 스프링부트를 실행할 수 있다.

spring-boot-maven-plugin 은 스프링부트 프로젝트를 패키징해준다. (pom.xml 설정파일에 의존성이 추가되어 있다.)

자바에는 내장 jar 를 로딩하는 표준적 방법은 없다. 따라서 같은 이름의 파일은 애플리케이션 클래스와 라이브러리의 경로를 구분한다.org.springframework.boot.loader.jar.JarFile 은 내장 jar를 읽고 org.springframework.boot.loader.Launcher 가 어플리케이션을 실행시킨다.

 

 

 

 

728x90

댓글