어노테이션은 코드 요소에 메타데이터를 추가하여, 코드의 동작을 설명하거나 처리할 정보를 제공하는 특별한 타입의 인터페이스이다.
1. 어노테이션이란?
어노테이션(Annotation)은 Java에서 코드에 메타데이터를 추가하는 방법으로, 클래스, 메서드, 필드 등 다양한 코드 요소에 추가적인 정보를 제공한다. 어노테이션은 주로 컴파일러나 런타임 환경에서 처리되며, 코드의 동작에 영향을 미치거나, 문서화 및 검사 등을 위한 목적으로 사용된다. 예를 들어,
@Override
, @Deprecated
와 같은 기본 어노테이션 외에도 개발자가 정의한 어노테이션을 통해 동적 처리를 할 수 있다.2. 주요 메타어노테이션
@Retention
- 해당 어노테이션이 어떤 시점까지 유효한지를 지정하는 메타 어노테이션이다. 이 메타 어노테이션은 어노테이션이 소스 코드, 컴파일 타임, 런타임까지 살아있는지 정의한다.
- 종류
RetentionPolicy.SOURCE
: 어노테이션이 컴파일러에서만 존재하고, 컴파일된 바이트코드에는 포함되지 않고 주로 코드 분석 도구나 IDE에서만 사용된다.RetentionPolicy.CLASS
: 기본값으로, 어노테이션이 클래스 파일에 포함되지만, JVM에서 런타임 동안에는 접근할 수 없다.RetentionPolicy.RUNTIME
: 어노테이션이 런타임까지 유지되어, 리플렉션을 통해 런타임에 어노테이션을 읽을 수 있다.
@Target
- 어노테이션이 어디에 적용될 수 있는지를 지정한다. 예를 들어, 클래스, 메서드, 필드 등에 어노테이션을 적용할 수 있다. 이 메타 어노테이션을 사용하여 어노테이션의 적용 대상을 제한할 수 있다.
- 종류
ElementType.TYPE
: 클래스, 인터페이스, 열거형ElementType.FIELD
: 필드(변수)ElementType.METHOD
: 메서드ElementType.PARAMETER
: 매개변수ElementType.CONSTRUCTOR
: 생성자ElementType.LOCAL_VARIABLE
: 지역 변수ElementType.ANNOTATION_TYPE
: 다른 어노테이션ElementType.PACKAGE
: 패키지
@Documented
- 해당 어노테이션이 JavaDocs에 포함되도록 한다. 이 메타 어노테이션을 사용하면, 해당 어노테이션을 적용한 코드가 문서화될 때 JavaDocs에 포함된다.
@Inherited
- 클래스에만 적용되며, 상속된 클래스에 어노테이션을 자동으로 상속하도록 지정한다. 부모 클래스에 적용된 어노테이션이 자식 클래스에서 자동으로 인식될 수 있도록 한다.
@Repeatable
- 해당 어노테이션이 여러 번 적용될 수 있도록 허용하는 메타 어노테이션이다. 컨테이너 어노테이션과 함께 사용되며, 컨테이너 어노테이션은 반복된 어노테이션을 배열로 감싸는 역할을 한다.
3. 예제 코드
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value();
}
public class Router {
UserController userController;
public Router(UserController userController) {
this.userController = userController;
}
public void routing(String path) {
// 1. 메서드 찾아내기
Method[] methods = userController.getClass().getMethods();
// 2. 어노테이션 체크하기
for (Method method : methods) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (requestMapping == null) continue;
// 3. value와 path 일치 확인해서 일치하면 invoke 하기
if (requestMapping.value().equals(path)) {
try {
method.invoke(userController);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("메서드 실행중 오류가 발생했어요.");
}
}
}
}
}
public class UserController {
@RequestMapping("/login")
public void login() {
System.out.println("로그인");
}
@RequestMapping("/join")
public void join() {
System.out.println("회원가입");
}
@RequestMapping(value = "/logout")
public void logout() {
System.out.println("로그아웃");
}
}
public class App {
public static void main(String[] args) {
Router router = new Router(new UserController());
Scanner scanner = new Scanner(System.in);
String path = scanner.nextLine();
router.routing(path);
}
}
4. 정리
RequestMapping
어노테이션 인터페이스@Target(ElementType.METHOD)
를 통해 메서드에만 적용될 수 있도록 정의된다.@Retention(RetentionPolicy.RUNTIME)
을 통해 어노테이션이 런타임에 읽힐 수 있도록 정의된다.String value()
를 경로를 인자로 받아, 해당 경로와 매칭되는 메서드를 지정한다.
Router
클래스UserController
객체를 받아, 경로에 맞는 메서드를 호출하는 역할을 한다.routing()
메서드에서 리플렉션을 사용하여UserController
클래스의 메서드들을methods
배열에 넣는다.getAnnotation()
메서드를 통해RequestMapping
어노테이션을 찾고, 경로(value
)와 일치하는 메서드를 실행한다.
UserController
클래스- 각 메서드에
@RequestMapping
어노테이션을 사용하여 해당 메서드가 처리할 경로를 지정한다.
App
클래스scanner
를 통해 사용자로부터 경로를 입력받고, 입력된 경로에 맞는 메서드를 실행한다.
Share article