Spring

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

파파누보 2024. 9. 10. 10:45
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
반응형