JaeWon's Devlog
article thumbnail
반응형

스프링으로 개발을 하다 보면 @GetMapping, @RequiredArgsConstructor 등의 다양한 어노테이션을 볼 수 있고, 사용을 하게 된다.

간단하게는 어노테이션을 쓰면 그 어노테이션의 로직(내용)을 컴파일/ 빌드 시 자동으로 추가해주는 것으로 알고 있다.

이렇게 자주 사용하는 어노테이션이지만 동작방식이나 정의되는 방법 등에 대해 정확하게 알고 있다고 생각하지 않아, 좀 더 알아보고 정리해보고자 한다.


1. 1. 어노테이션(Annotation)이란?

- 자바 소스 코드에 추가하여 사용할 수 있는 메타데이터의 일종.

- 보통 @ 기호를 앞에 붙여서 사용.(컴파일러는 컴파일 시 @문자로 시작이 되면 어노테이션으로 판단하여 진행)

- JDK 1.5 버전 이상부터 사용 가능.

<java />
// Example @RequiredArgsConstructor @GetMapping @Entity ...

- 클래스, 인터페이스, 메소드, 메소드 파라미터, 필드, 지역 변수 위에 작성하여 사용.

- 값을 저장할 수 있는 엘레먼트를 가지고, 어노테이션 이름 다음에 괄호 안에 엘레먼트를 정의.

<java />
@Entity(name = "MEMBER_TABLE")

1.1. 1-1. 어노테이션의 용도

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

2. 2. 어노테이션 사용 순서

  1. 어노테이션 정의
  2. 어노테이션 배치

2.1. 2-1. 어노테이션 정의

- 어노테이션을 적용할 때는 어노테이션이 어디(클래스, 메소드, 파라미터 등)에 적용되며, 언제까지 어노테이션 소스가 유지될 것인지를 설정해야 한다.

- 아래와 같이 어노테이션을 정의 할 수 있다.

<java />
@Target({ElementType.[적용대상]}) // 클래스, 파라미터, 메소드 등 @Retention(RetentionPolicy.[정보유지되는 대상]) // 런타임, 클래스, 소스 public @interface [어노테이션명]{ public 타입 elementName() [default 값] ... }

- 예를 들어보자면, 우리가 자주 사용하는 룸북에서 제공하는 생성자 관련 어노테이션을 볼 수 있다.

<java />
@Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface RequiredArgsConstructor { .... }

- @Target 에는 어느 위치(클래스, 파라미터, 메소드 등)에 어노테이션을 적용할지 작성한다.

- @Retention 에는 언제까지 어노테이션을 유지할 것인지 작성한다.

@Retention 어노테이션은 보통 Runtime 을 많이 사용하고 있어, 어노테이션을 확인하다 보면 Retention 값이 RUNTIME 으로 되어 있는 것을 확인할 수 있다.

2.2. 2-2. 어노테이션 배치.

- 어노테이션의 사용은 클래스를 참고하는 소스의 흐름상에서 Reflection 을 사용하는 방법을 통해 활용한다.

- 해당  내용을 이해하기 위해 간단하게 예제를 작성해서 확인해보고자 한다.

- 추가로 코드가 실행되는 중 Reflection(리플렉션)을 이용하여 추가 정보를 통해 기능 동작도 확인한다.

Reflection(리플렉션)???
구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API.
자바의 리플렉션은 클래스, 인터페이스, 메소드들을 찾을 수 있고, 객체를 생성하거나 변수를 변경하거나 메소드를 호출할 수 있다.
  • JaewonAnnotation.java (어노테이션)
<java />
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 JaewonAnnotation { String value() default "-"; String str() default "Test Jaewon Annotation :)"; }
  • TestAnnotationService.java (어노테이션을 사용하고자 하는 클래스)
<java />
public class TestAnnotationService { @JaewonAnnotation public void test1() { System.out.println("테스트1"); } @JaewonAnnotation("***") public void test2() { System.out.println("테스트2"); } @JaewonAnnotation(value = "Test", str = "JaewonAnnotaion 테스트 입니다!!!") public void test3() { System.out.println("테스트3"); } }
  • TestMain.java (TestAnnotationService.java 를 사용하는 클래스)
<java />
import java.lang.reflect.Method; public class TestMain { public static void main(String[] args){ Method[] methodList = TestAnnotationService.class.getMethods(); for(Method method : methodList) { // 어노테이션 적용 여부 확인 if(method.isAnnotationPresent(JaewonAnnotation.class)) { System.out.println("Method Name : " + method.getName()); JaewonAnnotation annotation=method.getDeclaredAnnotation(JaewonAnnotation.class); String value = annotation.value(); String str = annotation.str(); System.out.println("Value : " + value); System.out.println("Str : " + str); } } } }

- TestMain 을 실행하게 되면 결과는 아래와 같습니다.

<java />
Method Name : test1 Value : - Str : Test Jaewon Annotation :) Method Name : test2 Value : *** Str : Test Jaewon Annotation :) Method Name : test3 Value : Test Str : JaewonAnnotaion 테스트 입니다!!!

3. 3. 어노테이션의 종류

- 어노테이션에도 종류가 존재한다.

  • 표준(내장) 어노테이션 : 자바가 기본적으로 제공하는 어노테이션
  • 메타 어노테이션 : 어노테이션을 위한 어노테이션
  • 사용자 정의 어노테이션 : 사용자가 직접 정의(생성)하는 어노테이션

3.1. 3-1. 표준(내장) 어노테이션

- 7개의 표준 어노테이션 중에 3개가 java.lang 의 일부이고, 나머지 4개는 java.lang.annotation 으로부터 가져온다.

  • @Override
    - 오버라이딩을 올바르게 했는지 컴파일러가 체크.
    - Override 는 오버라이딩 시, 메서드의 이름을 잘못되었는지 확인한다.
<java />
// 부모 클래스 class Parent { void parentMethod(){} } // 자식 클래스(부모 클래스 상속) class Child extends Parent { @Override void parentMethodTest(){} // 컴파일 에러 발생! 오버라이드 메소드명이 틀림 }
  • @Deprecated
    - 앞으로 없어지거나, 사용하지 않을 것을 권장하는 필드나 메서드에 사용.
    - StringUtils.isEmpty() 과 같이 밑줄이 그어져 있지만, 사용은 가능한 것을 종종 확인할 수 있다.
그러나 나중에 버전업시 어느 순간 메소드가 사라져 있을 수 있다.
<java />
@Deprecated public static boolean isEmpty(@Nullable Object str) { return (str == null || "".equals(str)); }
  • @FunctionalInterface
    - Java 8 부터 지원하는, 함수형 인터페이스를 지정하는 어노테이션.
    - 함수형 인터페이스의 하나의 추상 메서드만 가져야 한다는 제약을 확인.
    - 메서드가 존재하지 않거나, 1개 이상의 메서드(default 메서드 제외) 가 존재할 경우 컴파일 오류를 발생.
  • @SuppressWarnings
    - 선언한 곳의 컴파일 경고를 무시하도록 하는 어노테이션.
<java />
@SuppressWarnings("unchecked") ArrayList list = new ArrayList(); // 제네릭 타입을 지정하지 않음 list.add(obj); // 경고 발생 !!!(UnChecked)

3.2. 3-2. 메타 어노테이션

  • @Target
    - @Target 에는 어느 위치(클래스, 파라미터, 메소드 등)에 어노테이션을 적용할지 작성한다.
ElementType 열거 상수 적용 대상
TYPE   클래스, 인터페이스, 열거 타입
ANNOTATION_TYPE   어노테이션
FIELD   필드
CONSTRUCTOR   생성자
PARAMETER   파라미터
METHOD   메소드
LOCAL_VARIAblE   로컬 변수(지역 변수)
PACKAGE   패키지
  • @Retention
    - @Retention 에는 언제까지 어노테이션을 유지할 것인지 작성한다.
RetentionPolicy 열거 상수 설명
RUNTIME   바이트 코드 파일(컴파일/빌드파일)까지 어노테이션 정보를 유지하여,
  리플렉션을 이용해서, 런타임에 어노테이션 정보를 얻을 수 있다.
CLASS   바이트 코드 파일까지 어노테이션 정보를 유지하지만, 리플렉션을 이용해서
  런타임에 어노테이션 정보를 얻을 수 없다.
SOURCE   소스상에서만 어노테이션 정보를 유지한다.
  소스코드를 확인할 때만 의미가 있고, 바이트 코드 파일에는 정보가 남지 않는다.
  • @Documented
    - 해당 어노테이션을 JavaDoc 에 포함시킨다.
  • @Inherited
    - 어노테이션도 상속이 가능하다.
    - 어노테이션을 자식 클래스에 상속하고자 할 때 해당 어노테이션을 작성한다.
<java />
@Inherited @interface SuperAnno{} @SuperAnno class Parent{} // SuperAnno 가 붙은 것으로 인식 class Child extends Parent{}
  • @Repeatable
    - Java 8 부터 지원하고, 연속적으로 어노테이션을 선언할 수 있게 한다.

3.3. 3-3. 사용자 정의 어노테이션

- 위 2번 항목에서 예제로 생서안 것을 참고하면 된다.

4. 4. 어노테이션 요소 특징

- 적용시 값을 지정하지 않으면, 사용될 수 있는 기본값을 지정할 수 있다.

- 요소가 하나이고 이름이 value 라면 어노테이션 작성 시 요소의 이름은 생략이 가능하다.

<java />
// 요소가 생략되면 default 값 @JaewonAnnotation public void test1() { } // 요소가 하나이고 이름이 value 라면 이름 생략 가능 @JaewonAnnotation("***") public void test2() { } // 요소가 하나가 아니면 이름 생략 불가능 @JaewonAnnotation(value = "Test", str = "JaewonAnnotaion 테스트 입니다!!!") public void test3() { }

- 요소의 타입이 배열인 경우 중괄호{} 를 사용해야 한다.

<java />
@interface TestAnnotation{ String[] strArr(); } // 요소가 배열이면 중괄호를 통해 선언한다. @TestAnnotation(strArr={"Test", "Annotation"}) // 요소가 1개일 때는 {}를 사용하지 않아도 된다. @TestAnnotation(strArr="Hello Annotation") // 요소가 없으면 {}를 써넣어야 한다. @TestAnnotation(strArr={})

 

5. 5. 어노테이션 규칙

- 어노테이션에서도 지켜야하는 규칙이 있다.

  1. 요소의 타입은 기본형, String, Enum, 어노테이션, Class 만 허용
  2. 괄호() 안에 매개변수를 선언할 수 없다.
  3. 예외를 선언 할 수 없다.
  4. 요소의 타입을 매개변수로 정의할 수 없다.(<T>)

참고

- https://velog.io/@jkijki12/annotation

- https://bangu4.tistory.com/199

- https://honeyinfo7.tistory.com/56

반응형
profile

JaeWon's Devlog

@Wonol

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