어노테이션의 역할
스프링 같은 프레임워크를 사용하면 아무래도 어노테이션의 역할은 더 광범히 해진다. 그럼 일단 Java 코드로 돌아와 어노테이션의 역할을 먼저 확인해 보자.
우선, 어노테이션은 기본적으로 아래 세 가지 기능을 제공한다.
- 컴파일러에서 코드 작성 문법 에러를 체크
- 빌드도구에서의 사용 (코드 생성)
- 실행 시(런타임 시) 특정 기능을 실행
이것에 대한 예시로 @Override 어노테이션을 확인해 보자. @Override 어노테이션은 직관적으로 자바의 오버라이딩을 표현해 주는 것을 알 수가 있다.
자바에 대한 오버라이딩을 배우면 알 수 있듯이 저 어노테이션이 없다고 하더라도 해당 기능을 쓰는 데는 문제가 발생하지 않는다.
class Parent {
public void hello(String name) {
System.out.println("안녕하세요, 저는 " + name + "입니다.");
}
}
class Child extends Parent{
// @Override가 있고 없고 차이가 없을까?
public void hello() {
System.out.println("안녕!");
}
}
위 코드는 @Override 어노테이션을 사용하지 않으면 동작이 되고, 사용하면 예외가 발생한다. 예외가 발생한다는 것은 오버라이딩이 아니라는 의미이다.
여기서 어노테이션의 첫 번째 역할을 나타낸다. 어노테이션은 사용자가 의도한 기능을 명시적으로 표현할 수 있다. 위 코드는 사용자가 오버라이딩을 의도하고 코드를 작성했다면 나중에 큰 문제를 야기할 수 있는 코드지만 컴파일 시 문제없이 실행이 된다.
하지만 @Override를 사용한다면 아래와 같이 컴파일러를 통해 에러를 체크할 수 있다.
다음으로는, Java에선 Maven이나 Gradle이 대표적인 빌드 도구이다. 빌드 도구엔 라이브러리를 의존성 등록을 할 수 있다.
예를 들어 설명하자면 Lombok 라이브러리가 있다. 해당 라이브러리를 빌드 도구에 등록하면 @Data, @Getter, @Setter 등 어노테이션이 제공된다.
이 어노테이션들을 통해 재사용, 반복적으로 사용되는 코드(보일러플레이트 코드)를 줄여주는 역할을 한다.
즉, 이러한 어노테이션들은 컴파일 시간 동안 빌드 도구에 의해 해석되어 적절한 코드를 생성해 주는 역할을 수행한다. 따라서 빌드도구와 어노테이션은 연관되어 동작하며, 개발자에서 효과적이고 효율적인 코드 환경을 제공해 준다.
@Getter
@Setter
public class Person {
private Long id;
private String name;
// @Getter, @Setter의 사용으로 코드를 직접 작성할 필요가 없다.
}
마지막으로 런타임 시 어노테이션의 역할을 보면 런타임 시 특정 기능을 실행하도록 정보를 제공한다고 정의된다.
이러한 행위는 리플렉션(Reflecion)이라는 단어로 정의된다.
리플렉션(Reflecion)이란 간단히 말해 Runtime시에 클래스의 메타 정보를 얻는 기능을 말한다. 예를 들어 클래스가 가지고 있는 필드가 무엇인지, 어떤 메서드를 가졌는지 등 클래스에 대한 정보를 알아내는 것을 리플랙션이라고 한다.
예를 들어, 스프링 프레임워크를 사용하면 @Autowired 어노테이션은 런타임에 의존성을 주입하기 위해 사용된다. 런타임에 Spring 컨테이너는 이 어노테이션이 붙은 필드나 메서드를 찾아 적절한 오브젝트를 주입시킨다.
결론적으로 런타임에 메타데이트를 바탕으로 특정 동작을 수행하게 하는 데 사용될 수 있다.
@Retention의 RetentionPolicy.RUNTIME을 사용하여 어노테이션을 정의하면, 리플렉션을 통해 런타임 시에 해당 어노테이션 정보에 접근할 수 있고, 이를 통해 런타임 시 동적으로 어노테이션 정보를 활용할 수 있게 된다.
어노테이션의 구조
- @Target
- 어노테이션을 어떤 것에 적용할지를 선언할 때 사용한다.
- 예시로는 @Target(ElementType.METHOD)라고 하면, 메서드 선언 시에만 적용 대상으로 하는 것이다.
- @Retention
- 얼마나 어노테이션 정보가 유지되는지를 선언하기 위해 사용한다.
- 예시로는 @Retention(RetentionPolicy.RUNTIME)라고 하면, 런타임 시간까지만 어노테이션 정보가 유지된다.
- 소스, 클래스, 런타임등의 옵션이 있다.
- @Documented
- 해당 어노테이션에 대한 정보가 Javadocs(API) 문서에 포함된다는 것을 선언할 때 사용된다.
- @Inherited
- 모든 자식 클래스에서 부모 클래스의 어노테이션을 사용 가능하다는 것을 선언할 때 사용된다. 즉, 부모 클래스에 이 어노테이션이 붙어 있다면 자식 클래스에도 붙은 것과 같이 인식된다.
나만의 어노테이션 만들기 (사용자 정의 어노테이션)
어노테이션은 기본 정의된 어노테이션을 사용할 수 있지만 사용자 정의 어노테이션을 만들 수 있다.
사용자 정의 어노테이션을 사용하기 위해 @interface를 통해 어노테이션 클래스를 작성할 수 있다.
public @interface MyAnnotation {
}
만든 어노테이션은 클래스나 메서드 등의 선언하여 사용할 수 있다.
// 클래스나 메서드 등 위에 선언하여 사용한다.
@MyAnonotaion
class MyInfo {
}
기본적으로 위와 같이 Defalut로 사용한다면 명시적 주석의 역할만 수행한다. 이런 어노테이션의 역할을 추가하려면 위에서 정의한 구조 어노테이션들을 활용하면 된다.
우선 어노테이션의 유지시점을 어디까지 유지하고 싶다고 하면 아래와 같이 @Retention 어노테이션을 활용하면 된다. 해당 어노테이션은 기본적으로 CLASS가 기본 세팅이다. 하지만 런타임시까지 유지하여 사용하고 싶으면 RUNTIME 세팅으로 변경해 주도록 한다.
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
그런 다음 어노테이션을 사용할 수 있는 위치(클래스, 메스드, 변수 등)를 지정하고 싶으면 @Target 어노테이션을 사용하면 된다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TPYE, EleemetType.FIELD})
public @interface MyAnnotation {
}
타깃이 들어갈 수 있는 내용은 밑에 표를 확인해 보자.
ANNOTATION_TYPE | 어노테이션 |
CONSTRUCTOR | 생성자 |
FIELD | 필드 선언 (emum 정수 포함) |
LOCAL_VARIABLE | 로컬 변수 |
METHOD | 메서드 |
PARAMETER | 파라미터 |
PACKAGE | 패키지 |
TYPE | 클래스, 인터페이스 (어노테이션을 포함), enum |
어노테이션은 내부 코드를 통해 동작의 대한 정의를 구현할 수 있다. 기본적으로 어노테이션의 필드 타입은 enum, String이나 기본 자료형, 기본 자료형의 배열만 사용할 수 있다. 어노테이션의 역할 중 메타데이터를 정의해 놓고 쉽게 가져올 수 있다는 장점이 여기에 나온다고 생각하면 된다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TPYE, EleemetType.FIELD})
public @interface MyAnnotation {
public 타입 elementName() [default 값]
...
}
추가적으로 상속에 대한 부분을 추가하려면 @Inherited를 사용하면 되고, JavaDoc 기준 API 용 명세 정의하고 싶으면 @Documented 어노테이션을 사용하면 된다.
추가적으로
추가적으로 Refiection에 대해 좀 더 알아볼 필요를 느낀다. 그리고 실제 커스텀 어노테이션이 어떤 곳에 주로 사용하는지랑 남용하지 말라는 이야기가 있는데 그 부분에 대한 좀 더 찾아보고 정의할 필요가 있을 것 같다.
'Language > Java' 카테고리의 다른 글
[java] 비트 연산자와 2진법 (0) | 2024.07.09 |
---|---|
[JAVA] 람다식과 함수형 인터페이스 (0) | 2024.05.02 |
[스진초5기/Java] Overriding과 Overloading (0) | 2023.10.27 |
[Java] String[]을 List , List를 String[]로 변환하는 방법 (0) | 2023.10.24 |
[스진초5기/Java] 오버라이딩을 구현할 때 꼭 @Override가 필요할까? [+어노테이션의 역할] (0) | 2023.10.23 |