이번 스터디에서는 스프링 부트 개발 환경을 구축한다. JDK 를 설치하고 최근 제일 유용한 개발 도구로 소문난 인텔리제이 IDEA를 살펴볼 것이다.
그레이들은 매이븐과 마찬가지로 규칙 기반으로 빌드를 구성할 수 있다. 또한 JVM 환경 언어인 그루비로 만들어져 언어의 장점을 그대로 활용할 수 있으며, 테스크를 사용하면 빌드 순서까지 제어할 수 있다. 그레이들의 기능은 방대하므로 이 장에서는 스프링 부트 프로젝트를 효과적으로 구성할 수 있는 멀티 프로젝트 기능에 집중한다. 그레이들로 빌드하고 멀티 프로젝트를 구성하여 공통으로 사용되는 코드를 재활용하겠다.
- JDK 설치하기
- 인텔리제이 IDEA 사용하기
- 그레이들 설치 및 빌드하기
- 환경 프로퍼티 파일 설정하기
- 자동 환경 설정 이해하기
인텔리제이를 실행하려면 JDK 가 필요하다. 먼저 JDK 를 설치하겠다. 버전은 8u202 버전을 사용한다.
https://www.oracle.com/kr/java/technologies/javase/javase8-archive-downloads.html
최근 급부상한 인텔리제이는 젯브레인스에서 제작한 상용 자바 통합 개발환경이다. 문법이 옳은지, 중복된 코드인지, 사용하지 않는 변수인지 표시햐주며, 더 나은 문법이나 람다식으로 변환을 제안한다. 깃허브와 연동되고 디버거와 기타 플러그인 등 여러 편리한 기능을 지원한다.
아직 많은 사람들이 메이븐을 사용하지만 메이븐 설정 파일인 pom.xml은 XML 기반으로 작성되어 있어서 동적인 행위에 제약이 있다. 그래서 대안으로 나온 그루비 기반의 그레이들이 주목받고 있다.
그레이들은 앤트로부터 기본적인 빌드 도구의 기능을, 메이븐으로부터 의존 라이브러리 관리 기능을 차용했다. 멀티 프로젝트 구성 시에는 메이븐처럼 상속 구조가 아닌 설정 주입 방식을 사용하여 훨씬 유연하게 빌드 환경을 구성할 수 있다. 아직 메이븐을 사용한다면 이 기회에 그레이들을 사용해보길 바란다.
이제 그레이들을 설정하겠다.
그레이들 메인 홈페이지에 접속하여 각자의 OS 환경에 맞게 그레이들을 다운로드하여 설치한다.
특별한 입력 없이 Next 버튼을 누르면 설치가 완료된다. 그레이들을 따로 설치하지 않더라도 스프링 이니셜라이저로 프로젝트를 생성하면 해당 프로젝트에 그레이들이 자동으로 설치된다. 기본으로 생성된 그레이들 프로젝트의 구조를 보겠다.
- gradle
- wrapper
- gradle-wrapper.jar
- gradle-wrapper.properties- gradlew
- gradlew.bat
각 설정 파일의 용도는 다음과 같다.
그레이들 멀티 프로젝트를 활용하며 여러 프로젝트를 마치 하나의 프로젝트처럼 사용할 수 있다. 일반적으로 이 기능은 공통 코드를 하나의 프로젝트로 분리하고 이를 재사용할 때 유용하다.
예를 들어 커뮤니티 서비스를 개발한다고 하자. 웹, API, 배치, 기타 프로젝트가 존재한다. 네 프로젝트 모두에서 공통된 도메인이나 유틸리티를 사용할 것이다. 만약 멀티 프로젝트로 구성하지 않으면 도메인에 컬럼이 추가되거나 변경되었을 때 모든 프로젝트를 일일이 수정해주어야 한다. 멀티 프로젝트를 구성하면 이러한 중복 코드를 제거할 수 있어 실수의 번거러움을 줄일 수 있다.
그럼 도메인 관련 코드를 별도의 프로젝트로 분리하여 관리하는 멀티 프로젝트를 구성해보겠다.
rootProject.name = 'demo'
이제 테스트로 사용할 demo-web 모듈과 공용으로 사용할 demo-domain 모듈을 생성하겠다.
2. 먼저 demo-web 모듈을 생성하겠다. 루트 경로에서 New -> Module 을 선택한다. 좌측 카테고리에서 Gradle을 선택(자바 애플리케이션을 만들 것이므로) 한 뒤 JAVA 를 선택하고 Next 버튼을 누른다.
3. Add as module to 에서 community 프로젝트를 선택한 다음, Artifactid 에 demo-web 을 입력하고 Next 버튼을 눌러 모듈을 생성한다.
4. 생성된 demo-web 모듈에는 build.gradle 파일만 존재한다. 다음과 같이 기본 패키지 경로를 수동으로 생성해준다.(인텔리제이 버전에 따라 아래 과정이 생략될 수 있다.)
위와 같이 인텔리제이에서 모듈 생성 기능을 사용하면 setting.gradle 에 자동으로 생성된 모듈명이 include 된다.
스프링 부트 프로퍼티 파일은 설정 관련 및 기타 정적인 값을 키값 형식으로 관리한다. 이 파일을 사용하면 기존 스프링 프레임워크의 복잡한 XML 설정을 파일 하나로 설정할 수 있다. 만약 시본 8080번에서 80번으로 서버 포트 설정을 변경한다면 다음과 같이 src/main/resources/ 에 있는 application.properties 파일에 server.port 값을 80으로 입력하면 된다.
server.port: 80
이와 같이 설정하고 다시 스프링 부트를 실행시키면 서버가 80번 포트로 활성화된다. 어떻게 이런 식으로 간단하게 되는지는 2.5절 '자동 환경 설정 이해하기' 에서 더 자세히 알아보겠다.
기존에서 Properties 파일을 많이 사용했지만 최근에는 표현의 한계로 YAML 파일을 더 많이 사용한다. YAML을 이용해 서버 포트를 80번으로 설정해보겠다. YAML 파일은 확장자로 .yml을 사용한다. 방금 사용했던 application.properties 파일을 삭제하고 src/main/resources/application.yml을 생성한 후 다음과 같이 변경한다.
server:
port: 80
프로퍼티 설정값의 깊이에 따라 들여쓰기를 해서 계층 구조를 훨씬 쉽게 파악할 수 있다.
YAML을 설정하려면 SnakeYAML 라이브러리를 포함해야 하지만 앞에서 살펴봤듯이 스프링 부트 스타터에 SnakeYAML 라이브러리가 기본적으로 내장되어 있어 별도의 설정 없이 곧바로 사용할 수 있다. 그러므로 이제부터 모든 설정을 YAML 기반으로 진행하겠다.
공부 목적으로 로컬에서만 개발한다면 프로파일에 따라 프로퍼티를 나눌 필요는 없다. 하지만 실제 서비스에서 개발한다면 로컬 DB, 개발 DB, 운영 DB의 설정값이 모두 다르다. 이런 경우를 대비해 프로파일에 따라 프러퍼티를 다르게 설정해야 한다.
YAML 파일에서 프로퍼티 설정을 구분하는 방법은 간단하다. 다음과 같이 --- 을 기준으로 설정값을 나눈다.
server:
port: 80
---
spring:
profiles: local
server:
port: 8080
---
spring:
profiles: dev
server:
port: 8081
---
spring:
profiles: real
server:
port: 8082
YAML 파일을 사용하면 깊이에 따라 관계를 구분 짓기 때문에 List, Set, Map 등 다양한 바인딩형 매핑이 훨씬 간편하다. YAML 파일에 있는 데이터를 가져와서 사용하는 방법은 다양하다. 유용하게 사용되는 @Value 와 @ConfigurationProperties 어노테이션을 비교해보겠다.
기능 | @Value | @ConfigurationProperties |
---|---|---|
유연한 바인딩 | X | O |
메타데이터 지원 | X | O |
SpEL 평가 | O | X |
각 기능은 다음과 같다.
- 유연한 바인딩: 프포터피값을 객체에 바인딩할 경우 필드를 낙타 표기법으로 선언하고 프로퍼티의 키는 다양한 형식, 언더바 표기법으로 선언하여 바인딩할 수 있다. 적용 방법에 대한 자세한 내용은 후에 설명한다.
- 메타데이터 지원: 프로퍼티의 키에 대한 정보를 메타데이터 파일(JSON)로 제공한다. 키의 이름, 타입, 설명, 디폴트값 등 키 사용에 앞서 힌트가 되는 정보를 얻을 수 있다.
- SpEL: SpEL은 런타임에 객체 참조에 대해 질의하고 조작하는 기능을 지원하는 언어이다. 특히 매서드 호출 및 기본 문자열 템플릿 기능을 제공한다. @value만 사용 가능하며 SpEL의 모든 기능을 다룰 수는 없지만 어떻게 사용하는지 정도를 다음 절에서 알아보겠다.
표만 봐서는 감이 잡히지 않을 것이다. @Value 와 @ConfigurationProperties 를 어떻게 사용하는지 알아보며 차이점을 체험해보겠다.
프로퍼티의 키를 사용하여 특정한 값을 호출할 수 있다. 키를 정확히 입력해야 하며 값이 없을 경우에 대해 예외 처리를 해주어야 한다. application.yml 파일에 테스트 프로퍼티값을 아래 코드와 같이 추가하겠다. 그리고 테스트 프로퍼티값을 매핑해서 사용하는 코드를 AutoconfigurationApplicationTests 클래스를 생성하여 테스트하겠다.
property:
test:
name: property depth test
propertyTest: test
propertyTestList: a, b, c
아래의 코드는 등록한 값을 @Value를 사용하여 다양하게 매핑하는 방법을 제시한다. 스프링부트 테스트를 사용하게 되는데 자세한 내용은 후에 알아보겠다.
/test/java/com/demo/AutoconfigurationApplicationTests.java
package com.demo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest
public class AutoconfigurationApplicationTests {
@Value("${property.test.name}")
private String propertyTestName;
@Value("${propertyTest}")
private String propertyTest;
@Value("${noKey:default value}")
private String defaultValue;
@Value("${propertyTestList}")
private String[] propertyTestArray;
@Value("#{'${propertyTestList}'.split(',')}")
private List<String> propertyTestList;
@Test
public void testValue(){
assertThat(propertyTestName, is("property depth test"));
assertThat(propertyTest, is("test"));
assertThat(propertyTest, is("test"));
assertThat(defaultValue, is("default value"));
assertThat(propertyTestArray[0], is("a"));
assertThat(propertyTestArray[1], is("b"));
assertThat(propertyTestArray[2], is("c"));
assertThat(propertyTestList.get(0), is("a"));
assertThat(propertyTestList.get(1), is("b"));
assertThat(propertyTestList.get(2), is("c"));
}
}
스프링 부트의 @SpringBootTest 라는 테스트 기능을 사용했다. 스프링 부트 실행은 인텔리제이에서 매서드 실행시킨 Control+Shift+R 을 누르거나 testvalue() 메서드의 왼쪽에 위치한 Run 버튼을 누르면 된다.
@Value의 매핑 방식은 다음과 같다.
- @Value("${property.test.name}"): 깊이가 존재하는 키값에 대해 '.'로 구분하여 해당 값을 매핑한다.
- @Value("${propertyTest}"): 단일 키값을 매핑한다.
- @Value("${noKey:default value}"): YAML 파일에 키값이 존재하지 않으면 디폴트값이 매핑되도록 설정한다.
- @Value("${propertyTestList}"): 여러 값을 나열할 때는 배열형으로 매핑한다.
- @Value("#{'${propertyTestList}'.split(',')}"): SpEL을 사용하여 ','를 기준으로 List 에 매핑한다.
@Value 에서 유일하게 지원되는 기능인 SpEL을 사용하여 매핑하는 방법을 알아봤다. 간단하게 설명하자면 YAML 파일에서 설정한 키값을 @value의 프로퍼티값으로 주면 해당 값이 필드값에 할당되는 방식이다. 주로 단일 필드값을 가져오는 데 사용한다.
@ConfigurationProperties의 프로퍼티를 사용하여 다양한 형의 프로퍼티값을 매핑할 수 있다. 여기서는 List, Map 자료구조로 프로퍼티값을 매핑하는 방식을 알아보겠다. @ConfigurationProperties 는 기본적으로 접두사를 사용하여 값을 바인딩한다. 아래 코드처럼 fruit를 키값으로 하여 리스트형의 테스트 데이터를 지정한다. 여기서 fruit 는 접두사이자 기본 키값이 된다.