인프런에서 백기선님의 스프링 부트 개념과 활용을 수강하고 개인적으로 공부한 내용을 정리한 글입니다.

스프링 부트 ≠ 서버

Spring 프레임워크를 쉽게 사용할 수 있게해주는 Tool일 뿐, 스프링 부트 자체는 웹 서버가 아니다.

내장 서블릿 컨테이너 개요

spring-boot-starter-web을 사용한 프로젝트엔 톰캣(Tomcat)이 기본적으로 내장되어 있다. 톰캣은 데이터를 동적으로 처리하기 위한 서블릿 컨테이너로, JSP, 서블릿 처리, HTTP 요청과 응답 등을 처리한다.

자동 설정이 없다고 가정하고 Servlet을 만들어서 톰캣에 등록해보자.

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        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("<h1>Hello world!</h1>");
            }
        };

        final String servletName = "myServlet";
        // 톰캣에 서블릿 추가
        tomcat.addServlet("/", servletName, servlet);
        // 컨텍스트에 서블릿 매핑
        context.addServletMappingDecoded("/home", servletName);

        try {
            // 톰캣 실행 및 대기
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }
}

웹 브라우저를 켜서 http://localhost:8080/home으로 접속해보면 doGet()에서 작성한 내용이 HTML 문서로 반환된다.

스프링에서는 이렇게 Tomcat에 Servlet을 등록하고 실행하는 일련의 작업이 자동 설정으로 정의되어 있다. 자동 설정 덕분에 이 모든 작업을 상세하고 유연하게 설정 후 실행해준다.

  • ServletWebServerFactoryAutoConfiguration : 서블릿 웹 서버 생성하는 설정 파일

    • TomcatServletWebServerFactory를 들어가보면 톰캣을 생성하고 서블릿, 디스패처 설정을 하는 코드가 있다.

        public WebServer getWebServer(ServletContextInitializer... initializers) {
                Tomcat tomcat = new Tomcat();
                File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
                tomcat.setBaseDir(baseDir.getAbsolutePath());
                Connector connector = new Connector(this.protocol);
                tomcat.getService().addConnector(connector);
                this.customizeConnector(connector);
                tomcat.setConnector(connector);
                tomcat.getHost().setAutoDeploy(false);
                this.configureEngine(tomcat.getEngine());
                // ...
        }

      ServletWebServerFactoryAutoConfiguration에서는 TomcatServletWebServerFactory를 사용하는 TomcatServletWebServerFactoryCustomizer를 반환한다.

        @Configuration
        @AutoConfigureOrder(-2147483648)
        @ConditionalOnClass({ServletRequest.class})
        @ConditionalOnWebApplication(
            type = Type.SERVLET
        )
        @EnableConfigurationProperties({ServerProperties.class})
        @Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
        public class ServletWebServerFactoryAutoConfiguration {
            // ...
              public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
                  return new TomcatServletWebServerFactoryCustomizer(serverProperties);
              }
            // ...
        }
  • DispatcherServletAutoConfiguration : 서블릿을 만들고 등록해주는 설정 파일

내장 서블릿 컨테이너 활용 방법

내장 웹 서버 변경

spring-boot-starter-web는 기본적으로 spring-boot-starter-tomcat 을 포함하고 있다. Tomcat 대신 Jetty를 내장 서버로 사용하도록 설정을 변경해보자.

pom.xml에서 dependencies를 아래와 같이 변경한다. tomcat의 include를 제거하기 위해 <exclusion> 태그를 추가했다.

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

다시 프로젝트를 빌드하고 로그를 확인해 보면 jetty를 사용하고 있다.

포트 변경하기

HTTP는 기본적으로 8080 포트를 사용하지만, resources/application.properties을 사용해서 포트 번호를 변경할 수 있다.

server.port=7070

포트가 잘 바뀌었는지 확인해보자. ServletWebServerInitializedEventonApplicationEvent는 Application이 생성되면 호출되는 콜백 메서드다.

@Component
public class PortListener implements ApplicationListener<ServletWebServerInitializedEvent> {

    @Override
    public void onApplicationEvent(ServletWebServerInitializedEvent servletWebServerInitializedEvent) {
        WebServer webServer = servletWebServerInitializedEvent.getApplicationContext().getWebServer();
        System.out.println("port: " + webServer.getPort());
    }
}

포트 번호를 고정하는 대신 랜덤 포트를 사용하고 싶다면 값을 0으로 설정하면 된다.

server.port=0

References