소나큐브

해당 포스팅은 자바 필수 프로젝트 유틸리티 책의 8.3 소나큐브 부분을 참고해 작성했습니다.

소나큐브

소나큐브를 사용해서 작성한 코드를 정적 분석하고 작성한 테스트로 얼마나 검증했는지 측정할 수 있다.

소나큐브를 로컬 환경에 설치하고 젠킨스와 소나큐브를 연결해서 소나큐브의 기본 퀄리티 게이트(Quality Gate)를 실행시켜보자. 퀄리티 게이트는 조직의 모든 소스가 통과해야만 하는 소스의 품질을 정의해둔 것으로 소스의 품질을 보증하는 수단으로 이용된다.

기능과 특징

소나큐브의 주요 기능은 아래와 같다.

  • 복잡도 확인
  • 코드 중복 검출
  • 코딩 규칙 확인
  • 잠재적 버그 검출
  • 단위 테스트
  • 커버리지

소나큐브 자체에서 지원하는 심플한 UI를 이용해 프로젝트의 소스 코드가 얼마나 개선되고 있는지를 직관적으로 확인할 수 있다. 소나큐브의 특징은 다음과 같다.

  • 서버는 크게 웹 서버, 검색 서버, 연산 서버로 구성된다.
  • 데이터베이스는 소나큐브 자체의 설정과 각 프로젝트의 정보가 저장된다.
  • 플러그인을 추가할 수 있다.
  • 소나큐브 스캐너(SonarQube Scanner)로 코드를 분석한다.

설치

brew를 이용해 설치 후 실행한다.

1
2
$ brew install sonarqube
$ brew services start sonarqube

brew를 이용해 설치하게 되면 다음과 /usr/local/Cellar/sonarqube/7.2.1/libexec/conf 디렉터리에 설치된다. 각 디렉터리의 역할은 다음과 같다.

  • bin : 운영체제별 실행파일이 있다.
  • conf : 소나큐브의 설정 팡리이 있다. 설정 파일에서 데이터베이스 연결과 웹 서버의 설정 등을 한다.
  • data : 기본 데이터베이스인 H2 데이터베이스를 사용할 때 데이터가 저장되는 곳이다. 테스트 목적이 아니라면, 실제 운영할 때는 다른 데이터베이스를 사용해야 한다.
  • elasticsearch : 루씬 기반의 검색 엔진인 elasticsearch가 포함되어 있다.
  • extensions : jdbc-driver와 플러그인이 포함된다.
  • lib : 실제 애플리케이션 바이너리가 포함되어 있다.
  • logs : 각 로그가 출력되는 디렉터리이다. 설정 파일에서 변경 가능하다.
  • temp : 서버 실행 시에 필요한 임시 파일이 저장된다. 실행 중에 삭제하면 안된다.
  • web : UI에 필요한 이미지와 CSS, JS 파일이 있다.

실행 후 http://localhost:9000에 접속하면 다음과 같은 화면을 볼 수 있다.

관리자로 접속해서 젠킨스와 연결할 사용자를 만들어 보자. 오른쪽 상단 위에 있는 ‘Log in’ 을 클릭한 후 초기 ID/PW인 admin/admin을 입력해서 로그인한다.

로그인이 완료되면 튜토리얼 페이지가 나온다. 우측 상단의 ‘Skip this tutorial’ 을 클릭해서 스킵한다.

아래와 같이 프로젝트 페이지를 확인할 수 있다.

젠킨스 사용자 생성

젠킨스에서 소나큐브에 접속할 때 사용할 사용자를 생성한다.

화면 상단의 메뉴에서 ‘Administration’ -> ‘Security’ -> ‘Users’ -> ‘Create User’ 를 클릭해서 사용자 생성 창을 출력한다.

jenkins라는 이름으로 사용자를 생성한다.

젠킨스에서 접속할 때 사용할 토큰을 생성한다. 조금전 생성한 jenkins 유저의 ‘Token’ 을 클릭한다.

‘Enter Token Name’ 에 적당한 이름을 넣고 ‘Generate’ 버튼을 눌러서 생성한다.

생성된 토큰 앞에 있는 ‘Copy’ 버튼을 눌러 젠킨스에서 사용할 키를 저장한다.

소나큐브 스캐너 설정

이번에는 젠킨스의 빌드 과정에서 소나큐브를 연동한다.

‘Jenkins 관리’ -> ‘플러그인 관리’에서 ‘SonarQube Scanner’를 설치 한다.

‘Jenkins 관리’ -> ‘시스템 설정’ 으로 이동하면, ‘SonarQube servers’ 라는 설정이 추가된것을 확인할 수 있다. 만약 플러그인을 설치했는데도 해당 설정이 표시되지 않는다면 젠킨스 서버를 재실행해본다.

‘Add SonarQube’ 를 클릭한다.

‘고급’ 버튼을 클릭한 후 시스템 환경 변수를 사용하도록 각각 다음과 같이 설정한다.

  • Environment variables : 체크 박스 선택
  • Name : SonarQube-Local 입력 (스페이스와 한글을 입력하지 않는다.)
  • Server URL : 소나큐브의 URL 입력(여기서는 default인 http://localhost:9000)
  • Server authentication token : 소나큐브에서 생성한 토큰 입력
  • Additional analysis properties :
    sonar.sources=src sonar.java.binaries=target/classes, target/test-classes

‘저장’ 을 클릭해서 저장한다.

소나큐브 스캐너를 추가하는 작업을 진행한다. ‘Jenkins 관리’ -> ‘Global Tool Configuration’ 을 클릭한다.

‘SonarQube Scanner for MSBuild’와 ‘SonarQube Scanner’ 아래의 버튼을 클릭해서 각각 스캐너를 추가한다.

‘Name’에 적당한 이름을 넣고 ‘Install automatically’ 가 체크되어 있는지 확인 후 저장한다.

새로운 잡을 생성해서 테스트 해보자.

  1. 젠킨스 메인 페이지에서 ‘새로운 Item’을 클릭하고 잡 이름에 ‘SonarQubeTset’를 입력하고 ‘Freestyle project’를 선택 후 저장 버튼을 클릭한다.

  2. 소스 코드 관리 : 테스트 프로젝트의 깃허브 주소를 넣는다. (여기서는 ‘자바 필수 프로젝트 유틸리티‘ 책에서 제공한 ‘spring-mvc-example‘ 프로젝트를 fork해 사용했다.)

  3. Credentials : 저장되어 있는 자격증명에서 선택한다.

  4. 빌드 환경에서 ‘Prepare SonarQube Scanner environment’를 선택한다. (이번 테스트에서는 필요하지는 않지만, ‘빌드 환경’에서 호나경 변수를 사용할 수 있게 하려고 체크하는 것이다.)

  5. Build : ‘Invoke top-level Maven targets’를 선택한다.

  6. Goals : ‘clean install’을 입력한다. (다른 골을 추가해도 된다.)

  7. 다시 Build의 ‘Add build step’을 클릭해서 ‘Execute SonarQube Scaner’를 추가한 후 ‘Analysis properties’에 아래의 정보를 입력한다.

    1
    2
    3
    sonar.projectKey=spring-mvc-example
    sonar.projectName=spring-mvc-example
    sonar.projectVersion=1.0.0

    이 테스트에서는 고정값으로 입력했지만 매개변수로 처리하거나 환경 변수에서 치환하는 방법도 많이 사용한다.

  8. ‘저장’ 버튼을 클릭해서 잡 대시보드로 이동한다.

소나큐브의 아이콘이 추가된것을 확인할 수 있다.

소나큐브 빌드

‘Build Now’ 를 클릭해서 빌드한다. 정상 종료되면 ‘SonarQube Quality Gate’ 가 표시되고 실행 결과를 알려준다.

화면에 표시되는 ‘SonarQube’ 링크나 ‘OK’를 클릭하면 소나큐브의 대시보드로 이동한다.

상단의 메뉴에서 ‘Issues’, ‘Code’를 통해 각 결과를 확인할 수 있다.

젠킨스

젠킨스

젠킨스 는 자바로 작성된 오픈 소스 소트트웨어로 지속적 통합(Continuous Integration, CI)과 지속적 배포(Continuous Delivery, CD)를 제공한다.

웹 애플리케이션 형태로 제공되고 있어서 어떠한 환경에서도 손쉽게 설치할 수 있으며 도커를 사용해 설치할 수도 있다. 또한 천 개 이상의 플러그인으로 다양한 시스템과 연동할 수 있다.

젠킨스의 주요 기능은 다음과 같다.

  • 형상관리 도구와의 연동
  • 소스 코드 체크아웃
  • 웹 인터페이스
  • 테스트 보고서 생성
  • 빌드 및 테스트 자동화
  • 실행 결과 통보
  • 코드 품질 감시
  • 다양한 인증 기반과 결합한 인증 및 권한 관리
  • 배포 관리 자동화
  • 분산 빌드(마스터 슬레이브)
  • 그루비 스크립트를 이용한 자유로운 잡 스케줄링

젠킨스는 개발자가 소스코드를 추가, 수정한 뒤 형상관리 도구에 저장하면 자동으로 읽어 빌드 및 테스트를 실행한다.

젠킨스 설치

젠킨스를 사용하려면 JDK와 메이븐이 필요하다.

macOS에서 brew를 이용해 쉽게 설치가 가능하다.

1
$ brew install jenkins

추후 필요하다면 다음과 같이 삭제할 수도 있다.

1
$ brew remove jenkins

설치 후 다음과 같이 젠킨스를 백그라운드 서비스로 구동 및 중지가 가능하다.

1
2
$ brew services start jenkins
$ brew services stop jenkins

젠킨스를 실행 후 웹사이트에 접속한다. http://localhost:8080

처음 접속 시 Administrator password를 입력하게 되어 있는데 아래의 경로에 있는 파일에서 키를 확인해 입력한다.

/Users/{사용자 계정}/.jenkins/secrets/initialAdminPassword


젠킨스를 어떻게 설치할지 결정할 수 있다. ‘Install suggested plugins’를 선택하면 젠킨스에서 추천하는 플러그인들이 같이 설치되고, ‘Select plugins to install’을 선택하면 필요한 플러그인을 선택하여 설치할 수 있다.


필요한 플러그인들을 자동으로 설치하기 시작한다.


설치가 끝나면 관리자 정보 입력 화면이 나온다. 정보를 입력한 후 ‘Save and Finish’ 버튼을 클릭한다.


빌드 잡(job) 생성하기

  1. 좌측 메뉴 상단에 있는 ‘새로운 Item’을 클릭한다. ‘Enter an item name’에 적당한 이름을 넣고 아래의 템플릿 중 ‘Freestyle project’를 선택한 후 ‘OK’ 버튼을 클릭한다. (Freestyle project는 거의 모든 젠킨스의 설정을 자유롭게 설정할 수 있다.)
  2. Github에 테스트를 위해 간단하게 만들어 놓은 프로젝트를 가져와 빌드를 테스트 한다.
  3. Github에서 프로젝트를 가져오기 위해서는 자격 증명을 추가해야 한다. ssh-key를 등록하거나 Github의 계정을 입력해야 한다.
  4. Credentials 선택 박스에서 조금 전 추가한 계정을 선택한 후 ‘저장’ 버튼을 클릭한다.
  5. ‘Build Now’를 클릭해 빌드를 진행한다.
  6. ‘Console Output’을 누르면 빌드가 성공한 것을 확인할 수 있다.

그레이들 기초

그레이들

그루비와 그레이들

그루비 는 자바 가상머신에서 동작하는 오픈 소스 스크립트 언어이다. 그루비는 자바 문법을 더욱 쉽게 쓰기 위해 스크립트 언어와 비슷한 문법으로 되어 있어서, 대부분의 자바 프로그래머는 자바 코드를 작성하는 느낌으로 그루비를 작성할 수 있다. 동적 언어이며 자바와 달리 작성한 스크립트를 컴파일할 필요 없이 직접 실행할 수도 있다.

또한, 일부 자바 프레임워크에서는 그루비를 지원한다. 대표적인 스프링 프레임워크에서도 그루비를 이용할 수 있다.

이처럼 자바와 거의 같고, 스크립트 언어처럼 부담 없이 작성해서 바로 실행할 수 있는 특징을 고려하면, 그루비를 사용해서 자바 빌드 도구를 만들려는 생각은 자연스럽다.

그루비의 이러한 이점을 최대한 활용해서 개발한 빌드 도구가 그레이들 이다.

그레이들이란

그레이들은 그루비를 사용한 빌드 도구이다. 메이븐은 XML을 이용하여 빌드 정보를 기술했는데, 그레이들은 그루비를 이용해 빌드 정보를 기술하기 한다. 때문에 자바 프로그래머가 좀 더 쉽게 다룰 수 있다.

그레이들의 특징은 다음과 같다.

  • 유연한 언어로 기술
    그루비라는 프로그래밍 언어를 사용해서 기술하기 때문에 유연하게 각종 처리를 수행할 수 있다. 또한 기술하는 내용을 분할하거나 구조화하는 것도 간단하다.
  • 태스크로 처리
    그레이들은 ‘태스크’라는 개념을 이용해 프로그램을 작성한다. 다양한 용도별로 태스크를 만들어서 그 안에 처리를 기술한다.
  • 자바/그루비/스칼라 기본 지원 + 알파
    그레이들은 자바 가상 머신에서 동작하는 언어를 중심으로 지원한다. (별도의 네이티브 코드 플러그인을 사용하면 C/C++ 등, 다른 언어에도 대응할 수 있다.)
  • 각종 도구와 통합
    여러 도구들(앤트, 아파치 아이비 등)과 통합되어 처리를 실행할 수 있다. 또한, 메이븐의 pom.xml을 그레이들용으로 변환하는 도구도 있다.
  • 메이븐 중앙 저장소 대응
    그레이들에서는 메이븐 중앙 저장소를 지원하기 때문에, 중앙 저장소에 있는 라이브러리 모두 그대로 이용 가능하다.

그레이들 사용하기

그레이들 소프트웨어에는 그루비가 포함되어 있기 때문에, 단지 그레이들을 사용하는 용도라면 그루비를 설치할 필요는 없다. (그레이들을 설치하는 것만으로는 그루비 언어를 이용해서 프로그래밍할 수는 없다.)

그레이들 프로젝트 생성

그레이들 명령어를 사용해 프로젝트를 생성할 수 있다.

1
2
3
mkdir gradle-app
cd gradle-app
gradle init --type java-library

gradle init

그레이들 명령어는 gradle OO 형태로 실행한다. 위에서 프로젝트를 생성하며 사용한 init은 그레이들에서 '태스크' 라고 부른다. 이 init은 프로젝트의 기본적은 파일과 폴더를 생성한다.

–type은 생성할 프로젝트의 타입을 지정하는 옵션인데, java-library는 자바 프로젝트임을 나타낸다. 지정한 언어의 샘플 코드를 생성한다. 이 옵션을 생략하면 그레이들 프로젝트의 기본적인 파일들만 생성된다.

그레이들 실행과 태스크

그레이들에서는 다양한 처리를 위해 ‘태스크’를 이용한다. 태스크란, 실행할 처리를 모아놓은 단위로 그레이들에서 처음부터 포함된 것도 있고, 프로그래머가 작성할 수도 있다. 이 태스크를 실행하는 것이 그레이들에서 빌드를 관리하는 기본적인 방법이다. 프로젝트를 컴파일하거나, 실행하는 모든 처리에 태스크를 이용한다.

생성된 gradle-app 폴더 내부의 구조를 살펴보자.

  • .gradle 폴더 : 태스크로 생성된 파일 등을 보존한다.
  • gradle 폴더 : 기본값으로는 그레이들 환경을 모아놓은 wrapper 파일이라고 하는 파일들이 들어 있다.
  • src 폴더 : 소스 코드 관련 파일을 이곳에 작성한다.
  • build.gradle : 그레이들 빌드 파일, 이곳에 프로젝트의 빌드 내용을 기술한다.
  • settings.gradle : 빌드 설정 정보를 기술한 파일. 빌드를 실행하기 전에 읽히기 때문에, 필요한 라이브러리를 읽는 등의 기술을 할 수 있다.

이번에는 생성된 파일의 코드를 확인해보자.

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 자바 프로그램을 빌드할 경우에는 java 플러그인을 로드한다.
apply plugin: 'java'

// 저장소 정보를 관리하는 프로퍼티이다. 이곳에서 저장소를 설정할 수 있다. (로컬 환경이나 원격 저장소 기술)
// jcenter는 그레이들에서 중앙 저장소로 이용되는 저장소이다.
// 메이븐 중앙 저장소는 mavenCentral 메서드를 이용해서 사용할 수 있다.
repositories {
jcenter()
}

// 의존성에 관한 설정을 관리하는 프로퍼티이다. 필요한 라이브러리등의 정보를 기술한다.
dependencies {
compile 'org.slf4j:slf4j-api:1.7.21'
testCompile 'junit:junit:4.12'
}

settings.gradle

1
2
// 루트 프로젝트의 이름을 설정한다. 루트 프로젝트는 다수의 프로젝트를 관리할 때 기본이 되는 프로젝트를 가리킨다. 여기에서는 '이 빌드 파일로 빌드할 프로젝트의 이름'이라고 생각하면 된다.
rootProject.name = 'gradle-app'

인텔리제이에서 사용하기

인텔리제이는 표준으로 그레이들을 지원한다. 인텔리제이에서 그레이들용 프로젝트를 생성할 경우, gradle init –type java-library 명령어를 사용하는 경우와 결과가 조금 다르다.

build.gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
group 'com.jongmin'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}

시작 부분에 그룹 ID와 버전을 지정하는 문장이 포함되어 있다. gradle init으로 작성한 build.gradle에서는 포함되지 않았는데, 그 이유는 메이븐 저장소를 이용하기 때문이다. 메이븐에는 모든 프로그램에 그룹 ID와 아티팩트 ID가 할당되어 있다.

sourceCompatibility는 자바 소스 코드의 버전을 가리킨다.

태스크 실행

메이븐과 마찬가지로 실행할 내용을 ‘Run…’ -> ‘Edit Configurations’ 메뉴에서 컨피그레이션에 설정하면 인텔리제이의 ‘Run’으로 프로그램의 빌드와 실행, 디버그 등의 기능을 수행할 수 있다.

build.gradle

그래이들은 build.gradle에 기술한 코드를 필요에 따라 실행하여 빌드를 실행한다.

그레이들은 ‘그루비를 사용하는 빌드 도구’이다. 하지만 이는 정확한 설명은 아니다. 그레이들은 ‘그루비 그 자체’는 아니고 **’그루비 기반의 DSL(Domain Specific Language)’**이다.

DSL은 ‘도메인 고유 언어’라고 불리는데, 특정한 용도에 한정된 언어를 말한다. 그 언어 그 자체는 아니고, 특정한 용도에 맞게 해당 언어를 기반으로 각색한 것이다. 그레이들에서 사용되는 언어는 ‘그루비를 기반으로 작성된 Gradle DLS’이다.

그레이들은 기본적으로 태스크를 작성하여 실행한다. 태스크는 실행할 처리를 모아서 명령어로서 실행할 수 있게 한 것이다. 다음과 같은 형식으로 정의한다.

1
2
3
task 이름{
...실행할 처리...
}

이렇게 정의한 태스크는 명령행에서 gradle 이름 형태로 실행할 수 있다.

간단한 예제를 작성해 실행해보자. 다음과 같이 build.gradle을 수정한다.

1
2
3
4
5
6
7
8
9
task hello {
doLast {
println();
println("=================");
println("Welcome to Gradle!");
println("=================");
println();
}
}

수정한 후, 명령행에서 다음과 같이 실행한다.

1
gradle hello

결과는 다음과 같다.

1
2
3
4
5
6
7
8
> Task :hello

=================
Welcome to Gradle!
=================

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

그레이들은 기본적으로 빌드 도구이지만, 여기에서는 빌드도 컴파일도 하지 않고, 단지 메시지를 표시했다. 태스크는 작성된 처리를 실행할 뿐이지, 반드시 빌드와 관련된 기능이 포함되어야 하는 것은 아니다.

quiet 모드로 실행

이번에는 quiet 모드로 실행해보자.

1
gradle -q hello

결과는 다음과 같다.

1
2
3
=================
Welcome to Gradle!
=================

이렇게 하면 hello에서 println한 내용만 출력되며, 그 이외의 내용은 표시되지 않는다. -q 옵션은 태스크를 quiet 모드로 실행해 예외 발생 등 중요한 문제 이외의 표시가 제한된다. 태스크의 실행 결과만 알고 싶을 때 편리한 옵션이다.

액션 리스트

태스크는 다양한 ‘액션’을 내부에 가지고 있다. 태스크를 실행하면 준비된 액션이 순서대로 실행된다. 이 액션을 관리하는 것이 ‘액션 리스트’이다. 액션은 어떤 역할을 하는지가 정해져 있다. 필요에 따라 액션에 처리를 추가해 태스크를 조합할 수 있다.

액션 중에서도 doFirstdoLast가 가장 많이 사용된다. 각각 ‘처음에 실행되는 액션’과 ‘마지막에 실행되는 액션’이다. 이들을 이용해 태스크의 처음과 마지막에 처리를 실행할 수 있다.

매개변수 이용

태스크를 실행할 때 어떤 정보를 태스크에 전달하고 싶은 경우도 있다. 이럴 때 매개변수를 이용할 수 있다.

1
2
3
4
5
6
7
8
9
task hello {
doLast {
def total = 0;
for(def i in 1..num.toInteger()) {
total += i;
}
println("total: " + total);
}
}

1에서 num까지 합하는 태스크이다. 여기서 사용되는 num이 매개변수로 전달되는 프로퍼티이다. 이처럼 매개변수에서 전달되는 값을 프로퍼티로 사용할 수 있다. 단, 주의할 점은 값이 String이라는 것이다.

hello 태스크는 다음과 같이 실행한다.

1
gradle hello -q -Pnum=100

이처럼 태스크를 실행하는 동안에는 -P프로퍼티=값 형태로 특정 변수에 값을 전달할 수 있다.

동적 태스크 실행

스크립트를 사용해서 동적으로 태스크를 생성할 수도 있다.

1
2
3
4
5
6
7
8
def arr = ["one", "two", "three"];
arr.each {s ->
task "$s" {
doLast {
println("this is $s task.");
}
}
}

다음과 같이 실행한다.

1
gradle -q one

java 플러그인 사용하기

자바로 개발할 때 필요한 기본적인 기능은 ‘java’ 플러그인에 포함되어 있다. build.gradle에서 로드하여 이용한다.

1
apply plugin: 'java'

다음의 명령어로 빌드한다.

1
gradle java

위 명령어를 실행하면 프로젝트가 컴파일되고 JAR 파일이 생성된다. 컴파일로 생성된 파일들은 build 폴더에 보관된다. 이 안의 libs 폴더 안에 gradle-app.jar 파일이 생성된다.

build 폴더 안에는 classes 폴더도 있는데, 여기에는 컴파일된 클래스 파일이 보관된다. gradle java에서는 우선 소스 코드를 컴파일하여 클래스 파일을 작성한 후, 이를 모아서 JAR 파일로 만드는 일련의 처리가 자동으로 수행된다.

gradle java와 gradle build

grade java는 자바 프로그램의 빌드를 수행하지만, gradle build는 어떤 언어로 작성된 프로젝트라도 빌드한다. (그레이들은 자바 이외에도 그루비나 스칼라 등 많은 언어를 지원하고 각각의 언어에서 빌드를 수행하는 플러그인을 제공한다.)

java 플러그인의 태스크

java 플러그인에는 이 외에도 몇 가지 태스크가 더 있다.

  • java
    자바 소스 코드를 컴파일하고 그 외에 필요한 리소스 파일들을 모아서 JAR 파일을 생성한다. 프로그램을 배포할 때 이 태스크로 JAR 파일을 만들면 유용하다. 단, 이 java 태스크로 생성된 JAR 파일은 Executable이 아니라는 점에 주의해야 한다.
  • compileJava
    자바 소스 코드를 모두 컴파일한다. 보존할 장소(build 안의 classes 폴더)가 없다면 폴더를 자동으로 생성하고 그 안에 클래스 파일을 작성한다.
  • processResources
    리소스 파일을 클래스 디렉터리(classes 폴더) 안에 복사한다.
  • classes
    소스 코드 컴파일과 리소스 파일 복사를 실행한다. compileJava와 processResources가 합쳐진 것이라 생각해도 된다.
  • test
    프로그램 테스트를 실행한다. 소스 코드와 관련된 컴파일을 수행하고 테스트에 필요한 리소스 복사 등을 수행한 뒤 JUnit으로 테스트를 실행한다. JUnit 라이브러리를 이용할 수 없는 상태에서는 테스트를 실행할 때 오류가 발생한다.
  • jar
    프로그램을 컴파일하고 리소스 파일 등을 준비한 뒤, JAR 파일로 패키징한다. 단, 파일을 단순히 JAR 파일에 모을 뿐이며 Executable jar을 작성하는 것은 아니다.
  • javadoc
    소스 코드를 해석하여 Javadoc 파일을 생성한다. build 안의 docs 폴더 안에 javadoc 폴더를 작성하여 파일을 보관한다.
  • clean
    빌드로 생성된 파일을 모두 삭제한다.

java 플러그인의 태스크 이용하기

1
2
3
4
5
task doit(dependsOn: [compileJava, jar]) {
doLast {
println "*** compiled and created jar! ***"
}
}

doit 태스크에는 매개변수가 포함되어 있다. dependsOn은 이 매개변수가 ‘의존성’을 지정하는 것을 가리킨다. ‘compileJava, jar’은 2개의 태스크를 모아놓은 배열이다. 즉, doit이 compileJava와 jar이라는 2개의 태스크에 의존한다.

이 상태에서 dependsOn에서 태스크를 지정하면 그 태스크가 실행되기 전에 의존하는 모든 태스크가 실행된다. 그리고 의존하는 태스크의 실행이 완료된 후에 doit 태스크의 doLast가 호출된다.

의존성을 지정해서 실행하는 방법 이외에 태스크를 직접 실행할 수도 있는데, 태스크의 execute 메서드를 호출하면 된다.

1
2
3
4
5
6
7
8
task doit {
doLast {
println "*** compiled now! ***"
tasks.compileJava.execute()
println "*** create jar! ***"
tasks.jar.execute()
}
}

실행하면 의존성을 지정해서 실행할 때와 같은 결과를 얻지만, “Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0.” 라는 메시지가 출력된다. 그레이들 버전 5.0 부터는 execute 메서드를 직접 호출하는 것이 호환되지 않기 때문이다.

application 플러그인

java 플러그인에는 프로그램을 실행하는 태스크가 없어 application 플러그인을 사용해 애플리케이션을 실행해야 한다.

1
apply plugin: 'application"

build.gradle에 이처럼 작성하고 그 다음에 mainClassName 프로퍼티에 메인 클래스(실행할 클래스)를 설정한다.

1
mainClassName = "com.jongmin.gradle.App"

application 플러그인에는 run 태스크가 포함되어 있다. 이것을 실행하면 mainClassName에 지정된 클래스가 실행된다.

1
gradle run

다음과 같이 작성하면 jar을 실행한 후 run으로 클래스를 실행할 수 있다.

1
2
3
4
5
6
7
8
9
10
apply plugin: 'java'
apply plugin: 'application'

mainClassName = "com.jongmin.gradle.App"

task doit(dependsOn:[jar, run]) {
doLast {
println("*** do it! ***")
}
}

빌드 도구와 메이븐

빌드 도구

빌드 도구란?

개발 환경의 변화와 빌드

자바를 처음 공부할 때는 이클립스 혹은 인텔리제이 같은 IDE만으로 프로그램을 뚝딱 만들 수 있다. 그러나 현업에 투입되면 무언가 복잡한 환경을 만나게 된다.

하지만 실제 자바 입문시절에 배운 과정과 현업에서 사용하는 과정에 차이는 없다. 다만 다양한 도구를 사용하여 더 전문화하여 현업 도구에 활용할 뿐이다.

  • 명령행에서 컴파일하기
    아주 간단한 프로그램이라면 명령행에서 javac를 이용하는 것만으로도 충분하다. 하지만 라이브러리 등을 이용하면, classpath에 다수의 라이브러리 경로를 기술한 뒤에 컴파일 해야 한다. 또한 생성된 클래스 파일을 모아 JAR 파일을 생성하려면 이 역시 모두 명령어로 실행해야 한다.
    소스 코드의 컴파일에서 JAR 파일의 생성까지 긴 과정을 수행하려면 방대한 명령어가 필요한데, 이를 매번 수작업으로 작성하면 큰 수고가 든다.
  • 프로젝트 및 라이브러리 설치
    최근에는 개발할 때 모든 프로그램을 처음부터 만드는 경우가 거의 없다. 프로그램에 필요한 기능은 라이브러리를 이용하거나, 프레임워크를 이용하여 애플리케이션을 개발한다.
    이런 경우, 필요한 소프트웨어를 갖추고 정해진 대로 파일을 구성해야 한다.
  • 테스트 자동화
    단순한 프로그램이라면 컴파일 후 실행 및 동작만 확인하는 것으로 충분하지만, 어느 정도 규모 있는 프로그램은 프로그램 생성과 함께 테스트를 실행하는 것이 일반적이다.
  • 프로그램 배포
    웹 애플리케이션은 구현한 프로그램을 서버에 배포하게 된다. 이런 작업을 수작업으로 시행하기가 번거롭다.

빌드 도구의 역할

‘빌드 도구’는 단순히 프로그램을 컴파일하여 애플리케이션을 생성하는 작업 그 이상으로 다양한 기능을 제공한다.

  • 프로그램 빌드
    프로그램을 컴파일하고, 지정된 디렉터리에 필요한 리소스를 모아서 프로그램을 완성한다. 그때 라이브러리등 필요한 파일을 설치하도록 지정할 수 있다.
  • 프로그램 테스트와 실행
    빌드된 프로그램의 실행뿐 아니라 테스트 기능도 제공한다. 빌드를 실행 할 때, 빌드가 완료되면 곧바로 테스트를 실행하는 도구도 있다.
  • 라이브러리 관리
    프로그램에서 필요한 라이브러리들을 관리한다. 빌드 실행 시 자동으로 라이브러리를 다운로드하고 설치하는 등의 작업을 한다.
  • 배포 작업
    빌드한 프로그램을 배포하는 기능을 제공한다.

개발 도구와 빌드 도구

개발 도구에 있어 빌드 도구를 다루는 방식은 크게 두 가지이다.

  • 빌드 도구를 이용하는 기능이 포함된 경우
    이클립스는 메이븐, 인텔리제이는 메이븐과 그레이들을 지원한다.
  • 개발 도구에서 명령어로 실행하는 경우

메이븐 기초

메이븐은 아파치 소프트웨어 재단이 개발하는 오픈 소스 빌드 도구이다. ‘아파치 앤트(Ant)’의 후속으로 개발되었고, 자바 프로그램 개발을 대상으로 한 오픈 소스 빌드 도구이다.

메이븐 특징

  • 빌드 파일은 XML로 작성
  • 단위 작업 ‘골’
    골은 메이븐에서 실행하는 작업의 목적을 지정한다. 메이븐 명령어를 실행할 때 골을 지정하면, 어떤 작업을 수행하여 무엇을 작성할지 지정할 수 있다.
  • 라이브러리 관리와 중앙 저장소
    빌드를 실행하는 사이에, 빌드 파일에 기술된 정보를 바탕으로 필요한 라이브러리를 자동으로 다운로드하여 포함시킨다. 이를 가능하게 하는 것이 중앙 저장소이다. 중앙 저장소는 메이븐에서 이용 가능한 라이브러리를 모아서 관리하는 웹 서비스이다.
  • 테스트와 문서 생성
    엔트의 표준에는 포함되지 않았던 JUnit 테스트 및 Javadoc 문서 생성 등의 기능을 갖추고 있다.
  • 플러그인을 이용한 확장
    플러그인을 사용하면 메이븐에 기능을 추가할 수 있다.

메이븐 프로젝트 생성

메이븐에 포함된 **archetype:generate**라는 골을 이용하면, 간단하게 프로젝트의 기본 부분을 만들 수 있다. 아키타입(archetype)은 프로그램의 템플릿 모음이다.

Intellij를 이용해서도 메이븐을 기반으로 프로그램을 빌드하는 프로젝트를 생성할 수 있다. New Project -> 목록에서 Maven 선택 -> Create from archetype 체크 -> maven-archetype-quickstart 선택

메이븐에서는 mvn 명령어로 각종 조작을 할 수 있는데, 이 명령어들을 인텔리제이의 ‘Run’을 이용하여 실행할 수 있다. 실행할 내용을 컨피그레이션에 설정하면 인텔리제이의 ‘Run’으로 프로그램의 빌드와 실행, 디버그 등의 기능을 수행할 수 있다. (‘Run…’ -> Edit Configurations’ 메뉴에서 설정 가능)

pom.xml

pom.xml 파일에서 POM은 ‘Project Object Model’을 말한다. 이 파일에 프로젝트에 관한 각종 정보를 기술한다.

<project>와 기본속성

  • 모델 버전

    1
    <modeVersion>4.0.0</modeVersion>

    기본적으로 메이븐은 하위 호환성을 지원하기 때문에 이후 새로운 버전이 되더라도 이곳의 버전 번호를 바꾸면 이외 부분은 크게 수정하지 않고도 사용할 수 있다.

  • 그룹 ID

    1
    <groupId>com.jongmin</groupId>

    그룹 ID는 작성할 프로그램이 어디에 소속되어 있는지를 나타낸다.

  • 아티팩트 ID

    1
    <artifactId>mvn-app</artifactId>

    그룹 ID와 함께 프로그램을 식별하는 데 사용된다. ID이기 때문에 같은 그룹 내에서 같은 프로젝트 이름이 중복되지 않도록 주의해야 한다.

  • 버전

    1
    <version>1.0-SNAPSHOT</version>

    메이븐을 사용하는 프로젝트를 빌드하거나 패키징한 경우 여기서 지정된 번호가 생성된 프로그램의 버전으로 설정된다. 보통은 생성된 JAR 파일의 파일명에도 사용된다.

  • 패키지 종류

    1
    <packaging>jar</packaging>

    보통은 jar을 지정하지만, zip이라고 지정하면 ZIP 파일로 패키징한다.

  • 애플리케이션 이름

    1
    <name>mvn-app</name>

    작성하는 애플리케이션의 이름을 지정한다. 그룹 ID나 아티팩트 ID와 달리 유일한 값일 필요가 없다.

  • URL

    1
    <url>http://maven.apache.org</url>

    기본값으로는 메이븐 사이트의 URL이 지정되어 있다.

**<properties>**는 pom.xml에서 이용되는 속성값을 설정한다.

1
2
3
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

기본값으로는 <project.build.sourceEncoding>라는 항목이 설정되어 있는데 이는 소스 파일의 문자 인코딩 방식을 지정한다.

<dependencies>와 의존성 설정

dependencies 태크를 통해 필요한 라이브러리를 관리할 수 있다. 여기에 의존성을 적어두면 필요한 라이브러리 등을 자동으로 다운로드하여 설치 등을 할 수 있다.

<dependency> 태그를 설정하는 것만으로 의존 라이브러리가 자동으로 로드되는 것은 메이븐의 중앙 저장소 때문이다. 중앙 저장소는 메이븐을 개발한 아파치 소프트웨어 재단이 운영하는 사이트이다.

기본적인 ‘골’

메이븐은 골을 지정하여 실행할 처리의 역할을 정한다.

1
mvn 골
  • compile

    1
    mvn compile

    자바 소스 코드 파일을 컴파일 한다. 프로젝트 폴더 내에 target 폴더가 생성된다.

  • test-compile

    1
    mvn test-compile

    유닛 테스트용 클래스를 컴파일한다. src 폴더 안의 test 안에 작성된 유닛 테스트용 소스 코드 파일을 컴파일하여, target 폴더 안에 test-class 폴더를 작성하고 그 안에 클래스 파일을 생성한다.

  • test

    1
    mvn test

    메이븐은 테스트(유닛 테스트)가 거의 표준 기능으로 포함되어 있다. 테스트를 개별적으로 실행하는 골이 test이다.작성한 유닛 테스트용 클래스를 이용하여 테스트가 실행되고 그 결과가 출력된다.

  • package

    1
    mvn package

    mvn compile을 실행하면 클래스 파일이 생성되지만, 일반적으로 자바 프로그램은 클래스파일을 그대로 배포하지는 않는다. 일반적으로 JAR 파일 등으로 패키징하여 배포한다.

    명령 한 번으로 프로그램을 컴파일하여 유닛 테스트를 실행한 후 JAR 파일로 패키징하는 처리가 모두 자동적으로 수행된다.

    실행 후 target 폴더 안에 jar 파일이 생성된다.

  • clean

    1
    mvn clean

    메이븐은 프로그램을 빌드하면서 컴파일된 클래스 파일 뿐만 아니라, 테스트, 압축을 실행하는 파일 등을 만든다. clean은 부가적으로 생성된 파일을 모두 지운다.

프로그램 실행하기

클래스가 하나인 코드는 java 명령어로도 쉽게 실행할 수 있다. 그러나 다양한 라이브러리를 이용하는 프로젝트에서는 모든 클래스 경로를 직접 지정해야 하기 때문에 java 명령어를 이용해 실행하는 일은 번거롭다.

메이븐에는 표준으로 자바 프로그램을 실행하는 골은 없다. 하지만 exec-java-plugin 플러그인을 이용하면 프로그램을 실행할 수 있다.

pom.xml의 <project> 태그 안에 있는 <dependencies> 종료 태그의 다음 행에 다음과 같이 플러그인을 추가한다.

1
2
3
4
5
6
7
8
9
10
11
12
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<mainClass>com.jongmin.App</mainClass>
</configuration>
</plugin>
</plugins>
</build>

명령행에 다음과 같이 실행하면 App 클래스가 실행된다. 만약 mvn clean으로 프로젝트의 빌드 결과물을 제거한 경우 다시 mvn package로 빌드한 후에 실행한다.

1
mvn exec:java

exec-maven-plugin은 메인 클래스를 지정해야 한다. 플러그인에 정보를 지정할 때는 <configuration> 태그를 이용한다.

빌드 플러그인

<build> 태그는 빌드에 관한 정보를 기술하는 태그이다. 형태는 다음과 같다.

1
2
3
4
5
6
7
<build>
<plugins>
<plugin>...</plugin>
<plugin>...</plugin>
...
</plugins>
</build>

<build>와 <plugins> 태그는 여러 개 사용할 수 없다. 반드시 1개씩 있고, 그 안에 모든 <plugin>을 모아서 사용한다.

메이븐의 골과 플러그인

exec:java 골은 플러그인을 사용해 추가된 것이다. 사실, 지금까지 사용했던 모든 골들도 플러그인으로 추가된 것이다.

  • compile : maven-compiler-plugin
  • package : maven-jar-plugin
  • test : maven-surefire-plugin

하지만 위의 플러그인은 표준으로 포함되어 있기 때문에 플러그인이라고 의식하지 못했던 것이다.

표준이 아닌 <plugin> 태그에 의해 추가된 플러그인의 골을 지정하는 경우에는 xx:xx와 같이 요소가 둘인 경우가 일반적이다. 플러그인 하나가 여러 골을 가질 수도 있기 때문에 ‘플로그인:골’ 형태로 기술한다.

<plugin>이 필수는 아니다. 플러그인으로 추가하여 이용하는 골이라고 해서 <plugin>에 기술하지 않으면 사용하지 못하는 것은 아니다. <plugin>은 플러그인에 포함된 설정 등의 정보를 기술하는 태그이다. 그렇기 때문에 설정이 필요하지 않으면 기술할 필요가 없다.

인텔리제이에서 사용하기

플러그인을 통해 개발 도구의 프로젝트로 변환이 가능하다.

1
mvn idea:idea

위 골을 실행하면 인텔리제이에서 프로젝트를 다루는데 필요한 파일들이 생성된다.

1
mvn idea:clean

인텔리제이 프로젝트에서 인텔리제이 관련 파일을 삭제하여 원래의 메이븐 프로젝트로 돌리려면 위의 골을 실행한다.

실행 가능한 JAR 파일 만들기

앞서 mvn package로 패키징했지만 이렇게 생성된 JAR 파일은 단순히 패키징 된 것이기 때문에 실행되지는 않는다.

1
java -jar 00.jar

따라서 위의 명렁을 실행해도 00.jar에 기본 Manifest 속성이 없어 실행에 실패하게 된다.

실행 가능한 JAR 파일을 만들기 위해서는 maven-jar-plugin을 이용해 다음과 같은 <plugin> 태그를 작성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>버전</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>메인 클래스</mainClass>
</manifest>
</archive>
</configuration>
</plugin>

<archive> 태그는 압축에 관한 설정이다. addClasspath는 클래스 경로에 JAR 파일이 있는 경로를 추가하기 위한 태그인데 보통은 true로 지정한다.

이렇게 설정 후 다시 mvn package로 JAR 파일을 생성한 후 java -jar로 실행해보면 문제없이 실행할 수 있다.

저장소 이용

<dependency> 를 추가하는 것만으로 필요한 라이브러리를 추가해 사용할 수 있었던 것은 중앙 저장소 때문이다. 그런데 저장소가 중앙 저장소만 있는 것은 아니다. 다른 원격 저장소나 로컬 저장소도 있다.

로컬 저장소

자신이 만든 라이브러린, 그다지 유명하지 않은 라이브러리라면 아직 중앙 저장소에 공개되지 않을 수도 있다. 이러한 라이브러리는 로컬 저장소를 이용해 사용할 수 있다.

  • 원격 저장소 : 네트워크를 거쳐 서버에 접속하여 이용하는 공개된 저장소. 중앙 저장소도 원격 저장소의 한 종류이다.
  • 로컬 저장소 : 로컬 환경에 있는 저장소이다.

원격 저장소 이용

원격 저장소는 pom.xml에 <repositories> 태그 안에 저장소 정보를 기술한다.

1
2
3
4
5
6
7
<repositories>
<repository>
<id>저장소 ID</id>
<name>이름</name>
<url>저장소 주소(URL)</url>
</repository>
</repositories>

로컬 저장소에 라이브러리 추가하기

추가하고자 하는 라이브러리 프로젝트에서 다음과 같이 실행하면 target에 빌드된 JAR 파일을 로컬 저장소에 설치한다.

1
mvn install

또는 설치할 JAR 파일이 별도로 준비되어 있다면 install:install-file 골을 실행해서 지정한 라이브러리 파일을 로컬 저장소에 설치할 수 있다.

1
2
3
4
5
6
mvn install:install-file
-Dfile="라이브러리 jar의 경로"
-DgroupId="그룹 ID"
-DartifactId="아티팩트 ID"
-Dpackaging="패키징(jar)"
-Dversio="버전(1.0)"

로컬 저장소의 위치 알아보기

로컬 저장소의 위치는 다음과 같다.

1
홈 디렉터리/.m2/repository

이 폴더에는 라이브러리가 그룹 ID마다 폴더로 정리되어 있다.