느린 것을 걱정하지 말고, 멈춰서는 것을 걱정하라
article thumbnail

 자바에서 어노테이션이라는 기술이 스프링 프레임워크상에서 자주 쓰이지만 이 기술에 대해 자세히 알아보지도 않고 IT에 몸담고 있는 5년간 대충 아는정도에 밖에 그치지 못하고 있다. 이러한 어노테이션에 대해 제대로 공부해 보려고 하는 이유는 스프링 프레임워크와 관련된 스프링 부트2라는 서적을 공부하면서 어노테이션이 정말 많이 나오는데 이에 대한 동작방식을 전혀 이해하지 못하고 있다는 생각이 들어서이다.

 

 어노테이션은 사전적 의미로는 주석이라는 뜻이다. 자바에서 사용될 때의 어노테이션은 코드 사이에 주석처럼 쓰여서 특별한 의미, 기능을 수행하도록 하는 기술이다. 즉, 프로그램에게 추가적인 정보를 제공해주는 메타데이터(meta data: 데이터를 위한 데이터)라고 볼 수 있다.

 

 

 다음은 어노테이션의 용도를 나타낸 것이다.

  1. 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공

  2. 소프트웨어 개발툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보 제공

  3. 실행시(런타임시)특정 기능을 실행하도록 정보를 제공

 

기본적으로 어노테이션을 사용하는 순서는 다음과 같다.

  1. 어노테이션의 정의

  2. 클래스에 어노테이션을 배치

  3. 코드가 실행되는 중에 Reflection을 이용하여 추가정보를 획득하여 기능실시

 

 

1. 어노테이션의 정의

어노테이션을 적용할 때는 어노테이션이 어디에 적용되며 언제까지 어노테이션 소스가 유지될 것인지를 설정하여야 하는데 소스코드에는 다음과 같이 어노테이션을 정의해 주면 된다.

@Target({ElementType.[적용대상]})
@Retention(RetentionPolicy.[정보유지되는 대상])
public @interface [어노테이션명]{
	public 타입 elementName() [default 값]
    ...
}

 

@Target에는 어떠한 값(ex : 클래스, 필드, 메서드 ...)에 어노테이션을 적용할 것이지 나타낼 수 있는데 넣을 수 있는 값은 다음 표와 같다.

ElementType 열거 상수

적용대상

TYPE

클래스, 인터페이스, 열거 타입

ANNOTATION_TYPE

어노테이션

FIELD

필드

CONSTRUCTOR

생성자

METHOD

메소드

LOCAL_VARIABLE

로컬 변수

PACKAGE

패키지

 

 

@Retention에는 어노테이션 값들을 언제까지 유지할 것인지 값을 입력하는데 각 값이 가지는 의미는 다음 표와 같다. 보통 어노테이션은 Runtime시에 많이 사용하므로 대부분의 어노테이션의 Retention 값은 RUNTIME으로 되어있다.

RetentionPolicy 열거 상수

설명

SOURCE

소스상에서만 어노테이션 정보를 유지한다. 소스 코드를 분석할

때만 의미가 있으며, 바이트 코드 파일에는 정보가 남지 않는다.

CLASS

바이트 코드 파일까지 어노테이션 정보를 유지한다. 하지만 리플렉션을

이용해서 어노테이션 정보를 얻을 수는 없다.

RUNTIME

바이트 코드 파일까지 어노테이션 정보를 유지하면서 리플렉션을 이용해서

런타임에 어노테이션 정보를 얻을 수 있다.

 

2. 어노테이션의 배치 및 사용

어노테이션의 사용은 클래스를 참고하는 소스의 흐름상에서 Reflection을 사용하는 방법을 통해서 어노테이션 값을 활용하도록 한다. 말로는 설명이 애매하니 예제 소스를 작성하도록 한다. 

 

작성하려는 예제 소스는 다음과 같다.

 

1. MyAnnotation.java (어노테이션)
2. MyService.java (사용되는 클래스)

3. MyMain.java (MyService를 사용하는 클래스)

 

다음 코드는 MyMain 클래스가 MyService 클래스의 메서드에  MyAnnotation어노테이션 설정이 있는지 확인하고 각 메서드에 설정된 어노테이션에 삽입된 값에 따라 특정값을 일정한 숫자만큼 출력하는 예제이다.

 

[MyAnnotation.java]

package myannotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String value() default "-";
	int number() default 15;
	
}

 

[MyService.java]

package myannotation;

public class MyService {
	@MyAnnotation
	public void method1() {
		System.out.println("실행내용1");
	}

	@MyAnnotation("*")
	public void method2() {
		System.out.println("실행내용2");
	}

	@MyAnnotation(value = "*", number = 20)
	public void method3() {
		System.out.println("실행내용3");
	}
}

 

[MyMain.java]

package myannotation;

import java.lang.reflect.Method;

public class MyMain {
	public static void main(String[] args) {
		Method[] methodList=MyService.class.getMethods();
		
		for(Method m : methodList) {
			if(m.isAnnotationPresent(MyAnnotation.class)) {
				System.out.println(m.getName());
				MyAnnotation annotation=m.getDeclaredAnnotation(MyAnnotation.class);
				
				String value=annotation.value();
				int number=annotation.number();
				for(int i=0;i<number;i++) {
					System.out.print(value);
				}
				System.out.println();				
			}
		}
	}
}

 

결과값

 

 

필드, 생성자, 메소드에 적용된 어노테이션 정보는 위에서 처럼 .class로부터 얻을 수 있다.

  • 클래스.class의 다음 메소드를 이용해서

  • java.lang.reflect 패키지의 Field, Constructor, Method 클래스의 배열을 얻어냄

리턴타입

메소드명(매개변수)

설명

Field[]

getFields()

필드 정보를 Field 배열로 리턴

Constructor[]

getConstructors()

생성자 정보를 Constructor 배열로 리턴

Method[]

getDelclaredMethods()

메소드 정보를 Method 배열로 리턴

 

[어노테이션 정보를 얻기 위한 메소드]

리턴타입

메소드명(매개변수)

boolean

isAnnotationPresent(Class<? Extends Annotation> annotationClass)

지정한 어노테이션이 적용되었는지 여부. Class에서 호출했을 경우 상위 클래스에 적용된 경우에도 true를 리턴한다.

Annotation

getAnnotation(Class<T> annotationClass

지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴한다. Class에서 호출했을 경우 상위

클래스에 적용된 경우에도 어노테이션을 리턴한다.

Annotation[]

getAnnotations()

적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 경우 상위 클래스에 적용된 어노테이션도 모두 포함한다. 적용된

어노테이션이 없을 경우 길이가 0인 배열을 리턴한다.

Annotation[]

getDeclaredAnnotations()

직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 경우 상위 클래스에 적용된 어노테이션은 포함되지 않는다.

 

이상 어노테이션에 대해 알아보았다. 스프링을 사용함에 있어 어노테이션의 정의 및 그에따른 동작은 스프링을 개발한 개발자들이 하였겠지만 이러한 어노테이션의 동작원리를 알고나서 소스코드를 보면 그에대한 이해도가 높아지리라 생각한다.

profile

느린 것을 걱정하지 말고, 멈춰서는 것을 걱정하라

@주현태

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!