Golang 테스트 커버리지 측정과 코드 정적 분석 그리고 SonarQube
요즘은 프론트엔드, 백엔드 등으로 개발이 분업화 되었고 또한 최근 많은 곳에서 마이크로서비스 아키텍처Microservice Architecture를 도입하면서 하나의 언어가 아닌 여러 언어로 개발하는 것이 추세인 듯하다. 여러 언어로 개발하다 보면 테스트 커버리지와 코드 코드 정적 분석Static program analysis 결과를 각각 보면 매우 불편한데 통합하여 한곳에서 볼 수 있으면 편할 것이다.
SonarSouce에서 만든 SonarQube는 코드를 분석하여 중복, 테스트 커버리지, 코드 복잡도, 버그, 보안 취약성 등을 보여주는 도구이다. 특히 다양한 프로그래밍 언어를 지원하기 때문에 여러 언어를 쓰더라도 통합해서 볼 수 있다. 또한 리뷰 기능이 있어 코드에 대한 의견을 나눌 수 있다.
이 글은 Golang으로 만든 코드를 테스트 커버리지를 측정하고 코드 정적 분석을 하여 SonarQube에 리포팅하는 방법을 다룬다.
SonarQube 설치
SonarQube는 도커Docker 이미지를 지원하기 때문에 아래 명령어로 설치할 수 있다.[1]
1
docker run -d --name sonarqube -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true -p 9000:9000 sonarqube:latest
SonarQube를 설치한 컴퓨터에서 웹 브라우저를 열고 http://localhost:9000(필자의 경우 192.168.0.15)으로 접속해 보면 아래와 같이 화면이 나온다.
아이디와 비밀번호는 admin/admin 이다.
SonarQube 프로젝트 생성
SonarQube 내에 프로젝트를 생성한다. 여러 방법이 있지만 필자는 ‘Manually’을 사용했다.
프로젝트 이름을 입력하고 ‘Set Up’ 버튼을 클릭한다.
인증 토큰 발행
이 글에서는 SonarQube로 리포팅을 하기 위해 SonarScanner를 사용한다. SonarScanner를 사용하기 위해서는 SonarQube 인증 토큰이 필요하다. 인증 토큰은 SonarQube에서 발급받을 수 있다.
SonarQube 화면 우측 상단의 ‘A' 를 클릭하고 'My Account’ 를 클릭한다.
‘Security’ 탭을 클릭하고 토큰 이름을 입력하고 ‘Generater' 버튼을 클릭하면 발행한 토큰을 확인할 수 있다.
테스트 커버리지 측정
Golang 프로젝트 최상위 디렉토리에서 아래 명령어를 실행한다. 그러면 테스트 코드가 실행되고 결과를 coverage.out 파일에 기록한다.
1
go test -json ./... -covermode=atomic -coverpkg=./... -coverprofile=coverage.out
만들어진 coverage.out 파일을 SonarScanner 매개변수로 SonarQube로 전송한다. 앞서 발행한 인증 토큰을 ‘SONAR_LOGIN’ 매개변수로 함께 넘겨 준다. ‘projectKey’ 매개변수는 앞서 만들었던 SonarQube 프로젝트 이름이다.
1 2 3 4 5 6 7 8 9 10 11 12
docker run \ --rm \ --network host \ -e SONAR_HOST_URL="http://192.168.0.15:9000" \ -e SONAR_LOGIN="89df2da2bcdd5508b55cb661e43bc6915655a7c8" \ -v "${PWD}:/usr/src" \ sonarsource/sonar-scanner-cli -Dsonar.projectKey=better-admin-backend-service -Dsonar.go.coverage.reportPaths=./coverage.out -Dsonar.exclusions=**/*_test.go,**/vend[or/**,**/testdata/* -Dsonar.test.inclusions=**/*_test.go -Dsonar.test.exclusions=**/vendor/**
(주의. sonar-scanner-cli 명령어 뒤의 -D 옵션은 줄바꿈 하지 않고 붙여서 실행해야 오류가 안난다. 이 글에서 가독성을 이유로 줄바꿈을 한 것이다.)
실행에 성공했다면 SonarQube에서 아래와 같이 테스트 커버리지를 확인할 수 있다.
GoVet
GoVet는 Golang에서 기본적으로 제공하는 코드 정적 분석 도구이다. Golang 프로젝트 최상위 디렉토리에서 아래 명령어를 실행하면 정적 분석 결과를 vet.out 파일에 기록한다.
1
go vet ./... &> vet.out
만들어진 vet.out 파일을 SonarScanner를 사용하여 SonarQube로 전송한다.
1 2 3 4 5 6 7 8 9
docker run \ --rm \ --network host \ -e SONAR_HOST_URL="http://192.168.0.15:9000" \ -e SONAR_LOGIN="89df2da2bcdd5508b55cb661e43bc6915655a7c8" \ -v "${PWD}:/usr/src" \ sonarsource/sonar-scanner-cli -Dsonar.projectKey=better-admin-backend-service -Dsonar.go.govet.reportPaths=./vet.out
실행에 성공했다면 SonarQube에서 아래와 같이 GoVet 결과를 확인할 수 있다.
GolangCI-Lint
GolangCI-Lint는 GoVet과 같은 코드 정적 분석 도구이다. 다만 기본적으로 제공하지 않기 때문에 추가로 설치해야 한다. 아래와 같은 기능을 제공한다. 재미있는 것은 기본적으로 GoVet 포함한다는 사실이다.
Golang 프로젝트 최상위 디렉토리에서 아래 명령어를 실행하면 정적 분석 결과를 go-lint.xml 파일에 기록한다.[2]
1
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.41.1 golangci-lint run ./... --out-format=checkstyle > go-lint.xml
만들어진 go-lint.xml 파일을 SonarScanner를 사용하여 SonarQube로 전송한다.
1 2 3 4 5 6 7 8
docker run \ --rm \ --network host \ -e SONAR_HOST_URL="http://192.168.0.15:9000" \ -e SONAR_LOGIN="89df2da2bcdd5508b55cb661e43bc6915655a7c8" \ -v "${PWD}:/usr/src" \ sonarsource/sonar-scanner-cli -Dsonar.projectKey=better-admin-backend-service -Dsonar.go.golangci-lint.reportPaths=./go-lint.xml
실행에 성공했다면 SonarQube에서 아래와 같이 GolangCI-Lint 결과를 확인할 수 있다.
그외에..
SonarQube는 Golint 와 GoMetaLinter를 지원하고 있으나 두 개 프로젝트 모두 GitHub에서 아카이브 되었기 때문에 이 글에서는 다루지 않는다.
마치며
정적 분석 도구가 버그라고 리포팅하면 무조건 고쳐야 하는 것이 아니라고 생각한다. 왜냐하면 개발하는 환경이 매우 다양하기 때문이다. 개발 성숙도에 맞게 선택하는 지혜가 필요하다고 생각한다. 정적 분석 도구는 보완적인 도구임을 잊지 말자.
주석
[1] https://docs.sonarqube.org/latest/setup/get-started-2-minutes/