두둥 서비스 백엔드에서는 api 문서로 open api swagger를 사용중이다.
스웨거를 조금 커스텀하게 하면, 덕지덕지 붙는 어노테이션의 양을 줄일 수 있는데,
커스텀 어노테이션을 만들어서 리플랙션으로 해당 메소드의 어노테이션 정보를 가져와,
런타임에 커스텀을 진행 할 수 있다. ( 스웨거도 런타임에 api에 대한 정보들이 만들어진다 )
그방법을 공유하고자 한다.
목차
1. 문제점
2. 스웨거를 커스텀 할려면?
3. 커스텀 어노테이션 만들기
4. 적용하기
1. 문제점
@SecurityRequirement(name = "access-token")
@Tag(name = "4. [호스트]")
보통 컨트롤러단에 SecurityRequirement를 적어주고 스웨거 api 를 테스팅할 때 인증정보를 보낼 수 있게 설정해 둔다.
간혹 그러다, 인증 정보가 필요없는 공개된 api 목록이 생기는데 (열린 이벤트들의 목록 조회등),
그럴때 spring security config 에서 해당 api 인증도 풀고, 우린 문서화도 중요하므로
스웨거에도 반영을 해줘야한다.
하나를 풀려고 봤더니, 방법이 없다.
일일히 적어줘야한다
@GetMapping("/health")
@DisableSwaggerSecurity
public void health() {}
@PostMapping
// 메소드 레벨로 내려와버린 SecurityRequirement
@SecurityRequirement(name = "access-token")
@ApiErrorExceptionsExample(ExampleException2Docs.class)
public ExampleResponse create() {
return exampleApiService.createExample();
}
api가 몇개 없는 컨트롤러면 상관없지만, 10개 넘어가면 한 api의 인증을 풀기위해 적용해야한다는 점이 너무나도 불편하다.
따라서 커스텀 어노테이션을 만들어, 인증을 풀 수 있게끔 스웨거를 커스텀을 해보려한다.
2. 스웨거를 커스텀 하려면?
//SwaggerConfig ( 예시를 위해 간략화했다 두둥건 더 복잡하다)
@Bean
public OperationCustomizer customize() {
return (Operation operation, HandlerMethod handlerMethod) -> {
DisableSwaggerSecurity methodAnnotation =
handlerMethod.getMethodAnnotation(DisableSwaggerSecurity.class);
if (methodAnnotation != null) {
operation.setSecurity(Collections.emptyList());
}
};
}
스웨거를 커스텀할 수있는부분은 OperationCustomizer 이다.
handlerMethod 로 api의 메소드를 받아올 수 있고,
operation 은 문서로 적힐 정보에 대한 객체이다.
{
"tags": [
"pet"
],
"summary": "Updates a pet in the store with form data",
"operationId": "updatePetWithForm",
"requestBody": {
//생략
},
"responses": {
"200": {
"description": "Pet updated.",
"content": {
"application/json": {},
"application/xml": {}
}
},
"405": {
"description": "Method Not Allowed",
"content": {
"application/json": {},
"application/xml": {}
}
}
},
// 지워줄 부분임
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
operation 객체 예시 ( 기본 pet store 예시에서 가져왔다. https://petstore.swagger.io/ )
스웨거의 문서에 대한 정보는 index html을 정볼받은후에 서버로 api대한 정보들을 콜해서 받아오는 형식이다.
스웨거에서 네트워크 탭을 킨 후 새로고침을 하면 저런 형태의 오퍼레이션들을 json으로 받아온다.
결국 우린 opreation 에서 security 부분을 빈 array로 바꿔주면 그만이다.
그러면? 어떻게 security를 지워줘야할 api인것을 알 수 있을까?
간단하다 컴파일 타임에 표시를 해주면 되는거다.
3. 커스텀 어노테이션 만들기
컴파일 타임에 표시를 해주려면 어노테이션이 적격이다. 일일히 api 를 목록을 handlerMethod 받아서 path 별로 빼도 되지만,
좀 더 스마트하게, 커스텀 어노테이션을 활용할 것이다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisableSwaggerSecurity {}
간단하다. 타겟은 메소드 레벨로 , 리텐션(어노테이션 정보가 살아있을 시간) 은 런타임까지 잡아놨다.
4. 적용하기
// 원하는 메소드 하나에만 적용
@DisableSwaggerSecurity
@GetMapping("/health")
public void health() {}
@Bean
public OperationCustomizer customize() {
return (Operation operation, HandlerMethod handlerMethod) -> {
DisableSwaggerSecurity methodAnnotation =
handlerMethod.getMethodAnnotation(DisableSwaggerSecurity.class);
// DisableSecurity 어노테이션있을시 스웨거 시큐리티 설정 삭제
if (methodAnnotation != null) {
operation.setSecurity(Collections.emptyList());
}
return operation;
};
}
커스텀 어노테이션을 생성한 후 원하는 메소드에 해당 어노테이션을 달아주고,
디버거에서 handlerMethod 를 조회해보면, 우리가 붙였던 DisableSwaggerSecurity 가 어노테이션으로 있는것을 알 수 있다.
즉 해당 api는 시큐리티를 지워줘야할 api 며,
operation.setSecurity 로 빈 리스트를 설정하면 지울 수 있다. ( swagger operation json의 security 필드 참고 )
이로써 커스텀 어노테이션을 활용해서 시큐리티 설정을 지우는 방법을 알아보았다.
사실 nest js 스웨거로 커스텀한 경험이 있어 , 이번에도 어렵지 않게 해냈다.
다음 포스팅은 위처럼 같은 코드 여러 응답 예시에 관련한 포스팅 일것 같다.
@Tag 도 class 레벨 , 메소드 레벨 두개 동시에 달게되면 api 정보가 중복해서 적히게 끔 되어있는데,
잘만 커스텀하면 , 아래처럼 같은 컨트롤러에서도 따로 태그로 빼고싶은 api는 뺄수 있다.
두둥 레포지토리를 참고하시길 바란다.
'스프링' 카테고리의 다른 글
[스프링] spring swagger 같은 코드 여러 에러 응답 예시 만들기 (3) | 2023.03.06 |
---|---|
[스프링] 공통 응답 형식 만들기 ResponseBodyAdvice (0) | 2023.03.05 |
[스프링] error code 도메인 별 분리하기 (0) | 2023.03.05 |
[스프링] spring open api swagger basic auth 세팅 (1) | 2023.03.04 |
[스프링] Spring disable Aop in test (1) | 2023.01.23 |