728x90

 

 


 

Jenkins&Springboot CI/CD 정리(1)

 

 

 

필자가 경험한 Springboot 프로젝트와 Jenkins CI 를 활용한 CI/CD 구축에 대해 정리를 해보고자 합니다. =)

이 시리즈에서는 이미 작성된 내용에 대해선 해당 글로 대체 할 예정이니 참고바랍니다.

 

필자의 경우 Springboot&Gradle 을 이용하였고, SonarQube의 경우 Ec2 프리티어에서 돌리기 버거워 로컬에서 설치 후 공유기 포트포워딩을 통해 Ec2 인스턴스와 연결하였습니다.

 

Nginx 배포 방식은 Blue&Green 배포 방식을 사용하였습니다. =)

 

해당 JenkinsPipeline의 jenkinsfile은 필자의 Github에 공유되어 있으니 아래 링크를 참고하여 주세요. =)

 

GitHub - 0AndWild/Jenkins-CICD

Contribute to 0AndWild/Jenkins-CICD development by creating an account on GitHub.

github.com

 

 

 

 

먼저 대략적인 서비스 아키텍쳐입니다. (2022.11.11 수정됨)

 

 

(Flow)

1. 스프링프로젝트에서 feature branch 단위로 작업 후 해당 branch로 푸시

2. dev branch로 풀리퀘스트 발생

3. 풀리퀘스트에 대한 Merge 발생(Gitea&Jenkins webhook 발동)

4. Gitea에서 Sonar-bot으로 웹훅 발동(pending)

5. Jenkins Multibranch Pipeline 프로젝트 build가 trigger됨

6. Jenkinsfile의 파이프라인 수행 시작

7. Gitea Repository 체크아웃

8. Gradle build 수행

9. Gradle 빌드시 unit test를 통해 Jacoco 코드커버리지 report 생성

10. Gradle 빌드시 CheckStyle을 통해 코드컨벤션 리포트 생성

11. SonarQube Scanner를 통해 Sonar analysis 시작

12. jacoco xml 리포트와 CheckStyle 리포트를 통해 SonarQuality Gate 충족 확인

13. SonarQube Quality Gate를 충족하면 통과 아니면 Jenkins 빌드 실패

14. SonarQube에서 Sonar-bot 으로 webhook 웹훅 발동

15. SonarQube 결과를 Gitea PR comment에 생성

16. SonarQube Quality Gate가 통과된 jar 파일 Docker Image 생성

17. Jenkins Plugin인 Publish Over SSH 를 통해 2번째 Ec2인스턴스에 Docker Compose.yml, Nginx파일, Deploy.sh 파일 전송

18. Deploy.sh 실행

19. Nginx 컨테이너 띄워져있지 않으면 Docker Compose 파일로 Nginx 컨테이너 실행, 실행중이면 skip

20. Blue 컨테이너 띄워져 있는지 확인 없으면 Blue up

21. 만약 이미 Blue 컨테이너가 띄어져 있으면 Green up 후 Green 컨테이너에 배포 후 컨테이너 스위칭

22. Green 컨테이너로 스위칭후 컨테이너가 잘 떠있는지 확인

23. Green이 정상적으로 띄워졌으면 Nginx conf 파일을 Green 컨테이너에 맞추어 변경후 reload

24. 이전 컨테이너인 Blue 컨테이너 down

 


 

먼저 AWS Ec2 인스턴스가 2개가 필요한데 필자의 경우 프리티어를 사용하기 위해 AWS 계정 2개를 가입하여 사용하였습니다. 하나의 계정으로 프리티어 인스턴스를 여러개 많들 수 있지만 이럴경우 한달에 750시간의 프리티어 시간이 빠르게 소모되어 과금이 될 수 있으므로 계정을 분리하여 사용하는 것이 좋습니다. =)

 

 

AWS가입은 생략을 하고 EC2인스턴스를 생성해보도록 하겠습니다. =)

 

(깨알지식)

EC2는 Elastic Compute Cloud의 약자로 단어의 앞글자인 C가 2개 들어가 EC2라고 합니다.=)

 

 

1. EC2인스턴스 생성

 

 

AWS가입 후 좌측 서비스 카테고리를 살펴 보시면 EC2라는 카테고리가 보입니다. 클릭 후 이동을 해줍니다.

 

이동을 하였다면 우측 상단에 나라를 서울로 바꾸어 주도록 하겠습니다. =)

 

 

필자의 경우 이미 인스턴스가 생성이 되어 있는 상태지만 인스턴스 시작 버튼을 통해 인스턴스를 생성해보도록 하죠.

 

인스턴스 시작버튼을 누르면 다음과 같은 화면이 나올겁니다. 여기에서 먼저 애플리케이션 및 OS 이미지 부분을 확인해보도록 하겠습니다. =)

보시면 여러가지 OS이미지들이 보이실텐데 여기에서 자신이 사용하고자 하는 AWS EC2 OS를 선택해주면 됩니다!

 

필자의 경우 Ubuntu를 사용해본 경험이 있어 첫번째 인스턴스는 Ubuntu를 선택하였고 두번째 인스턴스는 Amazon Linux를 선택하였습니다. 각 OS마다 사용하는 명령어가 다르니 자신이 편한 것을 선택해주면 될 것 같습니다.

 

더 많은 AMI를 누르시면 다음과 같이 더 많은 정보를 다음과 같이 확인할 수 있습니다!

필자는 첫번째 인스턴스로 Ubuntu Server 20.04 LTS 를 선택하였습니다. 선택하실 때 꼭 프리티어 사용 가능을 확인해주세요!

 

이렇게 선택을 완료해주었고 아래를 보시면 인스턴스 유형으로 초기에 프리티어 유형인 t2.micro로 잡혀 있는 것을 확인할 수 있습니다. 꼭 이부분이 t2.micro로 잡혀 있고 우측의 free tier eligible을 확인하여 주세요.

 

그 다음 해당 인스턴스에 접속하기위한 키페어를 생성해야 하는데 기존에 사용하고 있으신 키페어가 있다면 해당 키페어를 선택해주시면 공용으로 사용할 수 있습니다. 만약 없다면 새 키 페어 생성을 눌러주세요.

 

위와 같이 키 페어 이름을 자신이 원하는 이름으로 입력한 후 아래에 RSA, .pem을 선택해주고 키 페어 생성 버튼을 눌러주세요. =)

 

그럼 다음과 같이 생성한 키 페어 가 다운받아지는데 인스턴스로 ssh 접속을 할 때 꼭 필요한 파일이니 잘 저장해두시고 관리해두시길 바랍니다. =)

 

해당 과정은 인스턴스를 처음만든다는 가정하에 진행을 하므로 초기 상태 그대로 넘어가도록 하겠습니다. 기존 보안 그룹선택을 하고 자신이 사용하고자하는 규칙을 선택하면 자신이 기존에 사용하고 있던 인바운드 규칙등을 다시 설정하지 않고 그대로 사용할 수 있습니다.

 

마지막으로 스토리지 구성은 default로 8gib 로 잡혀 있을거지만 프리티어에선 30gib까지 스토리지볼륨을 무료로 제공하니 꼭 30gib로 해줍시다. ㅎㅎㅎ

아 그리고 만약 하나의 AWS계정에서 프리티어 인스턴스를 하나 이상 사용할 때 나머지 인스턴스도 스토리지 볼륨을 30gib로 늘리게 되면 나머지 인스턴스의 스토리지 볼륨은 무료로 측정이 되지 않는 것 같으니 꼭 하나만 30gib로 설정해 주세요. =)

 

모든 설정이 완료 되었다면 인스턴스 시작버튼을 눌러주도록 합시다. =)

필자는 설명을 위해 만드는 과정만 보여드렸고 따로 인스턴스를 생성하진 않았습니다.

 

 

인스턴스 생성 후 잠시 대기시간을 가지게 되면 인스턴스 상태가 실해중으로 바뀌게 될겁니다. =)

실행중으로 바뀌게 되면 이제 해당 인스턴스를 사용할 수 있게 되는거죠!

 

 


 

2. Jenkins 설치

 

방금 생성한 Ubuntu 인스턴스에 접속하기 전 미리 Jenkins서버가 띄워질 8080포트를 인바운드 규칙에서 열어주도록 하겠습니다. 설치를 하는데 있어선 문제가 되지 않지만 설치후 Jenkins 서버에 http://퍼블릭아이피주소:포트 로 접속하기 위해서 입니다. =) 

 

해당 인스턴스를 선택 후 하단의 보안을 눌러주세요.

그 후 보안그룹을 클릭해주도록 하겠습니다.

 

다음과 같은 창이 보일텐데 여기에서 인바운드 규칙 편집을 눌러주도록 하겠습니다.

 

필자의 경우 이미 많은 인바운드 규칙이 설정되어 있는데 다음과 같이 규칙추가를 눌러 주고 

사용자지정TCP, 8080, Anywhere-IPv4를 선택해주도록 합니다. 사실 실제 운영을 할 서버라면 사용자 지정으로 허용해야할 특정 IP만 허용하여 해당 IP에서만 열어준 포트로 접속을 할 수 있게 해주는게 좋습니다. =)

 

이제 모든 셋팅이 끝났으니 ssh 접속을 해보도록 하겠습니다. 필자의 경우 여러개의 인스턴스에 접속을 쉽게 하기 위해 ssh-client tool을 사용하고 있습니다. 이 방법은 필자가 정리해둔 아래 링크를 참고하여 주세요.

 

편리한 EC2 인스턴스 접속(SSH Client Tool)

편리한게 AWS EC2 인스턴스에 접속을 하기 위해 SSH Client Tool 을 사용해보자! 필자는 Windows 환경에서 EC2 인스턴스에 접속을 하기 위해 gitbash를 사용하는 편이지만 좀 더 편리하게 접속을 하기위해 Te

0andwild.tistory.com

 

만약 자신이 터미널 또는 gitbash를 통해 접속을 원한다면 다음과 같이 진행을 해주시면 됩니다. =)

 

ssh -i (키페어 넣어줌) ubuntu@(실행중인 ec2의 퍼블릭 IPv4 주소 넣어줌)

ssh -i 입력후 인스턴스 생성시 새로 만든 키페어를 드래그해주시면 키페어가 입력됩니다.

 

그 후 ubuntu를 입력해주었는데 이부분은 자신이 선택한 인스턴스 유형에 따라 즉, OS에 따라 초기 이름이 다르게 잡혀 있기 때문에 이부분은 찾아보시는게 좋을 것 같습니다. =) 추가적으로 AWS Linux의 경우는 초기 이름이 ec2-user 로 잡혀 있습니다.

 

퍼블릭 아이피의 경우 해당 인스턴스를 클릭 후 네트워킹을 선택하시면 퍼블릭 IP주소를 확인할 수 있고 좌측의 네모난 모양의 버튼을 클릭하면 쉽게 복사가 되어 붙여넣기를 진행해 주시면 됩니다. =)

이제 입력을 다하셨다면 엔터를 줄러주시고 다음과 같이 정상적으로 접속이 된 화면을 확인하실 수 있습니다. =)

처음 접속을 한다면 fingerprint(yes/no)가 나올 수 있는데 yes를 입력하시고 엔터를 눌러주세요.

 

이제 Jenkins를 설치해보도록 하겠습니다.

sudo apt-get update
sudo apt-get install open-jdk-11

Jenkins는 Java8버전부터 호환이 가능합니다. 아마 EC2 인스턴스에는 기본적으로 jdk 8이 설치되어 있는것으로 알지만 필자는 jdk 11로 설치를 해주었습니다. =)

java -version

 

jenkins 설치의 경우 이미 필자가 정리해둔 글이 있기에 해당 글로 대체를 하도록 하겠습니다.

여기서 아쉬운 점은 필자는 초반 Jenkins를 해당 인스턴스내에 직접 설치를 하였지만 Docker conatiner로 젠킨스를 띄워 사용해보는 것도 좋은 방법일거라 생각이듭니다.=)

 

Jenkins를 이용한 CI/CD Pipeline 구축해보기(2)

1편에서 만들어둔 EC2 인스턴스를 중지시키고 이미지(AMI)를 생성해줄 겁니다. 아직 인스턴스 생성과 초기 작업이 수행되지 않았다면 아래 링크를 참고하여주세요. Jenkins를 이용한 CI/CD Pipeline 구

0andwild.tistory.com

추가적으로 Jenkins가 생각보다 자원을 많이먹는 녀석이다보니 AWE EC2 프리티어 인스턴스에서는 Swap 메모리를 구축하여 사용하는 것이 좋습니다!

 

(Swap 메모리 구축)

 

SWAP메모리는 OS에서 메모리를 full로 사용하고 있으면 사용하지 않는 메모리 안의 페이지를 하드드라이브에 저장하게 되는데, 이게 SWAP메모리의 기본 개념이다.

 

Swap space의 기본 용량 단위는 128MB라고 한다.
AWS 프리티어는 RAM이 1GB이기 때문에 2GB의 SWAP Space를 할당하기 위해
count=16으로 설정해준다.
$ sudo dd if=/dev/zero of=/swapfile bs=128M count=16

스왑 파일 Read/ Write 권한을 변경한다.
$ sudo chmod 600 /swapfile

Linux SWAP File을 사용해서 영역을 설정한다.
$ sudo mkswap /swapfile

SWAP SPACE에 SWAP File을 추가해준다.
$ sudo swapon /swapfile

추가된 SWAP 영역을 확인한다.
$ sudo swapon -s

서버를 Reboot(재실행) 할 경우 SWAP을 자동으로 활성화 하기 위해
vim 에디터를 이용해 /etc/fstab 파일을 열어서 해당 내용을 추가해준다.
$ sudo vim /etc/fstab
/swapfile swap swap default 0 0      <--이부분을 추가해줌

 


 

 

3. Jenkins Multibranch Pipeline 프로젝트 생성 및 Gitea 연결

 

이부분도 이전 작성해둔 글로 대체를 하도록 하겠습니다. =)

다만 수정된 부분이 있어 그 부분에 대해서만 설명을 드리도록 하겠습니다.

 

Gitea Webhook Jenkins(Gitea 와 jenkins 연동하기)

최근 5일동안 Gitea와 Jenkins Webhook 연결 문제로 삽질을 엄청 많이 했습니다..... =( 혹여나 필자와 같은 상황에 있는 분들을 위해 정리를 해보고자 합니다. 만약 Gitea Jenkins Webhook 관련 연결 실패 때문

0andwild.tistory.com

 

생성한 Multibranch Pipeline 프로젝트의 Gitea 설정 부분에서 Filter by name을 prod 환경을 배포할 main branch와 dev환경을 배포항 develop 브랜치로 설정해두어 해당 브랜치에서 push또는 Pull request 발생 시 Jenkins build가 trigger 되도록 하였습니다.


 

 

4. Jenkins file 작성(1)

 

이제 본격적으로 Jenkins Pipeline의 핵심인 Jenkins file을 작성해보도록 하겠습니다.

 

Jenkinsfile 작성법

 

젠킨스파일을 작성하는 방법은 두 가지 방식이 존재하는데 선언형(Declarative)과 스크립트형(Scripted)이 있습니다.

스크립트형 문법의 경우 Groovy를 사용보시지 않았거나 Java 문법에 익숙지 않으며 쉽지 않다는 단점이 존재합니다.

반면 장점으로는 더 많은 절차적인 코드작성, 보다 복잡한 워크 플로우 및 파이프라인 작성 가능을 꼽을 수 있습니다.

선언형의 경우 보다 쉽게 작성이 가능하고 Groovy 문법을 잘 몰라도 작성이 가능합니다.

 

필자는 선언형을 선택하였고 그 기반으로 설명을 드리도록 하겠습니다.

 

Jenkinsfile의 시작은 pipeline{} 명시함으로써 시작이 됩니다.

agent 는 pipeline 내의 최상단에 위치해야 하며 any로 명시할 경우 어떠한 agent로도 실행해도 된다는 걸 나타냅니다.

즉, 파이프라인 전체에서 사용할 수 있게 되는 것이다. 반면 none으로 설정할 경우 각 stage마다 agent를 설정해주어야 합니다.

 

바로 아래의 예제 에서는 존재 하지 않지만 pipeline은 stages로 stage들을 감쌀 수 있으며, stage는 어떠한 job의 단계를 말합니다. stage안에는 steps가 존재하는데 이 부분에는 실행해야할 명령어를 입력해주는 부분이라 생각하면 됩니다.

 

그리고 아래의 post 부분은 steps가 끝난이후 어떠한 행동을 할 건지에대해 명시를 할 수 있습니다. 가장 대표적인 문법은 success와 failure로 steps안의 행동을 성공했을 때와 실패했을 때를 구분하여 다음 행동을 명시 할 수 있습니다.

//Git Checkout
pipeline {
    agent any
    
        stage('gitea clone') {
            steps {
                echo"Cloning Repository"
                git branch: '{main or dev 등 checkout을 할 branch}',
                    credentialsId: '{jenkins 에서 등록한 github or gitea or gitlab 등의 credential}',
                    url: '{github or gitea or gitlab 등 URL}'
            }
            post{
                success {
                    echo "Successfully Cloned Repository"
                }
                failure {
                    echo "Fail Cloned Repository"
                }
            }
        }
 
 }

먼저 gitea의 해당 branch 여기서는 develop branch를 가져오는 파이프라인입니다. 간단하게 클론을 성공하였을 때와 실패를 하였을 때 echo를 통해 해당 job을 성공했다 또는 실패했다를 남기도록 하였습다. 이부분은 jenkins 빌드기록의 log에 남게 됩니다.

 

다음으로 파이프라인에 대한 설명을 이어나가기전 젠킨스 빌드시 편의를 위해 Slack 알림 연동을 미리 설정해두도록 하겠습니다.


 

이부분 또한 필자가 작성해둔 글이 있어 해당 글로 대체를 하도록 하겠습니다.=)

 

Jenkins & Slack 연동하기(Slack Notification) 및 파이프라인 작성

Jenkins & Slack Notification 연동 Slack에서 Jenkins CI 앱을 추가해주면 다음과 같은 웹 사이트로 이동이 되며 설정 지침에 따라 Jenkins 설정을 진행해 주도록 합시다. =) 먼저 Jenkins Server로 들어와 Slack Notifi

0andwild.tistory.com

 


pipeline {
    agent any

    stages {
        stage("Set Variable") {
            //공통사용 항목 변수 지정 및 build 유발자와 commit 내역을 함께 Slack 알림으로 전송
            steps {
                script {
                    //알림받을 채널
                    SLACK_CHANNEL = "{사용하고자 하는 slack 채널명 입력}"
                    SLACK_START_AND_FINISH_COLOR = "#778899";
                    SLACK_SUCCESS_COLOR = "#2C953C";
                    SLACK_FAIL_COLOR = "#FF3232";
                    // Git Commit 계정
                    GIT_COMMIT_AUTHOR = sh(script: "git --no-pager show -s --format=%an ${env.GIT_COMMIT}", returnStdout: true).trim();
                    // Git Commit 메시지
                    GIT_COMMIT_MESSAGE = sh(script: "git --no-pager show -s --format=%B ${env.GIT_COMMIT}", returnStdout: true).trim();
                }
            }
            post {
                success {
                    slackSend (
                        channel: SLACK_CHANNEL,
                        color: SLACK_START_AND_FINISH_COLOR,
                        message:
                        "==================================================================\n" +
                        "\n" +
                        "배포 파이프라인이 시작되었습니다.\n" +
                        "${env.JOB_NAME}(${env.BUILD_NUMBER})\n" +
                        "\n" +
                        "-GIT_COMMIT_AUTHOR-\n" +
                        ":  ${GIT_COMMIT_AUTHOR}\n" +
                        "\n" +
                        "-GIT_COMMIT_MESSAGE-\n" +
                        ":  ${GIT_COMMIT_MESSAGE}\n" +
                        "\n" +
                        "<-More info->\n" +
                        "${env.BUILD_URL}"
                    )
                }
            }
        }

        //Git Checkout
        stage('gitea clone') {
            steps {
                echo"Cloning Repository"
                git branch: '{main or dev 등 checkout을 할 branch}',
                    credentialsId: '{jenkins 에서 등록한 github or gitea or gitlab 등의 credential}',
                    url: '{github or gitea or gitlab 등 URL}'
            }
            post{
                success {
                    slackSend (
                        channel: SLACK_CHANNEL,
                        color: SLACK_SUCCESS_COLOR,
                        message: "Gitea Checkout에 성공하였습니다."
                    )
                    echo "Successfully Cloned Repository"
                }
                failure {
                    slackSend (
                        channel: SLACK_CHANNEL,
                        color: SLACK_FAIL_COLOR,
                        message: "Gitea Checkout에 실패하였습니다.\n" +
                        "\n" +
                        "<-More info->\n" +
                        "${env.BUILD_URL}console\n" + //실패시 젠킨스서버의 해당 빌드 log로 이동
                        "=================================================================="
                    )
                    echo "Fail Cloned Repository"
                }
            }
        }
        
   }

Slack알림 연동을 완료 하였다면 기존 파이프라인 구문에서 slack과 관련된 내용이 추가될 것이고 위와 같이 작성이 될 수 있습니다. 추가 된 부분을 살펴보면 이제는 stage가 두 개가 되어 stages{}로 stage{}들을 감싸고 있는 것을 확인할 수 있습니다.

 

Set Variable이라는 stage가 추가 되었는데 이부분은 모든 stage들이 시작되기 전 공통적으로 또는 여러번 사용될 수 있는 값들의 하드코딩을 방지하기 위해 변수값에 담아주어 어떠한 stage에서 해당 값을 필요로 할때 변수명으로 호출할 수 있도록 하는 부분입니다.

 

이제는 slack notification 연동이 되었으므로 Stage안의 step이 끝난 후 post안의 success와 failure에 slack에 대한 정보와 어떠한 메세지를 보낼지에 대한 내용이 담겨 있는 것을 확인할 수 있습니다. =)

 

 

git push 또는 pull request를 통한 merge를 시도해보면 다음과 같이 슬랙으로 jenkins 파이프라인의 step별로 알림이 오는 것을 확인 할 수 있습니다.

 

//Gradle Build & Test
        stage('Build & Test') {
            steps {
                sh '''
                    echo 'Build Gradle Start'
                    ./gradlew clean build
                '''
            }
            post {
                success {
                    slackSend (
                        channel: SLACK_CHANNEL,
                        color: SLACK_SUCCESS_COLOR,
                        message: "Build & Test 에 성공하였습니다."
                    )
                    echo "Build Gradle Success"
                }
                failure {
                    slackSend (
                        channel: SLACK_CHANNEL,
                        color: SLACK_FAIL_COLOR,
                        message: "Build & Test 에 실패하였습니다." +
                        "\n" +
                        "<-More info->\n" +
                        "${env.BUILD_URL}console"
                        "=================================================================="
                    )
                    echo "Build Gradle Fail"
                }
            }
        }

이번 stage는 Spring project를 build 하여 jar파일을 생성하는 단계입니다.

 

여기서 필자는 gradlew를 사용하였는데 이는 gradle wrapper를 뜻하고 이건 내장되어있는 gradle을 사용한다는 겁니다. Gradle에서는 각 프로젝트마다 내장 Gradle을 넣어주게 만들어졌고, 프로젝트에서 내장 gradle을 사용할 수 있게됩니다.

이러한 장점으로 gradle wrapper를 사용시 해당 프로젝트에서 사용하는 gradle의 버전에 맞추어 설치하지 않아도 되고, gradle wrapper를 이용한 빌드시 알아서 해당 프로젝트에서 사용하고자 하는 gradle 버전에 맞추어 gradle을 설치하여 빌드를 진행해줍니다. 여기서 추가적으로 gradlew는 맥과,리눅스용 스크립트 이며, gradle.bat은 위도우용 스크립트를 말합니다.

 

clean 명령어란?

build 디렉토리 부분은 반복해서 파일이 생성되기 때문에, 이전에 생성된 파일과 중복되지 않도록 디렉토리를 한 번 비우고 build를 진행하게 해줍니다.

 

만약 여기서 빌드 시 test를 하고 싶지 않다면 --exclude-task test를 추가하여 진행할 수 도 있습니다.

하지만 본 CI/CD에서는 빌드 시 test를 진행하고 Jacoco 코드커버리지를 측정 및 Checkstyle을 통해 html과 xml로 리포트를 생성하고 SonarQube로 넘겨줄거기 때문에 test를 포함시켰습니다. =)

 

이제 이어서 gradle 에 Jacoco 설정을 진행해 보도록 하겠습니다.

 

 


5. Jacoco Code Coverage 설정

 

 

Jacoco는 Java의 코드커버리지를 체크하는 라이브러리로 테스트코드를 test한 후 그 커버리지 결과를 html, xml, csv 로 만들어주는 도구입니다. 이러한 도구를 통해 얼마나 테스트케이스가 충족되었는지의 결과를 쉽게 확인할 수 있고, 어떠한 커버리지를 측정할건지에 대해 설정과 함께 %를 설정하여 해당 %를 충족시키지 못하면 빌드를 실패시키게 할 수 도 있습니다. 따라서 프로젝트의 코드품질 향상에 굉장한 도움을 주는 녀석입니다. =)

 

id 'jacoco'

jacoco {
    toolVersion = '0.8.7'
}

먼저 Gradle 파일로 들어가 준 후 jacoco plugin을 추가해주도록 하겠습니다.

 

finalizedBy jacocoTestReport

그리고 하단부에 있는 test task의 useJunitPlatform 밑으로 finalizedBy jacocoTestReport 를 추가해주도록 합시다.

이는 test task가 먼저 수행 된 후 jacocoTestReport를 생성하겠다는 의미입니다.

 

 

 

//Jacoco 추가
jacocoTestReport {
    dependsOn test
    reports {
    	//여기서 설정을 통해 원하는 리포트 형태를 설정할 수 있음
        html.enabled true
        xml.enabled true
        csv.enabled true
        xml.destination = file("${buildDir}/jacoco/jacoco.xml") //파일경로 지정
    }
	
    def Qdomains = []
    for (qPattern in '**/QA'..'**/QZ') {
        Qdomains.add(qPattern + '*')
    }

    afterEvaluate {
        classDirectories.setFrom(
                files(classDirectories.files.collect {
                    fileTree(dir: it, excludes: [
                            '**.SpringInitProjectApplication*',
                            '**.*Request*',
                            '**.*Response*',
                            '**.constant.**',
                            '**.common.**',
                            '**.environment.**',
                            '**.*Dto*',
                            '**.*OAuthClient*',
                            '**.*Interceptor*',
                            '**.*Exception*',
                            "**.Q*.class"
                    ] + Qdomains)
                })
        )
    }
    //test -> testReport 생성 -> jacocoTestCoverageVerification 실행
    finalizedBy 'jacocoTestCoverageVerification' 
}

jacocoTestCoverageVerification {
    def Qdomains = []
    for (qPattern in '*.QA'..'*.QZ') {
        Qdomains.add(qPattern + '*')
    }

    violationRules {
        rule {
        	//커버리지를 측정할 기준(단위)
            element = 'CLASS'
            //해당 룰에대한 사용 boolean으로 표시
            enabled = true
            
            //조건문 등의 분기 수
            limit {
            counter = 'BRANCH'
            value = 'COVEREDRATIO'
            minimum = 0.80
            }
			
            //빈 줄을 제외한 실제 코드의 라인 수
            limit {
                counter = 'LINE'
                value = 'COVEREDRATIO'
                minimum = 0.80
            }
			
            //메서드 수
            limit {
                counter = 'METHOD'
                value = 'COVEREDRATIO'
                minimum = 0.80
            }

            excludes = [
                    '**.*SpringInitProjectApplication*',
                    '**.*Request*',
                    '**.*Response*',
                    '**.constant.**',
                    '**.common.**',
                    '**.environment.**',
                    '**.*Dto*',
                    '**.*OAuthClient*',
                    '**.*Interceptor*',
                    '**.*Exception*',
                    "**.Q*.class"
            ] + Qdomains
        }
    }
}

여기서 주의깊게 살펴봐야 할 부분은 violationRules 부분입니다. 이부분은 Jacoco의 Code Coverage를 측정할 때 어떤걸 기준으로 측정할건지와 어떠한 부분을 측정할건지 설정하는 부분입니다. 아래와 같은 항목들이 존재합니다.

 

  • BRANCH : 조건문 등의 분기 수
  • CLASS : 클래스 수, 내부 메서드가 한 번이라도 실행된다면 실행된 것으로 간주한다.
  • COMPLEXITY : 복잡도
  • INSTRUCTION : Java 바이트코드 명령 수
  • METHOD : 메서드 수, 메서드가 한 번이라도 실행된다면 실행된 것으로 간주한다.
  • LINE : 빈 줄을 제외한 실제 코드의 라인 수, 라인이 한 번이라도 실행되면 실행된 것으로 간주한다.

minimum 부분은 위에서 설정한 rules에 얼만큼 부합해야 통과를 시킬지에 대한 설정입니다. 0.80은 80%를 의미하며 일반적으로 80%를 기준으로 잡는다고 합니다. =)

 

excludes 부분은 jacoco Coverage 측정시 제외를 할 패키지 및 파일들을 지정할 수 있는 곳입니다. 일반적으로 dto와 같은 항목들은 테스트에서 제외시킨다고 합니다.

 

추가적으로 QueryDSL을 사용하고 있는 경우 자동으로 생성된 Qclass는 Coverage를 측정할 필요가 없기 때문에 위와 같이 제외를 시켜주었습니다. 

 

맨위를 보시면 afterEvaluate라는 항목이 존재하는데 이부분은 gradle의 빌드 라이프 사이클에 대한 메서드로 프로젝트 평가가 완료된 후 실행될 수 있도록 해줍니다. =)

 

마지막으로 @Lombok을 사용하고 있는 경우 generated code를 커버리지에서 제외하기 위해선 프로젝트의 루트경로에 다음과 같이 lombok.config 파일을 생성하고 아래의 코드를 추가해줍니다.

lombok.addLombokGeneratedAnnotation = true

 

 

다음과 같이 test를 할 겸 build 를 돌렸을 때 경로를 지정해준 xml report 파일이 잘 생성된 것을 확인 할 수 있습니다.

 

다음과 같이 html 파일로 정상적으로 커버리지가 측정되는 것을 확인 할 수 잇습니다. =)

 

 


6. CheckStyle 코드컨벤션 적용

 

 

다음은 Check Style 플러그인 설치 및 적용을 해보도록 하겠습니다.

 

필자의 경우 코드컨벤션 예제로 네이버 핵 데이의 네이버 Java 코드컨벤션을 적용하였고 해당 적용 과정은 필자가 최근에

상세하게 작성해둔 글이 있어 해당 글로 대체 하도록 하겠습니다.

 

 

Jenkins&Sonaqube&Checkstyle 을 이용한 코드컨벤션 적용기(Naver Code Convention)

필자의 경우 Jenkins를 이용한 CI/CD를 진행하며 코드 분석 툴로 Jacoco&Sonarqube를 사용하고 있는 상황이며, 더 나은 코드 품질을 위해 Sonarqube의 Rules 설정을 통한 코드컨벤션을 적용하려 합니다. 코드

0andwild.tistory.com

 

해당 글에서는 Sonarqube와의 연동까지 되어 있는데 우선 이부분은 다음과정에서 적용을 하시길 바라며 우선 로컬 프로젝트 환경에서 까지만 적용을 하신 후 체크를 진행하시길 바랍니다. =)

 

 


 

 

우선 check style 적용을 마무리로 1편을 마치며 다음 글에서 소나큐브 설치 및 셋팅, 도커이미지 생성, 도커허브 연동, Jenkins의 Publish Over SSH 플러그인을 통한 두 번째 EC2인스턴스와의 연동과 함께 NGINX 블루 그린 배포를 마무리 하도록 하겠습니다. =)

 

 

 

728x90
반응형
복사했습니다!