본문 바로가기
기술 부채 상환 중....

스프링 부트 3. Using Spring Boot

by 닮은 2018. 5. 2.

빌드 시스템, 자동 설정, 어플리케이션 실행에 알아본다.

원문

13. 빌드 시스템

의존성 관리를 지원하고, 메이븐 센트럴에 배포되는 artifact를 사용할 수 있는 빌드 시스템(메이븐이나 그레들)을 써야 한다.

의존성 관리

스프링 부트는 각 릴리즈 마다 제공하는 의존성 목록이 있다. 그래서 스프링 부트 버전만 설정하면 다른 의존성들은 버전을 명시할 필요가 없고, 부트를 업그레이드 하면 다른 의존성들도 자동으로 업그레이드 된다.

메이븐

starter parent를 상속받기

<!-- Inherit defaults from Spring Boot -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.1.RELEASE</version>
</parent>

개별적으로 버전을 지정하고 싶을 때

<properties>
    <spring-data-releasetrain.version>Fowler-SR2</spring-data-releasetrain.version>
</properties>

parent pom 없이 스프링 부트를 사용하기

<dependencyManagement>
    <dependencies>
        <dependency>
            <!-- Import dependency management from Spring Boot -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.0.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

이렇게 했을 때는 개별적으로 버전 지정하는 방법이 조금 번거롭다.

<dependencyManagement>
    <dependencies>
        <!-- Override Spring Data release train provided by Spring Boot -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-releasetrain</artifactId>
            <version>Fowler-SR2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.0.1.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

스프링 부트 메이븐 플러그인 사용하기

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

스프링 부트는 프로젝트를 실행가능한 jar로 패키징 해주는 메이븐 플러그인을 제공한다.

그레들

그레들 설명은 여기

앤트

staters

편리한 의존성들 모임으로 stater만 추가하면 작동하도록 되어 있다. 공식적인 스타터들은 spring-boot-stater-* 형태로 되어 있다. 서드 파티인 경우에는 3rdparty-spring-boot-stater 형태로 만들어야 한다.

14. 코드 구조

정해져 있는 레이아웃은 없지만, 도움이 되는 몇가지 모범 사례가 있다.

디폴트 패키지 사용하기

제목은 Using the "default" Package인데 내용은 사용하지 말라는 뜻입니다. 클래스에 패키지 선언이 없으면 디폴트 패키지로 간주되는데 이렇게 되면 @ComponentScan @EntityScan, @SpringBootApplication 어노테이션을 사용하는 스프링 부트 어플리케이션에서 모든 jar의 모든 클래스를 읽기 때문에 문제가 될 수 있다.

메인 어플리케이션 클래스 위치

다른 클래스 상위의 루트 패키지에 메인 어플리케이션 클래스를 두는 것을 추천한다. 그래야 @SpringBootApplication 어노테이션 등을 사용했을 때 기본 search package가 된다. 예를 들어 JPA 어플리케이션을 작성할 때 @Entity를 찾는데, 루트 패키지는 프로젝트 안에서만 컴포넌트 스캔을 할 수 있게 해 준다.

15. 설정 클래스

추가 설정 클래스 임포트

@Configuration 어노테이션을 하나에만 넣을 필요는 없다. @Import 어노테이션은 추가 설정 클래스를 임포트 하게 해준다. 또는 @ComponentScan를 사용하면 @Configuration 클래스를 포함한 모든 스프링 컴포넌트를 자동으로 선택할 수 있다.

XML 설정 임포트

@ImportResource 어노테이션을 사용하면 된다.

16. 자동 설정

스프링 부트 자동 설정은, 추가된 jar를 기반으로 자동 설정을 시도한다. 예를 들어 hsql이 클래스 패스에 있고, 직접 빈 설정을 따로 하지 않았다면 스프링 부트는 메모리 내 데이터베이스를 자동으로 구성한다.

점진적으로 자동 설정으로 갈아타기

원하고 싶으면 설정을 바꾸면, 스프링 부트의 디폴트 설정을 대체할 수 있다. 예를 들면 직접 DataSource 빈을 추가하면 디폴트 임베디드 데이터 베이스 지원을 사라지게 된다.

특정 자동 설정 클래스 사용하지 않기

@EnableAutoConfiguration 어노테이션에 exclude로 사용하지 않고자 하는 클래스를 지정할 수 있다.

17. 스프링 빈과 의존성 주입

간단하게, 빈을 찾기 위해서 @ComponentScan을, 생성자 주입을 위해서는 @Autowired를 사용하면 된다. 만약 앞에서 제안한대로 루트 패키지에 어플리케이션 코드가 있다면 @ComponentScan에 인자가 없어도 컴포넌트들이 자동으로 스프링 빈으로 등록될 것이다.

18. @SpringBootApplication 어노테이션 사용하기

@SpringBootApplication은 결국 @EnableAutoConfiguration(스프링 부트의 자동 설정 매커니즘을 활성화한다.), @ComponentScan(어플리케이션이 위치한 패키지에서 @Component를 스캔하도록 한다.), @Configuration(컨텍스트 안에서 나머지 빈을 등록하거나 추가 설정 클래스를 임포트 하게 해준다.)을 같이 쓰는 것과 같다.

19. 어플리케이션 실행하기

jar로 패키징 하는 가장 큰 장점 중 하나는 내장된 HTTP 서버를 사용하여 어플리케이션을 실행할 수 있다는 점이다. 디버깅을 하는 것도 매우 쉽다. 별다른 IDE 플러그인도 필요 없다.

IDE에서 실행하기

각자 사용하는 IDE에서 알아서 실행하면 됩니다.

패키지한 어플리케이션으로 실행하기

 $ 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

메이븐 플러그인 사용하기

 $ mvn spring-boot:run

 $ export MAVEN_OPTS=-Xmx1024m

그레들 플러그인 사용하기

$ gradle bootRun

$ export JAVA_OPTS=-Xmx1024m

핫 스와핑

좀 더 뒤에서 살펴 볼게요.

20. 개발자 도구

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

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

프로퍼티 기본값

프로덕션이 아닌 개발 모드에서 유용한 프로퍼티 기본값을 제공한다. 예를 들어 템플릿 엔진의 경우, 실제로는 퍼포먼스를 위해서 캐싱되어 있지만 개발 모드에서는 확인을 해야하기 때문에 캐싱하지 않아야 한다. 개발 도구는 이런 기본값을 세팅해 준다. 제공하는 목록은 여기에서 확인할 수 있다.

자동 재시작

클래스 패스 내부의 파일이 변경되면 자동으로 재시작 한다. (클래스 패스 내부의 파일이 변경된다는 것은 IDE 마다 다르다. 예를 들어 이클립스에서는 파일이 저장되는 것이 클래스 패스 내부의 수정으로 간주되고, intellij 에서는 프로젝트 빌드가 클래스 패스 내부의 수정이 된다.)

  • 포킹이 가능한 한, 적절한 조작을 위해서 DevTools가 고립된 어플리케이션 로더를 필요로하기 때문에, 지원되는 빌드 플러그인으로 자동으로 재시작 할 수 있다. 기본적으로, 클래스패스에서 DevTools가 감지되면 그레들과 메이븐은 이것을 지원한다.
  • 자동 재시작은 LiveReload와 사용될 때 잘 동작한다.
  • DevTools는 재시작하는 동안 응용 프로그램 컨텍스트의 셧다운 훅을 사용하여 닫는다. SpringApplication.setRegisterShutdownHook(false)로 셧다운 훅을 비활성화 한 경우에는 제대로 동작하지 않을 것이다.
  • 변경되면 클래스패스의 항목이 재시작을 발생할지 결정할 때, DevTools는 spring-boot, spring-boot-devtools, spring-boot-actuator, spring-boot-stater라는 이름을 가진 프로젝트는 자동으로 무시한다.
  • DevTools는 ApplicationContext에서 사용하는 ResourceLoader를 커스터마이징 해야한다. 응용 프로그램에서 이미 제공하는 경우에는 래핑됩니다. ApplicationContext에 대한 getResource 메소드의 직접 대체는 지원되지 않는다.
  • 재시작과 재로딩: 스프링 부트에서 제공되는 재시작 기술은 두 클래스로더를 사용하여 동작한다. 변하지 않는 클래스는 base 클래스로더로 로딩된다. (써드파티의 jar 등) 그리고 실제로 개발하고 있는 클래스들은 restart 클래스로더로 로딩된다. 어플리케이션이 재시작하면 restart 클래스로더가 삭제되고 새로운 하나가 생긴다. 이런 접근은 어플리케이션의 재시작이 보통 "cold starts"보다 빠르다는 것을 의미한다. base 클래스로더는 이미 사용가능하며 존재하는 상태이기 때문이다. 만약 어플리케이션 재시작이 의외로 느리거나, 클래스로딩이 이슈가 있다면, JRebel 같은 리로딩 기술을 고려할 수 있을 것이다. 이것은 클래스가 로딩될때, 더 쉽게 로딩하기 위해서 클래스를 다시 작성함으로써 동작한다.

조건 평가로 변경사항 로깅하기

기본적으로 재시작할 때마다 변경사항이 로깅되는 것을 보여준다.

리소스 제외하기

어떤 리소스들은 변경했을 때 재시작을 안 해도 된다. 기본적으로 /META-INF/maven, /META-INF/resources, /resources, /static, /public, /templates 에 있는 파일은 재시작을 발생시키지 않고, live reload를 한다. 이것은 다음과 같이 재정의 할 수 있다.

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

추가 경로 감지하기

클래스 패스 이외의 파일들까지 설정하고 싶다면 spring.devtools.restart.additional-paths 프로퍼티를 설정해주면 된다.

재시작 비활성화 시키기

spring.devtools.restart.enabled 속성을 설정해준다. 보통 application.properties 파일에 있다. 이렇게 하면 파일 변경을 감시하지는 않지만, 여전히 재시작 클래스로더는 초기화 된다.

완전히 재시작 지원을 비활성화 시키려면 spring.devtools.restart.enabled 시스템 속성을 SpringApplication.run 전에 false로 설정해야 한다.

public static void main(String[] args) {
    System.setProperty("spring.devtools.restart.enabled", "false");
    SpringApplication.run(MyApp.class, args);
}

트리거 파일 사용하기

변경된 파일을 지속적으로 컴파일하는 IDE로 작업하는 경우 특정 시간에만 재시작을 하는 것이 더 좋을 것이다. 이를 위해서는 "트리거 파일"이라는 실제로 재시작 검사를 발생시키고 싶을 때 수정해야 하는 특수한 파일을 사용하면 된다. 파일을 변경하면 검사가 시작되고 devtools가 작업을 수행해야한다는 것을 감지 한 경우에만 다시 시작됩니다. 트리거 파일은 수동으로 또는 IDE 플러그인을 사용하여 업데이트 할 수 있습니다.

리스타트 클래스로더 수정하기

앞에서 말한대로 재시작은 기능적으로 두개의 클래스로더로 구현되어 있다. 보통은 잘 동작하지만 때때로 클래스로딩 이슈가 생긴다.

기본적으로, IDE에서 열려진 프로젝트는 restart 클래스로더로 로딩되고, jar는 base 클래스로더로 로딩된다. 모든 모듈이 임포트 되지 않은 멀티 모듈 프로젝트에서 작업할 때는 커스터마이징 해야한다. 이를 위해서는 일단 META-INF/spring-devtools.properties 파일을 생성해야한다. spring-devtools.properties에서는 restart.exclude, restart.include를 프리픽스로 하는 속성을 설정한다. include는 restart 클래스로더로 포함되어야 하는 아이템이고, exclude는 base 클래스로더로 포함되어야 하는 아이템이다. 값은 정규식이 가능한다.

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

한계

스탠다드 ObjectInputStream에 의해 역직렬화된 오브젝트에 대해서는 리스타트 기능이 동작하지 않는다. 데이터를 역직렬화하고자 한다면, Thread.currentThread().getContextClassLoader()와 스프링의 ConfigurableObjectInputStream을 사용할 수 있을 것이다. 불행하게도, 대부분의 서드파티 라이브러리는 이를 고려하지 않고 역직렬화를 해버린다. 이런 문제가 있다면, 개발자에게 수정해달라고 요청해라; (정말 이렇게 적혀있답니다. If you find such a problem, you need to request a fix with the original authors.)

LiveReload

spring-boot-devtools 모듈은 리소스가 변경되었을 때, 브라우저 리프레시를 발생시키는 내장된 LiveReload server를 포함하고 있다. 크롬, 파이어폭스, 사파리의 익스텐션은 livereload.com 에서 확인할 수 있다. 비활성화 하려면 spring.devtools.livereload.enabled 속성을 false로 설정한다. 여러 어플케이션을 실행하는 경우, 최초 어플리케이션에서만 동작한다.

전역 설정

HOME 폴더에 .spring-boot-devtools.properties 파일을 추가하여 전역 devtools 설정을 설정할 수 있다. 이렇게 하면 해당 머신에 있는 모든 스프링 부트 프로젝트에 설정한 속성이 적용된다. 예를 들어 트리거 파일을 사용하고 싶다면, 파일에 다음과 같이 설정한다.

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

원격 어플리케이션

스프링 부트의 개발 도구는 원격으로 실행되는 어플리케이션에서도 사용할 수 있다. 이를 위해서는 다음과 같이 devtools가 리패키징된 아카이브에 포함될 수 있게 하고, spring.devtools.remote.secret=mysecret 속성을 설정해 준다. (그러면서 매우 위험하니 production level에서는 쓰지 말래요.)

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

원격 어플리케이션 실행

원격 클라이언트 어플리케이션은 IDE에서 실행되도록 디자인되었다. 연결되어 있는 원격 프로젝트의 동일한 프로젝트에서 org.springframework.boot.devtools.RemoteSpringApplication를 구동하여 실행한다. 이때 연결할 원격 url을 인자로 준다. Cloud Foundary에 배포한 my-app이라는 이름의 프로젝트를 이클립스에서 실행한다고 가정해보자.

  • run 메뉴에서 Run Configurations…을 선택한다.
  • launch configuration 새로운 자바 어플리케이션을 만든다.
  • my-app 프로젝트가 보인다.
  • org.springframework.boot.devtools.RemoteSpringApplication을 메인 클래스로 사용한다.
  • 인자로 https://myapp.cfapps.io를 추가한다.

원격 어플리케이션 업데이트

로컬에서와 마찬가지로, 원격 어플리케이션에 리소스가 업데이트 되면 리스타트를 발생한다.

제품을 위한 어플리케이션 패키징

실행가능한 jar를 제품 배포에도 쓰일 수 있고, 내장되어 있기 때문에 클라우드 기반 배포에도 이상적이다. Part V, “Spring Boot Actuator: Production-ready features에서 자세히 다룹니다.