Skip to content

Sonarqube 도입기

Eugine Park edited this page Aug 19, 2021 · 10 revisions

우리 프로젝트의 정적 분석 리포트를 만든 과정을 남겨보도록 한다.

정적 분석이란?

소프트웨어를 개발할 때 우리가 유념해야 할 것들이 있다.

  • 컨벤션을 잘 지키고 있는가?
  • 내 코드 안에 보안 취약점이 있을까?
  • 코드가 반복되고 있지는 않을까?
  • 의도하지 않은 버그가 발생하는가?
  • 새로 작성한 코드를 위한 테스트가 잘 마련되어 있나?

그런데 여러 팀원이 다양한 기능을 구현하다보면 다양한 이유로 그 요소들을 고려하지 못하고 넘어갈 수 있다. 시간이 없어서 자세히 못 볼 수도 있고, 개발도 결국 사람이 하는 일이라 예상하지 못한 문제가 발생할 수 있기 마련이다. 프로젝트의 규모가 커질수록, 그리고 함께 작업하는 팀원의 수가 많아질수록 이런 상황이 발생할 확률이 높아진다.

문제점이 생겼을 때 확실히 발견하기 위해 개발자는 정적 분석 도구를 이용할 수 있다. '정적'이라는 표현에서 알 수 있듯 '동적 분석'도 있는데, 이 둘의 가장 큰 차이점은 문제점을 찾기 위해 소프트웨어를 실행하는지에 있다. 정적 분석은 소프트웨어를 실제로 실행하지 않고 소스코드를 이용하여 분석하는 방법이다.

Sonarqube(소나큐브) 란?

정적 분석 툴의 종류로 여러가지가 있는데, 그 중 프로젝트에 사용한 것은 Sonarqube다. 위키피디아의 말을 빌리자면 Sonarqube는 20개 이상의 프로그래밍 언어에서 버그, 코드 스멜, 보안 취약점을 발견할 목적으로 정적 코드 분석을 하고 지속적으로 코드 품질을 검사한다. 검사 후 보고서를 제공하는데, 앞서 말한 보안 취약점, 중복코드, 그리고 테스트 커버리지와 같은 개발자가 유념해야할 것들에 관한 정보가 보고서에 포함된다.

소나큐브 설치

docker run -d --name sonarqube -p 9000:9000 sonarqube
  • 도커를 이용하여 EC2에 소나큐브를 설치했다.
  • 참고로 소나큐브는 일부 기능만 무료로 제공한다. 그래서 어떤 에디션의 소나큐브를 다운받을지 선택해야 하는데, 도커에서도 에디션을 골라서 이미지를 받아올 수 있다. 위의 커맨드를 실행하면 Community Edition, Developer Edition, 그리고 Enterprise Edition이 모두 포함된 이미지를 pull한 뒤 컨테이너를 생성한다.
  • 컨테이너가 생성되면 [EC2 public ip]:9000을 통해 소나큐브에 접속할 수 있다. 로그인을 해야하는데, 기본 계정은 다음과 같다.
    • login = admin
    • password = admin

토큰 및 프로젝트 생성

Gradle 프로젝트에서 방금 만든 소나큐브 서버에 접근할 수 있도록 토큰을 생성한다.

우측 상단의 프로필 -> My Account -> Security -> 토큰 이름 입력 -> Generate

profile my account image create token1 생성한 토큰은 안전한 곳에 저장한다. create token2 모두 완료 했으면 다시 소나큐브 메인 화면으로 돌아와 프로젝트를 생성한다. sonarqube create project1

Create Project -> manually -> 프로젝트 이름 및 Project Key 입력

sonarqube create project2 여기서 Project Key는 우리 Gradle 에서 소나큐브 서버에 연결할 때 해당 소스코드가 어떤 프로젝트와 관련된 것인지 명시할 때 쓰인다.

build.gradle 설정

이제 우리 프로젝트의 소스코드를 분석하고 정적 분석 리포트를 만들어야 한다. 정적 분석을 할 프로젝트의 build.gradle 파일로 간다.

1. plugin

plugins {
    id "org.sonarqube" version "3.3"
    id 'jacoco'
}

위와 같이 플러그인에 소나큐브와 jacoco(자코코)를 추가해야 한다. 그런데 자코코가 뭐지?

Jacoco 란?

소나큐브가 테스트 커버리지에 대한 정적분석을 하기 위해서는 테스트 리포트를 전달받아야 한다. 자코코는 테스트를 실행할 때 이 리포트를 만들어주는 라이브러리로, 다음과 같은 기능을 제공한다.

  1. 바이트 단위, 라인 단위, 그리고 브랜치(조건문의 분기) 단위 테스트 커버리지 검사
  2. 검사 후 html, xml, 그리고 csv 형태로 리포트 생성 (선택 가능)
  3. 커버리지가 일정 수준을 만족하지 못하면 빌드를 금지

2. tool version

jacoco {
    toolVersion = '0.8.5'
}

플러그인에 자코코를 추가했다면 이제 자코코 툴 버전을 명시한다. 0.8.0 버전 이상을 사용하는 것이 권장되는데, 롬복 생성 코드에 대한 커버리지를 제외하기 위해서다. 일단 build.gradle 설정을 다 끝내고 롬복 관련 설정을 마무리 짓도록 한다.

이제 jacocoTestCoverageVerificationjacocoTestReport를 사용할 수 있다. gradle jacoco tasks

3. test

test {
    jacoco {
        destinationFile = file("$buildDir/jacoco/jacoco.exec")
    }
    useJUnitPlatform()
    finalizedBy 'jacocoTestReport'
}

테스트가 테스트가 실행될 때 jacoco설정을 명시한다. 우리 프로젝트에서는 커버리지 결과 데이터를 저장할 경로를 지정하는 설정을 명시했다. 그리고 finalizedBy 'jacocoTestReport' 를 추가하여 test 태스크 이후 jacocoTestReport 태스크가 실행되도록 한다. 명시하지 않은 설정들은 아래와 같은 디폴트 값이 들어간다.

    enabled = true
    destinationFile = file("$buildDir/jacoco/$.exec")
    includes = []
    excludes = []
    excludeClassLoaders = []
    includeNoLocationClasses = false
    sessionId = "<auto-generated value>"
    dumpOnExit = true
    classDumpDir = null
    output = JacocoTaskExtension.Output.FILE
    address = "localhost"
    port = 6300
    jmx = false

4. jacocoTestReport

jacocoTestReport {
    reports {
        html.enabled true
        xml.enabled true
        csv.enabled false
    }
    finalizedBy 'jacocoTestCoverageVerification'
}

해당 태스크를 이용하여 커버리지 결과를 저장할 방식을 정할 수 있다. 개발자가 보기 쉬운 형태인 html 형태와 소나큐브에 전송하기 위한 xml 형태로 저장되도록 했고, finalizedBy 'jacocoTestCoverageVerification'을 추가해서 해당 태스크 이후 실행될 태스크를 명시했다.

5. jacocoTestCoverageVerification

해당 태스크를 이용하여 커버리지 기준을 세우고, 코드가 그 커버리지를 만족하는지 확인할 수 있다.

jacocoTestCoverageVerification {
    violationRules {
        rule {
            enabled = true
            // 아래의 커버리지 기준을 클래스 단위로 검사하겠다는 설정
            element = 'CLASS'

            limit {
                // 분기에 대한 커버리지 기준 : 최소 80%
                counter = 'BRANCH'
                value = 'COVEREDRATIO'
                minimum = 0.80
            }

            limit {
                // 코드 라인에 대한 커버리지 기준 : 최소 80%
                counter = 'LINE'
                value = 'COVEREDRATIO'
                minimum = 0.80
            }

            // 커버리지 기준을 만족하는지 검사할 때 제외할 패키지
            excludes = [
                    'botobo.core.infrastructure.**',
                    'botobo.core.exception.**',
                    'botobo.core.dto.**',
                    'botobo.core.DataLoader',
                    'botobo.core.BotoboApplication'
            ]
        }
    }
}

자코코 설정에 대한 정보는 이 곳을 참고했다.

6. sonarqube

build.gradle 파일에 소나큐브 관련 설정도 추가한다.

sonarqube {
    properties {
        // 소나큐브 프로젝트 생성할 때 만들었던 프로젝트 키
        property "sonar.projectKey", "botobo-develop"
        // 소나큐브가 실행되고 있는 인스턴스의 public ip
        property "sonar.host.url", System.getenv('SONAR_URL')
        // 소나큐브에서 만든 토큰
        property "sonar.login", System.getenv('SONAR_LOGIN')
        property "sonar.language", "java"
        property "sonar.binaries", "$buildDir/classes"
        property "sonar.sources", "src/main"
        property "sonar.tests", "src/test/java"
        property "sonar.junit.reportsPath", "$buildDir/test-reports"
        property "sonar.java.coveragePlugin", "jacoco"
        property "sonar.coverage.jacoco.reportPaths", "$buildDir/jacoco/jacoco.exec"
        // 소나큐브에서 볼 리포트에서 제외하고 싶은 디렉토리
        property "sonar.exclusions", "**/exception/**, " +
                "**/dto/**, " +
                "**/DataLoader.java, " +
                "**/BotoboApplication.java"
    }
}

ip 주소나 토큰과 같은 민감한 정보는 젠킨스 서버의 환경변수에 값을 등록한 뒤 System.getenv를 이용하여 사용하도록 했다.

7. 롬복 설정

롬복으로 생성된 코드에 대한 커버리지는 제외하고 싶다면 백엔드 프로젝트의 root 디렉토리에 설정파일을 하나 추가해주면 된다. lombok config

lombok.addLombokGeneratedAnnotation = true

젠킨스 파이프라인

이제 젠킨스가 프로젝트를 테스트하고 빌드할 때 소나큐브에서 정적 분석 리포트를 만들도록 해야한다. 미리 만들어놓은 파이프라인에 아래의 스테이지를 추가하면 된다.

stage('SonarQube Analysis') {
    steps {
        withSonarQubeEnv(credentialsId: 'sonar_jenkins', installationName: 'botobo-develop') {
            dir('backend') {
                sh "./gradlew sonarqube"   
            }
        }
    }
}

그리고 젠킨스에서 새로운 빌드를 실행하면 소나큐브 단계가 포함되는 것을 볼 수 있다. jenkins sonarqube 소나큐브에서 해당 프로젝트 페이지에 들어가보면 리포트가 생성된 것을 볼 수 있다. 이 곳에서 현재 프로젝트에 어떤 취약점이 있는지, 개선할 수 있는 부분이 있는지, 그리고 중복되는 코드가 있는지와 같은 정보를 확인할 수 있다. sonarqube report

Clone this wiki locally