스웨거(Swagger) - @ExampleObject value 속 긴 String 분리 과정 정리
2024. 3. 15. 00:01ㆍSpring Boot
728x90
반응형
#0. 개요
- controller에서 put / post method API 를 swagger 에 작성할 때, 스키마 뿐만 아니라, ExampleObject 코드로도 request body 가 어떤 모양이어야 하는 지 프론트엔드에게 설명을 해주고 싶다.
- 원할한 프론트 - 백 소통을 위해
- 그러나, controller의 코드가 swagger example value 문자열 때문에 너무 길어져서 백엔드 코드 가독성이 떨어졌다
- 심지어 exampleObject가 controller 마다 여러개 일 수도 있고, request Body Dto Class가 property 가 많으면 종잡을 수가 없다.
@PostMapping("/item") @Operation(description = "", summary = "메뉴 등록 API") public ResponseEntity<JsonResponse> operateFund( HttpServletRequest request, @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content( examples = { @ExampleObject(name = "메뉴 등록", value = """ { "id": 1, //.. 중략 } """, description = "설명") })) @org.springframework.web.bind.annotation.RequestBody MenuDto requestBody) { //..중략 JsonResponse response = JsonResponse.builder() .success(true) .code(ResultMessageCode.API_SUCCESS.getReturnCode()) .message(ResultMessageCode.API_SUCCESS.getReturnMessage()) .build(); return ResponseEntity.ok(response); }
- 어딘가에서 이 긴 json string 값들을 한 곳에서 보관하고, swagger 생성 시점에 주입시킬 수 없을까?
- 따라서 @ExampleObject 어노테이션의 externalValue 나 ref 를 활용할 방법을 검색
- 그런데 externalValue를 사용하지 못한다는 글이 많았고 실제로 그랬음
- 깃헙 이슈 댓글과 스택오버플로우를 찾던 중, 간간히 사람들이 올려준 다른 방법을 적용시켜보았다.
- 참조:
#1. 구현
1. 초기세팅
- 빌드 그래들:
- implementation 'com.googlecode.json-simple:json-simple:1.1.1'
- json 파일 생성:
- 각 post, put request body에 썼던 json text 들을 단일 json 파일에 저장.
- 파일 위치는 resources/static/swagger 이다.
- 디렉토리가 없으면 새로 만들거나, 편한 곳에 둬도 상관 없다.
- key 값 명칭은 url (사이에는 / 가 아닌 . 으로 처리)
- 이유는 추후 controller 에서 example Object 로 참조 할 때 아래 경로로 참조하기 때문
- 이때 key 명칭을 / 로 했으면 / 를 디렉토리 하위 경로로 인식
@ExampleObject(name = "메뉴 수정 예시 1", ref = "#/components/examples/menu.put")
- 이유는 추후 controller 에서 example Object 로 참조 할 때 아래 경로로 참조하기 때문
- 각 post, put request body에 썼던 json text 들을 단일 json 파일에 저장.
- swaggerExampleObject구현
- 메서드 역할:
- json 파일 → Example 객체 리스트로 변환
- 각 example의 description에 key 값을 매핑해서 추후 swagger 에 example Object를 주입시킬 때 description 으로 찾아감.
- 메서드 역할:
import io.swagger.v3.oas.models.examples.Example;
@Component
public class SwaggerExampleObject {
@Value("classpath:/static/swagger/requestBody.json")
Resource resource;
@Bean
public List<Example> exampleJsonValueMaker() throws
IOException,
ParseException,
IllegalAccessError {
JSONObject json = (JSONObject)new JSONParser().parse(
new InputStreamReader(resource.getInputStream(), "UTF-8"));
List<Example> exampleList = new ArrayList<>();
for (Object s : json.keySet()) {
Example example = new Example();
example.setValue(json.get(s));
example.setDescription(s.toString());
exampleList.add(example);
}
return exampleList;
}
}
- example 리스트 swagger 주입
- SwaggerConfig 측:
- for (Example e : swaggerExampleList) { openAPI.getComponents().addExamples(e.getDescription(), e); } return openAPI;
2. 여러 파일 대응
- 기존에는 Resource 객체에 단일 파일 경로만 집어넣어서 가져감.
- 이때 각 controller의 모듈에 따라 JSON string을 각각 다른 파일에 넣고 싶다는 니즈가 생김.
- 따라서 단일 json 파일이 아닌 여러 json 파일로 example 객체 리스트를 만들어 주입시키도록 수정.
- listFiles() :
- 디렉토리의 파일목록을 파일 배열로 반환한다
- 이때 각 controller의 모듈에 따라 JSON string을 각각 다른 파일에 넣고 싶다는 니즈가 생김.
@Component
public class SwaggerExampleObject {
@Value("classpath:/static/swagger")
File file;
@Bean
public List<Example> exampleJsonValueMaker() throws
IOException,
ParseException,
IllegalAccessError {
List<Example> exampleList = new ArrayList<>();
File[] swaggerExampleJsonFiles = file.listFiles();
for (File file : swaggerExampleJsonFiles) {
Resource resource = new FileSystemResource(file);
JSONObject json = (JSONObject)new JSONParser().parse(
new InputStreamReader(resource.getInputStream(), "UTF-8"));
for (Object s : json.keySet()) {
Example example = new Example();
example.setValue(json.get(s));
example.setDescription(s.toString());
exampleList.add(example);
}
}
return exampleList;
}
}
- 그래서 File 객체로 디렉토리 전체를 가져와서 파일 메서드 listFiles 로 파일 객체로 수정해줌. 그러나 배포 후 develop 서버로 올라갔을 때 해당 경로가 인식이 안 되고 오류가 난다!
- 이유인 즉슨,
- File은 파일 시스템의 로컬 파일을 기반을 둠
- Resource 객체는 URL 기반으로 파일을 찾음
- 수정 코드:
- PathMatchingResourcePatternResolver:
- classPath 상의 정적 리소스를 찾아서, 각 리소스에 대해 JSON 파일을 읽어와 JSONObject 를 파싱한다.
- getResources() 메서드로 정규식 문자열을 넣어주면, 해당 정규식 패턴과 일치하는 리소스의 정보를 가져올 수 있다.
-
- getResource() 메서드랑 다름. 해당 메서드는 정규식 지원 X
-
- PathMatchingResourcePatternResolver:
@Component
public class SwaggerExampleObject {
@Value("classpath:/static/swagger")
File file;
@Bean
public List<Example> exampleJsonValueMaker() throws
IOException,
ParseException,
IllegalAccessError {
List<Example> exampleList = new ArrayList<>();
File[] swaggerExampleJsonFiles = file.listFiles();
for (File file : swaggerExampleJsonFiles) {
Resource resource = new FileSystemResource(file);
JSONObject json = (JSONObject)new JSONParser().parse(
new InputStreamReader(resource.getInputStream(), "UTF-8"));
for (Object s : json.keySet()) {
Example example = new Example();
example.setValue(json.get(s));
example.setDescription(s.toString());
exampleList.add(example);
}
}
return exampleList;
}
}
#2. 사용 정리
1. controller 단
- post, put API request body exampleObject 단순화
@RequestBody(content = @Content(
examples = {
@ExampleObject(name = "메뉴 수정 예시 1", ref = "#/components/examples/menu.put"))
})
)
2. requestBody.json
- resources.static.swagger 폴더 내에 위치함
- 기존 작업된 내용을 requestBody.json 내의 json value 값으로 추가
- key값 부여 방식은 겹치지 않는 방식으로
- 본인은 url + api method 방식으로 결정
- 중간을 / 가 아닌 . 으로
{
"menu.put": [
{
"id": 1,
"name": "검색"
}
],
"menu.post": [
{
"name": "검색"
}
],
}
728x90
반응형
'Spring Boot' 카테고리의 다른 글
99클럽 2일차 TIL: Spring Boot 에러 핸들링 (2) | 2024.04.05 |
---|---|
Java Spring - 공부 여정 (0) | 2024.03.16 |
스웨거(Swagger) - 어노테이션 간단 정리 (0) | 2024.03.14 |
DTO 상속에 대하여 (0) | 2024.03.14 |
Java Spring - Cache 도입기 (2 / 2): Redis 적용기 (1) | 2024.03.14 |