2025년 3월 17일

사용기 - 맥에서 도커 기반 SonarQube 활용하기

SonarQube는 코드에 대한 버그, 코드 스멜(Code Smell), 보안 취약점을 자동으로 찾아내고, 코드 품질을 향상시키는 데 도움을 주는 소스 코드 품질 및 보안 분석 도구이다.

버전에 따라 기능의 차이가 있는데 Community Edition (무료) 은  GitHub Private Repository 연동을 지원하지는 않지만 Git 에서 직접 코드를 가져와 분석할 수 있기 때문에 사용하는데 있어 큰 이슈는 없을 것 같다.

버전

특징

GitHub Private Repo 분석

Community Edition

무료, 기본적인 코드 분석 가능

✗ (직접 클론 후 분석)

Developer Edition

추가적인 언어 지원, 분기(branch) 분석

Enterprise Edition

대규모 프로젝트 지원, 포트폴리오 관리


◼︎ 환경

  • Model : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 15.3.1 (24D70)
  • TOOLS : VS Code , Docker

1. Docker 설치

Docker 는 공식 웹사이트에서 Docker Desktop for Mac을 다운로드하여 설치한다.


https://www.docker.com/products/docker-desktop

설치파일을 다운로드 후 Docker.dmg 파일을 클릭하여 설치를 진행할 수 있다. 설치가 완료되면 터미널에서 docker --version 명력을 입력하여 설치 확인한다.


% docker --version
Docker version 27.5.1, build 9f9e405


2. SonarQube Community Edition 설치하기

SonarQube 는 간단하게 터미널에서 아래 명령으로 SonarSource에서 공식적으로 제공하는 이미지를 다운로드하여 설치하고 실행할 수 있다.  SonarQube 는 디폴트로 사용하는 H2 데이터베이스 보다는  postgres 사용을 권장하고 있어 먼저 postgres 데이터베이스를 설치하고 같이 실행할 필요가 있다.

docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community

SonarQube는 여러 버전의 Docker 이미지를 제공하고 있다. 

태그(Tag)

설명

sonarqube:latest

최신 안정 버전

sonarqube:lts

최신 LTS (Long-Term Support) 버전

sonarqube:lts-community

무료 Community Edition LTS 버전

sonarqube:developer

Developer Edition (유료)

sonarqube:enterprise

Enterprise Edition (유료)

sonarqube:datacenter

Data Center Edition (유료)



postgres는 터미널에 아래와 같이 한 줄 명령어로 postgres 를 설치&실행 할 수 있다. 

docker run -d --name postgres \
  -e POSTGRES_USER=sonar \
  -e POSTGRES_PASSWORD=sonar \
  -e POSTGRES_DB=sonarqube \
  -p 5432:5432 \
  postgres:16

참고로 PostgreSQL 공식 Docker 이미지는 다양한 버전이 있는데 여기에서는 16 을 사용했다. 

태그 (Tag)

의미

postgres:latest

최신 안정 버전 (현재 기준 PostgreSQL 16)

postgres:16

PostgreSQL 16.x

postgres:15

PostgreSQL 15.x

postgres:14

PostgreSQL 14.x

postgres:13

PostgreSQL 13.x


이제 SonarQube 을 앞서 실행한 postres 와 함께 설치&실행 해보자.

docker run -d --name sonarqube \
  -p 9000:9000 \
  --link postgres \
  -e SONAR_JDBC_URL=jdbc:postgresql://postgres:5432/sonarqube \
  -e SONAR_JDBC_USERNAME=sonar \
  -e SONAR_JDBC_PASSWORD=sonar \
 sonarqube:lts-community

옵션

설명

docker run

새로운 컨테이너를 실행하는 명령

-d

컨테이너를 백그라운드(Detached Mode)에서 실행

--name sonarqube

컨테이너 이름을 sonarqube로 지정

-p 9000:9000

호스트의 9000번 포트를 컨테이너의 9000번 포트와 연결 (SonarQube 웹 UI)

--link postgre-container

SonarQube가 postgre-container 컨테이너와 통신할 수 있도록 설정 (Deprecated - 다른 방법 권장)

-e SONAR_JDBC_URL=jdbc:postgresql://postgres:5432/sonarqube

SonarQube가 PostgreSQL 데이터베이스 sonarqube에 연결하도록 설정

-e SONAR_JDBC_USERNAME=sonar

PostgreSQL 접속 시 사용할 DB 사용자명 설정

-e SONAR_JDBC_PASSWORD=sonar

PostgreSQL 접속 시 사용할 DB 비밀번호 설정

sonarqube:lts-community

실행할 Docker 이미지 (sonarqube의 LTS 버전, 무료 Community Edition)


참고로 여기에서는 postgres 와 sonarquber 가 서로 통신가능하게 --link 옵션을 사용했지만 안정성 이유로 --network 을 사용하는 것이 권장된다고 한다. 설치가 완료되면 브라우저에서 localhost:9000 포트로 접속한다. 디폴트 계정과 비밀번호는 admin 과 admin 이다.

Docker Desktop 도구를 사용하면 좀더 쉽게 도커 컨테이너 제어와 자원 사용량을 실시간으로 확인 할 수 있다.
Docker Desktop 화면

일단 위 과정을 한번하고나면 다음부터는 Docker Desktop 을 사용하여 쉽게 컨테이너를 조작할 수 있다.

2. SonarQube Community Edition 사용하기

한글이 익숙하다면 로그인 후 macketplace 메뉴에서 "Korean Pack Localization" 을 설치하면 간단하게 한글을 지원하게 된다.

코드 품질을 분석하는 기본적인 과정은 아래와 같다. 

❶ 프로젝트를 생성
❷ SonarScanner 설치 및 실행
❸ 분석결과 확인 

2.1 프로젝트를 생성

"프로젝트" 메뉴를 클릭하고 Community Edition 이 GitHub Private Repository 연동을 지원하지 않기 때문에 로컬에 프로젝트를 복제하여 분석 하기 위하여 "Manually" 을 선택한다.

존재하는 프로젝트가 없는 경우

프로젝트 이름을 입력하고 Setup 을 클릭하여 프로젝트를 생성한다.


2.2 토큰 생성

보안된 인증을 통해 자동화된 코드 분석을 실행할 수 있도록 토큰을 생성한다. Loaclly 을 클릭한다.


① 토큰 생성하기에서 생성하기를 클릭하여 토큰을 생성한다.




생성된 토큰을 별도로 저장하여 보관한다. Continue 클릭하면 다양한 환경에 바로 적용 가능한 스크립트를 보여준다. 



기존 프로젝트가 maven 을 지원하고 있어 maven 스크립트를 복사했다.

2.3 SonarScanner 설치 및 실행

SonarQube 서버와 연결하여 코드 품질을 분석하고 결과를 업로드하는 SonarScanner 은 아래와 같이 터미널에 입력하여 간단하게 설치할 수 있다.

brew install sonar-scanner

SonarScanner가 분석하는 요소는 아래와 같다.
  • Bug (버그): 코드에서 오류가 발생할 가능성이 있는 부분
  • Code Smell (코드 냄새): 유지보수성이 낮거나 개선이 필요한 코드
  • Vulnerability (보안 취약점): SQL Injection, XSS 같은 보안 문제
  • Coverage (테스트 커버리지): 단위 테스트가 얼마나 잘 작성되었는지
  • Duplications (코드 중복): 중복된 코드 블록 확인
설치 결과는 아래 명령으로 확인 할 수 있다.

sonar-scanner --version


이제 Git 에서 가져온 Maven 프로젝트에서 앞서 복사한 스크립트를 사용하여 실행한다.

mvn clean verify sonar:sonar \
-Dsonar.projectKey=my_project \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=your_generated_token

옵션

설명

mvn

Maven을 실행하는 명령어

clean

기존 빌드 파일을 삭제하고 새롭게 빌드

verify

테스트를 실행하고 빌드를 검증

sonar:sonar

SonarQube 분석을 실행하는 Maven 플러그인 (sonar-maven-plugin)

-Dsonar.projectKey=my_project

SonarQube에서 프로젝트를 구별하는 고유한 키

-Dsonar.host.url=http://localhost:9000

SonarQube 서버의 주소

-Dsonar.login=your_generated_token

SonarQube 인증을 위한 API 토큰



실행이 완료되면 SonarQube 웹에서 분석 결과 확인할 수 있다.



결과 화면

SonarQube 분석 결과을 보면 이 프로젝트는 다음과 같은 조치가 필요한 것으로 나타난다.
  1. Bugs, Vulnerabilities, Coverage, Duplications 항목에서 개선 필요
  2. 특히 Bugs (170개), Coverage (0%), 취약점 (13개)가 심각한 문제
  3. 보안 문제를 해결하고, 테스트 커버리지를 높이는 것이 가장 시급함
이제 SonarQube 에서 "Issues" 탭을 확인하고, 하나씩 문제를 해결해 나가면 된다. 

이슈 목록 화면

각 이슈를 클릭하면 해당 건에 대한 상세 화면으로 이동하게 된다.  이슈 내용을 확인하고 코드를 수정한다. 이슈 상세 페이지를 보면 이슈 상태를 변경할 수 있는 기능이 있는데  코드 수정 후 다시 로컬에서 SonarScanner 을 실행하기 때문에 문자가 해결되면 자동으로  Resolved(Fixed) 로 상태가 변경 된다.  


이슈 상세화면에서 "See all issues in this file" 클릭하여 해당 파일에 대한 목록만 보여지는데  이를 활용하면 파일 별로 작업을 진행 할 수도 있다. 

이슈 상세 화면

파일 단위로 필터 된 이슈 목록


코드를 수정하고 SonarScanner 을 동일하게 실행하면 결과가 반영되어 다시 분석결과가 업데이트 된다. 

3. 느낀점

자바 언어를 사용하는 프로젝트에 1주 정도 적용을 해보았는데 아래와 같은 점에서 아주 만족스러웠다.
  1. 좋은 코딩 습관을 위한 아주 좋은 도구이다. 코드에 대한  결함을 해결하면서 좋은 코드를 작성하는 방법을 자연스럽게 학습할 수 있었다.
    • Bug (버그): 코드에서 오류가 발생할 가능성이 있는 부분
    • Code Smell (코드 냄새): 유지보수성이 낮거나 개선이 필요한 코드
    • Vulnerability (보안 취약점): SQL Injection, XSS 같은 보안 문제
    • Coverage (테스트 커버리지): 단위 테스트가 얼마나 잘 작성되었는지
    • Duplications (코드 중복): 중복된 코드 블록 확인
  2. 자바 언어의 버전에 따른 좋은 코드를 지적해주기 때문에 코딩 스킬 향상에 도움이 되었다.

2025년 3월 1일

사용기 - VSCode 에서 Gemini Code Assist

Gemini Code Assist 설치하기

Gemini Code Assist 사이트를  방문하여 Use in Visual Studio Code 버튼을 클릭한댜.


https://codeassist.google/#available-in-your-favorite-ides-and-platforms

Use in Visual Studio Code 버튼을 클릭하면 마이크로소프트 Visual Studio Maketplace 의 Gemini Code Assist 설치 사이트로 이동하게 된다. 


직접 Visual Studio Code 마켓플레이스 확장을 통하여 설치할 수도 있는데 gemini 로 검색하여 설치하면 된다. 


설치가 완료되면 먼저 가지고 있는 구글 계정으로 로그인을 하고 챗봇 형태로 사용할 수 있다. 


코드 파일에서 Gemini Code Assist 프롬프트

Control+I(Windows 및 Linux) 또는 Command+I(macOS)를 눌러 Gemini Code Assist 를 실행할 수 있다.


채팅을 사용하여 선택한 코드로 Gemini Code Assist에 프롬프트 입력

코드 블록을 선택하고 Gemini Code Assist 창에서 코드에 대한 프롬프트를 입력한다.
 


참고

2024년 12월 19일

코딩 - ChatGPT 을 이용한 맥 프로그램 만들기(네임나이저)

 ChatGPT 의 코딩 실력을 확인하는 목적과 Shift 코딩 연습을 목적으로 맥용 프로그램을 만들어 보았다. 

◼︎ 개발 환경
  • HW : MacBook Pro (14-inch, 2021)
  • CPU : Apple M1 Pro
  • MENORY : 16GB
  • DISK : 512 GB SSD
  • OS : macOS 15.0.1 (24A348)
  • TOOLS : XCode 16.1
  • Programming Language : Shift

◼︎ 프로그램 요구사항
맥은 UTF-8 NFD (Normalization Form Decomposed)로 저장하는 반면, 윈도우는 UTF-8 NFC (Normalization Form Composed)를 사용 하는 차이로 인하여 맥에서 생성된 파일이 윈도우로 이동하면 자모가 분리되어 보여지는 현상이다. 이를 해결하기 위하여 아래와 같은 목표를 정의했다.
  • Drag&Drop 기능을 활용
  • 자소가 분리된(NFD) 유니코드 파일명을 자소가 조합된(NFC) 파일명으로 변환

참고로 ChatGPT 는 유료버전의 4o을 사용했고 개발 과정에서 모양과 기능은 Contact 과 반디네이머 참고했다.


어떻게 프로그램을 만들 것인가

🆀 맥에서 생성하거나 수정한 파일의 이름이 윈도우에서 깨지는 이슈가 있는데 자동으로 변환을 해주는 맥용 앱을 만들고 싶어.

🅰 


전체적으로 어떻게 만들 것인가를 자세하게 설명해주고 있다. 추가로 좀더 구체적인 질문을 하며 상세하게 원하는 것을 설명해준다.

🆀 앱을 실행하면 파일 탐색기 처럼 좌측에는 폴더 우측에서 세부 파일 구조와 NFC, NFD 을 보여주고 선택된 파일 또는 폴더에 대하여 일괄적으로 NFD 형식의 유니코드 파일명을 NFC 변경하거나 반대로 변경하는 기능으로 구현하면

🅰


프로젝트 생성

제시된 코드를 기반으로 "Namenizer" 라는 이름으로 XCode 프로젝트를 생성하였다.
다음으로는 컴파일을 통하여 코드를 실행하면서 지속적으로 코드 수정을 요청하고 이를 반복하였다.


"Namenizer" 프로그램 코딩

코딩 과정의 최초의 문제는 모든 파일들을 보여주지 못한다는 점이었다.  download 등 주요한 폴더들은 표시되지 않았다.

🆀 downloads 와 같은 폴더는 접근할 수 없는데

🅰

해결방법은 크게 ❶ Sandbox 설정을 통하여 보안을 무력화 하거나 ❷ NSOpenPanel을 사용하여 사용자에게 권한 요청하는 방식을 알려주었는데 두번쨰 방법을 사용하는 것을 선택하고  제시되는 코드를 사용하여 코드를 수정했다.

두번째 문제는 반디네이머와 같이 NFD 유니코드로 인하여 윈도우에서 한글 자모가 분리되는 것을 미리 보여주는 기능을 구현하는 것이었다.

🆀 NFD 여부를 알려면 

🅰 


제안된 방법으로 코딩을 해보았지만 자모가 분리되어 보이지 않았다. 지루한 주고받는 방식의 질의를 통하여 알게된 유니코드 디버딩 과정에서 보여지는 텍스트와 유니코드 값의 차이를 확하게 되었다. (동일하게 보이는 텍스트가 유니코드 값이 다르게 보여짐. 보통 NFD 의 경우 유니코드 값이 더 많았다.) "그럼 유니코드 값의 동일 유무를 검사하면 되겠네" 가정을 수립하고 확인을 위한 질문을 계속한다.


🆀 항상 NFC 로만 보여지는 이유는

🅰 

NFC 여부를 판단하는 방법으로 제시된 다음 코드를 기반으로 추가 질의를 하여 이 문제를 좀더 깊이 확인 해보았다.

    

    func isNFC(_ string: String) -> Bool {

        return string == string.precomposedStringWithCanonicalMapping

    }


🆀 func isNFC(_ string: String) -> Bool { return string == string.precomposedStringWithCanonicalMapping } 코드가 항상 true 을 리턴하는 것 은

🅰 

이러한 내용을 바탕으로 유니코드 값을 활용하여 NFD 정규화 상태인지 확인하는 함수는 앞서 제시된 유니코드 디버깅을 활용하여 직접 문제를 아래와 같이 해결했다.


    /// 문자열이 NFD로 정규화된 상태인지 확인하는 함수

    public static func isNFDUsingUnicodeScalars(_ string: String) -> Bool {

        // NFD로 변환된 문자열

        let nfdString = string.decomposedStringWithCanonicalMapping

        

        // 원본 문자열과 NFD로 변환된 문자열의 유니코드 스칼라를 비교

        return string.unicodeScalars.elementsEqual(nfdString.unicodeScalars)

    }


이 과정에서 알게된 건 "macOS 파일 시스템에서 파일 이름을 읽어올 때는 NFD로 변환된 상태로 제공할 수 있다"라는 가능성이 있다는 것이었다. 

🆀 자모가 분리되는 것을 보여주는 것은 다음 질문에 대한 답으로 해결할 수 있었다.
originalString.decomposedStringWithCanonicalMapping 해도 자모가 분리되어 보이지 않는다면

🅰 
마지막 가장 큰 문제는 NFC 로 파일 이름을 변경하는 것이었다. 제시된 코드는 정상동작 하지 않아 관련하여 질문을 해보았다.

🆀 NFC 유니코드를 출력하면 다른 값으로 보이는데  파일이름을 NFC 로 저장하고 다시 읽으면 변경되어 있지 않는데

🅰 
결론은 macOS 파일 시스템(APFS, HFS+)은 파일 이름을 항상 NFD로 저장하기 때문에 정상적인 방법으로는 불가하다고 판단하였다.

🆀 커멘드를 실행하여 처리하면
🆀 별도의 설치없이 사용하는 커멘드는 없는가
🆀 Swift 코드에서 커멘드를 실행하도록 만들면 

Perl 명령을 사용한 파일 이름 변경은 실패하였다. 아래는 GPT 가 제시한 방법이다.

func convertFileNameToNFCUsingPerl(_ url: URL) {

    let originalPath = url.path

    let directory = url.deletingLastPathComponent().path

    let fileName = url.lastPathComponent


    let perlCommand = """

    /usr/bin/perl -CIO -MUnicode::Normalize -e 'rename shift, shift' "\(originalPath)" "\(directory)/$(/usr/bin/perl -CIO -MUnicode::Normalize -e 'print NFC(shift)' "\(fileName)")"

    """


    let process = Process()

    process.executableURL = URL(fileURLWithPath: "/bin/zsh") // 또는 "/bin/bash"

    process.arguments = ["-c", perlCommand]


    let pipe = Pipe()

    process.standardOutput = pipe

    process.standardError = pipe


    do {

        try process.run()

        process.waitUntilExit()


        let data = pipe.fileHandleForReading.readDataToEndOfFile()

        if let output = String(data: data, encoding: .utf8) {

            print(output)

        }


        if process.terminationStatus == 0 {

            print("파일 이름이 NFC로 변환되었습니다.")

        } else {

            print("perl 명령어 실행 실패")

        }

    } catch {

        print("파일 이름 변환 실패: \(error.localizedDescription)")

    }

}


결국 Contact 코드를 분석하여 문제를 해결해보기로 했다.

🆀 https://github.com/namhokim/cocoa_app 에서는 위 문제를 어떻게 해결하고 있지
🆀 https://github.com/namhokim/cocoa_app 를 참고하여 convertFileNameToNFC 함수를 만들면

문제 해결이 되지 않았고 Contact 프로젝트 소스를 확인해보니 perl 정규식이 아닌 단순 mv 명령으로 이름을 변경하는 것으로 확인하여 다시 질문을 했다.

🆀 https://github.com/namhokim/cocoa_app 는 mv 을 사용하여 처리하지 않았나

🅰 

Perl 을 정규식을 사용하는 점을 제외하면 동일한 방식으로 코드를 제안했지만 문제가 해결되지 않았다. 결국 Contact 의 변환소스에 해당하는 소스를 분석하게 하고 동일하게 코드를 만들라고 하여 문제를 해결 할수 있었다.
 
🅰 

mv 스크립트를 생성하여 실행하는 방식은 정상 동작하고 perl 스크립트 방식에서는 잘못된 문자들로 저장되는 원인을 물어보았으나 이부분에 대한 명확한 답변을 듣지 못했다. 다만 아래와 같은  mv 가 좀더 신뢰성 있게 동작한다는 답변을 얻었다.



아래는 마직막으로 작성된 코드를 실행하는 화면이다.