메서드의 오버로드와 오버라이드를 보고 있자니, 결국 자바 어노테이션의 활용인 것 같아서 그 원리가 궁금해졌다. 스프링 프레임워크의 어노테이션들이나 IT시장의 여러 자바기반 프레임워크들이 메타데이터를 주입시켜서 활용하고 있는 모습을 많이 볼 수 있는데, 그 원리를 확인해보려고 한다.
자바 어노테이션의 상세한 원리와 종류
자바 어노테이션은 메타데이터를 제공하는 기능으로, 코드에 대한 추가 정보를 제공하여 컴파일러나 런타임에 특정 동작을 수행할 수 있게 한다. 어노테이션은 주석처럼 코드에 부착되지만, 자바 바이트코드 파일에도 포함되어 런타임에 리플렉션을 통해 접근할 수 있다.
컴파일 타임과 런타임
컴파일 타임
컴파일 타임은 소스 코드가 바이트코드로 변환되는 시점을 의미한다. 컴파일 타임에 수행되는 작업은 다음과 같다:
- 구문 분석(Syntax Checking): 코드의 문법이 올바른지 확인한다.
- 타입 체크(Type Checking): 변수와 메서드의 타입이 올바른지 확인한다.
- 바이트코드 생성(Bytecode Generation): JVM이 실행할 수 있는 바이트코드 파일(.class)을 생성한다.
컴파일 타임에 발생하는 오류는 컴파일러가 감지하여 개발자에게 알려준다. 예를 들어, 타입 불일치, 접근 제어자 오류, 메서드 시그니처 불일치 등이 있다.
런타임
런타임은 바이트코드가 JVM에 의해 실행되는 시점을 의미한다. 런타임에 수행되는 작업은 다음과 같다:
- 클래스 로딩(Class Loading): 필요한 클래스 파일을 메모리로 로드한다.
- 바이트코드 해석(Bytecode Interpretation): JVM이 바이트코드를 해석하고 실행한다.
- 동적 바인딩(Dynamic Binding): 메서드 호출 시 실제 객체 타입에 따라 메서드를 바인딩한다.
- 가비지 컬렉션(Garbage Collection): 사용하지 않는 메모리를 자동으로 해제한다.
런타임에 발생하는 오류는 예외(Exception) 형태로 나타난다. 예를 들어, NullPointerException
, ArrayIndexOutOfBoundsException
등이 있다.
자바 어노테이션의 원리
어노테이션은 메타데이터를 제공하며, 주로 클래스, 메서드, 필드, 파라미터 등에 부착된다. 어노테이션은 컴파일 타임과 런타임에 모두 사용될 수 있다.
어노테이션 정의
어노테이션은 @interface
키워드를 사용하여 정의한다.
// 어노테이션 정의
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
String value();
}
@Retention: 어노테이션의 유지 정책을 지정한다.
- RetentionPolicy.SOURCE: 소스 코드에만 존재하며, 컴파일 후 제거된다.
- RetentionPolicy.CLASS: 클래스 파일에 존재하지만, 런타임에는 사용할 수 없다.
- RetentionPolicy.RUNTIME: 런타임에도 유지되어 리플렉션을 통해 접근할 수 있다.
@Target: 어노테이션이 적용될 수 있는 요소의 종류를 지정한다.
- ElementType.TYPE: 클래스, 인터페이스(애노테이션 포함), 열거형
- ElementType.FIELD: 필드(멤버 변수, enum 상수)
- ElementType.METHOD: 메서드
- ElementType.PARAMETER: 메서드 파라미터
- ElementType.CONSTRUCTOR: 생성자
- ElementType.LOCAL_VARIABLE: 지역 변수
- ElementType.ANNOTATION_TYPE: 어노테이션
- ElementType.PACKAGE: 패키지
어노테이션 사용 예제
어노테이션을 정의한 후, 이를 클래스나 메서드 등에 부착하여 사용할 수 있다.
public class MyClass {
// 어노테이션 사용
@MyAnnotation("example")
public void myMethod() {
// 메서드 구현
}
}
리플렉션을 통해 런타임에 어노테이션 정보를 읽을 수 있다.
import java.lang.reflect.Method;
public class AnnotationExample {
public static void main(String[] args) throws Exception {
Method method = MyClass.class.getMethod("myMethod");
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
System.out.println(annotation.value()); // 출력: example
}
}
자바의 주요 어노테이션과 예제
@Override
- 설명: 메서드가 슈퍼클래스의 메서드를 오버라이드하고 있음을 나타낸다.
- 용도: 컴파일러가 해당 메서드가 올바르게 오버라이드되었는지 검사하도록 돕는다.
class Parent {
void display() {
System.out.println("Parent display");
}
}
class Child extends Parent {
@Override
void display() {
System.out.println("Child display");
}
}
@Deprecated
- 설명: 해당 요소(클래스, 메서드, 필드 등)가 더 이상 사용되지 않음을 나타낸다.
- 용도: 개발자에게 더 이상 사용되지 않는 요소를 사용하지 말 것을 경고한다.
class MyClass {
@Deprecated
void oldMethod() {
System.out.println("This method is deprecated");
}
}
@SuppressWarnings
- 설명: 컴파일러에게 특정 경고를 무시하도록 지시한다.
- 용도: 불필요한 컴파일 경고를 제거하거나 무시한다.
class MyClass {
@SuppressWarnings("unchecked")
void myMethod() {
List list = new ArrayList(); // 경고를 무시
list.add("test");
}
}
@FunctionalInterface
- 설명: 인터페이스가 함수형 인터페이스임을 나타낸다.
- 용도: 단 하나의 추상 메서드를 가지는 인터페이스에 사용된다. 함수형 프로그래밍을 지원한다.
@FunctionalInterface
interface MyFunction {
void apply();
}
@SafeVarargs
- 설명: 제네릭 가변 인수를 사용하는 메서드 또는 생성자가 안전함을 나타낸다.
- 용도: 가변 인수를 사용하는 메서드에서 제네릭 타입 안전성을 보장할 때 사용한다.
class MyClass {
@SafeVarargs
final void myMethod(List<String>... stringLists) {
for (List<String> list : stringLists) {
System.out.println(list);
}
}
}
@Retention
- 설명: 어노테이션의 유지 정책을 지정한다.
- 용도: 어노테이션이 유지되는 기간을 정의한다. (소스, 클래스, 런타임)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String value();
}
@Target
- 설명: 어노테이션이 적용될 수 있는 요소의 종류를 지정한다.
- 용도: 어노테이션이 적용될 위치(클래스, 메서드, 필드 등)를 제한한다.
@Target(ElementType.METHOD)
@interface MyAnnotation {
String value();
}
@Documented
- 설명: 해당 어노테이션이 Javadoc에 포함되어야 함을 나타낸다.
- 용도: 어노테이션이 문서화되도록 한다.
@Documented
@interface MyAnnotation {
String value();
}
@Inherited
- 설명: 어노테이션이 서브클래스에 상속됨을 나타낸다.
- 용도: 클래스에 붙은 어노테이션을 서브클래스에도 적용하고자 할 때 사용한다.
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotation {
String value();
}
@MyAnnotation("BaseClass")
class BaseClass {
}
class DerivedClass extends BaseClass {
// DerivedClass는 MyAnnotation을 상속받는다
}
요약
자바 어노테이션은 주석처럼 코드에 부착되어 메타데이터를 제공하는 기능으로, 컴파일러와 런타임에 다양한 정보를 제공하여 코드의 동작을 제어할 수 있다. 주요 어노테이션과 그 사용 예제를 통해 어노테이션의 원리와 활용 방법을 이해할 수 있다.
- 컴파일 타임: 소스 코드가 바이트코드로 변환되는 시점. 구문 분석, 타입 체크, 바이트코드 생성 등의 작업이 수행된다.
- 런타임: 바이트코드가 JVM에 의해 실행되는 시점. 클래스 로딩, 바이트코드 해석, 동적 바인딩, 가비지 컬렉션 등의 작업이 수행된다.
이러한 개념과 어노테이션의 원리를 이해하면 자바 코드의 동작 방식과 어노테이션을 통한 메타데이터 제공 방법을 보다 명확히 알 수 있다.
'자료(RESOURCES) > IT개념' 카테고리의 다른 글
@Autowired를 지양하는 이유 (0) | 2024.06.28 |
---|---|
메서드 오버로드 오버라이드(개념서 요약) (0) | 2024.06.25 |
자바 프로그래밍 루프 검증 (0) | 2024.06.19 |
인스턴스란 무엇인가 (0) | 2024.06.18 |
프레임워크 vs 개발툴 (0) | 2024.06.18 |