티스토리 뷰

Backend

AOP , Swagger를 이용한 인증 로직 구현하기

지마켓 문한국 2022. 12. 13. 13:59

안녕하세요. SmilePay Engineering 팀 문한국입니다.  
이번 포스팅에서는 Spring의 주요 특징 중 하나인 AOP를 이용하여 인증 로직을 구현한 예시와 Swagger를 활용하여
header를 자동으로 연동한 내용을 공유하려고 합니다.


AOP 활용 하기

AOP는 기술 면접에서 Spring에 관한 부분에서 단골질문으로 나올 만큼 Spring에서는 중요한 개념이라고 생각합니다.  
관점 지향 프로그래밍인 AOP(Aspect Oriented Programming)는 proxy 패턴을 활용한 기술로 공통된 기능을 구현하여,  
필요한 상황에 사용할 수 있도록 하는 개념으로 코드의 중복을 피하고 기능을 모듈 형태로 사용할 수 있는 좋은 기능이라고 생각합니다.  

아래 이미지는 AOP를 설명하기 위하여 많이 활용되는 이미지입니다.
주황, 빨강, 파랑과 같이 각각의 기능은 ClassA,B,C 여러 위치에서 구현이 되고 사용되어 코드의 중복을 야기합니다.  
이러한 주황,빨강,파랑과 같은 기능을 하나의 관점으로 구현하여 Class와 격리시킵니다.  
그리고 PointCut(어디에 적용해야 하는지에 대한 정보)이라는 개념을 사용하여 특정 타이밍에 해당 기능이 실행되도록 합니다.  
설명을 하다 보니 다시 한번 AOP가 정말 객체지향에서 좋아할 만한 개념으로 여겨집니다.  

 

AOP 개념

개발 시에 로깅이나 인증 로직을 AOP로 구현하여 사용하면 코드의 간결함이나 중복 소스 제거 등 많은 이점이 있습니다.  
특히나 어노테이션을 활용하게 되면 더욱더 깔끔한 코드가 완성됩니다.
이러한 이유로 많은 라이브러리에서도 어노테이션을 추가하여 AOP를 많이들 구현하고 있습니다.

그래서 저도 Annotation, AOP를 이용하여 인증 로직을 구현한 내용을 공유드리려 합니다.
구현 순서는 아래와 같습니다.

1. Annotation 정의
2. Aspect 구현
3. 필요한 클래스 혹은 함수에 Annotation 추가

1. Annotation 추가  

이 단계는 간단합니다. Authorization class만 생성하면 됩니다.
@Target, Retention 등의 자세한 내용은 아래 포스팅을 참고하면 좋을 것 같습니다.

https://velog.io/@potato_song/Java-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EB%A7%8C%EB%93%A4%EA%B8%B0

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthorizationHeader {

}

2. Aspect 구현  

어떤 상황에서 실행을 할지 @PointCut을 이용하여 설정을 하고 로직이 실행될 위치를 정합니다.  
로직이 실행될 위치를 정하는 방법에는 함수가 성공적으로 실행된 후, 에러 발생 시 등 몇 가지가 더 존재하지만 크게는 3개가 있습니다.  

@Before : 전처리 로직을 구현할 때
@After : 후처리 로직을 구현할 때
@Around : 실행 전후를 모두 컨트롤하는 로직을 구현할 때

이후 해당 Annotation이 붙은 Method에 실행할 공통 로직을 구현하면 됩니다.

인증 로직의 경우는 전처리에 실행되는 경우로 아래와 같이 Before로 작성하였습니다.  
그리고 1번에서 만든 Annotation을 사용하기 위하여 Pointcut은 @AuthorizationHeader 붙인 함수의 경우로 설정하였습니다.

@Aspect
@Component
public class AuthorizationAspect {

    @Pointcut("@annotation(com.test.AuthorizationHeader)")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void Authorization(JoinPoint joinPoint) {
        //인증 로직 구현하기.
        //ex. header의 Authorization 값 체크 로직
    }

}

3. 필요한 클래스 혹은 함수에 Annotation 추가  

이제 모든 준비는 끝났습니다.  
인증이 필요한 API에 아래와 같이 @AuthorizationHeader만 붙이면 해당 API 호출 전에 인증 로직이 동작합니다.

@RestController
@RequestMapping("/api")
public class TestController {
    
    @GetMapping(value = "/test")
    @AuthorizationHeader
    public void testMethod(){

    }

}

이 같은 순서로 AOP를 통한 인증 로직을 간단하게 구현하고 필요한 API들에서 사용할 수 있습니다.


Swagger 활용하기

이렇게 인증 로직을 구현하면 동작은 이상이 없으나 저의 경우는 API Docs로 Swagger를 사용하고 있는데,  
Swagger에서는 header정보를 넣을 수가 없어서 실제 Swagger를 통한 호출에 어려움이 있었습니다.  
그래서 위에서 구현한 것과 같이 어노테이션만 붙이면 Swagger에서도 header값이 자동으로 추가되면 좋겠다고 생각하여 방법을 찾게 되었습니다.  

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class AuthorizationHeaderPlugin implements OperationBuilderPlugin {
    
    @Override
    public void apply(OperationContext context) {
        Optional<AuthorizationHeader> annotation = context.findAnnotation(AuthorizationHeader.class);
        if (annotation.isPresent()) { //1
            context.operationBuilder().requestParameters(Arrays.asList(createParameter()));
        }
    }

    protected RequestParameter createParameter() {
        return new RequestParameterBuilder()
                .name("Authorization")
                .in(ParameterType.HEADER) //2
                .query(p -> p.model(m -> m.scalarModel(ScalarType.STRING)))
                .description("Authorization Header")
                .required(true)
                .build();
    }
    
    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}

위 소스와 같이 OperationBuilderPlugin을 구현하여  Swagger에 필요한 Parameter를 커스텀할 수 있습니다.  
1과 같이 context의 annotation을 찾고 존재할 경우만 parameter를 추가합니다. 2와 같이 Authorization Header를 추가합니다.  
이렇게 OperationBuilderPlugin을 구현하면 아래와 같이 @AuthorizationHeader를 추가한 API는 Swagger에 자동으로 header가 추가됩니다.  

OperationBuilderPlugin 구현 전
OperationBuilderPlugin 구현 후


지금까지 AOP 기능을 활용하여 인증 로직을 공통화하고 Swagger까지 연동하여 편리하게 사용하는 방법에 대하여 공유드렸습니다.  
AOP 기능을 활용하고 Swagger까지 연동하는 부분은 개인적으로 매우 편리하였고 이 글을 보시는 분들에게도 많은 도움이 되었으면 좋겠습니다.  
끝까지 읽어주셔서 감사합니다.

댓글