Naver Financial 코드 품질 관리 — Part 2
안녕하세요. Naver Financial 에서 간편 결제 플랫폼 개발 업무를 담당하고 있는 장수진입니다.
Naver Financial 에서는 주문 / 결제 / 금융 서비스를 다양한 파트너사와 co-work 하여 사용자에게 제공하고 있습니다.
SonarQube 에서 제공하고 있는 코드 품질 분석 기능 및 연동 가이드에 대한 소개 그리고 NF 내에서 어떻게 SonarQube를 활용하고 있는지 설명합니다.
연재
- 코드 품질과 SonarQube 활용
- SonarQube 소개
- SonarQube 구성 및 운영
SonarQube의 기능
SonarQube 에서 제공하고 있는 코드 품질 분석 기능에 대해 소개합니다.
Rules
코드 품질에 대한 규칙은 아래 네 개의 분류로 나뉘어집니다.
- Code Smell (Maintainability domain): 유지 보수 관점으로 분석되는 코드로 리팩토링 하지 않는다면 코드 수정이 일어날 때 부수효과가 발생할 가능성이 높습니다.
- Bug (Reliability domain): 수정이 반드시 필요한 잘못된 코드
- Vulnerability (Security domain): 개발자의 리뷰가 필요한 보안에 민감한 코드
- Security Hotspot (Security domain): 보안과 관련있는 이슈로 공격자들이 선택할 수 있는 방법에 대한 대비가 되어 있는지를 분석합니다.
각 Rule은 상세 설명 및 권장되는 해결 방안을 포함하며, 코드 작성자가 이슈 대응에 대한 우선 순위를 매길 수 있도록 심각도 등급을 가지고 있습니다.
취약점 (Vulnerability) 또는 핫스팟 (Security Hotspot)?
핫스팟과 취약점의 주요 차이점은 수정 적용 여부를 결정하기 전에 검토가 필요하다는 것입니다.
- 핫스팟을 사용하면 보안에 민감한 코드 부분이 강조 표시되지만 전체 애플리케이션 보안에는 영향을 미치지 않을 수 있습니다. 코드를 보호하기 위해 수정이 필요한지 여부를 결정하기 위해 코드를 검토하는 것은 개발자의 몫입니다.
- 취약점으로 인해 애플리케이션의 보안에 영향을 미치는 문제가 발견되었으며 즉시 수정해야 합니다.
Quality Profiles
프로젝트에 적용할 rule 들의 집합입니다.
모든 프로젝트에 같은 Quality Profile를 이용하여 코드 품질을 검증하는 것이 이상적일 수 있지만 항상 실용적이진 않습니다. 각 프로젝트에서 사용하는 기술들이 다르고 다른 환경에서 동작할 수 있기 때문에, 서로 다른 Quality Profile을 통해 코드 품질을 유지해야할 때가 있습니다.
Quality Gates
오늘 당신의 프로젝트를 운영환경에 제공할 수 있나요?
품질 게이트는 프로젝트가 릴리즈 되기 위해 만족해야 할 매트릭 조건셋을 말합니다. 이를테면 새로운 blocker 이슈 없음, 신규 코드에 대한 Coverage가 80% 이상 과 같은 조건을 설정할 수 있습니다.
기본적으로 SonarQube의 품질 게이트는 Clean As You Code (개발자는 높은 표준을 유지하고 신규 코드에 대해 책임을 져야 한다) 관점에서 다음 규칙에 중점을 두고 있습니다.
- Focus on New Code — 오래된 코드를 수정하는데 많은 노력을 기울이기보다 신규 코드의 측정 값에 집중한다
- Personal Responsibility — 신규 코드에 대해서만 측정하므로 다른 사용자의 코드에 대한 책임이 없다
SonarQube 연동 가이드
1. 소스 저장소와 연동된 프로젝트 생성
- SonarQube를 Github과 통합하기 위해, 연동용 GitHub App을 생성해 Github Organization에 설치합니다. App의 자세한 설정은 GitHub integration 을 참고 부탁드립니다.
- SonarQube 내에서 새 프로젝트를 생성합니다. 연동되어 있는 Github을 통해 가이드 받거나 Manually로 직접 생성할 수 있습니다. 어떤 방법으로 생성하든 설정 값 중 프로젝트 키는 SonarQube 인스턴스 내에서 유니크해야 합니다.
2. CI 연동 및 소스코드 분석
가장 많이 사용되는 CI 툴인 Jenkins, Github Actions를 사용할 경우의 연동 가이드에 대해 간단하게 소개합니다.
Jenkins 사용시
1) Jenkins 멀티브랜치 파이프라인 잡 생성
모든 브랜치와 PR을 자동으로 분석하기 위해 멀티브랜치 파이프라인 잡 생성이 필요합니다. Jenkins 대시보드에서 잡을 생성할 때 Branch Source 로 Github 를 선택해 코드 저장소와 연동될 수 있게 합니다.
2) Github 웹훅 생성
코드 푸시가 발생하거나 PR이 생성되었을 때 Jenkins 잡을 트리거할 수 있도록 아래와 같이 설정한 웹훅을 생성합니다.
- URL: ***JENKINS_SERVER_URL***/github-webhook/
- 트리거 대상 이벤트: Pushes, Pull Requests
3) Jenkinsfile 생성
정적 분석에 사용되는 저장소에 사용하는 빌드 도구에 따른 Jenkinsfile을 생성합니다. 아래는 Gradle의 예제 코드입니다.
pipeline {
agent any
environment {
SONAR_SCANNER_HOME = tool 'sonar-scanner'
SONAR_PROJECT_KEY = 'your-project-key'
SONAR_ANALYSIS_VERSION = new Date().format('yyyy-MM', TimeZone.getTimeZone('Asia/Seoul'))
}
tools {
jdk 'openjdk11'
}
stages {
stage("Build") {
steps {
sh './gradlew clean build test'
}
}
stage("Analysis") {
steps {
withSonarQubeEnv("sonarqube-server") {
script {
if (env.CHANGE_ID) {
// pull request
sh """
${SONAR_SCANNER_HOME}/bin/sonar-scanner \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.pullrequest.key=${env.CHANGE_ID} \
-Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} \
-Dsonar.pullrequest.base=${env.CHANGE_TARGET} \
-Dsonar.sources=. \
-Dsonar.inclusions=**/src/main/** \
-Dsonar.tests=. \
-Dsonar.test.inclusions=**/src/test/** \
-Dsonar.junit.reportPaths=**/build/test-results \
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml
"""
} else {
sh """
${SONAR_SCANNER_HOME}/bin/sonar-scanner \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.branch.name=${env.BRANCH_NAME} \
-Dsonar.projectVersion=${SONAR_ANALYSIS_VERSION} \
-Dsonar.sources=. \
-Dsonar.inclusions=**/src/main/** \
-Dsonar.tests=. \
-Dsonar.junit.reportPaths=**/build/test-results \
-Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml \
-Dsonar.test.inclusions=**/src/test/**
"""
}
}
}
}
}
stage("Quality Gate") {
steps {
timeout(time: 1, unit: 'HOURS') {
waitForQualityGate abortPipeline: true
}
}
}
}
}
SonarScanner 실행시 사용한 주요 분석 옵션은 다음과 같습니다.
- sonar.sources: 소스 파일을 포함하는 디렉토리 설정
- sonar.tests: 테스트 소스 파일을 포함하는 디렉토리 설정
- sonar.coverage.jacoco.xmlReportPaths: jacoco 커버리지 리포트 경로 설정
- sonar.junit.reportPaths: XML 형식의 Surefire 리포트 파일 경로 설정
자세한 분석 옵션은 Analysis Parameters, Test Coverage, Narrowing the Focus 를 참고 바랍니다.
4) 분석 결과 확인
Jenkins 트리거를 통해 저장소에 변경이 있을 경우 분석을 진행합니다.
Github Actions 사용시
1) Github Secret 생성
Github Actions 에서 사용할 다음 환경 변수를 저장소의 Secret에 정의합니다.
- SONAR_TOKEN: SonarQube의 사용자를 식별하고 인증하는 토큰으로 사용자 계정 설정에서 생성할 수 있습니다.
- SONAR_HOST_URL: SonarQube 호스트 URL을 입력합니다.
2) Workflow YAML 파일 생성
사용하는 빌드 도구에 맞춰 Workflow 파일을 .github/workflows/build.yml 경로에 생성합니다. 아래는 Maven의 예제 코드입니다.
name: Build
on:
push:
branches:
- master
- develop
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11 # SonarQube Scanner 실행 환경의 JDK 최소 요구 버전은 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Build and analyze
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
3. 로컬 환경에 SonarLint 설정
SonarQube는 로컬 환경에서도 품질 이슈를 미리 감지하고 수정할 수 있도록 IDE 플러그인 SonarLint를 제공하고 있습니다. SonarLint는 Eclipse, IntelliJ IDEA, VS Code 등 다양한 IDE를 지원하며, 본 게시글에서는 IntelliJ IDEA 기준으로 공유합니다.
- IDE에 SonarLint 플러그인을 설치합니다.
- IDE 설정 (Preferences > SonarLint) 에서 SonarQube 서버를 등록합니다. Connection Type은 SonarQube이며 토큰은 사용자 계정 설정에서 생성할 수 있습니다.
- 프로젝트 설정 (Prefrences > SonarLint > Project Settings) 에서 위에서 등록한 SonarQube 서버와 프로젝트를 연결합니다.
NF 내 품질 분석과 활용 사례
Quality Gates in NF
레거시 코드가 많은 프로젝트의 경우, SonarQube에서 기본적으로 제공하는 엄격한 기준의 품질 게이트를 적용한다면 코드 품질 개선에 대한 의욕이 되려 반감될 수 있습니다. 기존 코드에서 한 줄만 변경했을 뿐인데 파일 전체의 버그를 모두 개선해야 하는 경우가 생길 수 있으니까요. 🤔
따라서 프로젝트의 상황에 따라 점진적으로 코드 품질을 향상시킬 수 있도록 네이버 파이낸셜에서는 아래와 같이 단계별 리그를 개설하여 제공하고 있습니다.
- Basic League: 기존에 서비스 되고 있던 프로젝트에서 초기 도입시 사용을 추천하는 단계
- Pro League: 코드 품질을 점진적으로 높여나가고 싶은 프로젝트를 위한 단계
- Champions League: 품질 최종 목표 단계
SonarQube 탐지 사례 in NF
1. SonarQube의 제안에 따라 대응 가능한 건
Rule에서 제기한 성능 혹은 정확성 등의 문제점에 공감하며 SonarQube에서 제안한 해결 방법이 적용 가능한 경우, 코드를 수정해 이슈를 해결하는 것이 바람직합니다.
2. 리뷰를 통해 해결해야 하는 건
SonarQube에서 이슈 탐지가 되었다고 해서 반드시 제안대로 수정해야 하는 것은 아닙니다. 프로젝트 혹은 조직에 적용하기 적합하지 않다고 판단된 경우 코드를 수정하지 않고도 이슈를 해결할 수 있습니다.
Rule에 대해서는 공감하나 프로젝트의 컨텍스트 혹은 레거시 코드로 인해 피치 못하게 해결 방안 적용이 어렵다면 개별 이슈의 해결 상태를 변경할 수 있습니다. Resolve as won’t fix (오류이나 수정하지 않음) 를 선택해 일시적으로 해결 상태로 만들고 이후 해당 이슈에 대해 대응하고자 할 때 재오픈이 가능합니다.
Rule 에서 제기한 문제점에 공감이 어렵거나 조직 내의 정책과 적합하지 않은 경우에는 Rule 을 끄거나 심각도를 변경할 수 있습니다. 이 경우, 설명 확장 기능을 이용하여 SonarQube 사용자에게 Rule을 조정한 이유를 공유할 수 있습니다.
다음 “SonarQube 구성 및 운영” 편에서는
SonarQube 시스템 및 NaverFinancial 의 SonarQube 환경 구성과 운영 사례에 대해 소개할 예정입니다.