6. Developing with Spring Boot

이번 장에서는 스프링 부트를 사용하는 방법에 대해 자세히 설명한다. 빌드 시스템, 자동 구성(auto-configuration), 애플리케이션 실행 방법 등의 주제를 다룬다. 또한 몇 가지 스프링 부트 모범 사례도 다룬다. 스프링 부트에는 특별한 것은 없지만(사용할 수 있는 또 다른 라이브러리일 뿐이다), 개발 프로세스를 좀 더 쉽게 만들어 주는 몇 가지 권장 사항이 있다.

스프링 부트를 이제 막 시작하는 경우 시작하기 가이드를 먼저 읽어야 한다.

6.1. Build Systems

의존성 관리를 지원하고 “메이븐 센트럴” 저장소(repository)에 게시된 아티팩트를 사용할 수 있는 빌드 시스템을 선택하는 것이 좋다. 메이븐이나 그레이들을 선택하는 것이 좋다. 스프링 부트가 다른 빌드 시스템(예: 앤트(Ant))과 작동하도록 하는 것이 가능하지만 특별히 잘 지원되지는 않는다.

6.1.1. Dependency Management

스프링 부트의 각 릴리스는 지원하는 의존성 목록을 제공한다. 실제로는 스프링 부트가 이를 관리하므로 빌드 구성에서 의존성 라이브러이의 버전을 제공할 필요가 없다. 스프링 부트 자체를 업그레이드하면 이러한 의존성도 일관된 방식으로 업그레이드된다.

필요한 경우 버전을 지정하고 스프링 부트 의존성 라이브러리의 권장 버전을 오버라이드 할 수 있다.

큐레이트된(curated) 목록에는 스프링 부트와 함께 사용할 수 있는 모든 스프링 모듈과 서드파티 라이브러리 목록이 포함되어 있다. 이 목록은 메이븐과 그레이들 모두에서 사용할 수 있는 표준 BOM(spring-boot-dependent)으로 제공된다.

스프링 부트의 각 릴리스는 스프링 프레임워크의 버전과 연관되어 있다. 해당 버전은 지정하지 않는 것이 좋다.

6.1.2. Maven

메이븐과 함께 스프링 부트를 사용하는 방법을 알아보려면, 스프링 부트의 메이븐 플러그인 설명서를 참고하자:

6.1.3. Gradle

그레이들과 함께 스프링 부트를 사용하는 방법을 알아보려면, 스프링 부트의 그레이들 플러그인 설명서를 참고하자:

6.1.4. Ant

아파치 앤트+아이비를 사용하여 스프링 부트 프로젝트를 빌드할 수 있다. spring-boot-antlib “AntLib” 모듈은 앤트가 실행 가능한 jar를 생성하는 데 도움을 주기도 한다. 의존성을 선언하기 위한 일반적인 ivy.xml 파일은 다음 예와 같다:

    <ivy-module version="2.0">
        <info organisation="org.springframework.boot" module="spring-boot-sample-ant" />
        <configurations>
            <conf name="compile" description="everything needed to compile this module" />
            <conf name="runtime" extends="compile" description="everything needed to run this module" />
        </configurations>
        <dependencies>
            <dependency org="org.springframework.boot" name="spring-boot-starter" rev="${spring-boot.version}" conf="compile" />
        </dependencies>
    </ivy-module>

일반적인 build.xml은 다음 예와 같다:

    <project
        xmlns:ivy="antlib:org.apache.ivy.ant"
        xmlns:spring-boot="antlib:org.springframework.boot.ant"
        name="myapp" default="build">
        <property name="spring-boot.version" value="3.1.1" />
        <target name="resolve" description="--> retrieve dependencies with ivy">
            <ivy:retrieve pattern="lib/[conf]/[artifact]-[type]-[revision].[ext]" />
        </target>
        <target name="classpaths" depends="resolve">
            <path id="compile.classpath">
                <fileset dir="lib/compile" includes="*.jar" />
            </path>
        </target>
        <target name="init" depends="classpaths">
            <mkdir dir="build/classes" />
        </target>
        <target name="compile" depends="init" description="compile">
            <javac srcdir="src/main/java" destdir="build/classes" classpathref="compile.classpath" />
        </target>
        <target name="build" depends="compile">
            <spring-boot:exejar destfile="build/myapp.jar" classes="build/classes">
                <spring-boot:lib>
                    <fileset dir="lib/runtime" />
                </spring-boot:lib>
            </spring-boot:exejar>
        </target>
    </project>

spring-boot-antlib 모듈을 사용하지 않으려면 앤트에서 실행 가능한 압축파일 구축 “How-to”를 참고하자.

6.1.5. Starters

스타터는 애플리케이션에 포함할 수 있는 편리한 의존성 세트이다. 샘플 코드를 검색하고 많은 의존성 라이브러리을 복사하여 붙여넣을 필요 없이 모든 스프링 및 관련 기술에 대한 설정을 원스톱으로 얻을 수 있다. 예를 들어, 데이터베이스 접근를 위해 스프링 및 JPA를 사용하여 시작하려면 프로젝트에 spring-boot-starter-data-jpa 의존성을 포함할 수 있다.

스타터에는 프로젝트를 신속하게 시작하고 실행하는 데 필요한 많은 의존성이 포함되어 있으며, 일관되게 지원되는 관리형 전이적 의존성(managed transitive dependencies) 세트가 포함되어 있다.

What is in a name

모든 공식 스타터는 비슷한 명명 패턴을 따른다. spring-boot-starter-*, 여기서 *는 특정 타입의 애플리케이션이다. 이 명명 구조는 스타터를 찾아야 할 때 도움을 주기 위한 것입니다. 많은 IDE의 메이븐 통합을 통해 명칭으로 의존성을 검색할 수 있다. 예를 들어 이클립스 또는 스프링 툴 플러그인이 설치된 경우 POM 편집기에서 ctrl-space를 누르고 “spring-boot-starter”를 입력하면 전체 목록을 볼 수 있다.

“자신만의 스타터 만들기” 절에 설명된 대로 서드 파티 스타터는 공식 스프링 부트 아티팩트용으로 예약되어 있으므로 spring-boot로 시작해서는 안 된다. 오히려 서드파티 스타터는 일반적으로 프로젝트 이름으로 시작한다. 예를 들어 thirdpartyproject라는 서드파티 스타터 프로젝트명은 일반적으로 thirdpartyproject-spring-boot-starter이다.

다음 애플리케이션 스타터는 스프링 부트의 org.springframework.boot 그룹으로 제공된다:

테이블 1. 스프링 부트 애플리케이션 스타터

명칭설명
spring-boot-starter자동 구성 지원, 로깅 및 YAML을 포함한 핵심 스타터
spring-boot-starter-activemq아파치 액티브MQ(ActiveMQ)를 사용하는 JMS 메시징용 스타터
spring-boot-starter-amqp스프링 AMQP 및 래빗(Rabbit) MQ 사용을 위한 스타터
spring-boot-starter-aop스프링 AOP 및 애스펙트J(AspectJ)를 사용한 관점 지향 프로그래밍을 위한 스타터
spring-boot-starter-artemis아파치 아르테미스(Artemis)를 사용하는 JMS 메시징용 스타터
spring-boot-starter-batch스프링 배치를 사용하기 위한 스타터
spring-boot-starter-cache스프링 프레임워크의 캐싱 지원을 사용하기 위한 스타터
spring-boot-starter-data-cassandra카산드라 분산 데이터베이스 및 스프링 데이터 카산드라 사용을 위한 스타터
spring-boot-starter-data-cassandra-reactive카산드라 분산 데이터베이스 및 스프링 데이터 카산드라 리액티브를 사용하기 위한 스타터
spring-boot-starter-data-couchbase카우치베이스(Couchbase) 도큐먼트 중심 데이터베이스와 스프링 데이터 카우치베이스를 사용하기 위한 스타터
spring-boot-starter-data-couchbase-reactive카우치베이스(Couchbase) 도큐먼트 중심 데이터베이스와 스프링 데이터 카우치베이스 리액티브를 사용하기 위한 스타터
spring-boot-starter-data-elasticsearch엘라스틱 서치 및 분석 엔진과 스프링 데이터 엘라스틱 서치를 사용하기 위한 스타터
spring-boot-starter-data-jdbc스프링 데이터 JDBC 사용을 위한 스타터
spring-boot-starter-data-jpa하이버네이트와 함께 스프링 데이터 JPA를 사용하기 위한 스타터
spring-boot-starter-data-ldap스프링 데이터 LDAP 사용을 위한 스타터
spring-boot-starter-data-mongodb몽고DB 도큐먼트 지향 데이터베이스와 스프링 데이터 몽고DB를 사용하기 위한 스타터
spring-boot-starter-data-mongodb-reactive몽고DB 도큐먼트 지향 데이터베이스와 스프링 데이터 몽고DB 리액티브를 사용하기 위한 스타터
spring-boot-starter-data-neo4j네오4j 그래프 데이터베이스와 스프링 데이터 네오4j를 사용하기 위한 스타터
spring-boot-starter-data-r2dbc스프링 데이터 R2DBC 사용을 위한 스타터
spring-boot-starter-data-redis스프링 데이터 레디스 및 레터스(Lettuce) 클라이언트와 함께 레디스 키-값 데이터 스토어를 사용하기 위한 스타터
spring-boot-starter-data-redis-reactive스프링 데이터 레디스 리액티브 및 레터스(Lettuce) 클라이언트와 함께 레디스 키-값 데이터 스토어를 사용하기 위한 스타터
spring-boot-starter-data-rest스프링 데이터 REST 및 스프링 MVC를 사용하여 REST를 통해 스프링 데이터 리포지터리를 노출하기 위한 스타터
spring-boot-starter-freemarker프리메이커(FreeMarker) 뷰를 사용하여 MVC 웹 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-graphql스프링 그래프QL(GraphQL)을 사용하여 그래프QL 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-groovy-templates그루비 템플릿 뷰를 사용하여 MVC 웹 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-hateoas스프링 MVC 및 스프리 HATEOAS를 사용하여 하이퍼미디어 기반 RESTful 웹 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-integration스프링 인테그레이션 사용을 위한 스타터
spring-boot-starter-jdbc히카리CP(HikariCP) 커넥션 풀과 함께 JDBC를 사용하기 위한 스타터
spring-boot-starter-jerseyJAX-RS 및 저지(Jersey)를 사용하여 RESTful 웹 애플리케이션을 구축하기 위한 스타터. spring-boot-starter-web의 대안이다.
spring-boot-starter-jooqjOOQ를 사용하여 JDBC로 SQL 데이터베이스에 접근하기 위한 스타터. spring-boot-starter-data-jpa 또는 spring-boot-starter-jdbc의 대안이다.
spring-boot-starter-jsonjson을 읽고 쓰기 위한 스타터
spring-boot-starter-mail자바 메일 및 스프링 프레임워크의 이메일 전송 지원을 사용하기 위한 스타터
spring-boot-starter-mustache머스태치(Mustache) 뷰를 사용하여 웹 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-oauth2-authorization-server스프링 Authorization 서버 기능을 사용하기 위한 스타터
spring-boot-starter-oauth2-client스프링 시큐리티의 OAuth2/OpenID Connect 클라이언트 기능을 사용하기 위한 스타터
spring-boot-starter-oauth2-resource-server스프링 시큐리티의 OAuth2 리소스 서버 기능을 사용하기 위한 스타터
spring-boot-starter-quartz쿼츠(Quartz) 스케줄러 사용을 위한 스타터
spring-boot-starter-rsocketRSocket 클라이언트 및 서버 구축을 위한 스타터
spring-boot-starter-security스프링 시큐리티 사용을 위한 스타터
spring-boot-starter-testJUnit 주피터(Jupiter), 햄크레스트(Hamcrest) 및 모키토(Mockito)를 포함한 라이브러리를 사용하여 스프링 부트 애플리케이션을 테스트하기 위한 스타터
spring-boot-starter-thymeleafThymeleaf 뷰를 사용하여 MVC 웹 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-validation하이버네이트 밸리데이터(Validator)와 함께 자바 빈(Bean) 밸리데이터를 사용하기 위한 스타터
spring-boot-starter-web스프링 MVC를 사용하여 RESTful, 애플리케이션을 포함한 웹 구축을 위한 스타터. 톰캣을 기본 내장 컨테이너로 사용한다.
spring-boot-starter-web-services스프링 웹 서비스 사용을 위한 스타터
spring-boot-starter-webflux스프링 프레임워크의 리액티브 웹 지원을 사용하여 웹플럭스(WebFlux) 애플리케이션을 구축하기 위한 스타터
spring-boot-starter-websocket스프링 프레임워크의 MVC 웹소켓(WebSocket) 지원을 사용하여 웹소켓t 애플리케이션을 구축하기 위한 스타터

애플리케이션 스타터 외에도, 다음 스타터를 사용하여 프로덕션 준비 기능을 추가할 수 있다:

테이블 2. 스프링 부트 프로덕션 스타터

명칭설명
spring-boot-starter-actuator애플리케이션을 모니터링하고 관리하는 데 도움이 되는 프로덕션 준비 기능을 제공하는 스프링 부트의 액추에이터(Actuator)를 사용하기 위한 스타터

마지막으로, 스프링 부트에는 특정 기술 측면을 제외하거나 교체하려는 경우 사용할 수 있는 다음 스타터도 있다:

테이블 3. 스프링 부트 기술 스타터

명칭설명
spring-boot-starter-jetty제티(Jetty)를 임베디드 서블릿 컨테이너로 사용하기 위한 스타터. spring-boot-starter-tomcat의 대안이다.
spring-boot-starter-log4j2로깅을 위해 Log4j2를 사용하기 위한 스타터. spring-boot-starter-logging의 대안
spring-boot-starter-logging로그백(Logback)을 사용하는 로깅을 위한 스타터. 기본(Default) 로깅 스타터
spring-boot-starter-reactor-netty리액터 네티를 내장된 반응형 HTTP 서버로 사용하기 위한 스타터
spring-boot-starter-tomcat톰캣을 내장 서블릿 컨테이너로 사용하기 위한 스타터. spring-boot-starter-web에서 사용되는 기본 서블릿 컨테이너 스타터
spring-boot-starter-undertow언더토우를 임베디드 서블릿 컨테이너로 사용하기 위한 스타터. spring-boot-starter-tomcat의 대안

기술적 측면을 교체하는 방법을 알아보려면, 웹 서버 교체로깅 시스템 교체에 대한 방법 문서를 참고하자.

추가 커뮤니티 기여 스타터 목록은 깃헙의 spring-boot-starters 모듈에 있는 README 파일을 참고하자.

6.2. Structuring Your Code

스프링 부트는 작동하기 위해 특정 코드 레이아웃이 필요하지 않다. 그러나 도움이 되는 몇 가지 모범 사례가 있다.

6.2.1. Using the “default” Package

클래스에 패키지 선언이 포함되어 있지 않으면, “기본 패키지(default package)”에 있는 것으로 간주된다. “기본 패키지”의 사용은 일반적으로 권장되지 않으며 피해야 한다. 모든 jar의 모든 클래스를 읽기 때문에 @ComponentScan, @ConfigurationPropertiesScan, @EntityScan 또는 @SpringBootApplication 어노테이션을 사용하는 스프링 부트 애플리케이션에 특정 문제가 발생할 수 있다.

자바에서 권장하는 패키지 명명 규칙을 따르고 역방향 도메인 이름(예: com.example.project)을 사용하는 것이 좋다.

6.2.2. Locating the Main Application Class

일반적으로 기본 애플리케이션 클래스를 다른 클래스 위의 루트 패키지에 배치하는 것이 좋다. @SpringBootApplication 어노테이션은 Main 클래스에 배치되는 경우가 많으며 특정 항목에 대한 기본 “검색 패키지”를 암시적으로 정의한다. 예를 들어 JPA 애플리케이션을 구축하는 경우 @SpringBootApplication 어노테이션이 달린 클래스 패키지는 @Entity 항목을 검색하는 데 사용된다. 루트 패키지를 사용하면 컴포넌트 스캔을 일부 프로젝트에만 적용할 수도 있다.

@SpringBootApplication을 사용하지 않으려면, 가져오는 @EnableAutoConfiguration@ComponentScan 어노테이션이 해당 동작을 정의하므로 이를 대신 사용할 수도 있다.

다음 목록은 일반적인 레이아웃을 보여준다:

 com
   +- example
       +- myapplication
           +- MyApplication.java
           |
           +- customer
           |   +- Customer.java
           |   +- CustomerController.java
           |   +- CustomerService.java
           |   +- CustomerRepository.java
           |
           +- order
               +- Order.java
               +- OrderController.java
               +- OrderService.java
               +- OrderRepository.java

MyApplication.java 파일은 @SpringBootApplication과 함께 main 메소드를 다음과 같이 선언한다:

자바

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        } 
    }

코틀린

    import org.springframework.boot.autoconfigure.SpringBootApplication
    import org.springframework.boot.runApplication

    @SpringBootApplication
    class MyApplication

    fun main(args: Array<String>) {
        runApplication<MyApplication>(*args)
    }

6.3. Configuration Classes

스프링 부트는 자바 기반 구성을 선호한다. XML과 함께 SpringApplication을 사용하는 것이 가능하지만, 코드가 @Configuration 클래스인 것이 좋다. 일반적으로 main 메소드를 정의하는 클래스는 @Configuration으로 좋은 후보다.

XML을 사용하는 많은 스프링 구성 예제가 인터넷에 있다. 가능하다면 항상 자바 기반 구성을 사용하자. Enable* 어노테이션을 검색하는 것이 좋은 시작점이 될 수 있다.

6.3.1. Importing Additional Configuration Classes

모든 @Configuration을 싱글 클래스에 넣을 필요는 없다. @Import 어노테이션을 사용하여 추가 구성 클래스를 가져올 수 있다. 또는 @ComponentScan을 사용하여 @Configuration 클래스를 포함한 모든 스프링 컴포넌트를 자동으로 선택할 수 있다.

6.3.2. Importing XML Configuration

반드시 XML을 사용해야 하는 경우에도 @Configuration 클래스로 시작하는 것이 좋다. 그런 다음 @ImportResource 어노테이션을 사용하여 XML 구성 파일을 로드할 수 있다.

6.4. Auto-configuration

스프링 자동 구성(auto-configuration)은 추가한 jar 의존성을 기반으로 스프링 애플리케이션을 구성한다. 예를 들어, HSQLDB가 클래스패스에 있고 데이터베이스 커넥션 빈(Bean)을 수동으로 입력하지 않은 경우 스프링 부트는 인메모리 데이터베이스를 자동으로 구성한다.

@Configuration 클래스 중 하나에 @EnableAutoConfiguration 또는 @SpringBootApplication 어노테이션을 추가하여 자동 구성을 선택해야 한다.

@SpringBootApplication 또는 @EnableAutoConfiguration 어노테이션은 하나만 추가해야 한다. 일반적으로 @Configuration 클래스에만 둘 중 하나를 추가하는 것이 좋다.

6.4.1. Gradually Replacing Auto-configuration

자동 구성(Auto-configuration)은 비침투적(non-invasive)이다. 언제든지, 자동 구성의 특정 부분을 대체하기 위한 자체 구성을 정의할 수 있다. 예를 들어, 자신만의 데이터소스 빈(DataSource Bean)을 추가하면 임베디드 데이터베이스 지원이 중단된다.

현재 적용되고 있는 자동 구성과 그 이유를 알아내려면 –debug 스위치를 사용하여 애플리케이션을 시작해보자. 이렇게 하면 선택한 코어 로거(logger)에 대한 디버그 로그가 활성화되고 상태 보고서가 콘솔에 기록된다.

6.4.2. Disabling Specific Auto-configuration Classes

원하지 않는 특정 자동 구성 클래스가 있는 경우, 다음 예제와 같이 @SpringBootApplication의 제외 애트리뷰트를 사용하여 해당 클래스를 비활성화할 수 있다:

자바

    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    
    @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
    public class MyApplication {
    }

코틀린

    import org.springframework.boot.autoconfigure.SpringBootApplication
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
    
    @SpringBootApplication(exclude = [DataSourceAutoConfiguration::class])
    class MyApplication

클래스가 클래스패스에 없으면 어노테이션의 excludeName 애트이뷰트를 사용하는 대신 자격있는 이름(fully qualified name)을 지정할 수 있다. @SpringBootApplication 대신 @EnableAutoConfiguration을 사용하려는 경우에는 excludeexcludeName도 사용할 수 있다. 마지막으로 spring.autoconfigure.exclude 프로퍼티를 사용하여 제외할 자동 구성 클래스 목록을 제어할 수도 있다.

어노테이션 레벨과 프로퍼티를 사용하여 제외를 정의할 수 있다.

자동 구성(auto-configuration) 클래스가 공개(public) 클래스이더라도, 공개 API 클래스로의 유일한 측면은 자동 구성을 비활성화하기 위해 사용하는 클래스명이다. 중첩된 구성 클래스 또는 빈 메소드와 같은 해당 클래스의 실제 컨텐츠는 내부용으로 사용되며 직접 사용하지 않는 것이 좋다.

6.5. Spring Beans and Dependency Injection

표준 스프링 프레임워크 기술을 자유롭게 사용하여 빈과 주입된 의존성을 정의할 수 있다. 일반적으로 생성자 인젝션(constructor injection)을 사용하여 의존성을 연결하고, @ComponentScan을 사용하여 빈을 찾는 것이 좋다.

위에서 제안한 대로 코드를 구성하는 경우(최상위 패키지에 애플리케이션 클래스 찾기) 아규먼트 없이 @ComponentScan을 추가하거나 이를 암시적으로 포함하는 @SpringBootApplication 어노테이션을 사용할 수 있다. 모든 애플리케이션 컴포넌트(@Component, @Service, @Repository, @Controller 등)는 자동으로 스프링 빈으로 등록된다.

다음 예는 필수 RiskAssessor 빈을 얻기 위해 생성자 인젝션을 사용하는 @Service 빈을 보여준다:

자바

    import org.springframework.stereotype.Service;

    @Service
    public class MyAccountService implements AccountService {
        private final RiskAssessor riskAssessor;

        public MyAccountService(RiskAssessor riskAssessor) {
            this.riskAssessor = riskAssessor;
        }

        // ... 
    }

코틀린

    import org.springframework.stereotype.Service

    @Service
    class MyAccountService(private val riskAssessor: RiskAssessor) : AccountService

빈에 하나 이상의 생성자가 있는 경우 스프링에서 사용하려는 생성자를 @Autowired로 표시해야 한다:

자바

    import java.io.PrintStream;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class MyAccountService implements AccountService {
        private final RiskAssessor riskAssessor;
        private final PrintStream out;
        
        @Autowired
        public MyAccountService(RiskAssessor riskAssessor) {
            this.riskAssessor = riskAssessor;
            this.out = System.out;
        }

        public MyAccountService(RiskAssessor riskAssessor, PrintStream out) {
            this.riskAssessor = riskAssessor;
            this.out = out;
        }
        // ... 
    }

코틀린

    import org.springframework.beans.factory.annotation.Autowired
    import org.springframework.stereotype.Service
    import java.io.PrintStream

    @Service
    class MyAccountService : AccountService {
        private val riskAssessor: RiskAssessor
        private val out: PrintStream
        
        @Autowired
        constructor(riskAssessor: RiskAssessor) {
            this.riskAssessor = riskAssessor
            out = System.out
        }
        
        constructor(riskAssessor: RiskAssessor, out: PrintStream) {
            this.riskAssessor = riskAssessor
            this.out = out
        }
        // ... 
    }

생성자 인젝션을 사용하면 RiskAssessor 필드가 파이널(final, val)로 표시되어 이후에 변경할 수 없음을 나타내는 것에 주목하자.

6.6. Using the @SpringBootApplication Annotation

대부분의 스프링 부트 개발자는 자동 구성, 컴포넌트 스캔을 사용하고 “애플리케이션 클래스”에 추가 구성을 정의할 수 있는 것을 좋아한다. 싱글 @SpringBootApplication 어노테이션을 사용하여 다음 세 가지 기능을 활성화할 수 있다:

  • @EnableAutoConfiguration: 스프링 부트의 자동 구성 메커니즘을 활성화한다.
  • @ComponentScan: 애플리케이션이 있는 패키지에서 @Component 스캔을 활성화한다 (참고: 모범 사례)
  • @SpringBootConfiguration: 컨텍스트에서 추가 빈을 등록하거나 추가 구성 클래스를 가져올 수 있다. 통합 테스트에서 구성 감지를 돕는 스프링의 표준 @Configuration의 대안이다.

자바

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    // @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan과 동일하다
    @SpringBootApplication
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        } 
    }

코틀린

    import org.springframework.boot.autoconfigure.SpringBootApplication
    import org.springframework.boot.runApplication

    // @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan과 동일하다
    @SpringBootApplication
    class MyApplication
  
    fun main(args: Array<String>) {
        runApplication<MyApplication>(*args)
    }

Note @SpringBootApplication@EnableAutoConfiguration@ComponentScan의 어트리뷰트를 커스텀하기 위한 별칭(aliases)도 제공한다. 이러한 기능은 필수 사항이 아니며 이 싱글 어노테이션을 활성화하여 대체할 수 있다. 예를 들어, 애플리케이션에서 컴포넌트 스캔이나 컨피규레이션 프로퍼티 스캔을 사용하고 싶지 않을 수 있다:

자바

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.SpringBootConfiguration;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.context.annotation.Import;

    @SpringBootConfiguration(proxyBeanMethods = false)
    @EnableAutoConfiguration
    @Import({ SomeConfiguration.class, AnotherConfiguration.class })
    public class MyApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
        } 
    }

코틀린

    import org.springframework.boot.SpringBootConfiguration
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration
    import org.springframework.boot.docs.using.structuringyourcode.locatingthemainclass.MyApplication
    import org.springframework.boot.runApplication
    import org.springframework.context.annotation.Import

    @SpringBootConfiguration(proxyBeanMethods = false)
    @EnableAutoConfiguration
    @Import(SomeConfiguration::class, AnotherConfiguration::class)
    class MyApplication
    
    fun main(args: Array<String>) {
        runApplication<MyApplication>(*args)
    }

이 예제에서 MyApplication@Component 어노테이션이 달린 클래스와 @ConfigurationProperties 어노테이션이 달린 클래스가 자동으로 감지되지 않고, 커스텀 빈을 명시적으로 가져오는 점을 제외하면 다른 스프링 부트 애플리케이션과 같다(@Import 참고).

6.7. Running Your Application

애플리케이션을 jar로 패키징하고 임베디드 HTTP 서버를 사용하는 것의 가장 큰 장점 중 하나는 다른 애플리케이션과 마찬가지로 애플리케이션을 즉시 실행할 수 있다는 것이다. 이 샘플은 스프링 부트 애플리케이션 디버깅에 적용된다. 특별한 IDE 플러그인이나 확장이 필요하지 않다.

이 절에서는 jar 기반 패키징만 다룬다. 애플리케이션을 war 파일로 패키징하기로 선택한 경우 서버 및 IDE 설명서를 참고하자.

6.7.1. Running From an IDE

IDE에서 스프링 부트 애플리케이션을 자바 애플리케이션으로 실행할 수 있다. 그러나 먼저 프로젝트를 가져와야 한다. 가져오기 단계는 IDE 및 빌드 시스템에 따라 다르다. 대부분의 IDE는 메이븐 프로젝트를 직접 가져올 수 있다. 예를 들어 이클립스 사용자는 File 메뉴에서 Import… → Existing Maven Projects를 선택할 수 있다.

프로젝트를 IDE로 직접 가져올 수 없는 경우, 빌드 플러그인을 사용하여 IDE 메타데이터를 생성할 수 있다. 메이븐에는 이클립스IDEA용 플러그인이 포함되어 있다. 그레이들은 다양한 IDE용 플러그인을 제공한다.

실수로 웹 애플리케이션을 두 번 실행한 경우 “이미 사용 중인 포트” 오류가 표시된다. 스프링 툴(Spring Tools) 사용자는 Run 버튼 대신 Relaunch 버튼을 사용하여 기존 인스턴스가 닫혔는지 확인할 수 있다.

6.7.2. Running as a Packaged Application

스프링 부트 메이븐 또는 그레이들 플러그인을 사용하여 실행 가능한 jar를 생성한 경우, 다음 예제와 같이 java -jar을 사용하여 애플리케이션을 실행할 수 있다:

    $ java -jar target/myapplication-0.0.1-SNAPSHOT.jar

원격 디버깅 지원이 활성화된 패키지 애플리케이션을 실행할 수도 있다. 이렇게 하면 다음 예제와 같이 패키지된 애플리케이션에 디버거를 연결할 수 있다:

    $ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n \
        -jar target/myapplication-0.0.1-SNAPSHOT.jar

6.7.3. Using the Maven Plugin

스프링 부트 메이븐 플러그인에는 애플리케이션을 빠르게 컴파일하고 실행 시 사용할 수 있는 실행 목표가 포함되어 있다. 애플리케이션은 IDE에서와 마찬가지로 분해된 형태로 실행된다. 다음 예에서는 스프링 부트 애플리케이션을 실행하는 메이븐 명령을 보여준다:

    $ mvn spring-boot:run

다음 예와 같이 MAVEN_OPTS 운영 체제 환경 변수를 사용할 수도 있다.

    $ export MAVEN_OPTS=-Xmx1024m

6.7.4. Using the Gradle Plugin

스프링 부트 그레이들 플러그인에는 분해된 형식으로 애플리케이션을 실행하는 데 사용할 수 있는 bootRun 태스크가 포함되어 있다. bootRun 태스크는 org.springframework.boot자바 플러그인을 적용하면 자동 추가되며 다음 예제에 나타난다:

    $ gradle bootRun

다음 예와 같이 JAVA_OPTS 운영 체제 환경 변수를 사용할 수도 있다:

    $ export JAVA_OPTS=-Xmx1024m

6.7.5. Hot Swapping

스프링 부트 애플리케이션은 일반 자바 애플리케이션이므로 JVM 핫 스와핑(hot-swapping)은 즉시 작동해야 한다. JVM 핫 스와핑은 대체할 수 있는 바이트코드에 따라 다소 제한된다. 보다 완벽한 솔루션을 위해 JRebel을 사용할 수 있다.

spring-boot-devtools 모듈에는 빠른 애플리케이션 재시작 지원이 포함되어 있다. 자세한 내용은 핫 스와핑 “사용 방법”을 참고하자.

6.8. Developer Tools

스프링 부트에는 애플리케이션 개발 경험을 좀 더 즐겁게 만들 수 있는 추가 툴 세트가 포함되어 있다. spring-boot-devtools 모듈은 추가 development-time 기능을 제공하기 위해 모든 프로젝트에 포함될 수 있다. devtools 지원을 사용하려면 메이븐 및 그레이들에 대한 모듈 의존성을 추가하자:

메이븐

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

그레이들

    dependencies {
        developmentOnly("org.springframework.boot:spring-boot-devtools")
    }

Devtools는 특히 멀티 모듈 프로젝트에서 클래스 로딩 문제를 일으킬 수 있다. 클래스 로딩 문제 진단에서는 문제를 진단하고 해결하는 방법을 설명한다.

완전히 패키지된 애플리케이션을 실행하면 개발자 도구가 자동으로 비활성화된다. 애플리케이션이 java -jar에서 시작되거나 특수 클래스로더에서 시작되면 “프로덕션 애플리케이션”으로 간주된다. spring.devtools.restart.enabled 시스템 프로퍼티를 사용하여 이 동작을 제어할 수 있다. 애플리케이션을 시작하는 데 사용된 클래스로더에 관계없이 devtools를 활성화하려면 -Dspring.devtools.restart.enabled=true 시스템 프로퍼티를 설정해보자. devtools은 보안 위협 될 수 있으므로 프로덕션 환경에서 이 작업을 수행해서는 안 된다. devtools를 비활성화하려면 의존성을 제외하거나 -Dspring.devtools.restart.enabled=false 시스템 프로퍼티를 설정하자.

메이븐에서 의존성을 옵셔널로 표시하거나 그레이들에서 developmentOnly 구성을 사용하면(위에 표시된 대로) devtools가 프로젝트를 사용하는 다른 모듈에 전이적으로(transitively) 적용되는 것을 방지할 수 있다.

리패키징된 압축파일에는 기본적으로 devtools가 포함되어 있지 않다. 특정 원격 devtools 기능을 사용하려면 해당 기능을 포함해야 한다. 메이븐 플러그인을 사용하는 경우, excludeDevtools 프로퍼티를 false로 설정하자. 그레이들 플러그인을 사용할 때, developmentOnly 구성을 포함하도록 태스크의 클래스패스를 구성한다.

6.8.1. Diagnosing Classloading Issues

재시작과 리로드 절에 설명된 대로 재시작 기능은 두 개의 클래스로더를 사용하여 구현된다. 대부분의 애플리케이션에서는 이 접근 방식이 잘 작동한다. 그러나 멀티 모듈 프로젝트에서 클래스 로딩 문제가 발생할 수 있다.

클래스 로딩 문제가 실제로 devtools 및 두 클래스로더로 인해 발생했는지 진단하려면 재시작을 비활성화해보자. 문제가 해결되면 전체 프로젝트를 포함하도록 재시작 클래스로더를 커스텀하자.

6.8.2. Property Defaults

스프링 부트에서 지원하는 여러 라이브러리는 성능을 향상시키기 위해 캐시를 사용한다. 예를 들어, 템플릿 엔진은 템플릿 파일을 반복적으로 파싱하는 것을 방지하기 위해 컴파일된 템플릿을 캐시한다. 또한 스프링 MVC는 스태틱(static) 리소스를 제공할 때 응답(response)에 HTTP 캐싱 헤더를 추가할 수 있다.

캐싱은 프로덕션에서 매우 유익하지만 개발 중에는 비생산적일 수 있으므로 방금 애플리케이션에서 변경한 내용을 볼 수 없게 된다. 이러한 이유로 spring-boot-devtools는 기본적으로 캐싱 옵션을 비활성화한다.

캐시 옵션은 일반적으로 application.properties 파일의 설정으로 구성된다. 예를 들어 Thymeleafspring.thymeleaf.cache 프로퍼티를 제공한다. 이러한 프로퍼티를 수동으로 설정할 필요 없이 spring-boot-devtools 모듈은 합리적으로 development-time 구성을 자동 적용한다.

다음 표에는 적용되는 모든 프로퍼티가 나열되어 있다:

명칭기본 값
server.error.include-binding-errorsalways
server.error.include-messagealways
server.error.include-stacktracealways
server.servlet.jsp.init-parameters.developmenttrue
server.servlet.session.persistenttrue
spring.docker.compose.readiness.waitonly-if-started
spring.freemarker.cachefalse
spring.graphql.graphiql.enabledtrue
spring.groovy.template.cachefalse
spring.h2.console.enabledtrue
spring.mustache.servlet.cachefalse
spring.mvc.log-resolved-exceptiontrue
spring.reactor.netty.shutdown-quiet-period0s
spring.template.provider.cachefalse
spring.thymeleaf.cachefalse
spring.web.resources.cache.period0
spring.web.resources.chain.cachefalse

프로퍼티의 기본값을 적용하지 않으려면 application.properties에서 spring.devtools.add-propertiesfalse로 설정할 수 있다.

스프링 MVC 및 스프링 웹플럭스 애플리케이션을 개발하는 동안 웹 요청에 대한 추가 정보가 필요하기 때문에 개발자 도구에서는 웹(web) 로깅 그룹에 대해 DEBUG 로깅으로 제안한다. 그러면 들어오는 요청, 이를 처리하는 처리기, 응답 결과 및 기타 세부 정보에 대한 정보가 제공된다. 모든 요청 세부 정보(잠재적으로 민감한 정보 포함)를 기록하려면 spring.mvc.log-request-details 또는 spring.codec.log-request-details 구성 프로퍼티를 켤 수 있다.

6.8.3. Automatic Restart

spring-boot-devtools를 사용하는 애플리케이션은 클래스패스의 파일이 변경될 때마다 자동으로 재시작된다. 이는 코드 변경에 대한 매우 빠른 피드백 루프를 제공하므로 IDE에서 작업할 때 유용한 기능이 될 수 있다. 기본적으로 디렉토리를 가리키는 클래스패스의 모든 항목은 변경 사항을 모니터링한다. 스태틱 자원(static assets) 및 뷰 템플릿과 같은 특정 리소스는 애플리케이션을 재시작할 필요가 없다.

Triggering a restart DevTools이 클래스패스 리소스를 모니터링하므로, 재시작을 트리거하는 유일한 방법은 클래스패스를 업데이트하는 것이다. IDE를 사용하든 빌드 플러그인 중 하나를 사용하든 재시작을 트리거하려면 수정된 파일을 재컴파일해야 한다. 클래스패스를 업데이트하는 방법은 사용 중인 도구에 따라 다르다:

  • 이클립스에서 수정된 파일을 저장하면 클래스패스가 업데이트되고 재시작된다.
  • 인텔리J IDEA에서 프로젝트 빌드(Build +→+ Build Project)도 동일한 효과를 갖는다.
  • 빌드 플러그인을 사용하는 경우 메이븐용 mvn compile 또는 그레이들 gradle build를 실행하면 재시작된다.

빌드 플러그인을 사용하여 메이븐 또는 그레이들을 재시작하는 경우 분기 설정(forking set)을 활성화된 상태로 두어야 한다. 분기 설정을 비활성화하면 devtools에서 사용하는 격리된 애플리케이션 클래스로더가 생성되지 않고 재시작이 제대로 작동하지 않는다.

LiveReload와 함께 사용하면 자동 재시작이 매우 잘 작동한다. 자세한 내용은 LiveReload 절을 참고하자. JRebel을 사용하는 경우 다이나믹 클래스 리로드를 위해 자동 재시작이 비활성화된다. 다른 devtools 기능(예: LiveReload 및 프로퍼티 오버라이드)은 계속 사용할 수 있다.

DevTools은 애플리케이션 컨텍스트의 종료 후크를 사용하여 재시작하는 동안 이를 닫는다. 종료 후크(SpringApplication.setRegisterShutdownHook(false))를 비활성화한 경우 올바르게 작동하지 않는다.

DevTools애플리케이션컨텍스트(ApplicationContext)에서 사용하는 리소스로더(ResourceLoader)를 커스텀해야 한다. 애플리케이션이 이미 제공한 경우, 래핑된다. 애플리케이션컨텍스트에서 getResource 메소드를 오버라이드하는 것은 지원되지 않는다.

AspectJ 위빙(weaving)을 사용할 때는 자동 재시작이 지원되지 않는다.

Restart vs Reload

스프링 부트에서 제공하는 재시작(restart) 기술은 두 개의 클래스로더를 사용하여 작동한다. 변경되지 않는 클래스(예: 서드 파티 jar의 클래스)는 기본 클래스로더에 로드된다. 현재 개발 중인 클래스는 재시작 클래스로더에 로드된다. 애플리케이션이 재시작되면 재시작 클래스 로더가 삭제되고, 새 클래스 로더가 생성된다. 이 접근 방식은 기본 클래스로더가 이미 사용 가능하며 채워져 있으므로 애플리케이션 재시작이 일반적으로 “콜드 스타트(cold starts)”보다 훨씬 빠르다는 것을 의미한다.

애플리케이션을 재시작하는 것이 충분히 빠르지 않거나 클래스 로딩 문제가 발생하는 경우 ZeroTurnaround에서 JRebel과 같은 기술을 재로드하는 것을 고려할 수 있다. 이는 클래스가 로드될 때 클래스를 재작성하여 재로드하기 쉽게 만드는 방식으로 작동한다.

Logging Changes in Condition Evaluation

기본적으로, 애플리케이션이 재시작될 때마다, 조건 평가 델타(evaluation delta)를 보여주는 보고서가 기록된다. 보고서에는 빈 추가 또는 제거, 구성 속성 설정 등 변경 작업을 수행할 때 애플리케이션의 자동 구성에 대한 변경 사항이 표시된다.

보고서 로깅을 비활성화하려면 다음 프로퍼티를 설정하자:

프로퍼티스(Properties)

    spring.devtools.restart.log-condition-evaluation-delta=false

Yaml

    spring:
        devtools:
            restart:
                log-condition-evaluation-delta: false

Excluding Resources

특정 리소스는 변경될 때 반드시 재시작을 트리거할 필요가 없다. 예를 들어 Thymeleaf 템플릿은 내부에서 편집할 수 있다. 기본적으로 /META-INF/maven, /META-INF/resources, /resources, /static, /public 또는 /templates에서 리소스를 변경하면 재시작(restart)이 트리거되지 않지만 라이브 리로드(live reload)가 트리거된다. 이러한 제외를 커스텀하려면, spring.devtools.restart.exclude 프로퍼티를 사용할 수 있다. 예를 들어 /static/public만 제외하려면 다음 프로퍼티를 설정하자:

프로퍼티스(Properties)

    spring.devtools.restart.exclude=static/**,public/**

Yaml

    spring:
        devtools:
            restart:
                exclude: "static/**,public/**"

이러한 기본값을 유지하고 추가 제외를 설정하려면 대신 spring.devtools.restart.additional-exclude 프로퍼티를 사용하자.

Watching Additional Paths

클래스패스에 없는 파일을 변경할 때 애플리케이션을 재시작하거나 리로드할 수 있다. 그렇게 하려면 spring.devtools.restart.additional-paths 프로퍼티를 사용하여 변경 사항을 감시할 추가 경로를 구성해야한다. 앞에서 설명한 spring.devtools.restart.exclude 프로퍼티를 사용하여 추가 경로 아래 변경 사항이 전체 재시작 또는 라이브 리로드를 트리거하는지 여부를 제어할 수 있다.

Disabling Restart

재시작 기능을 사용하지 않으려면 spring.devtools.restart.enabled 프로퍼티를 사용하여 비활성화할 수 있다. 대부분의 경우 application.properties에서 이 프로퍼티를 설정할 수 있다. 이렇게 하면 클래스로더 재시작이 초기화되지만 파일 변경 사항은 감시되지 않는다.

재시작 지원 기능을 완전히 비활성화해야 하는 경우(예: 특정 라이브러리에서 작동하지 않기 때문에) 다음 예에 표시된 대로 SpringApplication.run(...)을 호출하기 전에 spring.devtools.restart.enabled 시스템 프로퍼티를 false로 설정해야 한다:

자바

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
  
    @SpringBootApplication
    public class MyApplication {
        public static void main(String[] args) {
            System.setProperty("spring.devtools.restart.enabled", "false");
            SpringApplication.run(MyApplication.class, args);
        }
    }

코틀린

    import org.springframework.boot.SpringApplication
    import org.springframework.boot.autoconfigure.SpringBootApplication
    
    @SpringBootApplication
    object MyApplication {
        @JvmStatic
        fun main(args: Array<String>) {
            System.setProperty("spring.devtools.restart.enabled", "false")
            SpringApplication.run(MyApplication::class.java, *args)
        }
    }

Using a Trigger File

변경된 파일을 지속적으로 컴파일하는 IDE로 작업하는 경우 특정 시간에만 재시작을 트리거하는 것을 선호할 수 있다. 이를 위해 실제로 재시작 확인을 트리거할 때 수정해야 하는 특수 파일인 “트리거 파일”을 사용할 수 있다.

파일을 업데이트하면 검사가 시작되지만, 실제로는 Devtools에서 수행할 작업이 감지된 경우에만 재시작된다.

트리거 파일을 사용하려면, spring.devtools.restart.trigger-file 프로퍼티를 트리거 파일의 이름(경로 제외)으로 설정하자. 트리거 파일은 클래스패스 어딘가에 있어야 한다.

예를 들어 다음과 같은 구조의 프로젝트가 있는 경우:

src
+- main
    +- resources
        +- .reloadtrigger

그러면 트리거 파일 프로퍼티는 다음과 같다:

프로퍼티스(Properties)

    spring.devtools.restart.trigger-file=.reloadtrigger

Yaml

    spring:
        devtools:
            restart:
                trigger-file: ".reloadtrigger"

이제 재시작은 src/main/resources/.reloadtrigger가 업데이트될 때만 발생한다.

spring.devtools.restart.trigger-file을 글로벌 설정하여 모든 프로젝트가 동일한 방식으로 작동하도록 할 수 있다.

일부 IDE에는 트리거 파일을 수동으로 업데이트할 필요가 없도록 해주는 기능이 있다. 이클립스용 스프링 툴(Spring Tools)와 인텔리J IDEA(Ultimate Edition) 모두 이러한 지원을 제공한다. 스프링 툴즈를 사용하면 트리거 파일 이름이 .reloadtrigger인 경우 콘솔에서 “reload” 버튼을 사용할 수 있다. 인텔리J IDEA의 경우 해당 문서의 지침을 따를 수 있다.

Customizing the Restart Classloader

재시작과 리로드 절에서 앞서 설명한 대로 재시작 기능은 두 개의 클래스 로더를 사용하여 구현된다. 이로 인해 문제가 발생하는 경우 어떤 클래스로더에 의해 로드되는 항목을 커스텀해야 할 수도 있다.

기본적으로 IDE에 열려 있는 모든 프로젝트는 “재시작” 클래스로더를 사용하여 로드되고 모든 일반 .jar 파일은 “기본(“base”)” 클래스로더를 사용하여 로드된다. mvn spring-boot:run 또는 gradle bootRun을 사용하는 경우에도 마찬가지다. @SpringBootApplication을 포함하는 프로젝트는 “재시작(restart)” 클래스로더로 로드되고 그 외 모든 것은 “기본” 클래스로더로 로드된다.

META-INF/spring-devtools.properties 파일을 생성하여 스프링 부트에 다른 클래스로더를 사용하여 프로젝트의 일부를 로드하도록 지시할 수 있다. spring-devtools.properties 파일에는 restart.excluderestart.include 접두사가 붙은 프로퍼티가 포함될 수 있다. include 엘리먼트는 “재시작” 클래스로더로 끌어 올려야 하는 항목이고, exclude 엘리먼트는 “기본” 클래스 로더로 푸시해야 하는 항목이다. 속성 값은 다음 예에 표시된 것처럼 클래스 경로에 적용되는 정규식 패턴이다.

프로퍼티스(Properties)

    restart.exclude.companycommonlibs=/mycorp-common-[\\w\\d-\\.]+\\.jar
    restart.include.projectcommon=/mycorp-myproj-[\\w\\d-\\.]+\\.jar

Yaml

    restart:
        exclude:
            companycommonlibs: "/mycorp-common-[\\w\\d-\\.]+\\.jar"
        include:
            projectcommon: "/mycorp-myproj-[\\w\\d-\\.]+\\.jar"

모든 프로퍼티 키는 유니크해야 한다. 프로퍼티가 restart.include 또는 restart.exclude으로 시작하는한 항상 고려해야 한다.

클래스패스의 모든 META-INF/spring-devtools.properties가 로드된다. 프로젝트 내부 또는 프로젝트가 사용하는 라이브러리에 파일을 패키징할 수 있다.

Known Limitations

표준 오브젝트인풋스트림(ObjectInputStream)을 사용하여 역직렬화(deserialize)된 객체에서는 재시작 기능이 제대로 작동하지 않는다. 데이터를 역직렬화해야 한다면 Thread.currentThread().getContextClassLoader()와 함께 스프링의 컨피규러블오브젝트인풋스트림(ConfigurableObjectInputStream)을 사용해야 할 수도 있다.

불행하게도, 몇몇 서드 파티 라이브러리는 컨텍스트 클래스로더를 고려하지 않고 역직렬화한다. 그러한 문제를 발견하면 라이브러리 개발자에게 수정을 요청해야 한다.

6.8.4. LiveReload

spring-boot-devtools 모듈에는 리소스가 변경될 때 브라우저 새로 고침을 트리거하는 데 사용할 수 있는 내장 라이브리로드(LiveReload) 서버가 포함되어 있다. 라이브리로드(LiveReload) 브라우저 익스텐션은 livereload.com에서 크롬, 파이어폭스 및 사파리용으로 무료로 제공된다.

애플리케이션이 실행될 때 라이브리로드(LiveReload) 서버를 시작하지 않으려면 spring.devtools.livereload.enabled 프로퍼티를 false로 설정하면 된다.

한 번에 하나의 라이브리로드 서버만 실행할 수 있다. 애플리케이션을 시작하기 전에 다른 라이브리로드 서버가 실행되고 있지 않은지 확인하자. IDE에서 여러 애플리케이션을 시작하는 경우 첫 번째 애플리케이션만 라이브리로드를 지원한다.

파일이 변경될 때, 라이브리로드를 실행하려면 자동 재시작을 활성화해야 한다.

6.8.5. Global Settings

$HOME/.config/spring-boot 디렉토리에 다음 파일을 추가하여 글로벌 devtools 설정을 구성할 수 있다:

  1. spring-boot-devtools.properties
  2. spring-boot-devtools.yaml
  3. spring-boot-devtools.yml

이러한 파일에 추가된 모든 프로퍼티는 devtools를 사용하는 컴퓨터의 모든 스프링 부트 애플리케이션에 적용된다. 예를 들어 항상 트리거 파일을 사용하도록 재시작을 구성하려면 spring-boot-devtools 파일에 다음 속성을 추가하자:

프로퍼티스(Properties)

    spring.devtools.restart.trigger-file=.reloadtrigger

Yaml

    spring:
        devtools:
            restart:
                trigger-file: ".reloadtrigger"

기본적으로 $HOME은 사용자의 홈 디렉토리다. 이 위치를 커스텀하려면 SPRING_DEVTOOLS_HOME 환경 변수 또는 spring.devtools.home 시스템 프로퍼티를 설정하자.

$HOME/.config/spring-boot에서 devtools 구성 파일을 찾을 수 없으면 $HOME 디렉토리의 루트에서 .spring-boot-devtools.properties 파일이 있는지 검색하자. 이를 통해 $HOME/.config/spring-boot 위치를 지원하지 않는 이전 버전의 스프링 부트에 있는 애플리케이션과 devtools 글로벌 구성을 공유할 수 있다.

devtools properties/yaml 파일에서는 프로필(Profiles)이 지원되지 않는다.

.spring-boot-devtools.properties에서 활성화된 모든 프로필은 프로필별 구성 파일 로드에 영향을 주지 않는다. YAML 및 Properties 파일 모두 프로필별 파일명(spring-boot-devtools-<profile>.properties 포맷) 및 spring.config.activate.on-profile 문서는 지원되지 않는다.

Configuring File System Watcher

파일시스템와처(FileSystemWatcher)는 특정 시간 간격(certain time interval)으로 클래스 변경 사항을 폴링한 다음 더 이상 변경 사항이 없는지 확인하기 위해 미리 정의된 침묵(quiet) 기간을 기다리는 방식으로 작동한다. 스프링 부트는 IDE에 전적으로 의존하여 파일을 컴파일하고 스프링 부트가 읽을 수 있는 위치로 복사하므로 devtools가 애플리케이션을 재시작할 때 특정 변경 사항이 반영되지 않는 경우가 있을 수 있다. 이러한 문제가 지속적으로 보인다면 spring.devtools.restart.poll-intervalspring.devtools.restart.quiet-period 파라미터를 개발 환경에 맞는 값으로 늘려보자:

프로퍼티스(Properties)

    spring.devtools.restart.poll-interval=2s
    spring.devtools.restart.quiet-period=1s

Yaml

    spring:
        devtools:
            restart:
                poll-interval: "2s"
                quiet-period: "1s"

모니터링되는 클래스패스 디렉토리는 이제 변경 사항에 대해 2초마다 폴링되며, 추가 클래스 변경이 없는지 확인하기 위해 1초의 침묵 기간이 있다.

6.8.6. Remote Applications

스프링 부트 개발자 도구는 로컬 개발에만 국한되지 않는다. 원격으로 애플리케이션을 실행할 때 여러 기능을 사용할 수도 있다. 원격 지원은 보안 위험이 있을 수 있으므로 옵셔널(opt-in)하게 제공됩니다. 신뢰할 수 있는 네트워크에서 실행 중이거나 SSL로 보안이 유지되는 경우에만 활성화해야 합니다. 이러한 옵션 중 어느 것도 사용할 수 없는 경우 DevTools의 원격 지원을 사용해서는 안 된다. 프로덕션 배포에서는 이 기능을 활성화하면 안 된다.

이를 활성화하려면, 아래 표시된 대로 devtools가 리패키징된 압축파일에 포함되어 있는지 확인해야 한다. To enable it, you need to make sure that devtools is included in the repackaged archive, as shown in the following listing:

  <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludeDevtools>false</excludeDevtools>
                </configuration>
            </plugin>
        </plugins>
    </build>

그런 다음 spring.devtools.remote.secret 프로퍼티를 설정해야 한다. 중요한 비밀번호와 마찬가지로 값은 추측하거나 무차별 공격을 가할 수 없도록 고유하고 강력해야 한다.

원격 devtools 지원은 연결을 허용한 서버 측 엔드포인트와 IDE에서 실행하는 클라이언트 애플리케이션이라는 두 부분으로 제공된다. spring.devtools.remote.secret 프로퍼티가 설정되면 서버 컴포넌트가 자동으로 활성화된다. 클라이언트 컴포넌트를 수동으로 시작해야 한다.

스프링 웹플럭스 애플리케이션에는 원격 devtools가 지원되지 않는다.

Running the Remote Client Application

원격 클라이언트 애플리케이션은 IDE 내에서 실행되도록 설계됐다. 연결하는 원격 프로젝트와 동일한 클래스패스를 사용하여 org.springframework.boot.devtools.RemoteSpringApplication을 실행해야 한다. 애플리케이션의 하나의 필수 아규먼트는 연결되는 원격 URL이다.

예를 들어, 이클립스 또는 스프링 툴즈를 사용하고 있고 Cloud Foundry에 배포한 my-app이라는 프로젝트가 있는 경우 다음과 같이 수행하자:

  • Run menu에서 Run Configurations...을 선택.
  • 자바 애플리케이션 “실행 구성” 생성.
  • my-app 프로젝트를 찾기.
  • org.springframework.boot.devtools.RemoteSpringApplication을 메인 클래스로 사용.
  • 프로그램 아규먼트(또는 원격 URL이 무엇이든)에 https://myapp.cfapps.io를 추가한다.

실행 중인 원격 클라이언트는 다음과 유사할 수 있다:

  .   ____          _
 /\\ / ___'_ __ _ _(_)_ __  __ _          __                _     \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |        | - \___ _ __  ___| |_ ___\ \ \ \ 
 \\/  ___)| |_)| | | | | || (_| []::::::[]   / -_) '  \/ _ \  _/ -_) ) ) ) )
  '  |____| .__|_| |_|_| |_\__, |        |_|_\___|_|_|_\___/\__\___|/ / / /
 =========|_|==============|___/===================================/_/_/_/
 :: Spring Boot Remote ::  (v3.1.1)
2023-06-22T12:06:32.435Z  INFO 20337 --- [           main]
o.s.b.devtools.RemoteSpringApplication   : Starting RemoteSpringApplication v3.1.1
using Java 17.0.7 with PID 20337
(/Users/myuser/.m2/repository/org/springframework/boot/spring-boot-
devtools/3.1.1/spring-boot-devtools-3.1.1.jar started by myuser in /opt/apps/)
2023-06-22T12:06:32.441Z  INFO 20337 --- [           main]
o.s.b.devtools.RemoteSpringApplication   : No active profile set, falling back to 1
default profile: "default"
2023-06-22T12:06:32.729Z  INFO 20337 --- [           main]
o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2023-06-22T12:06:32.943Z  INFO 20337 --- [           main]
o.s.b.devtools.RemoteSpringApplication   : Started RemoteSpringApplication in 0.995
seconds (process running for 1.372)

원격 클라이언트는 실제 애플리케이션과 동일한 클래스패스를 사용하기 때문에 애플리케이션 프로퍼티를 직접 읽을 수 있다. 이는 spring.devtools.remote.secret 프로퍼티을 읽고 인증을 위해 서버에 전달하는 방법이다.

트래픽이 암호화되고 비밀번호를 가로챌 수 없도록 항상 https://를 연결 프로토콜로 사용하는 것이 좋다.

원격 애플리케이션에 접근하기 위해 프록시를 사용해야 하는 경우 spring.devtools.remote.proxy.hostspring.devtools.remote.proxy.port 프로퍼티를 구성하자.

Remote Update

원격 클라이언트는 로컬 재시작과 동일한 방식으로 애플리케이션 클래스패스의 변경 사항을 모니터링한다. 업데이트된 모든 리소스는 원격 애플리케이션에 푸시되고 (필요한 경우) 재시작을 트리거한다. 이는 로컬에 없는 클라우드 서비스를 사용하는 기능을 반복하는 경우 도움이 될 수 있다. 일반적으로 원격 업데이트 및 재시작은 전체 재구축 및 배포 주기보다 훨씬 빠르다.

느린 개발 환경에서는 침묵(quiet) 시간이 충분하지 않아 클래스의 변경 사항이 일괄적으로 분할될 수 있다. 첫 번째 클래스 변경 사항 배치가 업로드된 후 서버가 재시작된다. 서버를 재시작하는 중이므로 다음 배치를 애플리케이션으로 보낼 수 없다.

이는 일반적으로 일부 클래스 업로드 실패 및 그에 따른 재시도에 대한 리모트스프링애플리케이션(RemoteSpringApplication) 로그의 경고로 나타난다. 그러나 이로 인해 애플리케이션 코드 불일치가 발생하고 변경 사항의 첫 번째 배치 처리가 업로드된 후 재시작하지 못할 수도 있다. 이러한 문제가 지속적으로 보인다면 spring.devtools.restart.poll-intervalspring.devtools.restart.quiet-period 파라미터를 개발 환경에 맞는 값으로 늘려보자. 이러한 프로퍼티를 구성하려면 파일 시스템 와처(File System Watcher) 절을 참조하자.

파일은 원격 클라이언트가 실행 중일 때만 모니터링된다. 원격 클라이언트를 시작하기 전 파일을 변경하면 원격 서버로 푸시되지 않는다.

6.9. Packaging Your Application for Production

실행 가능한 jar는 프로덕션 배포에 사용될 수 있다. 독립형이므로 클라우드 기반 배포에도 이상적으로 적합하다.

상태(health), 감사(auditing), 메트릭(metric) REST 또는 JMX 엔드포인트와 같은 추가적인 “프로덕션 준비” 기능을 위해 spring-boot-actuator 추가를 고려해보자. 자세한 내용은 프로덕션 준비 기능(Production-ready)을 참고하자.

이제 스프링 부트를 사용하는 방법과 따라야 할 몇 가지 모범 사례를 이해해야 한다. 이제 특정 스프링 부트 기능에 대해 자세히 알아볼 수도 있고, 스킵 후 스프링 부트의 “프로덕션 준비” 측면에 대해 읽어볼 수도 있다.