본문 바로가기
개발/안드로이드

안드로이드 Annotation을 이용한 코드 생성(1)

by 창이2 2021. 8. 2.

안녕하세요.

이번 포스팅에서는 Annotation을 이용한 코드 생성하는 방법을 알려드리겠습니다.

이전에 어노테이션과 관련된 포스팅을 한적이 있었는데 이것에 대한 연장선이라고 생각하시면 되겠습니다.

https://dog-footprint.tistory.com/43

 

자바 어노테이션(2) - 리플렉션

안녕하세요. 이번 포스팅에서는 어노테이션이 리플렉션을 통해 어떻게 사용될 수 있는지 적어보려고 합니다. 리플렉션이란 클래스의 정보를 알아내는 것인데 런타임에 동적으로 클래스 정보를

dog-footprint.tistory.com

 

일단 안드로이드에서 코드 생성을 하기 위한 기본적인 구조로는

processor 모듈과 annotation 모듈을 생성해야 합니다.

processor 모듈은 annotation에 대한 코드를 생성하는 부분이며 

annotation모듈은 annotation을 정의하는 부분입니다.

 

 

이렇게 프로젝트를 만들으셧으면 이제 gradle 설정을 해야합니다.

 

앱수준에서의 gradle

더보기
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'kotlin-android-extensions'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.changgyu.codegeneration"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':my-annotation')
    kapt project(':my-processor')
}

 

processor 모듈의 gradle

더보기
plugins {
    id 'kotlin'
    id 'kotlin-kapt'
}
compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation project(':my-annotation')
    implementation 'com.squareup:kotlinpoet:1.7.1'
    implementation("com.squareup:kotlinpoet-metadata:1.7.1")
    implementation("com.squareup:kotlinpoet-metadata-specs:1.7.1")
    implementation "com.google.auto.service:auto-service:1.0"
    kapt "com.google.auto.service:auto-service:1.0"
}

 

annotation 모듈의 gradle

더보기
plugins {
    id 'kotlin'
}
compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

 

여기서 중요한 부분은 이 두 라이브러리입니다.

implementation 'com.squareup:kotlinpoet:1.7.1' 
implementation "com.google.auto.service:auto-service:1.0"

 

kotlinpoet은 스퀘어에서 만든  code generator이고 auto-service 는 annotation 프로세서를 

서비스로더에 등록하기 위한 라이브러리입니다.

auto-service를 사용한다면 사용하는 Processor에 아래의 어노테이션을 붙이고

@AutoService(Processor::class)

 

auto-service를 사용하지 않고 수동으로 등록하려면

 

위와 같이 resources 폴더 밑에 META-INF를 만들고 그 밑에 services 폴더를 만들고

javax.annotation.processing.Processor 파일을 생성합니다. 

파일을 클릭한 후 아래처럼 프로세서가 놓인 패키지 위치를 적습니다.

com.changgyu.my_processor.MyProcessor

 

 

이제 어노테이션을 활용한 코드생성을 위해 2개의 파일을 만들겠습니다.

my-annotation 모듈에 가서 MyAnnotation이라는 annotation 클래스를 만듭니다.

package com.changgyu.my_annotation

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class MyAnnotation(
    val name: String,
    val age: Int,
    val job: String
)

 

그리고 my-annotation 모듈에 가서 기본적인 형태의 MyProcessor를 작성합니다.

@KotlinPoetMetadataPreview
@AutoService(Processor::class)
@SupportedAnnotationTypes("com.changgyu.my_annotation.MyAnnotation")
class MyProcessor: AbstractProcessor() {

    lateinit var testFileBuilder: FileSpec
   
    override fun process(
        annotations: MutableSet<out TypeElement>?,
        roundEnv: RoundEnvironment
    ): Boolean {

        return true
    }
}

 

이후 app 폴더로 가서 MyInfoTest라는 MyAnnotation이 붙은 클래스를 작성해 줍니다.

@MyAnnotation(name = "changgyu", age = 30, job = "programmer")
class MyInfoTest {
    var test = ""
}

 

 

다음 포스팅에서는 이 프로세스를 code generation 할수 있게 구체화하는 내용을 작성해보겠습니다.

감사합니다.

댓글