본문 바로가기
백엔드

[Swagger] 2. Swagger3에서 Group별로 Tag 설정하기(feat. OpenApiCustomiser)

by BeforB 2023. 5. 16.
728x90

 

목차

1) Group이 하나일 때 비어있는 Tag(API가 없는 태그)는 Swagger-ui에 미노출하기

2) SwaggerConfig에서 path에 따라 그룹을 나누고 각 그룹별로 Tag 리스트 관리하기

 

 

 

 

API가 많아지면서 각 API를 path나 package에 따라 Group으로 나누고 해당 그룹 내에서 Tag를 별도로 관리하고 싶다는 니즈가 있었고, 추가로 Tag를 클래스 단위가 아니라 메서드에 달았을 때는 description을 작성할 수 없어서 name과 description을 모두 사용할 수 있도록 Tag를 한 번에 관리하고 싶었다.

 

구글링했을 때 나오듯이 SwaggerConfig에서 프로젝트에서 사용하는 모든 태그를 한 번에 관리할 수도 있지만 그 경우에 태그를 해당 그룹에서 사용하지 않더라도 모든 태그 리스트가 노출되는 이슈가 있어서 OpenApiCustomiser를 이용하여 각 태그를 그룹별로 관리하는 법을 찾아냈다.

 

 

 

Swagger 그룹

 

 

 

 

 

 

 

이슈) 기본 SwaggerConfig.java 작성 방법

구글링하면 그냥 이렇게 모든 태그를 한 번에 관리하고 설정하는 방법만 나오는데 이 경우에는 해당 그룹에서 사용하지 않는 태그도 모두 노출되는 이슈가 있었다.

 

@Configuration
public class SwaggerConfig {

    @Bean
    public GroupedOpenApi group1() {
    	String[] pathsToMatch = {"/admin/**"};
        
        return GroupedOpenApi.builder()
        		     .group("admin")
                             .pathsToMatch(pathsToMatch)
                             .build();
    }
    
    @Bean
    public GroupedOpenApi group2() {
    	String[] pathsToMatch = {"/customer/**"};
        
        return GroupedOpenApi.builder()
        		     .group("customer")
                             .pathsToMatch(pathsToMatch)
                             .build();
    }
    
    @Bean
    public OpenAPI springOpenApi() {
    	List<Tag> tagList = {
            new Tag(name = "AdminBaseController", description = "관리자 공통 API"),
            new Tag(name = "AdminManageCustomerController", description = "관리자 회원 관리 API"),
            new Tag(name = "AdminLoginController", description = "관리자 로그인 API"),
            new Tag(name = "LoginController", description = "로그인 API"),
            new Tag(name = "MypageController", description = "회원 마이페이지 API")
        }
    	return new OpenAPI().info(new Info()
        			        .title("forB's API")
                                        .description("Swagger API 설정 프로젝트")
                                        .tags(tagList)
                                        .version("v0.0.1"));
        								
    }
	
}

위의 경우 admin 그룹에서는 AdminBaseController, AdminManageCustomerController, AdminLoginController 세 개의 태그에 해당하는 API만 있더라도 Swagger-ui에서 다섯 개의 태그가 모두 노출되었다. (LoginController, MypageController 두 태그는 비어있는 채로 노출됨)

 

 

1차 시도(Group이 여러 개일 경우 X.  Group이 하나 뿐일 때는 가능)

비어있는 태그를 미노출시키는 법을 Stackoverflow에서 찾다가 아래 솔루션으로 해결이 되었다길래 해보았는데 실패했다.

그룹이 하나일 경우에는 가능하지만, 그룹이 나눠져 있는 경우에는 아래 솔루션으로 해결되지 않는 듯 했다.

// Tag 한 곳에서 관리
@Bean
public OpenAPI springOpenApi() {
    List<Tag> tagList = {
        new Tag(name = "AdminBaseController", description = "관리자 공통 API"),
        new Tag(name = "AdminManageCustomerController", description = "관리자 회원 관리 API"),
        new Tag(name = "AdminLoginController", description = "관리자 로그인 API"),
        new Tag(name = "LoginController", description = "로그인 API"),
        new Tag(name = "MypageController", description = "회원 마이페이지 API")
    }
    List<Tag> filteredTags = tagList.stream()
                                .filter(tag -> !tag.getName().isEmpty())
                                .collect(Collectors.toList());
                                
    return new OpenAPI().info(new Info()
                        .title("forB's API")
                                    .description("Swagger API 설정 프로젝트")
                                    .tags(filteredTags)
                                    .version("v0.0.1"));

}

 

 

 


 

 

 

최종 해결법!!

 

 

1. SwaggerConfig.java 파일

Swagger에서 관리자용 API와 회원 API를 구분하여 그룹을 나누고, 각 그룹에서만 노출되는 태그 리스트들을 한 번에 관리하기 위한 SwaggerConfig 파일 작성하기.

 

이렇게 하면 해당 그룹에서 실제로 사용 중인 태그만 모아서 볼 수 있다!

import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;

@Configuration
public class SwaggerConfig {

    @Bean
    public GroupedOpenApi group1() {
        // 관리자 API 그룹
        List<Tag> tags = List.of(
                                new Tag().name("AdminBaseController").description("<b>[관리자]</b> 공통 API"),
                                new Tag().name("AdminManageCustomerController").description("<b>[관리자]</b> 고객 관리 API"),
                                new Tag().name("AdminLoginController").description("<b>[관리자]</b> 로그인 API")
                                );

        return GroupedOpenApi.builder()
                             .group("admin")
                             .pathsToMatch("/admin/**")
                             .addOpenApiCustomiser(openApi -> {
                                 openApi.setTags(tags);
                             })
                             .build();
    }

    @Bean
    public GroupedOpenApi group2() {
        // 회원 API 그룹
        List<Tag> tags = List.of(
                                new Tag().name("MypageController").description("<b>[회원]</b> 마이페이지 API"),
                                new Tag().name("LoginController").description("<b>[회원]</b> 로그인 API")
                                );

        return GroupedOpenApi.builder()
                             .group("customer")
                             .pathsToMatch("/customer/**")
                             .addOpenApiCustomiser(openApi -> {
                                 openApi.setTags(tags);
                             })
                             .build();
    }
}

 

 

 

 

 

2. 각 Controller 클래스, 메서드에 @Tag 작성

1) Class 단위에 @Tag 설정하기

해당 클래스의 모든 메서드는 MypageController 태그에 포함된다.

@Tag(name = "MypageController", description = "회원 마이페이지 API")
@RestController("/customer/mypage")
public class MypageController {
	
    @GetMapping(value = "/reservation")
    @Operation(summary = "예약 내역 조회 API", description = "마이페이지 예약 내역 조회")
    public ResponseEntity<CustResponse> mypage(CustRequest custRequest) {
    	// ...
    }
    
}

 

 

2) 각 API 별로 @Tag 설정하기

주의! API 별로 

@RestController("/customer")
public class LoginController {
	
    @PostMapping(value = "/login")
    @Operation(summary = "로그인 API", 
    		description = "회원 로그인",
                tags = "LoginController")
    public ResponseEntity<LoginResponse> login(LoginRequest loginRequest) {
    	// ...
    }
    
    @PostMapping(value = "/logout")
    @Operation(summary = "로그아웃 API", 
    		description = "회원 로그아웃",
                tags = "LoginController")
    public ResponseEntity<LoginResponse> logout(LoginRequest loginRequest) {
    	// ...
    }
    
}

 

 

3) 클래스, 메서드에 각각 중복으로 @Tag를 설정한다면?

하나의 API에 여러 @Tag를 설정할 경우 설정된 모든 태그에 해당 API가 존재하게 된다.

ex) 아래의 경우 /customer/login API는 CustomerController에도, LoginController에도 모두 속하게 된다.

@Tag(name = "CustomerController", description = "회원 관리 API")
@RestController("/customer")
public class LoginController {
	
    @PostMapping(value = "/login")
    @Operation(summary = "로그인 API", 
    		description = "회원 로그인",
                tags = "LoginController")
    public ResponseEntity<LoginResponse> login(LoginRequest loginRequest) {
    	// ...
    }
    
}

 

 

 

 

 

 

728x90

댓글