본문 바로가기

백엔드 개발일지

[Spring] 코드 정적 분석을 위한 SonarCloud 도입기

현재 진행 중인 사이드 프로젝트 모카콩(https://github.com/mocacong/Mocacong-Backend)에는 아직 코드 정적 분석을 위한 도구가 따로 없었습니다. 사실상 MVP 기능들은 다 개발이 되었고 현재는 리팩터링 위주로 진행중인데 현재 프로젝트의 테스트 코드가 꼼꼼히 잘 작성되었는지, 코드에 취약점이 있는지 파악하고 싶어 SonarCloud를 도입하게 되었습니다.

 

1. SonarCloud란

 

코드 정적분석기로 이미 SonarQube가 유명합니다. SonarQube는 이미 널리 알려진 문제가 발생하는 코드나 일반적으로 사용되는 규칙을 통해 버그나 취약점을 파악할 수 있고 테스트 코드의 커버리지를 파악해주어 개발자가 소스코드의 품질을 유지할 수 있게 돕습니다.

 

하지만 SonarQube는 직접 서버를 구축해야 하는 단점이 있습니다. 반면 SonarCloud는 이미 구축되어 있는 서버를 이용하기에 개발자가 보다 쉽게 세팅할 수 있습니다. SonarQube가 SonarCloud에 비해 더 다양한 기능을 제공하지만 SonarCloud만으로도 소스코드 품질 유지를 위한 다양한 기능들을 충분히 제공하기에 사이드 프로젝트인 모카콩에 적합하다 생각하여 SonarCloud를 택했습니다.

 

 

 

 

 

2. SonarCloud 설정

소나클라우드는 자바 프로젝트 커버리지 report를 직접 생성하지 않아 빌드 프로세스의 일부로 보고서를 생성하는 third pary 도구를 설정해야 합니다. (참고: https://docs.sonarsource.com/sonarcloud/enriching/test-coverage/java-test-coverage/)

공식 사이트에서도 JaCoCo 세팅을 추천하기에 저희 프로젝트에서도 코드 커버리지 측정을 위해 JaCoCo를 도입했습니다. 이전 포스트에서 JaCoCo 세팅과 관련해 작성한 글이 있어 해당 포스트에서는 설명을 생략합니다.

 

1) 소나 클라우드 시작

 

https://sonarcloud.io 에 들어가 소나클라우드를 세팅하고 싶은 프로젝트를 가져옵니다. (저는 이미 모카콩에 세팅을 해서 화면과 같이 뜹니다.) public repository라면 무료 플랜을 사용할 수 있습니다!

 

 

 

저희는 이미 Github Actions CI로 진행하고 있었기에 소나클라우드에서 제공하는 자동 분석은 사용하지 않고 CI-based로 코드 정적 분석을 진행합니다. 그렇기에 자동 분석은 꼭 꺼줘야 Github Actions에서 CI를 돌릴 때 충돌이 나지 않습니다.

 

 

 

렇게 진행한다면 깃헙 액션에서 SonarCloud에 접근할 수 있는 액세스 권한이 필요한데 이는 SONAR_TOKEN이 해결해줍니다! 깃헙 액션 Secrets에 소나클라우드가 제공하는 SONAR_TOKEN을 등록합니다.

 

 

 

이건 필수는 아니지만 Quality Gates를 측정할 때 소나클라우드 디폴트 값이 Sonar way입니다. 그런데 Sonar way의 측정 기준이 조금 빡세다고 생각해서 Mocacong 기준을 따로 만들어서 적용했습니다!

 

 

 

2) build.gradle 설정

 

친절하게도 소나클라우드는 본인 프로젝트에 맞는 build들에 대해 설정 코드를 줍니다. 저희 모카콩은 Gradle을 사용하기에 위와 같은 형식으로 작성했습니다. 빨간 박스 안에 있는 값은 본인 프로젝트에 맞게 작성하셔야 합니다.

 

plugins {
    // ...
    id 'org.sonarqube' version '4.2.1.3168'
}

sonar {
    properties {
        property 'sonar.host.url', 'https://sonarcloud.io'
        property 'sonar.organization', 'mocacong'
        property 'sonar.projectKey', 'mocacong_Mocacong-Backend'
        // 자코코 결과 리포트 주소
        property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/index.xml' 
        property 'sonar.sources', 'src'
        property 'sonar.language', 'java'
        property 'sonar.sourceEncoding', 'UTF-8'
        // 테스트 커버리지에서 제외할 클래스
        property 'sonar.exclusions', '**/test/**, **/resources/**, **/*Application*.java, **/*Controller*.java, **/*Config.java' +
                '**/*Response.java, **/*Exception.java, **/security/**, **/support/**, **/Q*.java'
        property 'sonar.test.inclusions', '**/*Test.java'
        property 'sonar.java.coveragePlugin', 'jacoco'
    }
}

 

모카콩에서는 다음과 같이 작성했습니다. 소나클라우드에서 테스트 커버리지를 측정할 때 자코코 테스트 리포트를 이용해야 하므로 자코코 리포트(xml 파일)가 생성되는 주소를 설정해줍니다.

 

 

3) CI 스크립트

name: Java CI with Gradle

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    branches:
      - main
      - develop

jobs:
  test:
    name: Code Quality Check
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'

      - name: Grant execute permisson for gradlew
        run: chmod +x gradlew

      - name: Cache SonarCloud packages
        uses: actions/cache@v3
        with:
          path: ~/.sonar/cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar

      - name: Setup Gradle
        uses: gradle/gradle-build-action@v2
        with:
          arguments: check
          cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}

      - name: Build and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: ./gradlew sonar --info --stacktrace

 

모카콩에서는 Github Actions CI 수행할 CI 스크립트를 위와 같이 작성했습니다.

핵심 부분만 설명하면 다음과 같은 기능을 수행합니다.

  • Cache SonarCloud packages: 소나클라우드 패키지를 캐시합니다
  • Setup Gradle
    • gradle/gradle-build-action 액션을 사용하여 Gradle을 설정하고 빌드를 실행합니다.
    • arguments: check: Gradle 빌드 시에 check 태스크를 실행하여 코드 품질 및 유효성을 검사합니다.
    • cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}: 빌드 시에 캐시를 읽기 전용으로 사용하며, 이는 main 또는 develop 브랜치에서만 변경되는 경우에만 유효합니다.
  • Build and analyze: Gradle을 사용하여 프로젝트를 빌드하고, SonarCloud에 정적 코드 분석을 실행합니다. --info--stacktrace 옵션은 실행 정보 및 스택 트레이스를 자세히 표시하도록 합니다.

 

 

 

 

 

3. 소나클라우드 적용 결과

 

 

위의 코드들을 다 적용한 PR을 날렸을 때 소나클라우드가 코드 분석을 하여 코멘트를 남긴 모습입니다.

저희 프로젝트는 이미 MVP 기능들을 다 개발하고 코드 정적 분석을 진행했기에 Fail이 떴네요 ㅎㅎ...

코드 분석에 대한 자세한 내용은 소나클라우드 홈페이지에서 확인할 수 있습니다!

모카콩 v2.0.0 업데이트 진행하면 code smell 바로 잡는데 신경 써야할 거 같네요. 😅