이 영역을 누르면 첫 페이지로 이동
Coding Groot 블로그의 첫 페이지로 이동

Coding Groot

페이지 맨 위로 올라가기

Coding Groot

코딩 블로그

[Java] Annotation (feat. Reflection)

  • 2024.02.19 23:58
  • Programming Language/Java
글 작성자: Coding Groot

Annotation?

Annotate는 어떤 문서에 간단한 설명이나 의견을 달아놓는 것을 뜻한다. "주석을 달다"라고 번역할 수 있다. 이런 추가 정보는 어떤 데이터를 설명하기 위한 데이터이며 우리는 것을 메타데이터라고 부른다. 

어노테이션(Annotation)을 사용하는 이유

자바에서 어노테이션은 코드에 메타데이터(코드를 설명하는 추가적인 데이터)를 표현하기 위한 방법 중에 하나이다.

이런 메타데이터를 통해 프로그램은 추가적인 정보를 알 수 있고 그에 따라 여러가지 기능을 지원할 수 있다.
대표적으로 여러 프레임워크에서는 메타데이터와 리플렉션을 통해 런타임에 다양한 기능을 지원해주고는 한다. 우리는 몇 가지 어노테이션으로 의존성 주입, 트랜젝션 관리, 테스트 코드 등을 깔끔하게 구현할 수 있다.

단순히 메타데이터를 제공하는 용도면 성능에 직접적인 영향을 미치지 않지만 런타임에 리플렉션으로 처리될 때는 주의해야 한다. 어노테이션이 런타임에 처리하여 프레임워크는 여러가지 편의 기능을 제공하고는 한다. 하지만 너무 남용하면 알아야 할 정보가 잘보이지 않아서 디버깅할 때 많이 힘들어질 수 있다. 

경험해본 어노테이션의 사용 예시

  • 컴파일 시 정보 제공
    • @Override처럼 컴파일러에게 상위 메서드를 오버라이드 함을 알려서 시그니처 검사를 위한 추가 정보를 제공한다
  • 코드 문서화
    • 같이 개발하는 동료를 위해 가독성을 높이는 용도로 사용한다
  • 테스트 코드
    • 테스트코드 프레임워크에서도 어노테이션을 기반으로 실행과정을 자주 제어하는 것을 볼 수 있다
  • 객체에 대한 추가적인 런타임 작업
    • 주로 프레임워크에서 리플렉션과 결합하여 보일러 플레이트를 제거하기 위해 이러한 것을 제공하는 것을 볼 수 있다.

말고도 데이터 모델링, 보안을 위한 권한 정보 제공 등 여러 가지 사용법이 있다. 주로 다른 객체/사용자에게 기능/정보을 제공하고 싶을 때 사용할 수 있다고 생각한다.

그럼 나만의 어노테이션은 어떻게 만들까?

커스텀 어노테이션 - 나만의 어노테이션을 개발해보자

자바는 나만의 어노테이션을 만들기 위한 문법으로 @interface 키워드를 사용한다. 이 키워드로부터 시작하여 어노테이션을 정의하고 세부적으로 제어할 수 있다.

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation {
    String description() default "나의 작고 소중한 어노테이션!";
}

어노테이션 세부 설정법

1. 어노테이션의 Retention - 언제까지 보유할 것인가?

@Retention - 언제까지 사용할까?

위해서 말했듯이 런타임, 컴파일타임에도 쓰일 수 있다. 이런 정보는 @Retention을 사용하여 어느 시점까지 유지할지 정의할 수 있다.

리플렉션에서 접근할 것이면 RUNTIME으로 정의하자! 그래야지 런타임에 접근할 수 있다.

2. 어노테이션의 Target - 어디에 메타데이터를 달 것인가? 

@Target - 메타데이터를 적용할 대상은? (메서드/클래스/모듈/...)

@Target은 어노테이션을 적용할 수 있는 대상을 지정한다.

나는 일단은 메서드를 대상으로 지정했다.

커스템 어노테이션의 사용

class Example {
    @MyAnnotation(description = "나만의 어노테이션 설명!")
    public static void main(String[] args) {

    }
}

위의 코드처럼 메서드에 대한 메타데이터를 추가할 수 있다.

리플렉션으로 메인메서드에 어노테이션으로 준 메타데이터를 읽어와보자.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String description() default "나의 작고 소중한 어노테이션!";
}

class Example {
    @MyAnnotation(description = "메인 메소드")
    public static void main(String[] args) {
        try {
            Method mainMethod = Example.class.getMethod("main", String[].class);
            if (mainMethod.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = mainMethod.getAnnotation(MyAnnotation.class);
                System.out.println("메타데이터: " + annotation.description());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

출력 결과

리플렉션과 함께 사용한다면 런타임에 강력하면서 위험한 짓을 할 수 있다. 메타데이터를 보고 원래는 못 건들이는 클래스에 관한 동작을 동적으로 변경할 수 있다. 이건 합의가 되지 않으면 누군가의 코드를 깨부수는 행위일 수 있다. 하지만 누군가에게 여러 기능을 쉽게 제공하려다보면 필요할 때도 있다. 

간단하게 어노테이션의 메타데이터를 기반으로 public/private 같은 접근 제어자를 깨부수는 코드이다.

import java.lang.annotation.*;
import java.lang.reflect.Method;

// 실행할 메소드를 지정하기 위한 어노테이션
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface RunMe {}

// 리플렉션을 사용하여 특정 어노테이션이 붙은 메소드만 실행하는 클래스
class ReflectionExample {
    @RunMe
    private void method1() {
        System.out.println("Method 1 실행");
    }

    private void method2() {
        System.out.println("Method 2 실행");
    }

    @RunMe
    private void method3() {
        System.out.println("Method 3 실행");
    }

    // 모든 메소드를 순회하며 @RunMe 어노테이션이 붙은 메서드만 실행하는 메서드
    public static void runAnnotatedMethods(Object obj) {
        Class<?> objClass = obj.getClass();

        for (Method method : objClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(RunMe.class)) {
                try {
                    method.setAccessible(true); // 접근 제한을 무시하고 실행!
                    method.invoke(obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        ReflectionExample example = new ReflectionExample();
        runAnnotatedMethods(example);
    }
}

ReflectionClass의 method1, method2, method3은 private이지만 @RunMe 어노테이션이 달려 있는 경우 접근 제어자를 무시하고 실행하도록 했다.

실행 결과 - 메서드 1, 메서드 3이 실행됐다.

이런 코드는 여러 기능을 제공해주는 프레임워크나 라이브러리 등을 공부하다보면 종종 볼 수 있다. 더 궁금하다면 테스트 프레임워크나 DI(Dependency Injection) 컨테이너 등에서 찾아보자!

반응형

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • 서로 닮아 보이는 Decorator, Composite Pattern - 1편

    서로 닮아 보이는 Decorator, Composite Pattern - 1편

    2025.01.15
  • IntelliJ 자주 쓰는 단축키 모음

    IntelliJ 자주 쓰는 단축키 모음

    2024.10.23
  • [Java] 람다식과 익명 클래스

    [Java] 람다식과 익명 클래스

    2024.02.21
다른 글 더 둘러보기

정보

Coding Groot 블로그의 첫 페이지로 이동

Coding Groot

  • Coding Groot의 첫 페이지로 이동

검색

메뉴

  • 홈
  • 태그
  • 방명록
  • 소개
  • 블로그 저작권

카테고리

  • 분류 전체보기 (188)
    • Git (23)
      • Git Tutorial (9)
      • Git Note (7)
      • Git Lecture (7)
    • Programming Language (1)
      • C (2)
      • C Sharp (5)
      • Java (4)
      • JavaScript (7)
      • Julia (5)
      • Python (4)
    • Programming (8)
      • Algorithm (2)
      • Compiler (5)
      • Data Structure (0)
      • Web (12)
      • NestJS (2)
    • DevOps, Infra (36)
      • Apple (6)
      • Cloud (15)
      • Database (1)
      • Network (4)
      • Linux (8)
    • Game Programming (11)
      • Unity Tutorial (5)
      • Unity Note (6)
    • Hardware Design (1)
      • Digital Circuit (1)
    • Note (20)
      • Coffee (2)
      • Retrospect (15)
      • Reading List (14)
    • Mathematics (1)

최근 글

인기 글

댓글

공지사항

아카이브

태그

  • 서평
  • Github
  • 회고
  • 한빛미디어
  • aws
  • tutorial
  • git
  • javascript
  • 전체 보기…

정보

Coding Groot의 Coding Groot

Coding Groot

Coding Groot

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기

나의 외부 링크

  • GitHub
  • SlideShare
  • 유니티 2020 수업
  • TIL Blog
  • 모도코

방문자

  • 전체 방문자
  • 오늘
  • 어제
Powered by Tistory / Kakao. Copyright © Coding Groot.

티스토리툴바