본문 바로가기
스프링 부트3

스프링 부트 3에서 커스텀 어노테이션 만들기

by 굿센스굿 2024. 12. 5.
반응형

 

**스프링 부트 3(Spring Boot 3)**는 다양한 기능과 유연한 확장성을 제공하여 개발자가 효율적으로 애플리케이션을 개발할 수 있도록 돕습니다. 그중에서도 어노테이션(annotation)은 코드의 간결성과 가독성을 높이는 데 중요한 역할을 합니다.
특히, 상황에 맞는 커스텀 어노테이션을 만들면 공통 로직을 추상화하거나 복잡한 설정을 간단히 표현할 수 있어 코드의 재사용성을 극대화할 수 있습니다. 이 글에서는 스프링 부트 3에서 커스텀 어노테이션을 만드는 방법을 예제를 통해 상세히 설명하겠습니다.


1. 커스텀 어노테이션의 기본 구성

커스텀 어노테이션은 Java의 메타 어노테이션을 사용해 정의할 수 있습니다. 자주 사용하는 메타 어노테이션은 다음과 같습니다.

  • @Target: 어노테이션을 적용할 수 있는 위치를 지정합니다. 예를 들어, 클래스, 메서드, 필드 등입니다.
  • @Retention: 어노테이션의 유지 기간을 설정합니다. 주로 RUNTIME으로 설정하여 실행 시점에 어노테이션 정보를 참조합니다.
  • @Documented: 어노테이션이 Javadoc에 포함되도록 설정합니다.
  • @Inherited: 어노테이션이 하위 클래스에 상속되도록 지정합니다.

기본 예제: 간단한 커스텀 어노테이션

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 MyCustomAnnotation {
    String value() default "Default Value"; // 속성 정의
}

2. 커스텀 어노테이션 활용 사례

커스텀 어노테이션은 다양한 방식으로 활용됩니다. 아래에서 대표적인 세 가지 사례를 다뤄보겠습니다.


사례 1: 메서드 실행 시간을 로깅하는 어노테이션

많은 개발자는 특정 메서드의 실행 시간을 측정하고 로그로 남기고 싶어 합니다. 이를 커스텀 어노테이션으로 간단히 구현할 수 있습니다.

  1. 어노테이션 정의
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
  1. Aspect 구현
    스프링의 AOP(Aspect-Oriented Programming)를 활용해 어노테이션이 적용된 메서드의 실행 시간을 로깅합니다.
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogExecutionTimeAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogExecutionTimeAspect.class);

    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = joinPoint.proceed();
        long executionTime = System.currentTimeMillis() - start;
        logger.info("{} executed in {} ms", joinPoint.getSignature(), executionTime);
        return proceed;
    }
}
  1. 사용 방법
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {
    @LogExecutionTime
    @GetMapping("/sample")
    public String sampleEndpoint() {
        // 간단한 로직
        return "Hello, World!";
    }
}

사례 2: 사용자 권한 검사 어노테이션

특정 API에 접근할 때 사용자의 권한을 확인해야 하는 경우, 커스텀 어노테이션으로 로직을 캡슐화할 수 있습니다.

  1. 어노테이션 정의
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckRole {
    String role();
}
  1. Aspect 구현
@Aspect
@Component
public class RoleCheckAspect {
    @Around("@annotation(checkRole)")
    public Object checkRole(ProceedingJoinPoint joinPoint, CheckRole checkRole) throws Throwable {
        String requiredRole = checkRole.role();
        // 사용자 권한 검증 로직 (예: SecurityContextHolder에서 권한 가져오기)
        String userRole = "USER"; // 예제용 하드코딩
        if (!userRole.equals(requiredRole)) {
            throw new SecurityException("권한 부족: " + requiredRole);
        }
        return joinPoint.proceed();
    }
}
  1. 사용 방법
@RestController
public class AdminController {
    @CheckRole(role = "ADMIN")
    @GetMapping("/admin")
    public String adminEndpoint() {
        return "Admin Access Granted!";
    }
}

사례 3: DTO 유효성 검사 어노테이션

입력 데이터의 유효성을 검사할 때 스프링의 Validator와 연계하여 커스텀 어노테이션을 사용할 수 있습니다.

  1. 어노테이션 정의
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = CustomValidator.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidName {
    String message() default "Invalid name";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
  1. Validator 구현
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class CustomValidator implements ConstraintValidator<ValidName, String> {
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && value.matches("^[a-zA-Z]+$");
    }
}
  1. 사용 방법
public class UserDTO {
    @ValidName
    private String name;
    // Other fields and methods...
}

3. 커스텀 어노테이션의 장점

  • 코드 재사용성: 반복되는 로직을 추상화하여 재사용 가능.
  • 가독성 향상: 코드가 더 간결하고 명확하게 표현됨.
  • 확장성: 다양한 설정과 검증 로직을 손쉽게 추가 가능.

결론

스프링 부트 3에서 커스텀 어노테이션을 활용하면 공통 로직을 효율적으로 관리하고 코드의 유지보수성을 크게 향상시킬 수 있습니다. 이 글에서 설명한 세 가지 사례를 참고해 실제 프로젝트에 적합한 커스텀 어노테이션을 설계해 보세요. 이를 통해 더욱 효율적이고 직관적인 코드를 작성할 수 있을 것입니다.

반응형