[Android][Kotlin] Spotless

D.O·2023년 10월 16일
0

Spotless 도입 결정

저는 Now in Android’라는 오픈소스 프로젝트를 분석 및 참고하여 ‘mineme’ 프로젝트를 작성하였습니다.

Now in Android 프로젝트는 Apache 2.0 라이선스를 사용하고 있습니다.

이 라이선스는 원본 코드를 사용하거나 수정할 때 라이선스를 명시하는 것을 요구하므로, 저의 ‘mineme’ 프로젝트에도 Apache 2.0 라이선스를 명시하려고 합니다.

Apache 2.0 라이선스 전문은 여기에서 확인하세요

Apache 2.0 라이선스를 사용하는 경우, 다음과 같은 절차를 따르는 것이 권장됩니다:

  1. LICENSE 파일을 포함시킵니다.
  2. 각 파일 상단에 라이선스 헤더를 포함시키는 것이 좋습니다.
  3. 소스 코드 내의 변경 사항을 명시합니다.
  4. README 파일에 라이선스와 원본 프로젝트의 출처를 명시합니다

그 중 2번 라이선스 헤더를 모든 파일에 추가하는 것을 수동으로 하면 번거로울 수 있기 때문에 여러 다른 언어와 파일 형식에 대한 코드 서식을 자동으로 처리하는 도구인 spotless를 활용하여 자동 포맷팅을 진행하려고 한다.

Spotless

Spotless는 Gradle 플러그인으로, 코드의 서식이나 스타일 규칙을 정의하고 이를 프로젝트의 코드에 자동으로 적용하는 포맷터의 역할을 합니다.

spotless와 ktlint 차이점

spotless와 ktlint를 비슷하게 생각할 수 있다.

나는 둘 다 이번에 공부하면서 배웠지만 처음에 뭔 차이지? 라는 생각을 좀 했다.

사실 비교하기가 좀 그런게 Spotless는 다양한 코드 포매팅 도구를 통합할 수 있는 포괄적인 포매터인데, 그 중에 KtLint도 포함되어있고 ktLint를 Kotlin 코드 포매팅을 위해 사용할 수 있다고 한다.

이런 포괄적인 포메터인 Spotless는 Kotlin뿐만 아니라 Java, Groovy, XML 등 여러 언어와 파일 형식에 대한 코드 서식을 처리한다.

하나의 프로젝트 내에서 여러 언어를 사용하는 경우, Spotless 하나로 모든 서식 관리를 통일하게 해준다.

Spotless는 Gradle 플러그인으로 제공되기 때문에 Gradle을 사용하는 프로젝트에 쉽게 통합할 수 있다

./gradlew spotlessCheck 와 같은 Spotless 관련 스탭을 포함하면 된다고한다.

결론적으로 여러 장점이 있지만 내가 Spotless를 결정한 이유는 Android 프로젝트 모든 파일에 라이선스 헤더를 추가할건데 Kotlin, KTS, Java, Xml등을 모두에 대해서 간단히 추가하기위해서 Spotless로 결정하였다.

1. Spotless 플러그인 설정

저는 아래와 같이 rootProject (즉, 최상위 프로젝트)에 있는 모든 subprojects (하위 프로젝트들)에 대해 설정을 정의했습니다.

rootProject {
    subprojects {
				// Spotless 플러그인 적용
        apply<com.diffplug.gradle.spotless.SpotlessPlugin>()

				// Spotless 설정
        extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
							
            kotlin {
                target("**/*.kt")
                targetExclude("**/build/**/*.kt")
                ktlint(ktlintVersion).userData(mapOf("android" to "true"))
                licenseHeaderFile(rootProject.file("spotless/copyright.kt"))
            }
            format("kts") {
                target("**/*.kts")
                targetExclude("**/build/**/*.kts")
                licenseHeaderFile(rootProject.file("spotless/copyright.kts"), "(^(?![\\/ ]\\*).*$)")
            }
            format("xml") {
                target("**/*.xml")
                targetExclude("**/build/**/*.xml")
                licenseHeaderFile(rootProject.file("spotless/copyright.xml"), "(<[^!?])")
            }
        }
    }
}

이 설정은 gradle폴더 내의 init.gradle.kts 파일에 위치해 있습니다. init.gradle.kts는 Gradle의 초기화 스크립트입니다.

이 초기화 스크립트는 모든 하위 프로젝트에 Spotless 플러그인을 적용하고, 코드 스타일 검사 및 라이센스 헤더 추가와 같은 일련의 코드 포맷팅 작업을 정의합니다.

공통적으로 kt,kts,xml에 대해서 라이선스 헤더가 추가되도록 했습니다. 추가로 build 디렉토리 내의 파일은 제외하도록 지정하였습니다.

추가적으로 .kt 파일에서는 추가적으로 Android 스타일 가이드를 따르도록 하였습니다.

결론적으로 Spotless 플러그인의 전역 설정을 포함하고 있기 때문에 spotlessCheck 및 spotlessApply 작업을 실행할 때 이 스크립트를 이용해야 합니다.

2. HeaderFile 생성

그리고 위에서 지정해줬던 LincessHeaderFile의 위치에 각 파일 형식에 맞는 헤더파일을 추가해뒀습니다.

3. spotlessApply Gradle Run Configuration 설정 파일 추가

<component name="ProjectRunConfigurationManager">
  <configuration default="false" name="spotlessApply" type="GradleRunConfiguration" factoryName="Gradle">
    <ExternalSystemSettings>
      <option name="executionName" />
      <option name="externalProjectPath" value="$PROJECT_DIR$" />
      <option name="externalSystemIdString" value="GRADLE" />
      <option name="scriptParameters" value="--init-script=gradle/init.gradle.kts" />
      <option name="taskDescriptions">
        <list />
      </option>
      <option name="taskNames">
        <list>
          <option value="spotlessApply" />
        </list>
      </option>
      <option name="vmOptions" />
    </ExternalSystemSettings>
    <ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
    <ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
    <DebugAllEnabled>false</DebugAllEnabled>
    <method v="2" />
  </configuration>
</component>

이전에 정의해두었던 Spotless 자동 포메팅을 실행하기 위해 Run Configuration 설정 파일을 정의 해두었습니다.

이 파일을 생성하고 프로젝트 Sync를 다시 맞춰주면

Configuration이 적용된 것을 볼 수 있습니다.

4. spotless 실행

이미 저는 한번 실행시켰기 때문에 의도적으로 MainActivity에 라이센스 헤더를 삭제하고 의미없는 띄어쓰기와 import를 넣어보겠습니다.

그 다음 SpotlessCheck를 실행합니다.

/gradlew spotlessCheck --init-script gradle/init.gradle.kts

해당 명령어는 Gradle Wrapper를 사용하여 Spotless 플러그인의 spotlessCheck 태스크를 실행합니다

또한, --init-script gradle/init.gradle.kts 옵션을 통해 1번에서 정의해두었던 초기화 스크립트를 사용하도록 지정하였습니다.

실행이 완료되면 SpotlessCheck의 결과가 나옵니다.

코드의 어떤 부분에서 어떠한 패턴이 부적절한지 나옵니다.
이 부분을 직접 수정해도 되지만 SpotlessApply로 자동 포메팅이 가능합니다

Configuration을 이전에 정의해두었던 SpotlessAppy로 변경하고 실행하시면됩니다.

이렇게 완료 Apply 적용이 완료되고 다시 Check를 해보면 이번에는 BUILD SUCCESSFUL이 뜨는 것을 볼 수 있습니다.

저는 초기 SpotlessAppy 실행에 220file에 대해서 포메팅이 적용된 것을 볼 수 있습니다.

이것을 일일이 적용하려면 굉장히 번거롭고 실수하기 쉬운 작업일텐데 Spotless를 통해 한번에 적용했습니다.

spotless를 실행하면 이렇게 정의해두었던 라이선스 헤더파일이 모든 파일에 추가가 되고 기본적인 android Style 가이드로 수정이 되었고 사용하지 않는 import문이나 의미없던 공백이 다 정리가 되었습니다.

4. CI 파이프라인 통합

Continuous Integration 파이프라인에 spotlessCheck 단계를 추가하여, 푸시 또는 PR 시에 코드 스타일이 일치하는지 자동으로 확인하게 하였습니다.
이를 통한 효과로는 여러 개발자가 해당 프로젝트에 참여하더라도 코드베이스에 대해 일관적인 코드 스타일을 강제할 수 있습니다.

아래는 github action으로 Spotless 검사를 CI에서 실시하는 사진입니다

마무리

Spotless를 활용하여 모든 파일에 라이센스 헤더를 추가하고, 코드를 체계적으로 정리하였습니다.

Spotless로 인한 장점으로는 여러가지가 있지만 무엇보다 일관된 코드 스타일을 유지하면 코드의 가독성이 향상되고,팀 내에서의 코드 통일성을 높일 수 있다는 장점이 가장 클 것 같습니다.

팀 내에서 특정한 컨벤션을 Spotless의 규칙으로 설정함으로써, 모든 팀원이 동일한 코드 스타일을 따르게 되므로, 코드 리뷰나 협업 과정에서 발생할 수 있는 불필요한 논란이나 오해를 최소화할 수 있습니다. 또한, 신규 팀원이 프로젝트에 참여할 때, 코드 스타일에 대한 별도의 교육이나 지침을 제공할 필요가 줄어들게 되어, 빠르게 프로젝트에 적응할 수 있게 될 것 같다고 생각이 듭니다.

감사합니다 😻

profile
Android Developer

0개의 댓글