[JAVA] 어노테이션과 리플렉션 원리

2025. 12. 11. 18:45·공부/JAVA

자바 개발자, 특히 스프링 부트를 사용하는 개발자라면 숨 쉬듯이 어노테이션(Annotation)을 사용한다. @Controller, @Service, @Transactional 등 골뱅이(@) 하나만 붙이면 빈(Bean)이 등록되고 트랜잭션이 관리되는 마법 같은 일들이 일어난다.

하지만 많은 개발자가 어노테이션을 "그냥 붙이면 기능이 생기는 것" 정도로 이해하고 넘어간다. 도대체 이 텍스트 조각이 어떻게 코드를 변화시키는 것일까? 그리고 public @interface라는 선언은 인터페이스와 무슨 관계일까?

이번 글에서는 어노테이션의 본질인 메타데이터와 인터페이스로서의 정체, 그리고 이를 작동시키는 리플렉션(Reflection)의 원리를 파헤쳐본다.

 

1. 어노테이션의 정체: 메타데이터

사전적 의미로 어노테이션은 '주석'이다. 하지만 우리가 흔히 쓰는 // 나 /* */ 주석과는 목적이 완전히 다르다.

  • 일반 주석(Comment): 사람(개발자)에게 정보를 제공하기 위함. 컴파일러는 이를 무시하고 버린다.
  • 어노테이션(Annotation): 컴파일러나 프로그램(JVM, 프레임워크)에게 정보를 전달하기 위함.

즉, 어노테이션은 코드 로직에 직접적인 영향을 주지 않는다. 그저 "이 메서드는 특별한 처리가 필요하다"라는 메타데이터(Meta-Data), 즉 견출지(Post-it)를 붙여놓은 것과 같다.

 

2. 어노테이션은 특수한 인터페이스다

어노테이션을 직접 정의해 본 적이 있다면 키워드가 @interface라는 것을 알 것이다.

public @interface MyAnnotation {
    String value();
}

이 코드는 컴파일(javac) 후 바이트코드 레벨에서 아래와 같은 형태의 인터페이스로 변환된다. 모든 어노테이션은 내부적으로 java.lang.annotation.Annotation 인터페이스를 상속받는다.

 

컴파일된 어노테이션의 실제 구조 (Decompiled)

// 어노테이션은 결국 인터페이스다.
public interface MyAnnotation extends java.lang.annotation.Annotation {
    public abstract String value();
}

우리가 어노테이션에 값을 할당하는 행위(@MyAnnotation(value="A"))는, 런타임에 다이내믹 프록시(Dynamic Proxy)를 통해 이 인터페이스의 구현체를 생성하고 값을 반환하도록 메서드를 호출하는 과정과 유사하다.

 

java.lang.annotation.Annotation 소스 코드

그렇다면 모든 어노테이션의 부모인 java.lang.annotation.Annotation은 어떻게 생겼을까? JDK 소스를 확인해보면 다음과 같다.

/*
 * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
 * ...
 */

package java.lang.annotation;

/**
 * The common interface extended by all annotation types.
 * ...
 */
public interface Annotation {
    /**
     * Returns true if the specified object represents an annotation
     * that is logically equivalent to this one.
     */
    boolean equals(Object obj);

    /**
     * Returns the hash code of this annotation.
     */
    int hashCode();

    /**
     * Returns a string representation of this annotation.
     */
    String toString();

    /**
     * Returns the annotation type of this annotation.
     */
    Class<? extends Annotation> annotationType();
}

출처: OpenJDK java.lang.annotation.Annotation

이처럼 어노테이션은 마법의 문법이 아니라, 자바 언어 스펙 내에서 정의된 특수한 인터페이스일 뿐이다.

 

3. 메타 어노테이션: 설정의 설정

어노테이션을 정의할 때는 "이 견출지를 어디에, 언제까지 붙여둘 것인가"를 정의해야 한다. 이를 설정하는 어노테이션을 메타 어노테이션(Meta-Annotation)이라 한다.

 

@Target

어노테이션을 부착할 수 있는 위치를 제한한다.

  • ElementType.TYPE: 클래스, 인터페이스
  • ElementType.METHOD: 메서드
  • ElementType.FIELD: 필드 (멤버 변수)

 

@Retention (가장 중요)

어노테이션이 유지되는 수명(Life Cycle)을 결정한다. 스프링이 작동하는 원리를 이해하려면 이 부분이 핵심이다.

스프링 프레임워크가 서버 기동 중에 클래스를 스캔해서 빈을 등록하려면, 해당 어노테이션은 반드시 RetentionPolicy.RUNTIME 이어야 한다.

 

4. 작동 원리: 리플렉션 (Reflection)

어노테이션 자체는 아무런 기능이 없는 메타데이터라고 했다. 그렇다면 실제 기능은 누가 수행하는가?
바로 리플렉션(Reflection) API가 그 역할을 담당한다.

리플렉션은 구체적인 클래스 타입을 알지 못해도 런타임에 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API다.

 

스프링 컨테이너의 동작 과정 예시

  1. 애플리케이션이 실행되면 스프링은 컴포넌트 스캔을 수행한다.
  2. 리플렉션을 통해 클래스 파일들을 읽어들인다 (Class<?> clazz = ...).
  3. 클래스나 메서드에 특정 어노테이션(@Component, @Transactional)이 붙어있는지 검사한다 (isAnnotationPresent).
  4. 붙어있다면 그에 맞는 처리(객체 생성, 프록시 생성 등)를 수행한다.

 

5. 커스텀 어노테이션 구현 실습

이론을 확인하기 위해, 메서드 실행 시 로그를 출력하는 어노테이션을 직접 만들고 리플렉션으로 처리해보자.

1) 어노테이션 정의

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // 런타임까지 살아있어야 리플렉션이 읽을 수 있다.
public @interface PrintLog {
    String value() default "기본 로그";
}

2) 비즈니스 로직 적용

public class MyService {

    @PrintLog("메서드 실행됨!") 
    public void doSomething() {
        System.out.println(">>> 비즈니스 로직 수행");
    }
}

3) 리플렉션 프로세서 구현 (프레임워크의 역할)

import java.lang.reflect.Method;

public class MyFramework {
    public static void main(String[] args) throws Exception {
        MyService service = new MyService();

        // 1. 리플렉션으로 클래스 정보 획득
        Class<?> clazz = service.getClass();

        // 2. 모든 메서드 순회
        Method[] methods = clazz.getDeclaredMethods();

        for (Method method : methods) {
            // 3. 특정 어노테이션이 붙어있는지 확인
            if (method.isAnnotationPresent(PrintLog.class)) {

                // 4. 어노테이션 정보 추출
                PrintLog annotation = method.getAnnotation(PrintLog.class);
                System.out.println("[LOG] " + annotation.value());

                // 5. 실제 메서드 실행
                method.invoke(service);
            }
        }
    }
}

실행 결과

[LOG] 메서드 실행됨!
>>> 비즈니스 로직 수행

MyService 코드 내부에는 출력문([LOG]...)이 없지만, 외부의 MyFramework가 리플렉션을 통해 어노테이션을 감지하고 기능을 주입했다. 이것이 스프링 AOP와 어노테이션 기반 개발의 핵심 원리다.

 

6. 결론

  1. 어노테이션은 java.lang.annotation.Annotation을 상속받는 인터페이스다.
  2. 어노테이션 그 자체는 기능이 없는 메타데이터(견출지)일 뿐이다.
  3. 실제 기능은 리플렉션(Reflection) 기술을 이용해 런타임에 어노테이션을 읽고 처리하는 제3자(프레임워크)가 수행한다.

그러므로 우리가 @SpringBootApplication이나 @Autowired를 사용할 때, 단순히 '마법'이라고 생각하기보다 "스프링이 리플렉션을 통해 이 표시를 읽고, 빈을 주입하거나 설정을 구성하겠구나"라고 이해하는 것이 정확하다.

 

Reference

  • Oracle Java SE Documentation - Annotations
  • Java Language Specification - Annotation Types
  • Class Annotation (Java SE 17 & JDK 17)

'공부 > JAVA' 카테고리의 다른 글

[JAVA] 배열(Array) vs ArrayList vs Vector  (0) 2026.02.06
[Java] Record는 무엇인가?  (0) 2025.12.05
[Java] 프로세스와 스레드  (0) 2025.11.27
[Spring]서블릿(Servlet)과 Spring MVC  (0) 2025.11.17
'공부/JAVA' 카테고리의 다른 글
  • [JAVA] 배열(Array) vs ArrayList vs Vector
  • [Java] Record는 무엇인가?
  • [Java] 프로세스와 스레드
  • [Spring]서블릿(Servlet)과 Spring MVC
gepetton
gepetton
공부하며 얻은것들을 공유합니다!
  • gepetton
    gepetton의 블로그
    gepetton
  • 전체
    오늘
    어제
    • 분류 전체보기 (16)
      • 개발 기록 (3)
        • 프로젝트 - BUKAE (2)
        • 프로젝트 - GULON (1)
      • 공부 (10)
        • DB (1)
        • JAVA (5)
        • CS (4)
      • 자격증 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    springboot
    java record
    태크스큐
    SQLD 2025
    Python
    java
    이벤트 기반 처리
    자격증
    spring boot
    파이썬 TTS
    정보처리기사필기후기
    정보처리기사실기후기
    API제한
    WebSocket 인증
    Python Worker
    PK 설계
    정보처리기사2025
    리눅스마스터 2502
    리눅스마스터1급 2502
    큐기반비동기
    ratelimit
    비동기 아키텍처
    토큰버킷알고리즘
    2025년3회정보처리기사
    FailFast
    자바
    인덱스 성능
    리눅스마스터 1급 후기
    Celery
    58회 SQLD
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
gepetton
[JAVA] 어노테이션과 리플렉션 원리
상단으로

티스토리툴바