ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring boot 2.7.18에서 swagger 설정하기 <2>
    프레임워크/Spring Boot 2024. 1. 23. 02:21

     

    프로젝트를 함께 만들고 있는 프론트엔드 개발자들과 효율적인 커뮤니케이션을 위해서 스웨거를 사용하려고 합니다.
    적용하면 만난 이런 저런 이슈 사항들을 기록해보았습니다.
     

    프로젝트 버전

    spring boot: 2.7.18

    springdoc-openapi-ui: 1.7.0

     

     


     

     

    1. 프로젝트 코드에 작성

    어노테이션을 사용해서 컨트롤러의 메소드와 관련된 dto들에 명세를 추가해주면 web에서 확인이 가능합니다.

     

    •  api 명세 작성
      @Operation(summary = "게시글 생성", description = "제목, 내용, 작성자, 카테고리를 포함하는 게시글을 작성합니다.")
      @ApiResponses(value = {
              @ApiResponse(responseCode = "201", description = "게시물 생성 성공하였습니다."),
              @ApiResponse(responseCode = "400", description = "잘못된 요청입니다. 입력 데이터를 확인하세요."),
              @ApiResponse(responseCode = "500", description = "서버 내부 오류가 발생했습니다.")
      })
      @ResponseStatus(HttpStatus.CREATED)
      @PostMapping("/post")
      public void createPost(@Valid @RequestBody CreateReqDto dto) {
        postService.createPost(dto);
      }

     

    • 요청 dto 의 스키마 작성
    @Getter
    @ToString
    @RequiredArgsConstructor
    public class CreateReqDto {
    
      @Schema(description = "게시글의 제목", example = "Why this error occurs?")
      @NotBlank(message = "제목은 필수 입력 값입니다.")
      private final String title;
    
      @Schema(description = "게시글의 내용", example = "When I start my server, the error below shows")
      @NotBlank(message = "내용은 필수 입력 값입니다.")
      private final String content;
    
      @Schema(description = "게시글의 카테고리", example = "질문, 홍보, 상담")
      @NotNull(message = "카테고리는 필수 입력 값입니다.")
      private final String category;
    
      @Schema(description = "게시글의 작성자", example = "jack")
      @NotNull(message = "작성자는 필수 입력 값입니다.")
      private final String creator;
    
    }

     

    • 에러 dto의 스키마 작성
    @Schema(description = "에러 응답")
    @Getter
    public class HttpErrorInfo {
    
      @Schema(description = "에러 발생 시간", example = "2023-01-01T12:00:00")
      private final LocalDateTime timestamp;
    
      @Schema(description = "에러 발생 경로", example = "/api/v1/post")
      private final String path;
    
      @Schema(description = "HTTP 상태 코드", example = "400, 500")
      private final HttpStatus httpStatus;
    
      @Schema(description = "에러 메세지", example = "잘못된 입력입니다. 입력값을 확인해주세요")
      private final String message;
    
      public HttpErrorInfo(HttpStatus httpStatus, String path, String message) {
        timestamp = LocalDateTime.now();
        this.httpStatus = httpStatus;
        this.path = path;
        this.message = message;
      }
    }

     

    • application.yml에 media-type 추가
    springdoc:
      default-produces-media-type: application/json
      default-consumes-media-type: application/json

     

     

    모두 작성해주고 서버를 다시 실행하면 Swagger-ui 화면에서 반영된 내용을 확인할 수 있다.

    에러 dto 같은 경우 Controller에서 직접 'content' 프로퍼티로 추가하지 않아도 자동으로 맵핑이 됩니다.

     

     

    하지만 Controller의 모든 메소드에 성공, 실패 케이스를 꼼꼼하게 작성하다보면 컨트롤러 로직보다 api 명세 관련 코드가 더 많아질 것이고 유지보수에 어려움이 생길 것 같은 느낌이 강하게 듭니다.

     

     

     

    2. 인터페이스를 사용

    명세를 인터페이스로 분리하여 관리하는 것도 하나의 방법이 될 수 있다고 생각합니다.

    최소한 로직과 명세를 분리하여 작업자가 하나의 관심사만 신경쓸 수 있게 됩니다.

     

    • 인터페이스에 명세 작성
    public interface PostController {
      @Operation(summary = "게시글 생성", description = "제목, 내용, 작성자, 카테고리를 포함하는 게시글을 작성합니다.")
      @ApiResponses(value = {
              @ApiResponse(responseCode = "201", description = "게시물 생성 성공하였습니다."),
              @ApiResponse(responseCode = "400", description = "잘못된 요청입니다. 입력 데이터를 확인하세요."),
              @ApiResponse(responseCode = "500", description = "서버 내부 오류가 발생했습니다.")
      })
      @ResponseStatus(HttpStatus.CREATED)
      @PostMapping("/post")
      void createPost(@Valid @RequestBody CreateReqDto dto);
    
    
      @GetMapping("/post")
      ListReqDto<Post> getPostList(
              @RequestParam(defaultValue = "1") int page,
              @RequestParam(defaultValue = "10") int size
      );
    }

     

    • 컨트롤러에 로직 작성
    @Slf4j
    @RequiredArgsConstructor
    @RestController
    @RequestMapping("/api/v1")
    public class PostControllerImpl implements PostController {
    
      private final PostService postService;
    
      @Override
      public void createPost(@Valid @RequestBody CreateReqDto dto) {
        postService.createPost(dto);
      }
    
    
      @Override
      public ListReqDto<Post> getPostList(
              @RequestParam(defaultValue = "1") int page,
              @RequestParam(defaultValue = "10") int size
      ) {
        Pageable pageable = PageRequest.of(page - 1, size);
        ListReqDto<Post> dto = postService.getPostList(pageable);
    
        return dto;
      }
    }

     

     

    파일이 분리된 만큼 로직을 변경하면서 명세 갱신은 누락되는 경우가 발생할 수도 있겠으나 저는 개인적으로 나눠쓰는 것도 나쁘지 않다고 생각이 됩니다.

     

     

     

     


    [글 대표 이미지 출처]

    https://swagger.io/solutions/api-development/

Designed by Tistory.