gradle

원문

메이븐에서 그레들로 마이그레이션

Making a case for migration

  • 기능 비교는 여기
  • 그레들은 3.0 이후로 빌드 캐싱, 컴파일 회피(?), 개선된 점증적인 자바 컴파일러 등과 같이 더 빨리 빌드 하는 것에 많은 투자를 했다. 그래서 지금은 메이븐보다 2~10배 정도 빠르다고 한다.
  • 더 많은 마이그레이션 케이스는 여기에서 살펴 볼 수 있다.

Understanding the functional differences

  • 그레들은 태스크 의존성 그래프를 기반으로 동작한다. 메이븐은 고정된 선형의 phases에 골을 추가하여 동작하는데, 이렇게 다름에도 마이그레이션은 쉽다.
  • 빌드 스캔 웹 기반의 특정 빌드의 스냅샷으로 협업 디버깅 및 세분화된 성능 분석을 할 수 있게 해준다.
  • 일단 메이븐 빌드에 대한 인풋, 아웃풋을 기록하는 것부터 시작해야 그레들 빌드로 마이그레이션 했을 때 기능적으로 같다는 것으로 증명할 수 있다.

Automated conversion

  • $ gradle init으로 프로젝트를 생성할 수도 있지만 기존 메이븐 프로젝트를 그레들로 마이그레이션 해주기도 한다. 루트 프로젝트 디렉토리에서 실행하면 POM들을 파싱하고, 만약 멀티 프로젝트 빌드라면 setting.gradle도 만들어 준다.
  • 어셈블리는 자동으로 변환되지 않아서 직접 해줘야할 것들이 좀 있다.
  • 마이그레이션이 잘 되었다면 $ gradle build 빌드한다.

Bills of Materials (BOMs)

  • 메이븐은 pom 파일의 dependencyManagement 내부에 <packaging>pom</packaging>를 정의하여 의존성에 대한 제약사항을 공유하는데, 이런 특수한 타입의 pom(bom)은 다른 pom에 임포트 하여 프로젝트에서 일괄된 라이브러리를 가지도록 하게 해준다. 그레들도 4.6 이상에서, (5.0 이하에서는 명시적으로)settings.gradle을 추가하여 이것을 지원한다.

setting.gradle

enableFeaturePreview("IMPROVED_POM_SUPPORT")

build.gradle

dependencies { 
    // 스프링 부트 의존성 BOM implementation 
    'org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE'

    //버전 없이도 의존성이 해결된다. 
    implementation 'com.google.code.gson:gson' 
    implementation 'dom4j:dom4j' 
}

Provided scope and optional dependencies

그레들은 설정을 통해 의존성을 관리한다는 점에서 유연하다고 할 수 있지만, war 플러그인을 쓰지 않는 이상, provided 스코프를 가지고 있지 않다. 대신 비슷한 행동을 그레들 2.12부터 추가된 compileOnly 설정으로 할 수 있다.

선택적 의존성은 그레들 4.6 이상에서 pom 파일을 사용할 때 종속성 제약 조건을 생성하고, 5.0 이전 버전의 경우에는 setting.gradle에 enableFeaturePreview('IMPROVED_POM_SUPPORT')을 추가해야 한다.

4.5 버전 이전에서는 선택적 의존성을 지원하지 않는데, 대신 optional-base plugin을 사용해서 가능하게 할 수 있다.

Maven profiles and properties

메이븐은 다양한 종류의 속성을 사용하여 빌드를 매개 변수화 한다. 어떤 것은 프로젝트 모델의 읽기 전용 속성이고, 어떤 것은 유저가 직접 pom 에서 정의 하는 것이다. 또한 시스템 속성을 프로젝트 속성으로 쓸 수도 있다.

그레들 또한 비슷한 프로젝트 속성 시스템을 가지고 있다. 다음과 같은 곳에서 정의할 수 있따.

  • 빌드 파일
  • 루트 프로젝트 디렉토리의 gradle.properties
  • $HOME/.gradle 디렉토리의 gradle.properties

이 외에도 다양하게 적용할 수 있으며 자세한 것은 이곳에서 참조하자.

같은 속성이 외부 프로퍼티 파일과 빌드 파일에서 동시에 정의된 경우에는 빌드 파일의 것이 우선 순위가 높다.

메이븐 프로필로 돌아가서, 환경, 대상 플랫폼 등등에 따라 다른 설정들은 활성화 비활성화 하는 방법이 있다. 논리적으로 이것들은 조건문에 지나지 않는다. 그레들에는 이런 조건을 선언하는 더 강력한 방법이 있기 때문에 앞에서 말한 프로필 지원이 필요가 없다. (의존성의 pom 파일 제외하고)

환경에 따라 다른 배포를 해야한다고 생각해보자. (로컬 환경, 테스트 환경, 프로덕션) 프로필 처럼 동작하게 하기 위해 먼저 루트 프로젝트에 각 환경을 위한 빌드 파일 profile-default.gradle, profile-test.gradle, profile-prod.gradle 를 만든다. 그리고 메인 빌드 파일에는 이렇게 작성한다.

if (!hasProperty('buildProfile')) ext.buildProfile = 'default'
apply from: "profile-${buildProfile}.gradle"

다음으로는 환경을 특정 짓는 설정을 해주면 되는데, 특정 프로필을 확설화 시키기 위해서는 그에 맞는 프로젝트 설정을 커맨드 라인에서 넘기면 된다.

$ gradle -PbuildProfile=test build

하지만 조건문은 빌드를 이해하고 유지하기가 쉽지 않다. 그레들은 메이븐이 요구하는 프로필 사용을 피하기 위해서 변형 (variants) 등을 제공한다. 이 글은 많은 도움을 줄 것이다!

Resource filtering

메이븐에는 process-resource라는 페이즈가 있다. 이 과정에서 웹 리소스, 패키지 속성 파일 등과 같은 다양한 파일에 대한 변수를 대체할 수 있다.

그레들에서는 자바 플러그인의 processResources 태스크가 같은 일을 한다.

build.gradle

processResources {
    expand(version: version, buildNumber: currentBuildNumber)
}

왼쪽이 토큰 이름이고 오른쪽이 프로젝트 속성이다. 이 변수 대체는 모든 리소스 파일에 적용된다. (보통 src/main/resources 에 있는 것들)

그레들은 속성을 처리하는 또 다른 강력크한 방법을 가지고 있다. 클로져를 통해 필터 안에서 훅 해서 컨텐츠를 한줄 한줄 처리하거나 FilterReader를 구현해서 추가할 수 있다. 더 자세한 것은 여기에서 확인하자.

Integration tests

유닛 테스트가 있지만 그것이 모든 것을 보장하지는 않는다. 객체 간의 인터렉션이나, 환경에 따른 인터렉션에서 버그가 생기기 때문에 많은 프로젝트가 통합, 기능, 또는 수용 테스트라고 불리는 더 높은 테스트를 하게 된다.

메이븐은 pre-integration-test, integration-test. post-integration-test. verify 등의 부가적인 페이즈를 제공하여 이러 유형들의 테스트를 지원한다.

또 하나 생각해야할 것은 통합 테스트 클래스가 어디에 있느냐 하는 것이다. 초기 접근은 유닛 테스트 클래스를 한데 모으는 것이지만 이상적이지는 않다. 대체적으로 프로필을 사용하여 두가지 타입의 테스트를 따로 수행한다.

그레들에서는 플러그인이 아니라 기본적으로 자바 프로젝트에 이미 포함되어 있는 유닛 테스트를 소스셋으로 설정하여 테스트 하도록 한다. 통합 테스트를 여기에 작성하면 된다.

build.gradle

sourceSets {
    integTest {
        compileClasspath += main.output + test.output
        runtimeClasspath += main.output + test.output
    }
}
configurations {
    integTestCompile.extendsFrom testCompile
    integTestRuntime.extendsFrom testRuntime
}

integTest라는 소스셋을 생성한다. 어플리케이션, 라이브러리 클래스, 의존성들이 통합 테스트를 컴파일 할때 클래스패스에 포함되어 있는지 확인한다.

통합 테스트는 서드파티 라이브러리를 사용할 수 있고, 그것도 컴파일 클래스패스에 추가해야 한다. 이건 그냥 dependencies 블록에 넣어주면 된다.

build.gradle

dependencies {
    // ...
    integTestCompile 'org.codehaus.groovy:groovy-all:2.4.3'
    integTestCompile 'org.spockframework:spock-core:0.7-groovy-2.0', {
        exclude module: 'groovy-all'
    }
}

이렇게 하면 컴파일이 되고, Test라는 태스크를 작성하자. build.gradle

task integTest(type: Test) {
    dependsOn startApp
    finalizedBy stopApp
    testClassesDirs = files(sourceSets.integTest.output.classesDirs)
    classpath = sourceSets.integTest.runtimeClasspath
    mustRunAfter test
}

어플리케이션 서버를 따라 적절한 시간에 시작하고 종료하도록 한다. Test 설정에 관한 더 자세한 것은 여기에서 확인하자.

이제 남은건 태스크 그래프에 integTest를 포함시키는 방법이다. 이건 하고 싶은대로 알아서 하면 된다. 예를 들면 build 할때 하게 한다든가.

Common plugins

그레들도 메이븐처럼 플러그인을 통해 빌드를 확장한다. 표면적으로 플러그인 시스템이 다른 것 같지만 다음과 같이 기능 기반의 플러그인을 많이 공유한다.

  • Shade/Shadow
  • Jetty
  • Checkstyle
  • JaCoCo
  • AntRun

    ... org.apache.maven.pluginsmaven-checkstyle-plugin2.17

    <execution>
      <id>validate</id>
      <phase>validate</phase>
      <configuration>
        <configLocation>checkstyle.xml</configLocation>
        <encoding>UTF-8</encoding>
        <consoleOutput>true</consoleOutput>
        <failsOnError>true</failsOnError>
        <linkXRef>false</linkXRef>
      </configuration>
      <goals>
        <goal>check</goal>
      </goals>
    </execution>
    

    ...

마이그레이션 할 때 configuration 블락 바깥은 깨끗하게 무시되는 경우가 있다. 이런 경우에는 다음과 같이 그레들 설정으로 바꾸면 된다.

checkstyle {
    config = resources.text.fromFile('checkstyle.xml', 'UTF-8')
    showViolations = true
    ignoreFailures = false
}

CheckStyle 태스크는 check 태스크의 의존성으로 자동으로 추가되는데, 이것은 test 또한 포함한다. 테스트 전에 CheckStyle이 실행하는지 확인하고 mustRunAfter() 메소드로 우선순위를 지정하면 된다.

test.mustRunAfter checkstyleMain, checkstyleTest

마이그레이션에서 소스셋을 꼭 기억하라. 메이븐이 제공하는 것보다 훨씬 우아한 방법으로 통합 테스트를 처리하기 때문에 만드시 마이그레이션 계획에 고려해야 한다.

Ant goals

많은 메이븐 빌드는 커스텀 메이븐 플러그인 구현에 대한 부담 없이 앤드 플러그인에 의존해서 빌드를 커스터마이징 한다. 그레들은 and 객체를 통해 Ant가 first-class 시티즌이 되기 때문에 그런 플러그인이 없다.

task sayHello {
    doLast {
        ant.echo message: 'Hello!'
    }
}

심지어 앤트 속성과 파일셋이 기본적으로 지원된다. 더 자세한 것은 여기에서 참조한다.

Plugins you don’t need

메이븐을 대체하기 위한 그레들 플러그인이 필요하지 않을 수도 있다. 예를 들면 메이븐 Enforcer 플러그인으로 의존성 버전과 환경 요소를 관리하지만 이것들은 일반 그레들 빌드 스크립트로 설정할 수 있다.

Uncommon and custom plugins

흔지 않은 플러그인과 커스텀 플러그인은 직접 작성해야 할 수도 있다. 한땀 한땀 배워서 잘 작성하라는 내용인 것 같다.

Next steps

그레들 커뮤니티에 온 것을 환영해! 빌드 로직 구성하기빌드 성능 향상시키기를 더 읽어 보면 좋겠다.

Posted by 닮은

 

원문

소개

스프링 부트 그레들 플러그인은 그레들에서 스프링 부트를 지원하고, 실행가능한 jar (또는 war 아카이브)를 패키징 하고, 스프링 부트 어플리케이션을 실행하고 spring-boot-dependencies가 제공하는 의존성 관리를 사용할 수 있게 해준다.

시작하기

buildscript {
    repositories {
        maven { url 'https://repo.spring.io/libs-snapshot' }
    }

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.0.BUILD-SNAPSHOT'
    }
}

apply plugin: 'org.springframework.boot'

별로 변화가 없는 것 같지만, 이 플러그인은 다른 플러그인이 적용되는지 탐지하고 적절하게 반응한다고 한다. groovy, java, kotlin 플러그인과 io.spring.dependency-management 플러그인은 최소한으로 적용해야 한다. (??)

apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'

의존성 관리하기

io.spring.dependency-management 플러그인을 적용하면 스프링 부트 플러그인이 자동으로 알맞는 버전의 spring-boot-dependencies bom을 가져온다. 의존성을 가져올 때 버전을 생략해도 된다는 말인 것 같다.

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
}

관리되는 버전 직접 정의하기

해당하는 속성을 설정해주면 된다.

ext['slf4j.version'] = '1.7.20'

고립적으로 부트의 의존성 관리 사용하기

스프링 부트 의존성 관리는 그 프로젝트에 스프링 부트 플러그인을 적용하지 않고도 쓸 수 있다. SpringBootPlugin 클래스는 BOM_COORDINATES 상수를 제공하는데, group id, artifact id, 버전을 알 필요 없이 사용된다.

일단은 설정만 하고, 적용하지 않는다.

buildscript {
    repositories {
        maven { url 'https://repo.spring.io/libs-snapshot' }
    }

    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.0.BUILD-SNAPSHOT'
    }
}

의존성 관리 플러그인에서의 스프릥 부트 플러그인 의존성이라는 것은, 의존성 선언 없이 의존성 관리 플러그인을 사용할 수 있다는 뜻이다. 이것은 또한, 스프링 부트에서와 같은 의존성 관리 플러그인을 자동으로 사용한다는 의미이다.

의존성 관리 플러그인을 적용하고, 스프링 부트의 bom을 임포트 하자.

apply plugin: 'io.spring.dependency-management'

dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}

더 자세한 것

의존성 관리 플러그인의 기능에 대한 자세한 내용은 여기를 참조한다.

실행 파일 아카이브로 패키징하기

플러그인은 java -jar로 실행 가능한 파일 아카이브를 생성해 준다.

jar로 패키징하기

실행가능한 jar는 bootJar 태스크를 통해 빌드 된다. 이 태스크는 java 플러그인이 적용 될 때 생성되며, BootJar의 인스턴스이다. assemble 태스크는 bootJar 태스크에 따라 자동적으로 설정되기 때문에 assemble(또는 build) 태스크가 실행되면 bootJar 태스크도 실행된다.

war로 패키징하기

실행가능한 war는 bootWar 태스크를 통해 빌드 된다. 이 태스크는 war 플러그인이 적용 될 때 생성되며, BootWar의 인스턴스이다. assemble 태스크는 bootWar 태스크에 따라 자동적으로 설정되기 때문에 assemble(또는 build) 태스크가 실행되면 bootWar 태스크도 실행된다.

실행가능하고 배포가능한 war 패키징하기

war 파일은 외부 컨테이너에 배포되고, java -jar를 사용하여 실행 할 수 있도록 패키지 될 수 있다. 이렇게 하기 위해서는 내장 servlet 컨테이너 의존성이 다음과 같이 providedRuntime 설정이 추가되어야 한다.

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
}

이렇게 함으로써, 외부 컨테이너의 원래 클래스와 war 파일의 WEB-INF/lib-provided 디렉토리 간의 충돌이 없다는 것을 보장할 수 있다.

일반 아카이브로 패키징하기

기본적으로 bootJar 또는 bootWar 태스크를 설정할 때, jar 또는 war 태스크는 비활성화 된다.

Posted by 닮은

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

원문

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에서 자세히 다룹니다.

Posted by 닮은

원문

시작하기

시작하기.

8. 스프링 부트 소개

스프링 부트는 독립적인 프로덕션 수준의 스프링 기반 어플리케이션을 쉽게 만들수 있게 해준다.

9. 시스템 요구사항

스프링 부트 2.0.1.RELEASE는 자바 8(또는 9)과 스프링 프레임워크 5.0.5.RELEASE 또는 이상을 필요로 한다. 메이븐 3.2 이상 그리고 그레들 4에서 제공된다.

10. 스프링 부트 설치하기

spring-boot-*.jar를 클래스 패스에 포함시켜서 사용하며 된다.

자바 개발자를 위한 설치 지침

maven

pom.xml에서 spring-boot-starter-parent를 상속받고 하나 이상의 stater를 의존성에 추가해준다.

<?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>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>0.0.1-SNAPSHOT</version>

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

    <!-- Add typical dependencies for a web application -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- Package as an executable jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

gradle

스프링 부트가 제공하는 플러그인으로 간단하게 의존성을 추가하고 실행가능한 jar를 만들 수 있다.

plugins {
    id 'org.springframework.boot' version '2.0.1.RELEASE'
    id 'java'
}

jar {
    baseName = 'myproject'
    version =  '0.0.1-SNAPSHOT'
}

repositories {
    jcenter()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

Spring Boot CLI 설치하기

(통과)

Srping Boot 이전 버전에서 업그레이드 하기

마이그레이션 가이드 참고

11. 첫번째 스프링 부트 어플리케이션 개발하기

pom 생성

<?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>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>

    <!-- Additional lines to be added here... -->

</project>

클래스패스 의존성 추가

spring-boot-stater-parent는 유용한 메이븐 기본값을 제공하는 특별한 스타터이다. dependency-management로 의존성의 버전을 생략할 수도 있다.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

코드 작성하기

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;

@RestController
@EnableAutoConfiguration
public class Example {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Example.class, args);
    }

}

@RestController, @RequestMapping

@RequestMapping 어노테이션은 라우팅 정보를 제공한다. @RestController 어노테이션은 콜러에서 직접 결과 문자열을 돌려주도록 스프링에게 지시한다. 이 둘은 spring MVC 어노테이션으로 MVC 섹션에서 더 자세히 보도록 한다.

@EnableAutoConfiguration

@EnableAutoConfiguration 어노테이션은 추가한 jar를 기반으로 어떻게 스프링을 설정할 것인지 스프링 부트가 추측하도록 지시한다. spring-boot-stater-web은 톰캣, 스프링 MVC를 추가하기 때문에 자동-설정은 웹 어플리케이션을 개발한다고 예상하고, 그에 따라 스프링을 set up 한다.

"main" 메소드

어플리케이션의 entry point 이다. 메인 메소드는 스프링 부트의 SpringApplication 클래스를 run 하여 위임한다. SpringApplication 클래스는 어플리케이션을 부트스트랩하고, 스프링을 시작하고 자동 설정된 톰캣 웹 서버를 시작한다. 인자로 Example.class와 커맨드 라인 인자인 args를 넘긴다.

예제 실행하기

$ mvn spring-boot:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::  (v2.0.1.RELEASE)
....... . . .
....... . . . (log output here)
....... . . .
........ Started Example in 2.222 seconds (JVM running for 6.514)

실행가능한 jar 생성하기

실행가능한 jar란, 코드를 실행하는데 필요한 모든 의존성을 포함하여 컴파일된 아카이브이다. 이것을 만들기 위해서는 spring-boot-maven-pluginpom.xml에 포함해줘야한다. 이렇게 하면 spring-boot-stater-parent의 pom에 있는 <executions> 설정을 포함하고, repackage 골에 바인딩 되어 있다. 만약 spring-boot-stater-parent을 상속받지 않는다면 직접 설정해 주어야 한다. 플러그인 문서

mvn package 패키징을 하면 jar 파일이 target 디렉토리 밑에 생성된다. java -jar target/***.jar로 실행할 수 있다.

12. 다음 읽을 내용 (생략)

Posted by 닮은

자세한 것은 여기서 보시면 됩니다.

여기

설정

대충 보기

변수

    ${value}
  • $value -> ${value} 반드시 중괄호로 감싸야 한다.
  • ${StringUtil.isEmpty($value)} -> ${StringUtil.isEmpty(value)}
  • #set($value = "velocity") -> <#assign value = "freemarker">

리스트

    <#list list as item>
        ${item.value}
    </#list>

조건문

    <#if(value == true)>
        ${value.string('true','')}
    </#if>
  • 변수가 있는지는 확인하려면 ??를 쓰면 된다. <#if value??>this value is not null</#if>

매크로

    <#macro layout title>
      <html>
          <head>
            <title>${title}</title>
        </head>
        <body>
            <#nested>
        </body>
      </html>
    </#macro>

    <@layout>
        content
    </@layout>

include

  • 벨로시티의 #parse
  • #parse("header.vm") -> #include("header.ftl")

BeansWrapper

  • toolbox 대용(?)으로 이렇게 설정하면 ${statics["java.lang.System"].currentTimeMillis()} 이런식으로 쓸 수 있다.
    BeansWrapper w = new BeansWrapper(); 
    TemplateHashModel statics = w.getStaticModels();
    model.addAttribute("statics", statics);

삽질 방지

변수 비교

  • <#if value > 0> 라고 쓴다면 크다의 > 를 프리마커 닫는 태그로 인식하기 때문에 gt를 쓰거나 <#if value gt 0> 비교 하는 부분을 괄호 <#if (value > 0)>로 감싸주어야 한다.

숫자

  • 별도로 설정을 바꾸지 않은 경우에 숫자에 comma가 찍혀서 나온다;
  • number?c c는 computer라는 뜻인데 이렇게 해야 comma가 제거되어서 나온다.
  • 모든 숫자값에 comma가 안 찍히게 보여주고 싶으면 파일 설정을 바꾸면 된다. <#setting number_format="computer">

인코딩

  • 자동 인코딩 <#ftl output_format="HTML" auto_esc=true>
  • 파일 자체에 auto_esc 설정을 했으나, escape 원하지 않는 경우에는 ${value?no_esc}

꽤 괜찮았던 벨로시티

벨로시티 레이아웃 설정 있어요...

  • 이렇게 기본 레이아웃을 설정해주면 $screen_content 여기에 본문이 들어가게 된다.
    <html>
        <head>
            <title>Velocity</title>
        </head>
        <body>
            #parse("/fragments/header.vm")
                $screen_content
            #parse("/fragments/footer.vm")
        </body>
    </html>
  • 다른 레이아웃을 쓰고 싶으면
    #set($layout = "myOtherLayout.vm")
  • 안 쓰고 싶으면 noLayout.vm을 아래와 같이 만들어면; 된다.
    $screen_content

일단은 뭐라도 찍어줍니다...

  • 벨로시티는 변수가 없더라도 ${value} 그대로 찍어주긴 하는데 프리마커에서를 렌더링 에러가 난다. <#if value??>${value}</#if> 이렇게 한땀 한땀 해야한다.

타입

  • 벨로시티에서는 타입이 헷갈리면 #if ('${number}' == '${str}' 라고 해버리면 되는데 프리마커에서는 타입이 다른 변수끼리 비교하려고 한다는 렌더링 에러가 난다. number가 number 구나.. 를 확인하고 와서 <#if number1?string == str> 이렇게 해야하는 번거로움이 있다ㅠ

참고

Posted by 닮은
이전버튼 1 이전버튼

블로그 이미지
instragram @commonsigmchi
닮은

공지사항

Yesterday
Today
Total

달력

 « |  » 2025.1
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함