본문 바로가기

나의 플랫폼/안드로이드

[Annotation] 내가 생각하는 Annotation

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

요즈음 많이 사용되고 있는 Retrofit2 라이브러리나 Dagger 라이브러리를 보시면,

Annotation을 이용하고 있는 것을 확인 할 수 있습니다.


Retrofit은 Method에 @Get이나 @Post Annotation을 사용 하고,

Dagger 같은 경우 Class에 @Module, Method에 @Provides 라는 Annotation을 사용 합니다.


그럼 어떻게 Annotation만 설정 해놨는데 자동적으로 제어가 가능 한 걸까??


https://medium.com/@ggikko/java-%EC%BB%A4%EC%8A%A4%ED%85%80-annotation-436253f395ad#.s04jpt1cw

이 블로그를 보고 전 느낌을 받았습니다.


1. 클래스, 메소드, 매개변수등에 할당되는 특정 Annotation을 만든다.

2. 특정 Annotation이 설정되어 있는지 체크 한다.

3. 특정 Annotation이 설정 되어 있으면 그에 맞는 행동을 진행 한다.


즉, Retrofit2 예제를 하나 보면


interface GithubApiService {

@GET("/search/repositories")
fun repoSearch(@Query("q") query: String,
@Query("sort") sort: String,
@Query("order") order: String): Single<SearchResponse>
}

## Kotlin 소스 입니다.


/** Make a GET request. */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
/**
* A relative or absolute path, or full URL of the endpoint. This value is optional if the first
* parameter of the method is annotated with {@link Url @Url}.
* <p>
* See {@linkplain retrofit2.Retrofit.Builder#baseUrl(HttpUrl) base URL} for details of how
* this is resolved against a base URL to create the full endpoint URL.
*/
String value() default "";
}

## retrofit2 @GET Annotation 소스 입니다.


먼저 Retrofit2 @GET Annotation에서 알 수 있는 내용은 아래와 같습니다.


1. @GET Annotation은 메소드에서 사용 하겠다.

2. RUNTIME 시, VM에 해당 내용을 할당 하겠다. 

3. @GET Annotation은 String 값의 매개변수로 가지겠다.


@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {
/** The query parameter name. */
String value();

/**
* Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded.
*/
boolean encoded() default false;
}


## retrofit2 @Query Annotation 소스 입니다.


@Query Annotation을 보시면 알 수 있는 내용도 아래와 같습니다.


1. @Query Annotation은 매개변수에서 사용 하겠다.

2. RUNTIME 시, VM에 해당 내용을 할당 하겠다. 

3. @Query Annotation은 String 값의 매개변수로 가지겠다.

4. @Query Annotation은 encoded 라는 매개변수를 통해 Boolean 값을 정의 하겠다.


private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
if (!Void.class.equals(responseType)) {
throw methodError("HEAD method must use Void as response type.");
}
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError("@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError("Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError("Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}


## retrofit2에 있는 ServiceMethod 클래스에서 Annotation을 Parsing하는 함수 입니다.


여기서 @GET Annotation의 value 값을 받는 거죠.


이 value 값을 이용해서 라이브러리 내부에서 Request를 요청 하는 겁니다.


다시 GitApiService 소스를 보시면


interface GithubApiService {

@GET("/search/repositories")
fun repoSearch(@Query("q") query: String,
@Query("sort") sort: String,
@Query("order") order: String): Single<SearchResponse>
}

## Kotlin 소스 입니다.


좀 이해가 가시죠^^

@GET Annotation의 value로 '/search/repositories'를 받아서 baseURL에 합쳐 지는 거죠. ㅎ

이렇게 해서 특정 메소드에 @GET이 붙어 있으면 Request를 진행 할 수 있는 것이죠.



이렇게 Annotation으로 정의해 놓은 것만으로도 제어가 가능 하고,

소스 에서도 Annotation만 보고도 이 함수가 무엇이구나 어떻게 쓰이겠구나

를 판단 할 수 있는 것이죠.


전 이전엔 Anntation을 머 @Deprecated 나 @TargetApi 나 정의만 할 줄 알았지,

자세한 내용은 신경 쓰지 않았습니다.


이해 하고 나니 정말 좋은 시스템이네요^^


모두들 참고 하세요.