본문 바로가기

Spring

Spring Boot에서 Custom Annotation으로 코드를 더욱 우아하게 관리하는 방법

728x90
반응형

Spring Boot에서 애노테이션은 코드의 가독성을 높이고 반복 코드를 줄여주는 강력한 도구입니다. 특히, Custom Annotation(커스텀 애노테이션)을 활용하면 특정 로직을 재사용하거나 추상화할 수 있어, 코드의 유지보수성과 확장성을 극대화할 수 있습니다. 이 블로그에서는 Custom Annotation을 만들어 사용하는 방법을 설명하고, 실제 프로젝트 예시와 함께 어떻게 활용할 수 있는지 알아보겠습니다.

1. Custom Annotation을 사용하는 이유

  • 중복 코드 제거: 반복되는 코드를 한 곳에 모아 커스텀 애노테이션으로 처리함으로써 코드의 중복을 줄일 수 있습니다.
  • 비즈니스 로직 분리: 핵심 비즈니스 로직에서 부가적인 로직(로그 처리, 유효성 검사 등)을 분리하여 코드의 가독성을 높이고 유지보수가 쉬워집니다.
  • 강한 결합도 감소: 특정 기능을 애노테이션으로 분리하면 여러 클래스에서 같은 기능을 재사용할 수 있고, 의존성을 줄일 수 있습니다.
  • 데코레이터 패턴과 유사한 효과: 커스텀 애노테이션은 메서드나 클래스에 부가 기능을 추가하는 데 유용합니다. 예를 들어, 로깅, 트랜잭션 관리 등을 자동화할 수 있습니다.
반응형

2. 프로젝트 설정 (Gradle 기반)

Spring Boot 프로젝트에서 커스텀 애노테이션을 사용하려면 Spring AOP와 같은 의존성이 필요합니다. 이를 위해 Gradle에서 아래와 같은 설정을 추가합니다.

plugins {
    id 'org.springframework.boot' version '3.1.0'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'java'
}

group = 'com.example'
version = '1.0.0-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-aop'
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

위 설정에서 spring-boot-starter-aop는 AOP(Aspect-Oriented Programming) 기반으로 커스텀 애노테이션을 처리할 때 필요합니다.

3. Custom Annotation 개발하기

3.1 커스텀 애노테이션 정의

간단한 예로 메서드 실행 시간을 측정하는 @ExecutionTime 애노테이션을 만들어보겠습니다.

package com.example.customannotation.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 애노테이션을 메서드에 적용하기 위해 ElementType.METHOD 사용
@Target(ElementType.METHOD)
// 런타임에 유지되어야 AOP에서 인식 가능하므로 RetentionPolicy.RUNTIME 지정
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecutionTime {
}

3.2 애노테이션을 처리하는 Aspect 클래스 작성

Aspect 클래스를 작성하여 @ExecutionTime 애노테이션이 붙은 메서드의 실행 시간을 측정할 수 있습니다.

package com.example.customannotation.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ExecutionTimeAspect {

    @Around("@annotation(com.example.customannotation.annotation.ExecutionTime)")
    public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object proceed = joinPoint.proceed(); // 실제 메서드 실행
        long endTime = System.currentTimeMillis();
        
        System.out.println(joinPoint.getSignature() + " 실행 시간: " + (endTime - startTime) + "ms");
        return proceed;
    }
}
  • @Aspect는 이 클래스가 AOP 기능을 제공하는 클래스임을 나타냅니다.
  • @Around는 특정 애노테이션이 적용된 메서드의 실행 전후를 감싸서 처리하는데 사용됩니다. 여기서는 @ExecutionTime 애노테이션이 적용된 메서드를 감싸고 실행 시간을 측정합니다.

4. 애노테이션 적용 및 활용

이제 @ExecutionTime 애노테이션을 실제 메서드에 적용하여 어떻게 동작하는지 확인해보겠습니다.

package com.example.customannotation.service;

import com.example.customannotation.annotation.ExecutionTime;
import org.springframework.stereotype.Service;

@Service
public class SampleService {

    @ExecutionTime
    public void executeTask() throws InterruptedException {
        // 예제: 2초 대기
        Thread.sleep(2000);
        System.out.println("작업 완료!");
    }
}

4.1 애플리케이션 실행

package com.example.customannotation;

import com.example.customannotation.service.SampleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CustomAnnotationApplication implements CommandLineRunner {

    @Autowired
    private SampleService sampleService;

    public static void main(String[] args) {
        SpringApplication.run(CustomAnnotationApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        sampleService.executeTask();
    }
}

애플리케이션을 실행하면 executeTask 메서드의 실행 시간이 콘솔에 출력됩니다.

5. Custom Annotation을 활용한 확장

위 예제에서는 메서드의 실행 시간을 측정하는 단순한 기능을 구현했지만, Custom Annotation을 통해 할 수 있는 일은 무궁무진합니다. 다음과 같은 시나리오에서도 유용하게 사용될 수 있습니다.

  • 로그 자동화: 특정 애노테이션을 붙인 메서드에 자동으로 로그를 남기는 로직을 구현.
  • 트랜잭션 관리: 애노테이션을 통해 특정 메서드나 클래스의 트랜잭션 처리 로직을 자동으로 적용.
  • 권한 검사: 메서드 호출 전에 사용자의 권한을 체크하고, 권한이 없으면 예외를 던지는 로직 추가.
728x90

Custom Annotation을 사용하면 코드의 가독성과 재사용성을 크게 높일 수 있습니다. 특히 AOP와 결합하여 부가적인 기능(로그, 트랜잭션, 권한 체크 등)을 애플리케이션의 핵심 비즈니스 로직에서 분리할 수 있습니다. 이로 인해 개발자는 비즈니스 로직에만 집중할 수 있고, 유지보수와 확장이 훨씬 수월해집니다.

728x90
반응형