IntelliJ Platform Plugins Qodana

Qodana 확장하기: 맞춤형 코드 검사 추가

Read this post in other languages:

Qodana는 JetBrains IDE의 코드 검사와 빠른 수정 기능을 지속적 통합 환경으로 확장하는 정적 코드 분석 도구입니다. 클라우드에서 실행하거나, Docker 컨테이너에서 실행하거나, CI/CD 파이프라인에 통합하거나, JetBrains IDE를 통해 호출할 수도 있습니다.

Qodana에는 이미 뛰어난 검사 모음이 있지만 기본 제공되는 검사 외에도 프로젝트별 특성과 규칙을 적용하기 위한 맞춤형 검사를 추가할 수 있습니다.

예를 들어, 특정 코드 규칙이 있는 프로젝트를 생각해 보겠습니다.

service 패키지의 각 Kotlin 클래스에는 Service 접미사가 있어야 합니다.

이 경우, com.jetbrains.service.JetComponent는 이 규칙에 맞지 않지만 com.jetbrains.service.BrainComponentService는 완전히 부합합니다. 이어서 이 검사를 구현하는 플러그인을 만들어 Qodana가 향후 프로젝트에서 이 규칙을 적용할 수 있도록 하겠습니다.

이 코드 규칙은 플러그인에 포함된 맞춤형 코드 검사를 만들어 구현할 수 있습니다. Qodana 플러그인은 JetBrains IDE 플러그인과 동일한 방식으로 개발됩니다. 즉, Qodana에서 실행할 수 있는 IntelliJ Platform 플러그인을 만들기만 하면 됩니다. 다음은 여기서 진행할 단계에 대한 간단한 개요입니다.

  1. IntelliJ Platform Plugin Template에서 프로젝트를 초기화합니다.
  2. 필요한 종속성과 함께 프로젝트 프로퍼티와 플러그인 설명자를 조정합니다.
  3. 플러그인 설명자에 로컬 검사를 선언하고 Kotlin에서 이를 구현합니다.
  4. 플러그인을 빌드하고 패키지화합니다.
  5. 예시 플레이그라운드 프로젝트에서 플러그인 아티팩트를 적절한 디렉터리에 배치합니다.
  6. Qodana 구성 파일을 조정합니다.
  7. Qodana를 실행하고 보고서를 확인합니다!

플러그인 프로젝트 준비

프로젝트를 시작하려면 IntelliJ Platform Plugin Template 저장소로 이동하여 Use this template(이 템플릿 사용) 버튼을 클릭해 플러그인 저장소를 생성합니다. 이름을 classname-inspection-qodana-plugin으로 지정하고 프로젝트 URL을 복사한 다음 IntelliJ IDEA에서 엽니다. 프로젝트 준비가 완료되면 필요에 따라 pluginGroup, pluginNamepluginRepositoryUrl을 선언하여 gradle.properties를 사용자 지정합니다. 변경 사항을 적용하려면 Sync Gradle Changes(Gradle 변경 사항 동기화) 플로팅 버튼을 클릭하는 것을 잊지 마세요. 고유한 플러그인 식별자를 수정하려면 플러그인 설명자 plugin.xmlid 요소를 변경합니다.

종속성 선언

이 코드 검사는 Kotlin 클래스를 대상으로 하므로 Qodana 플러그인의 종속성에 Kotlin 플러그인을 추가해야 합니다. gradle.properties 파일에는 다음을 선언해야 합니다.

platformBundledPlugins = org.jetbrains.kotlin

또한 플러그인 설명자 plugin.xml의 종속성에도 동일한 번들 Kotlin 플러그인이 포함되어야 합니다.

org.jetbrains.kotlin

이번에도 플로팅 버튼을 클릭하여 Gradle 변경 사항을 동기화하세요.

또한 Kotlin 클래스 검사는 IntelliJ Platform 2025.1 버전부터 기본적으로 활성화된 Kotlin K2 컴파일러를 지원해야 합니다. 플러그인 설명자에서 org.jetbrains.kotlin.supportsKotlinPluginMode 확장을 선언합니다.

    

코드 검사 만들기

Kotlin 클래스를 대상으로 하는 모든 코드 검사의 실제 코드에는 세 단계가 필요합니다.

  1. 필요한 속성과 구현 클래스에 대한 완전히 정규화된 참조를 포함하여 플러그인 설명자에서 com.intellij.localInspection 확장을 선언합니다.
  2. 가능하면 Kotlin에서 구현 클래스를 만듭니다.
  3. 검사 설명, 사용 가이드라인, 예시를 포함한 독립형 HTML 파일을 제공합니다.

확장 선언

다음 선언을 plugin.xml 플러그인 설명자 파일에 추가합니다.

    

language 속성은 해당 검사가 Kotlin 소스 코드 파일에 적용됨을 나타냅니다. 검사를 기본적으로 활성화하도록 명시해야 합니다. 그러지 않으면 Qodana가 해당 검사를 실행하지 않습니다. 그런 다음, 보고서와 설정에 표시될 사람이 읽기 쉬운 설명용 displayName을 제공합니다. groupName 속성은 Qodana 보고서와 IDE 설정 모두에 표시되는 검사 카테고리를 설정합니다. 마지막으로, 구현 클래스의 완전히 정규화된 이름을 제공합니다.

코드 검사 소스 코딩

Kotlin 플러그인은 Kotlin 검사 기능을 위한 유용한 기본 검사 클래스인 AbstractKotlinInspection을 제공합니다. buildVisitor 메서드를 재정의하고 Kotlin 클래스 요소를 타입에 안전한 방식으로 순회하는 PSI 방문자 인스턴스를 제공합니다. classVisitor는 이러한 종류의 PSI 방문자를 반환하는 편리한 DSL 형태의 함수이며, 검사 중인 프로젝트의 모든 Kotlin 클래스에서 호출됩니다.

package org.intellij.sdk.codeInspection

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.classVisitor

class ServicePackageClassNameInspection : AbstractKotlinInspection() {
    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = classVisitor { klass ->
        val classNamePsi = klass.nameIdentifier ?: return@classVisitor
        val classFqn = klass.fqName?.asString() ?: return@classVisitor
        if (klass.packageLastComponent == "service" && !classFqn.endsWith("Service")) {
            holder.registerProblem(
                classNamePsi,
                "Class name in the 'service' package must have a 'Service' suffix"
            )
        }
    }

    private val KtClass.packageLastComponent: String
        get() = containingKtFile.packageFqName.shortName().asString()
}

방문자 하위 클래스는 완전히 정규화된 Kotlin 클래스 이름을 추출하고 가장 오른쪽 패키지 요소를 검사한 다음 해당 접미사를 확인합니다. 잘못된 클래스 이름은 PSI 요소인 감싸는 클래스와 사람이 읽을 수 있는 문제 설명과 함께 ProblemsHolder 인스턴스에 보고됩니다.

검사 설명

각 로컬 검사는 HTML로 작성된 별도의 설명 파일이 필요합니다. Create description file ServicePackageClassNameInspection.html(설명 파일 ServicePackageClassNameInspection.html 생성) 빠른 수정을 사용하면 src/main/resources/inspectionDescriptions/ServicePackageClassName.html이라는 파일이 올바른 위치에 생성됩니다. Qodana 보고서와 IDE 설정에 표시될 설명도 함께 제공해야 합니다.

Reports class names in the service packages that lack the Service suffix.

Example:

  package com.example.foo.service
  class SomeComponent {
    /* class members */
  }

플러그인 빌드

준비는 모두 끝났고 이제 빌드할 차례입니다! buildPlugin Gradle 작업을 실행하고 Gradle 출력 디렉터리에 생성된 build/distributions/qodana-code-inspection-0.0.1.zip 아티팩트를 확인하세요. JAR 파일은 Qodana 검사에서 기본 아티팩트로 사용됩니다. 

플러그인 아티팩트 유형 염두에 두기

Qodana는 추가 JAR 아카이브나 타사 종속성을 포함한 로컬 ZIP 플러그인 아티팩트를 직접 지원하지 않습니다. 모든 플러그인은 단일 JAR로 패키지화하거나 특정 디렉터리에 압축 해제한 형태여야 합니다.

플레이그라운드 프로젝트에서 Qodana 실행

이제 플러그인으로 확장했으니 Qodana로 검사할 수 있도록 Kotlin으로 작성된 플레이그라운드 프로젝트를 만들어보겠습니다. Qodana 플러그인을 로컬에서 실행하려면 시스템에 두 가지 소프트웨어 구성 요소가 준비되어 있어야 합니다.

플레이그라운드 프로젝트에는 Service 접미사가 없어서 코드 규칙을 따르지 않는 src/main/kotlin/org/intellij/sdk/qodana/service/SomeComponent.kt라는 클래스가 포함되어 있어야 합니다. Qodana 플러그인을 Qodana에 통합하는 방법에는 두 가지가 있습니다.

  • JetBrains Marketplace에 게시
  • 더 빠르게 적용하려면 플러그인의 JAR 아티팩트를 프로젝트의 .qodana 디렉터리에 배치

빌드를 단순화하려면 플러그인 프로젝트의 build/distributions/qodana-code-inspection-0.0.1.zip 파일을 플레이그라운드 프로젝트의 .qodana/qodana-code-inspection-0.0.1.zip 파일로 복사합니다. 아직 없는 경우 .qodana 디렉터리를 만듭니다. 그런 다음 원하는 프로그램이나 도구를 사용하여 아카이브의 압축을 풉니다. Qodana는 build/distributions/qodana-code-inspection 디렉터리에 있는 플러그인에 액세스할 수 있습니다.

또한, 사용자 지정 코드 검사를 포함하도록 Qodana를 구성해야 합니다. 플레이그라운드 프로젝트의 루트 디렉터리에 있는 qodana.yaml 파일을 다음과 같이 변경합니다.

version: "1.0"
linter: qodana-jvm-community
include:
  - name: org.intellij.sdk.codeInspection.ServicePackageClassNameInspection

include 블록은 플러그인에 포함된 코드 검사의 완전히 정규화된 클래스 이름을 참조해야 합니다. 이제 터미널에서 다음 명령어를 실행하여 Qodana에 작업 지시를 내립니다.

qodana scan --volume $PWD/.qodana/qodana-code-inspection:/opt/idea/custom-plugins/qodana-code-inspection

그러면 해당 Qodana Docker 이미지가 다운로드됩니다. Qodana 구성에 따라 Docker 컨테이너가 생성되고 실행됩니다. 사용자 지정 플러그인을 Qodana 실행 환경에서 사용할 수 있도록 하려면 로컬 파일 시스템의 Qodana 플러그인 디렉터리를 Qodana Docker 컨테이너 내부의 해당 디렉터리에 마운트합니다. 몇 분 후 Qodana가 보고서 요약을 생성하고 이를 표준 출력에 표시합니다.

Qodana - Detailed summary                                                                                                                                                                                                                
Analysis results: 1 problem detected                                                                                                                                                                                                     
By severity: High - 1                                                                                                                                                                                                                    
-------------------------------------------------------                                                                                                                              
Name                           Severity  Problems count                                                                                                                                                             
-------------------------------------------------------                                                                                                                              
SDK: Discouraged class name    High      1                                                                                                                                                             
-------------------------------------------------------

브라우저에서 전체 보고서를 엽니다.

  Do you want to open the latest report [Y/n]Yes

!  Press Ctrl+C to stop serving the report

  Showing Qodana report from http://localhost:8080/... (10s)

Qodana는 SomeComponent가 Qodana 플러그인의 로컬 검사에서 정의한 코드 규칙을 따르지 않는다는 것을 표시합니다.

향후 실행을 위한 팁

Qodana 플러그인이 수정되고 다시 빌드되면 Qodana 캐시도 함께 다시 생성해야 합니다. 이러한 경우 Qodana 실행의 모든 종속성을 다시 로드하려면 --clear-cache CLI 스위치를 사용하세요.

qodana scan --clear-cache --volume $PWD/.qodana/qodana-code-inspection-0.0.1.jar:/opt/idea/custom-plugins/codeinspection.jar

IDE에 Qodana 통합

Qodana 플러그인은 IDE에 디스크에서 설치 방식으로 설치할 수 있습니다. 그러면 해당 코드 검사가 자동으로 활성화되어 프로젝트의 모든 Kotlin 클래스에 대해 호출됩니다. 정상적으로 작동하는지 확인하려면 플레이그라운드 프로젝트로 돌아가 org.intellij.sdk.qodana.service.SomeComponent 클래스를 열고 문제가 있는 클래스 이름에 밑줄이 표시되는지 확인하세요. 간편하게 클래스 이름 위에 마우스를 올리면 문제 설명과 함께 코드 검사 결과가 표시됩니다. 또는 Problems 도구 창을 열어 코드 검사에서 보고된 모든 문제 목록 중 해당 문제를 확인할 수 있습니다. 

이제 코드 검사가 IDE에서 제공하는 다른 검사와 동일하게 동작합니다. Settings(설정) | Editor(에디터) | Inspections(검사) | Kotlin에서 이전에 제공한 HTML 파일의 설명과 함께 SDK: Discouraged class name(SDK: 권장되지 않는 클래스 이름) 검사를 확인할 수 있습니다.

IDE 내에서 Qodana 실행

플러그인이 설치되면 이제 IDE에서도 Qodana를 실행할 수 있습니다. Problems(문제) 도구 창에서 Qodana 탭으로 이동한 다음, Try Locally(로컬에서 실행) 버튼을 클릭합니다. Qodana는 qodana.yaml 파일을 기반으로 구성된 후 실행됩니다. Qodana 보고서는 도구 창에서 바로 확인할 수 있습니다. 

Qodana 플러그인과 JetBrains Marketplace

올바르게 빌드하고 테스트를 마친 사용자 지정 검사 플러그인은 JetBrains Marketplace에 게시할 수 있으며 이렇게 하면 .qodana 디렉터리에서 제공할 필요가 없어집니다. Qodana 구성에서 플러그인 설명자의 id 요소와 일치하는 공개 플러그인 식별자를 지정하기만 하면 됩니다.

version: "1.0"
linter: qodana-jvm-community
plugins: 
  - id: com.github.novotnyr.qodanacodeinspection

qodana 디렉터리를 마운트할 필요가 없어지므로 Qodana의 .scan 명령어가 단순해집니다.

qodana scan

Qodana는 JetBrains Marketplace에서 이 플러그인을 다운로드하고 모든 검사를 실행하여 콘솔 출력과 웹 브라우저에서 확인할 수 있는 HTML 보고서를 생성합니다.

요약

특정 코드 규칙을 검사하는 코드 검사가 포함된 Qodana 플러그인을 만들었으며 이를 실행하는 방법에는 다음과 같이 여러 가지가 있습니다.

  • .qodana 디렉터리에 JAR로 배치하고 Qodana YAML 파일에 포함하는 방법
  • JetBrains Marketplace에 공개된 플러그인을 참조하는 방법
  • IDE에 JAR로 설치하여 로컬 Qodana 실행에서 검사가 적용되도록 하는 방법
  • IDE에 JAR로 설치하여 통합된 코드 검사에 검사가 포함되도록 하는 방법 

Qodana 플러그인과 플레이그라운드 프로젝트의 간단한 예시는 IntelliJ SDK 코드 샘플을 참고하세요.

게시물 원문 작성자

Róbert Novotný

Róbert Novotný

Róbert Novotný is a Developer Advocate, specializing in the IntelliJ Platform and plugin development.

image description