[SpringBoot]3. 관점지향프로그래밍 AOP 뿌시기

이한음·2024년 5월 20일

SpringBoot 개발 기록

목록 보기
3/3

"관점지향프로그래밍 AOP에 대해 아는대로 설명해주세요" 몇 일전 기술 면접에서 받은 질문이었다.
아는대로 설명했지만 여태 적용 해본 적이 없기에 이번 기회를 통해 제대로 학습하고자 한다.

AOP의 개념

AOP는 "Aspect-Oriented Programming"의 줄임말로 "관심사의 분리"를 위해 사용된다.
반복되고 공통적으로 사용되는 부분을 분리함으로써 모듈성을 증가시키는 프로그래밍 방법이다.

어렵다...
조금 더 쉽게 설명하자면
AOP를 사용하면 코드에서 로깅, 보안, 트랜잭션 관리 등을 따로 떼어 관리할 수 있다.

예를 들어, 어떤 프로그램에는 사용자가 로그인할 때마다 로그를 남기는 기능이 필요할 수 있다.
이 로그 기능은 여러 곳에서 사용되고 같은 코드가 여러 곳에 흩어져 있을 수 있다.
AOP를 사용하면 이런 로그 기능을 한 곳에서 관리할 수 있는 것이다.
그러면 로그인 관련 코드를 변경해야할 때 해당부분만 수정하면 된다.
다른 부분은 건드리지 않아도 되고, 결과적으로 코드가 더 깔끔해지고 유지보수하기 편해진다.

AOP의 장점

  1. 관심사의 분리 : BL과 부가적인 관심사를 분리함으로써 코드의 가독성과 유지보수성을 향상시킴
  2. 재사용성 : 공통으로 사용되는 기능을 하나의 모듈로 추출하여 재사용할 수 있음.
  3. 중앙 집중화된 관리 : 특정 관심사를 중앙 집중화하여 관리하므로 코드를 변경하거나 수정할 때 일일이 찾아 수정할 필요가 없다.
  4. 모듈화된 코드 : 코드를 모듈화하여 더 간결하고 명확한 형태로 유지할 수 있음.

AOP의 단점

  1. 학습 곡선 : 처음 접하는 개발자에게는 관점 지향적인 사고방식을 이해하고 적용하는데 시간이 필요함 (바로 저에요... ㅠㅠ)
  2. 과도한 사용의 위험 : 너무 많은 관점(Aspects)을 적용하면 코드의 복잡성이 증가할 수 있다.
  3. 런타임 오버헤드 : 런타임 오버헤드가 발생할 수 있다. 성능에 영향을 준다는 뜻.

AOP 용어 정리

  • Aspect(관점) : 특정 관심사를 나타냄. 예를 들면 로깅이나 보안같은 관심사가 Aspect로 구현될 수 있다.
  • Advice(조언) : 관점에서 수행되는 작업을 말한다. 다시 말하면, 특정 지점에 실행되는 코드이다. Before, After, Around 같은 종류로 나뉜다.
  • Pointcut(지점선택) : 어디에 Advice를 적용할지 정의한다. 특정 메서드 호출, 필드 변경, 예외 발생 등과 같은 특정 지점을 가리킴.
  • Join point(결합 지점) : 코드 실행중 Aspect가 적용될 수 있는 특정 지점을 말한다.

AOP로 로그 구현하기

먼저, gradle에 의존성을 추가해주자.

implementation 'org.springframework.boot:spring-boot-starter-aop'

공통 로깅 모델을 구현하기 위해 LoggingAspect 클래스를 생성해주었다.

@Slf4j
@Aspect
@Component
public class LoggingAspect {

}

스프링 AOP를 사용한다는 의미인 @Aspect 어노테이션을 추가하고 @Component 어노테이션을 함께 추가하였다.
이때 여러 블로그를 보면 @EnableAspectJAutoProxy 어노테이션에 대해 언급하지만, 스프링부트에서는 자동으로 AOP 프록시 빈을 등록하기 때문에 굳이 붙여주지 않아도 된다!

다음으로 @Pointcut 어노테이션으로 sopio.app.server.controller 하위의 모든 메소드에 적용되도록 설정하였다.

@Slf4j
@Aspect
@Component
public class LoggingAspect {

	@Pointcut("execution(* sopio.app.server.controller.*.*(..))")
    private void cut() {}

}

Advice 관련 어노테이션은 다음과 같은 것이 있다.

  • @Before : 대상 메소드 실행 전에 Advice 실행
  • @After : 대상 메소드 실행 후에 Advice 실행
  • @AfterReturning : 대상 메소드가 정상적으로 실행되고 반환 후에 Advice 실행
  • @AfterThrowing : 대상 메소드에서 예외가 발생했을 때 Advice 실행
  • @Around : 대상 메소드 실행 전 후 또는 예외 발생 시에 Advice 실행

아래 코드가 내가 AOP로 구현한 로그 모듈이다!!

@Slf4j
@Aspect
@Component
public class LoggingAspect {

    @Pointcut("execution(* sopio.app.server.controller.*.*(..))")
    private void cut() {}

    @Around("cut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Method method = getMethod(joinPoint);
        log.info("Method Log: {} || Args: {}", method.getName(), Arrays.toString(joinPoint.getArgs()));

        Object result = joinPoint.proceed();

        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        log.info("Method {} is finished || Execution Time: {} ms", method.getName(), executionTime);
        return result;
    }

    @AfterThrowing(pointcut = "cut()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
        Method method = getMethod(joinPoint);
        log.error("AfterThrowing Method: {} || Exception: {}", method.getName(), exception.getMessage());
        log.error("Exception type: {}", exception.getClass().toGenericString());
        log.error("Exception point: {}", exception.getStackTrace()[0]);
    }

    private Method getMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        return signature.getMethod();
    }
}

@Around 어노테이션을 사용하여, 메소드의 이름과 파라미터를 출력한다.
proceed() 함수를 통해 메소드가 실행되고 결과를 받으면, 성공 메시지와 함께 실행 시간을 출력한다.
만약 예외가 발생하면 @AfterThrowing 어노테이션을 통해 예외에 대한 정보(메시지, 예외타입, 발생지점)를 출력한다.

swagger로 테스트 해보니 모두 잘 작동한다!!
진작 알았으면, 면접 때 술술 말했을텐데... 약간의 아쉬움이 밀려오지만 그래도 뿌듯하다.

profile
Server Engineer 이한음 입니다.

0개의 댓글