본문 바로가기
Back-end/Spring Boot

[SpringBoot] 스프링부트 이해하기 - 5편 Annotation

by whatamigonnabe 2022. 7. 16.

지금까지 알아본 것을 정리해보면 스프링부트가 어떤 기술을 활용하여 사용자의 요청을 개발자가 작성한 컨트롤러까지 연결시켜주는가에 대해 정리해보고 있습니다. IoC/DI부터 서블릿, 리플렉션까지. 하지만 스프링부트를 사용할 때는 우리는 결국 어노테이션을 사용하여 컨트롤러를 만듭니다. 그래서 오늘은 어노테이션에 대해서 다뤄볼까합니다.

 

Annotation(@)

더보기

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code.  -wikipedia

위키피디아의 정의는 다음과 같습니다. 무언가 추가적인 정보를 주는 기능을 하나봅니다. 어떻게 추가적인 정보가 컨트롤러의 역할을 할 수 있는 걸까요? 

 

사실 제 생각엔 어노테이션만 놓고 보면 개발자 친화적으로 보기 좋게 메타 데이터를 제공하는 목적이 맡는 것 같습니다. 거기에 어노테이션에 붙은 정보에 접근할 수 있는 기능이 더해진 것 같습니다.  아무튼 어노테이션의 기능은 다음과 같습니다.

  1. 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공(@Override)
  2. 소프트웨어 개발 툴이나 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보 제공
  3. 런타임 시 특정 기능을 실행하도록 정보를 제공(서블렛, 컨트롤러)

그럼 어떻게 어노테이션을 활용해서 정보를 제공하고, 어떻게 그 정보를 활용하는지 알아보겠습니다.

어노테이션 구현하기

어노테이션 정의

//AnnotationName.java //어노테이션 이름과 같게 파일 생성

public @inferface AnnotationName { //어노테이션은 @interface라고 붙여줍니다.
[타입] elementName() ((선택적) default 값);
}

위의 elementName()을 엘리먼트라고 부르는데요, 외부의 값을 입력받을 수 있도록 하는 역할을 하며, 원시타입과 참조타입을 모두 사용할 수 있습니다.

또한 디폴트 값도 위처럼 정의할 수 있고, 어노테이션을 활용할 때 따로 명시해주지 않으면 디폴트 값이 할당됩니다.

 

만약 엘리먼트 변수 이름을 value라고 지정한다면, 어노테이션을 적용할 때 엘리먼트 이름을 명시하지 않고 값을 하나만 입력한다면, 묵시적으로 value에 할당합니다. 만약 입력할 엘리먼트가 두 개 이상이라면 value = "값"을 명시해야합니다. 아래에서 더 설명하겠습니다.

 

어노테이션 유지 기간

사용한 어노테이션이 언제까지 유지되는지를 아래와 같이 정의할 수 있습니다.

https://koreanfoodie.me/639

@Override 어노테이션은 컴파일할 때, 상위 클래스에 오버라이드하는 메서드가 있는지 확인하고 없으면 에러를 나타내는 역할을 하는데요, 바로 이 오버라이드는 컴파일 때만 쓰이기 때문에 RetentionPolicy가 SOURCE입니다.

보통은 어노테이션을 런타임 시에 동적을 활용하기 때문에  RetentionPolicy.RUNTIME을 주로 사용합니다. 때문에 어노테이션은 리플렉션과 아주 관계가 깊습니다. 리플렉션에 대해서 이해하고 싶다면 이전 글을 참고해주세요.

2022.07.16 - [풀스택 개발자/Spring Boot] - [SpringBoot] 스프링부트 이해하기- 4편 Reflection(feat. 동적 로딩)

어노테이션 적용대상

어디에나 어노티이션을을 붙일 수도 있지만, 목적을 명시하여 제한할 수도 있습니다.

https://honeyinfo7.tistory.com/56

어노테이션 활용하기

어노테이션 붙이기

import anno.PrettySout; //어노테이션 클래스 임포트

public class Ex {
    @PrettySout(value = "%", howMany = 10) //엘리멘트=값 형태로 할당
    public static void printA() {
        System.out.println("AAAAAAA");
    }

    @PrettySout("@#$") //값 하나만 입력하게 되면 묵시적으로 value="@#$"으로 인식, howMany는 디폴트인 5 할당
    public static void printB() {
        System.out.println("BBBBBBBB");
    }

어노테이션이 붙은 메서드에서 정보를 얻어 활용하기

//같은 Ex 클래스
//import java.lang.reflect.*; Method클래스가 속해있는 패키지 import

public static void main(String[] args) {
    //Ex.class => Ex클래스의 Class 객체 리턴
    //getDeclaredMethods => 리플렉션 기법으로 클래스의 메서드들을 Method[]로 반환
    Method[] methods = Ex.class.getDeclaredMethods();

    //순회하면서
    for(Method method : methods) {
        //어노테이션이 붙었는지 확인.
        //인자로 Class 객체를 받는데, 자바 자체가 동적 로딩으로 이뤄져있기 때문에, JVM은 어떤 어노테이션이 있는지 모름.
        //따라서 인자로 받은 클래스만 스캔해서 클래스의 어노테이션이 있는지만 확인
        if(method.isAnnotationPresent(PrettySout.class)) {
            //어노테이션 객체 생성(PrettySout이 어노테이션 클래스)
            PrettySout prettySout = method.getDeclaredAnnotation(PrettySout.class);
            System.out.print("메소드 " + method.getName() + "은 ");
            //어노테이션 객체를 통해서 정의해놓은 엘리먼트에 할당된 값 조회
            System.out.println("어노테이션" + prettySout.toString() + "이(가) 있습니다.");
            System.out.println("value 값: " + prettySout.value());
            System.out.println("howMany 값: " + Integer.toString(prettySout.howMany()));

            //그 값을 바탕으로 특정 로직 수행
            for(int i = 0 ; i < prettySout.howMany(); i++) {
                System.out.print(prettySout.value());
            }
            System.out.println();

            //리플렉션으로 메서드 실행. static 메서드의 경우 object로 null 전달
            method.invoke(null);
        }
    }
}

이렇게 어노테이션 기법을 알아보았습니다. (근데 이제 리플렉션을 곁들인)

스프링부트의 어노테이션들이 어떻게 동작하는지에 대해 한층 깊게 이해하게 된 것 같습니다.

 

참조

https://www.youtube.com/watch?v=ZmB9LCf6jaw&t=48s 

https://www.infoworld.com/article/2077455/dynamically-invoking-a-static-method-without-instance-reference-july-6-1999.html

https://koreanfoodie.me/639