본문 바로가기
TIL

Delivery프로젝트_3. 어노테이션을 만든다고?

by Wanado 2025. 2. 14.
728x90

팀플을 하며 총괄을 담당하는 조장님이 공통 설정부분을 이야기하면서 어노테이션을 만든다고 했다.

 

어노테이션은 제공되는거라고만 생각했는데..

 

어노테이션이란?

어노테이션은 다른 프로그램에게 유용한 정보를 제공하기 위해 사용되는 것으로 주석과 같은 의미를 가진다.

어노테이션의 역할

  1. 컴파일러에게 문법 에러를 체크하도록 정보를 제공한다.
  2. 프로그램을 빌드할 때 코드를 자동으로 생성할 수 있도록 정보를 제공한다.
  3. 런타임에 특정 기능을 실행하도록 정보를 제공한다.

어노테이션은 @를 사용하여 작성하며, 해당 타겟에 대한 동작을 수행하는 프로그램 외에는 다른 프로그램에게 영향을 주지 않는다.

 

 

어노테이션의 종류

어노테이션은 크게 세 가지로 구분된다.

 

- 자바에서 기본적으로 제공하는 표준 어노테이션

- 어노테이션을 정의하는 데 사용되는 메타 어노테이션

- 사용자 어노테이션

 

Ex) uuid를 생성하여 주는 커스텀 어노테이션 구현

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UUIDGenerator {
}

@Interface를 사용하여 아무런 요소도 갖지 않고, UUIDGenerator 명을 가진 어노테이션을 만들어준다. 해당 어노테이션은 실행시에도 유효하며, 필드를 대상으로 하는 어노테이션이다.

public class UUIDGeneratorProcessor {
    public static void process(Object object) {
        Class<?> clazz = object.getClass(); // 해당 클래스 정보를 가져온다.
        Field[] fields = clazz.getDeclaredFields(); // 해당 클래스에 선언된 필드 정보를 가져옴
        for (Field field : fields) { 
            if (field.isAnnotationPresent(UUIDGenerator.class)) {
            // 해당 필드에 UUIDGenerator 어노테이션이 존재한다면
                field.setAccessible(true);
               	// 해당 필드에 접근할 수 있도록 하여 준다.
                try {
                    field.set(object, UUID.randomUUID().toString());
                    // 해당 필드에 uuid 값을 할당한다.
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

자바의 리플렉션 api를 사용하여 해당 어노테이션이 붙은 필드의 경우는 uuid 값을 가지도록 하는 클래스를 구현하였다.

public class TestClass {
    @UUIDGenerator
    private String id;

    public String getId() {
        return id;
    }
}

 

공통 코드를 만드는것과는 어떤 부분이 다를까?

 

1. 중복 제거 및 코드 가독성 향상

예를 들어, 특정 메서드 실행 전에 로깅을 수행해야 하는 경우를 생각해 봅시다.

기존 방식 (공통 서비스 메서드 호출)

@Service
public class MyService {

    @Autowired
    private LoggingService loggingService;

    public void doSomething() {
        loggingService.logBefore("doSomething");
        System.out.println("Business Logic");
        loggingService.logAfter("doSomething");
    }
}

이 방식은 공통 서비스(LoggingService)를 호출하는 방식이지만, 모든 메서드에서 로깅을 직접 호출해야 한다는 단점이 있습니다.

커스텀 어노테이션 방식

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
@Aspect
@Component
public class LoggingAspect {

    @Around("@annotation(Loggable)")
    public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before: " + joinPoint.getSignature());
        Object result = joinPoint.proceed();
        System.out.println("After: " + joinPoint.getSignature());
        return result;
    }
}
@Service
public class MyService {

    @Loggable
    public void doSomething() {
        System.out.println("Business Logic");
    }
}
 
 

장점: @Loggable만 붙이면 자동으로 로깅이 수행되므로 중복 코드가 사라지고 가독성이 향상됩니다.


2. AOP와 함께 활용 가능 (비침투적, 유지보수성 향상)

  • AOP(Aspect-Oriented Programming)와 함께 사용하면 비즈니스 로직을 변경하지 않고도 공통 기능을 적용할 수 있습니다.
  • 즉, 모듈화된 공통 서비스를 직접 호출하지 않아도 자동으로 적용되므로 유지보수가 편해집니다.

예를 들어, @Transactional이 커스텀 어노테이션으로 되어 있어 비즈니스 로직에서 직접 트랜잭션을 관리할 필요가 없는 것과 같은 원리입니다.


3. 개발자의 실수 방지

공통 서비스 방식으로 직접 호출하는 경우, 개발자가 실수로 호출을 빼먹을 가능성이 있습니다.

예를 들어, 특정 API에서 권한 검증을 해야 하는 경우를 생각해 봅시다.

공통 서비스 방식 (실수 가능성 O)

public void deletePost(Long postId, User user) {
    authService.checkPermission(user, postId);  // 개발자가 호출해야 함
    repository.delete(postId);
}

문제점: 개발자가 authService.checkPermission() 호출을 깜빡하면 보안 문제가 발생할 수 있음.

커스텀 어노테이션 방식 (실수 가능성 X)

 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
}
@Aspect
@Component
public class PermissionAspect {

    @Before("@annotation(RequiresPermission)")
    public void checkPermission(JoinPoint joinPoint) {
        // 권한 체크 로직
    }
}
@RequiresPermission
public void deletePost(Long postId, User user) {
    repository.delete(postId);
}

4. 공통 서비스 vs. 커스텀 어노테이션 선택 기준

선택 기준공통 서비스 방식커스텀 어노테이션 방식

코드 가독성 서비스 메서드 호출이 많아 가독성이 낮을 수 있음 어노테이션만 추가하면 되므로 가독성이 좋음
재사용성 여러 클래스에서 직접 호출해야 함 한 번 정의하면 여러 곳에서 간편하게 사용 가능
AOP 적용 가능 여부 직접 호출 방식이라 AOP 적용이 어려움 AOP와 결합하면 비즈니스 로직 변경 없이 적용 가능
실수 가능성 개발자가 호출을 잊을 가능성이 있음 어노테이션을 붙이기만 하면 자동 적용됨

결론

  • 모듈화된 공통 서비스를 만들어도 되지만, 중복 코드가 발생하고 실수 가능성이 존재.
  • 커스텀 어노테이션을 사용하면 가독성이 향상되고, AOP를 활용하여 비침투적으로 공통 기능을 적용할 수 있음.
  • 특히 트랜잭션, 로깅, 권한 체크 등 반복적으로 필요한 기능을 적용할 때 커스텀 어노테이션이 효과적.

따라서 공통 서비스와 커스텀 어노테이션은 서로 보완적인 관계이며, 특정 상황에 맞게 선택하는 것이 중요합니다! 🚀

 

 

 

 

 

 

출처:

https://mihee0703.tistory.com/207

https://ittrue.tistory.com/156#google_vignette

728x90