개요
스프링을 이용해 개발하며 언제든지 사용할 수 있는 나만의 레거시 코드를 작성해 두어야겠다는 생각이 들어 개발하던 도중 적용했던 권한검사에 관련된 부분을 적어두고자 한다. 다른 많은 방법들이 있겠지만 AOP와 Annotation을 이용한 방법이 개발 도중 만났던 문제점과 현재 회사에서 제시했던 AOP에 대한 문제점을 커버하는 해결책이라 생각하여 적용해 보았다.
AOP 사용 시 문제점
현재 근무하고 있는 회사에서는 AOP를 사용하지 않는다. 한눈에 코드의 흐름이 들어오지 않기 때문이다. 또한 나만의 레거시 코드를 위해 만들던 프로젝트에서는 일부 메서드에만 권한을 적용하고 싶었으나 순수 AOP만으로는 메서드 별로 적용할 수 없었다.
정리하자면 다음과 같다.
- 복잡성: 코드의 모듈화가 향상되지만, 여러 곳에서 적용되는 규칙 때문에 코드의 복잡성이 증가한다.
- 숨겨진 동작: AOP는 코드의 흐름을 바꿀 수 있기 때문에 코드의 동작을 이해하기 힘들다.
- 디버깅의 어려움: 코드의 실행 경로가 예상치 못하게 변경될 수 있기 때문에 디버깅이 힘들다.
- 문제 추적의 어려움: 코드의 흐름이 여러 곳으로 분산되므로 문제가 발생했을 때 문제 추적이 힘들다.
- 일부 메서드 적용 불가능: 메서드 별로 적용하고 싶었으나 AOP만을 이용했을 때는 어려움이 있다.
AOP 사용 시 장점
단점들이 분명 있지만 AOP 사용시 장점들도 존재한다. 그렇기에 AOP를 공부하고 적용해보고 싶었다.
장점들을 살펴보자.
- 관점 지향 프로그래밍: 애플리케이션을 특정 관점(보안, 로깅, 트랜잭션)으로 바라보고, 이러한 관점을 모듈화 하여 적용할 수 있다.
- 중복 코드 제거: AOP를 사용하면 여러 곳에서 반복되는 코드를 한 곳에 모아서 관리할 수 있다.
- 모듈화 및 재사용성: AOP는 비즈니스 로직과는 독립적으로 동작하기 때문에 모듈화와 재사용성을 높일 수 있다.
- 가독성 향상: AOP를 사용하면 핵심 비즈니스 로직과 별개로 발생하는 횡단 관심사(로깅, 트랜잭션, 보안 등...)를 분리할 수 있어 가독성이 향상된다.
AOP에는 포기하고 싶지 않은 장점들이 존재한다. 필자는 AOP를 한번쯤 사용해보고 싶었고 단점을 어느 정도 상쇄할 수 있는 방법을 찾아 적용해 보았다. 한번 살펴보자.
Custom Annotation과 함께 AOP 적용하기
AOP만을 사용한다면 특정 메서드에만 권한검사 로직을 적용하기 힘든 문제점이 존재하였다. 이를 해결하기 위해 Custom Annotation을 함께 사용하였고, Annotation이 존재한다면 무언가가 적용되어 동작하고 있다는 것을 알릴 수 있어 숨겨진 동작에 대한 단점을 어느 정도 상쇄할 수 있다고 판단하였다.
Custom Annotation 적용하기
/**
* 권한 확인 관련 ANNOTATION 구성
* AOP 적용 AuthAspect.java 클래스 파일 확인
* */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BeforeRequiresAuthorization {
String value();
}
먼저 위와 같이 어노테이션을 직접 만들어준다. value()를 선언한 이유는 각 메서드 별로 검사해야 하는 권한명을 받기 위해서다.
이후 컨트롤러에 직접 만든 어노테이션을 적용해 준다.
@PostMapping("/bs/admin")
@BeforeRequiresAuthorization("admin-create")
public APIDataResponse<AdminResponseDto> createAdmin(
@Validated @RequestBody AdminCreateRequestDto adminCreateRequestDto
) {
AdminResponseDto adminResponseDto = adminService.createAdmin(adminCreateRequestDto);
return APIDataResponse.of(adminResponseDto);
}
마지막으로 어노테이션이 적용된 메서드마다 AOP가 적용되도록 AOP클래스를 만들어 적용해 준다.
@Aspect
@Component
public class AuthAspect extends BaseController{
private final AuthFuncRepository authFuncRepository;
@Autowired
public AuthAspect(AuthFuncRepository authFuncRepository){
this.authFuncRepository = authFuncRepository;
}
@Before("@annotation(requiresAuthorization)")
public void checkAuthorization(BeforeRequiresAuthorization requiresAuthorization) {
String authorization = requiresAuthorization.value();
AuthFuncEntity auth = authFuncRepository.findByFuncNameAndAuthId(this.getSessionInfo().getFuncId(), authorization);
if(auth == null){
throw new APIException(ErrorCode.UNAUTHORIZED_FAIL);
}
}
}
필자가 메서드를 따로 적용하고 싶었던 이유는 해당 프로젝트에서 read에 관한 영역은 권한검사를 하지 않아도 된다고 생각했기 때문이다. 메뉴권한을 따로 관리하기 때문에 메뉴 접근권한 자체가 read 권한검사에 대한 역할을 수행하고 있다고 생각했기 때문이다.
그래서 AOP를 이용해서 read를 제외한 다른 기능에 대해서만 권한검사를 적용하고 싶었고 이에 대한 방법을 찾던 도중 어노테이션을 이용하여 적용할 수 있다는 방법을 알게 되어 적용하게 되었다.
정리
커스텀 어노테이션을 달아주어 무언가 동작하고 있다는 것을 알려주었고, 어노테이션의 이름을 통해 메서드가 실행되기 전에 실행된다는 것을 알려주었다. 단점들을 어느 정도 상쇄한 것처럼 보인다(필자의 주관적인 생각). 하지만 무분별하게 많이 사용했다가는 숨겨진 동작들이 많아 디버깅이 어렵고 복잡해질 것이라는 생각이 든다. AOP는 팀원들과 미리 약속된 영역들만 사용하는 게 좋을 것 같다. 권한검사, 로깅 등.... 어느 프로젝트를 유지보수하게 되어도 우리 팀에서 이 영역들은 AOP가 적용되어 있다!라는 걸 인지할 수 있도록 말이다.
참조
깃허브 소스
https://github.com/Doosic/backoffice-backend
https://github.com/Doosic/backoffice-front
'Experience' 카테고리의 다른 글
MCMS 웹 고도화 프로젝트 후기 (0) | 2024.01.25 |
---|---|
Instagram Api Refresh Token batch 리펙토링 (0) | 2023.06.24 |
JRebel이 안될때 확인해 볼 사항들 (0) | 2023.06.21 |
특수 사례 패턴 (외부 API를 대하는 방법) (0) | 2022.12.19 |
MCMS 웹 프로젝트 후기 (2) | 2022.12.04 |