콘텐츠로 이동

어노테이션 개념 & 커스텀 어노테이션

PHASE 2 | Java 핵심 — 객체지향
키워드: @, 어노테이션, 메타데이터, @Target, @Retention, 리플렉션, 커스텀 어노테이션, RUNTIME


어노테이션이란?

코드에 붙이는 메타데이터. @로 시작하며, 코드 자체의 동작은 바꾸지 않고 부가 정보를 제공한다.

메타데이터란?

데이터에 대한 데이터, 즉 코드 자체가 아니라 코드에 대한 부가 정보다.

사진 파일 = 데이터
사진의 촬영 날짜, 위치, 카메라 기종 = 메타데이터

코드(메서드) = 데이터
@Override = 메타데이터 ("이 메서드는 오버라이딩이다"라는 부가 정보)

어노테이션의 3가지 용도

용도 예시 동작 시점
컴파일러 검사 @Override, @Deprecated 컴파일 시점
코드 자동 생성 @Getter, @Builder (Lombok) 컴파일 시 코드 생성
런타임 동작 @Transactional, @Autowired 실행 시점 (리플렉션)

자주 쓰는 Java 기본 어노테이션

@Override        // 오버라이딩 검증 (부모 메서드 없으면 컴파일 에러)
@Deprecated      // 더 이상 사용하지 않는 코드 표시
@SuppressWarnings("unchecked")  // 컴파일 경고 억제

커스텀 어노테이션 만들기

@Target — 어디에 붙일 수 있는지

ElementType.METHOD       // 메서드
ElementType.TYPE         // 클래스, 인터페이스
ElementType.FIELD        // 필드
ElementType.PARAMETER    // 파라미터

@Retention — 언제까지 유지되는지

유지 시점 용도
SOURCE 컴파일 전까지 @Override
CLASS .class 파일까지 거의 안 씀
RUNTIME 실행 시점까지 Spring 어노테이션 대부분

Spring 어노테이션 대부분이 RUNTIME인 이유:
실행 시점에 리플렉션으로 어노테이션을 읽어서 동작하기 때문이다.
SOURCECLASS면 실행 시점에 어노테이션 정보가 사라져서 Spring이 읽을 수 없다.


어노테이션의 동작 원리

어노테이션 자체는 표시만 한다. 실제 동작은 어노테이션을 읽는 코드가 따로 있다.

어노테이션 = 표시
리플렉션   = 그 표시를 읽는 도구
실제 동작  = 표시를 읽고 처리하는 별도 코드 (인터셉터, AOP 등)

리플렉션이란?

실행 시점에 클래스, 메서드, 필드 정보를 읽고 조작하는 기능이다.
Spring이 @Autowired, @Transactional 같은 어노테이션을 실행 시점에 읽고 동작하는 게 전부 리플렉션 덕분이다.


커스텀 어노테이션 실무 예시

어노테이션 단독으로는 아무 동작도 안 한다

반드시 그걸 읽고 처리하는 코드(인터셉터, AOP 등)가 같이 있어야 한다.

// 1. 어노테이션 정의 (표시만)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired { }
// 2. 동작 코드 정의 (인터셉터에서 어노테이션 읽고 처리)
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                              HttpServletResponse response,
                              Object handler) {

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        // 리플렉션으로 어노테이션 있는지 확인
        LoginRequired annotation =
            handlerMethod.getMethodAnnotation(LoginRequired.class);

        if (annotation == null) {
            return true;  // 어노테이션 없으면 그냥 통과
        }

        // 어노테이션 있으면 로그인 체크
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
}
// 3. 사용 - 붙이기만 하면 자동으로 처리됨
@LoginRequired
@GetMapping("/mypage")
public String myPage() { ... }

왜 메서드 직접 호출 대신 어노테이션을 쓰나?

// 메서드 방식 - 매번 직접 호출, 까먹으면 누락
@GetMapping("/mypage")
public String myPage() {
    loginCheck();  // 매번 직접 호출해야 함
    ...
}

// 어노테이션 방식 - 붙이기만 하면 자동 처리
@LoginRequired
@GetMapping("/mypage")
public String myPage() { ... }

커스텀 어노테이션을 쓰는 핵심 이유:
공통 로직을 자동으로 처리해서 중복 코드를 없애고, 누락 실수를 방지하기 위해.

런타임 어노테이션을 읽는 방법

어노테이션을 읽고 처리하는 코드가 어디 있느냐는 용도에 따라 다르다.

용도 읽는 방법
HTTP 요청 앞에서 처리 (로그인, 권한) 인터셉터
메서드 실행 전후 처리 (트랜잭션, 로깅) AOP
그 외 커스텀 처리 직접 리플렉션

인터셉터는 PHASE 8 Spring MVC, AOP는 PHASE 7 Spring Core에서 자세히 다룬다.


면접 포인트

Q. 어노테이션이란 무엇인가요?

코드에 붙이는 메타데이터로, 컴파일 시점 검사, 코드 자동 생성, 런타임 동작 등 다양한 용도로 사용된다.

Q. @Retention(RUNTIME)이 왜 필요한가요?

Spring이 실행 시점에 리플렉션으로 어노테이션을 읽어서 동작하기 때문이다.
SOURCE나 CLASS면 실행 시점에 어노테이션 정보가 사라져서 Spring이 읽을 수 없다.

Q. 커스텀 어노테이션을 왜 사용하나요?

공통 로직을 자동으로 처리해서 중복 코드를 없애고 누락 실수를 방지하기 위해 사용한다.
어노테이션 자체는 표시만 하고, 인터셉터나 AOP가 그 표시를 읽고 실제 동작을 처리한다.