Swagger API λ¬Έμ„œν™”

JeongMinΒ·2023λ…„ 5μ›” 22일
0

πŸ“„ μš”κ΅¬ 사항

μ•„λž˜ API에 λŒ€ν•΄ API λ¬Έμ„œλ₯Ό μž‘μ„±ν•΄λΌ.

  1. κ²Œμ‹œκΈ€ μž‘μ„± κΈ°λŠ₯
  2. κ²Œμ‹œκΈ€ 전체 쑰회 κΈ°λŠ₯
  3. νŠΉμ • κ²Œμ‹œκΈ€ 쑰회 κΈ°λŠ₯
  4. νŠΉμ • κ²Œμ‹œκΈ€ μˆ˜μ • κΈ°λŠ₯
  5. νŠΉμ • κ²Œμ‹œκΈ€ μ‚­μ œ κΈ°λŠ₯
  6. κ²Œμ‹œκΈ€ 검색 κΈ°λŠ₯

μ œμ•½ 쑰건

  • Swagger, Github Wiki, Notion, Postman, Gitbook 쀑 μ‚¬μš©ν•˜κΈ°
  • API λ¬Έμ„œμ— μ•„λž˜μ˜ λ‚΄μš©μ€ λ°˜λ“œμ‹œ ν¬ν•¨μ‹œν‚€κΈ°
    • URL μ£Όμ†Œ / HTTP λ©”μ„œλ“œ
    • API μ„€λͺ…
    • μš”μ²­ ν˜•νƒœ
      • Path Parmas
      • Query Params
      • Body Params
      • ν•„μˆ˜ μ—¬λΆ€
      • 데이터 νƒ€μž…
    • 응닡 ν˜•νƒœ
      • 응닡 μ½”λ“œ(status code)
      • 각 응닡 μ½”λ“œλ³„ μ„€λͺ…
      • 응닡 ν˜•νƒœ (response value)
      • 응닡 κ°’μ—μ„œ 각 νŒŒλΌλ―Έν„°μ˜ 의미
      • 응닡 κ°’μ—μ„œ 각 νŒŒλΌλ―Έν„°μ˜ ν•„μˆ˜ μ—¬λΆ€
      • 응닡 κ°’μ—μ„œ 각 νŒŒλΌλ―Έν„°μ˜ 데이터 νƒ€μž…

OpenApiConfig

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI openAPI(){
        Info info = new Info()
                .title("읡λͺ… κ²Œμ‹œνŒ API λ¬Έμ„œ")
                .version("v0.0.1")
                .description("읡λͺ… κ²Œμ‹œνŒμ˜ API λ¬Έμ„œμž…λ‹ˆλ‹€.");
        return new OpenAPI()
                .components(new Components())
                .info(info);
    }
}
  • ν•΄λ‹Ή ConfigνŒŒμΌμ„ ν†΅ν•΄μ„œ API에 λŒ€ν•œ 제λͺ©, 버전, μ„€λͺ…을 달 수 μžˆλ‹€.

κ²°κ³Ό


PostController

@RestController
@RequestMapping("/boards")
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    @Operation(summary = "κ²Œμ‹œκΈ€ 쑰회", description = "ν•΄λ‹Ή id의 κ²Œμ‹œκΈ€ 쑰회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ 쑰회 성곡",
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "404", description = "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²Œμ‹œκΈ€ 쑰회",
                    content = @Content(schema = @Schema(implementation = ErrorResult.class)))
    })
    @GetMapping("/{postId}")
    public PostResponse getPost(@Parameter(description = "κ²Œμ‹œκΈ€μ˜ id", in = ParameterIn.PATH)
                                @PathVariable Long postId) {

        return postService.findPost(postId);
    }

    @Operation(summary = "κ²Œμ‹œκΈ€ 생성", description = "μš”μ²­ DTO에 λ‹΄κΈ΄ λ°μ΄ν„°λ‘œ κ²Œμ‹œκΈ€ 생성")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ 생성 성곡" ,
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "400", description = "잘λͺ»λœ μš”μ²­",
                    content = @Content(schema = @Schema(hidden = true)))
    })
    @PostMapping
    public ResponseEntity<PostResponse> createPost(@Valid @RequestBody PostRequest postRequest){

        PostResponse saveResponse = postService.writePost(postRequest);
        return ResponseEntity.ok(saveResponse);
    }

    @Operation(summary = "κ²Œμ‹œκΈ€ μˆ˜μ •", description = "ν•΄λ‹Ή id의 κ²Œμ‹œκΈ€μ„ μš”μ²­ DTO에 λ‹΄κΈ΄ λ°μ΄ν„°λ‘œ μˆ˜μ •")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ μˆ˜μ • 성곡",
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "404", description = "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²Œμ‹œκΈ€ 쑰회",
                    content = @Content(schema = @Schema(implementation = ErrorResult.class)))
    })
    @PatchMapping("/{postId}")
    public PostResponse modifyPost(@Parameter(description = "κ²Œμ‹œκΈ€μ˜ id", in = ParameterIn.PATH)
                                   @PathVariable Long postId,
                                   @Valid @RequestBody PostRequest postRequest) {

        return postService.modifyPost(postId, postRequest);
    }

    @Operation(summary = "κ²Œμ‹œκΈ€ μ‚­μ œ", description = "ν•΄λ‹Ή id의 κ²Œμ‹œκΈ€ μ‚­μ œ")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ μ‚­μ œ 성곡",
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "404", description = "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” κ²Œμ‹œκΈ€ 쑰회",
                    content = @Content(schema = @Schema(implementation = ErrorResult.class)))
    })
    @DeleteMapping("/{postId}")
    public ResponseEntity deletePost(@Parameter(description = "κ²Œμ‹œκΈ€μ˜ id", in = ParameterIn.PATH)
                             @PathVariable Long postId) {

        postService.deletePost(postId);
        return ResponseEntity.ok("μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
    }

    @Operation(summary = "κ²Œμ‹œκΈ€ 검색", description = "검색 ν‚€μ›Œλ“œλ₯Ό 톡해 κ²Œμ‹œκΈ€ 제λͺ©μ„ 검색")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ 검색 성곡",
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "400", description = "검색 ν‚€μ›Œλ“œκ°€ 쑰건에 λ§žμ§€ μ•ŠμŒ",
                    content = @Content(schema = @Schema(implementation = ErrorResult.class)))
    })
    @GetMapping
    public List<PostResponse> searchPostTitle(@RequestParam("keyword") String keyword) {

        return postService.searchPostTitleList(keyword);
    }

    @Operation(summary = "κ²Œμ‹œκΈ€ λͺ¨λ‘ 쑰회", description = "λͺ¨λ“  κ²Œμ‹œκΈ€μ„ 쑰회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "κ²Œμ‹œκΈ€ 쑰회 성곡",
                    content = @Content(schema = @Schema(implementation = PostResponse.class))),
            @ApiResponse(responseCode = "400", description = "잘λͺ»λœ μš”μ²­",
                    content = @Content(schema = @Schema(hidden = true)))
    })
    @GetMapping("/list")
    public List<PostResponse> findAllPost(){
        return postService.findAllPosts();
    }

}
  • @Operation μ• λ…Έν…Œμ΄μ…˜μ€ API μž‘μ—…μ— λŒ€ν•œ λ‹€μ–‘ν•œ 속성을 지정할 수 μžˆλ‹€. summaryλŠ” API μž‘μ—…μ— λŒ€ν•œ κ°„λ‹¨ν•œ μš”μ•½μ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. descriptionλŠ” API μž‘μ—…μ— λŒ€ν•œ μžμ„Έν•œ μ„€λͺ…을 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • @ApiResponse μ• λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ API μž‘μ—…μ˜ 응닡 ν˜•μ‹μ„ μ •μ˜ν•  수 μžˆλ‹€. responseCode, description둜 μ½”λ“œμ™€ κ·Έ μ½”λ“œμ— λŒ€ν•œ μ„€λͺ…을 ν•  수 μžˆλ‹€. 그리고 contentλ₯Ό 톡해 응닡 λ‚΄μš©μ„ 담을 수 μžˆλŠ”λ°, @Contentλ₯Ό μ‚¬μš©ν•΄μ„œ schemaλ₯Ό 톡해 응닡 μŠ€ν‚€λ§ˆλ₯Ό 지정할 수 μžˆλ‹€.
    hidden을 톡해 응닡에 λŒ€ν•œ λ‚΄μš©μ„ 가릴 수 μžˆλ‹€.

  • @Paramater μ• λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ λ§€κ°œλ³€μˆ˜λ₯Ό μ •μ˜ν•  수 μžˆλ‹€. descriptionλ₯Ό ν†΅ν•΄μ„œ λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ μ„€λͺ…을 ν•  수 μžˆλ‹€.

κ²°κ³Ό


PostResponse

@Getter
public class PostResponse {

    @Schema(description = "κ²Œμ‹œνŒ id")
    private final Long id;

    @Schema(description = "κ²Œμ‹œνŒ 제λͺ©")
    private final String title;

    @Schema(description = "κ²Œμ‹œνŒ λ‚΄μš©")
    private final String content;

    @Schema(description = "κ²Œμ‹œνŒ μƒμ„±μΌμž")
    private final LocalDateTime regTime;

    public PostResponse(Post post) {
        this.id = post.getId();
        this.title = post.getTitle().getValue();
        this.content = post.getContent().getValue();
        this.regTime = post.getRegTime();
    }

}
  • @Schemaλ₯Ό ν†΅ν•΄μ„œ ν•΄λ‹Ή μŠ€ν‚€λ§ˆμ— λŒ€ν•œ μ„€λͺ…을 달 수 μžˆλ‹€.

κ²°κ³Ό


πŸ“’ λ‚˜μ˜ 생각

  • @Paramater μ• λ…Έν…Œμ΄μ…˜μ„ ν†΅ν•΄μ„œ νŒŒλΌλ―Έν„°λ₯Ό name으둜 μˆ˜μ •ν•˜λŠ” 것이 μ μš©λ˜μ§€ μ•ŠλŠ”λ‹€. @Paramater μ• λ…Έν…Œμ΄μ…˜μ„ μ μš©ν•˜κ²Œ 될 경우 Swaggerμ—μ„œ νŒŒλΌλ―Έν„°κ°€ μ•„μ˜ˆ μ—†μ–΄μ§€λŠ” ν˜„μƒμ΄ λ°œμƒν•œλ‹€. 이 뢀뢄은 아직 ν•΄κ²°ν•˜μ§€ λͺ»ν–ˆλ‹€.
  • springdoc은 μ• λ…Έν…Œμ΄μ…˜μ„ ν†΅ν•΄μ„œ λͺ…μ„Έν™”λ₯Ό ν•˜λŠ”λ°, 컨트둀러 뢀뢄에 μ• λ…Έν…Œμ΄μ…˜μ΄ 많이 λ‹¬λ¦¬κ²Œ λ˜μ„œ 가독성이 λ–¨μ–΄μ§€κ²Œ λœλ‹€. spring rest docs에 λŒ€ν•΄μ„œλ„ ν•™μŠ΅μ΄ ν•„μš”ν•  것 κ°™λ‹€.
  • API λͺ…μ„Έμ„œμ—μ„œ μ˜ˆμƒλ˜λŠ” μ˜ˆμ™Έλ₯Ό 닀루기 μœ„ν•΄μ„œ μ—λŸ¬μ½”λ“œμ— λŒ€ν•œ ν•™μŠ΅κ³Ό, μ˜ˆμƒλ˜λŠ” μ—λŸ¬μ— λŒ€ν•΄μ„œ μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό μ—°μŠ΅μ΄ ν•„μš”ν•  것 κ°™λ‹€.

0개의 λŒ“κΈ€