본문 바로가기

정리/유용 기능

[소프트웨어 분석] Sonarqube

정적 분석 VS 동적 분석

구분 정적 분석 동적 분석
분석 대상 소스 코드 또는 컴파일된 바이너리 프로그램 실행 환경
테스트 범위 소스 코드의 모든 부분 실행 가능한 경로
활용 코드 상의 문제나 실수를 찾기 테스트, 모니터링
  • 정적 분석은 주로 개발 단계에서 코드의 구조적 문제를 파악할 때 사용한다.
  • 동적 분석은 주로 테스트나 모니터링에 사용한다.

 

Sonarqube

  • 정적 분석을 도와주는 대표적인 도구
  • 프로젝트 코드 품질 측정
    • 프로젝트 코드의 품질 정보 측정
    • 빌드 및 통합 후 품질 변화 측정

  •  폴리그랏 언어별 Rule 지원
    • Java, GO 언어 등 언어별 Rule 지원
    • Jacoco 등 분석 플러그인 지원

  • Quality Profiles 관리
    • 분석 Ruleset 정의 및 적용
    • Ruleset 기반 Profile 구성
    • 자신의 프로젝트에 맞는 코드 검사 Rule을 설정할 수 있다.

  • Quality Gates 설정
    • 다수 그룹의 품질 요구사항 설정
    • 다수 항목별 품질 요구사항 설정
    • 품질 기준을 설정하여 만족하는지 확인 가능
    • A~D 등급으로 나타난다.

  • DevOps 통합 가능
    • CI 도구와 통합, DevOps 방식 관리 가능

 

Jacoco

  • Java Code Coverage
    • 자바 소스 파일의 코드 커버리지 제공

    • 테스트 케이스에 의한 테스트 수 측정
    • Instruction Coverage
      1. 코드 실행량 측정 커버리지
    • Branch Coverage
      1. if나 swtich 문의 분기 확인
      2. 실행된 것과 실행되지 않은 부분 측정
    • Cyclomatic Complxity
      1. function 테스트 시 최소 경로 정보
      2. 모든 코드를 커버하기 위한 테스트 수

실습

환경: AWS EC2 ubuntu 20.04 + Docker + AWS ECR

 

1. AWS EC2에 SonarQube 설치

  • docker run -d -p 9000:9000 sonarqube:latest 명령어를 사용해서 SonarQube를 설치한다.

 

2. AWS EC2 인바운드 규칙 설정

  • AWS EC2의 보안 -> 인바운드 규칙 설정에서 9000번 포트를 허용한다.

 

3. SonarQube 접속

  • 브라우저에서 [AWS EC2 퍼블릭 IPv4 DNS]:9000을 입력하여 SonarQube에 접속하면 로그인 창이 뜬다.
  • 최초 계정은 admin/admin으로 다음을 입력하고 로그인을 진행하고 새로운 패스워드로 변경한다.

 

4. Test 계정 생성하기

  • SonarQube 메인 페이지에서 Administration 버튼을 클릭한다.

  • Security -> Users를 클릭한다.

  • Test 계정을 생성하기 위해 Create User 버튼을 클릭한다.

 

  • 항목을 채워주고 Create 버튼을 클릭한다.
  • 로그아웃을 진행하고 방금 생성한 테스트 계정으로 로그인을 진행한다.

 

  • 테스트 계정으로 로그인 시 권한이 없기 때문에, 빨간 영역의 항목들이 disable된 것을 확인할 수 있다.

 

  • Admin 계정으로 로그인 한 후, Administration -> Security -> Global Permissions에서 계정에 대한 권한을 설정할 수 있다.

 

SonarQube의 Rules에서 프로젝트 언어에 맞게 검사하는 Bug, Code Smell 등을 확인할 수 있다.

 

 

5. Project build.gradle 수정

buildscript {
  ext {
      springBootVersion = '1.5.4.RELEASE'
      jacocoVersion = '0.8.7' // Jacoco version 명시
  }
  repositories {
      mavenCentral()
      maven {
          url "https://plugins.gradle.org/m2/"
      }
  }
  dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
      classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1" // Sonarqube Dependency 설정
  }
}

plugins {
	id 'org.springframework.boot' version '2.5.2'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
    id 'com.google.cloud.tools.jib' version '3.1.4'
}

apply plugin: 'org.sonarqube' // plugin 적용
apply plugin: 'jacoco' // plugin 적용
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}
jacoco{
  toolVersion = "${jacocoVersion}"
}

jacocoTestReport { // Jacoco 분석 결과를 어떤 파일 형태로 작성할 것인가
 reports{
  html.enabled=true
  xml.enabled=true
  csv.enabled=true
 }
}

tasks.withType(JavaCompile) { // 한글, 영어 이외의 문자를 인코딩
    options.encoding = 'UTF-8'
}

sonarqube {
  properties {
      property "sonar.projectName","<Project Name>"
      property "sonar.exclusions", "**/generated-*/**/*"
      property "sonar.projectKey", "org.sonarqubeJacocoCodeCoverage"
      property "sonar.reportPath" , "${project.buildDir}/jacoco/test.exec"
      property "sonar.host.url", "http://<SonarQube Private IP>:9000"
      property "sonar.sources", "src/main/java"
      property "sonar.tests", "src/test/java"
      property "sonar.login", "<SonarQube ID>"
      property "sonar.password", "<SonarQube PW>"
  }
}

tasks['sonarqube'].dependsOn test

jib { // docker Image build
    from {
        image = 'adoptopenjdk/openjdk11:alpine-jre'
    }
    to {
        image = '<AWS ECR URL>/<Image Repository Name>'
        tags = ['<Image Tag Name>']
    }
    container {
        entrypoint = ['java', '-Dspring.profiles.active=test', '-jar', 'spring-boot-gradle-demo-0.0.1-SNAPSHOT.jar']
        // mainClass = 'com.test.StartApplication'
        jvmFlags = ['-Xms512m', '-Xmx512m', '-Xdebug', '-XshowSettings:vm', '-XX:+UnlockExperimentalVMOptions', '-XX:+UseContainerSupport']
        ports = ['8080']

        environment = [SPRING_OUTPUT_ANSI_ENABLED: "ALWAYS"]
        labels = [version:project.version, name:project.name, group:project.group]

        creationTime = 'USE_CURRENT_TIMESTAMP'
        format = 'Docker'
    }
    extraDirectories {
        paths {
            path {
                from = file('build/libs')
            }
        }
    }
}

 

* 실행 명령어

  • 예제 코드 빌드 명령어
    • ./gradlew clean build --info

  • Jacoco 코드 커버리지 측정 및 리포트 작성 명령어
    • ./gradlew jacocoTestCoverageVerification --info
    • ./gradlew jacocoTestReport --info

  • SonarQube 코드 품질 스캔 결과 연동 명령어
    • ./gradlew sonarqube --info

  • Docker 이미지 빌드 및 Push 명령어
    • ./gradlew jib --console=plain

'정리 > 유용 기능' 카테고리의 다른 글

Multi Module  (0) 2022.11.13
Android Studio 없이 Emulator 실행하기  (0) 2022.10.20
유용한 도구 모음 (1)  (0) 2022.10.06
[IntelliJ] 클래스 생성 시 자동 주석 설정  (0) 2022.09.06