์šฐ๊ทœ์ด์ธ์šฐ์œค
Eager To Learn ๐ŸŒŒ
์šฐ๊ทœ์ด์ธ์šฐ์œค
์ „์ฒด ๋ฐฉ๋ฌธ์ž
์˜ค๋Š˜
์–ด์ œ

๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

  • ๐Ÿก ํ™ˆ
  • ๐Ÿš€ ๊นƒํ—ˆ๋ธŒ
  • โ›… ํƒœ๊ทธ ํด๋ผ์šฐ๋“œ
  • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (217)
    • ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป PS (170)
      • JAVA (82)
      • MYSQL (1)
      • Docker (2)
      • PYTHON (24)
      • LeetCode 150 (39)
      • Algorithm ๊ธฐ๋ฒ• (1)
      • ๋ฐ”ํ‚น๋… (21)
    • ๋ธ”๋กœ๊ทธ ์ด์‚ฌ (0)
    • Error (1)
    • CS (15)
      • DataBase (2)
      • OS (7)
      • Network (1)
      • Spring (1)
      • ์ž๋ฃŒ๊ตฌ์กฐ (3)
      • Java (1)
    • Learned (7)
      • Spring (7)
    • ๊ฐœ๋ฐœ์„œ์  (15)
      • ๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์„ค๊ณ„ ๊ธฐ์ดˆ (1)
      • ์˜ค๋ธŒ์ ํŠธ - ์กฐ์˜ํ˜ธ (7)
      • ์นœ์ ˆํ•œ SQL ํŠœ๋‹ (7)
    • ํšŒ๊ณ  (2)
hELLO ยท Designed By ์ •์ƒ์šฐ.
์šฐ๊ทœ์ด์ธ์šฐ์œค
Learned/Spring

Swagger API ์ ์šฉ ์‹œ, Controller ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋”๋Ÿฌ์›Œ์ง„๋‹ค.. ๋ถ„๋ฆฌํ•ด๋ณด์ž

Learned/Spring

Swagger API ์ ์šฉ ์‹œ, Controller ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋”๋Ÿฌ์›Œ์ง„๋‹ค.. ๋ถ„๋ฆฌํ•ด๋ณด์ž

2023. 8. 13. 20:28

Swagger ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, API ๋ฌธ์„œ๋ฅผ ์ž๋™์œผ๋กœ ๊ตฌ์„ฑํ•ด์ฃผ๋ฉฐ ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

 

 

@Tag , @Operation , @ApiResponses ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šค์›จ๊ฑฐ ๋ฌธ์„œ์— ๋ถ€๊ฐ€์ ์ธ ์„ค๋ช…์„ ์ถ”๊ฐ€์ ์œผ๋กœ ๋„ฃ์„ ์ˆ˜ ์žˆ์–ด์„œ API ๋ฌธ์„œ๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

 

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostApiController {

    private final PostService postService;

    @Tag(name = "Post", description = "๊ฒŒ์‹œ๊ธ€ ๊ด€๋ จ API")
    @Operation(summary = "๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ", description = "๐Ÿ’ก์นดํ…Œ๊ณ ๋ฆฌ์™€ ์ƒ๊ด€์—†์ด ๋ชจ๋“  ๊ฒŒ์‹œ๊ธ€์„ ํŽ˜์ด์ง€๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "โญ• SUCCESS", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                    examples = {@ExampleObject(value = "{\"message\":\"SUCCESS\",\"result\":{\"content\":[{\"postId\":1,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/04/27 21:14\",\"lastModifiedDate\":\"2023/04/27 21:14\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0},{\"postId\":2,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/05/13 00:15\",\"lastModifiedDate\":\"2023/05/13 00:15\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0}],\"pageable\":{\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"offset\":0,\"pageNumber\":0,\"pageSize\":20,\"unpaged\":false,\"paged\":true},\"totalPages\":1,\"totalElements\":2,\"last\":true,\"size\":20,\"number\":0,\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"numberOfElements\":2,\"first\":true,\"empty\":false}}")}, schema = @Schema(implementation = Response.class))),
    })
    @GetMapping
    public ResponseEntity<Response<Page<PostGetResponse>>> getAll(Pageable pageable) {
        Page<PostGetResponse> response = postService.getAllPosts(pageable);
        return ResponseEntity.ok(Response.success(response));
    }


    @Tag(name = "Post", description = "๊ฒŒ์‹œ๊ธ€ ๊ด€๋ จ API")
    @Operation(summary = "๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ", description = "<strong>๐Ÿ”‘JWT ํ•„์š”</strong><br>๐Ÿ’ก๊ฒŒ์‹œ๊ธ€์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.<br>๐Ÿšจ๊ฐ€์ž…๋œ ํšŒ์›์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ์‹œ ์—๋Ÿฌ ๋ฐœ์ƒ")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "โญ• SUCCESS", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                    examples = {@ExampleObject(value = "{\"message\":\"SUCCESS\",\"result\":{\"id\":1,\"nickName\":\"nickName\",\"blog\":\"blog\",\"email\":\"email\"}}")}, schema = @Schema(implementation = Response.class))),
            @ApiResponse(responseCode = "404", description = "โŒ ERROR (๊ฐ€์ž…๋œ ํšŒ์›์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ์‹œ)", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                    examples = {@ExampleObject(value = "{\"message\":\"ERROR\",\"result\":\"๊ฐ€์ž…๋œ ํšŒ์›์ด ์•„๋‹™๋‹ˆ๋‹ค.\"}")}, schema = @Schema(implementation = Response.class)))
    })
    @PostMapping
    public ResponseEntity<Response<PostResponse>> create(Authentication authentication, @Validated @RequestBody PostCreateRequest requestDto, BindingResult br) {
        String userName = authentication.getName();
        PostResponse response = postService.createPost(userName, requestDto);
        return ResponseEntity.status(CREATED).body(Response.success(response));
    }

}

 

์œ„์™€ ๊ฐ™์ด, Controller์˜ ํŠน์ • ๋ฉ”์„œ๋“œ์— ๋ถ€๊ฐ€ ์„ค๋ช…์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@Tag ์‚ฌ์šฉ์‹œ,

 

 

์œ„์™€ ๊ฐ™์ด ๊ฐ™์€ Tag name ์ด ๋‹ฌ๋ ค์žˆ๋Š” ์—”๋“œํฌ์ธํŠธ๋ผ๋ฆฌ ๋ฌถ์„ ์ˆ˜ ์žˆ๋‹ค.

 

 

@Operation ์˜ ์˜ต์…˜์ธ summary ์™€ description์„ ํ†ตํ•ด  

 

 

์ด๋Ÿฐ์‹์œผ๋กœ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•œ ๋ถ€๊ฐ€ ์„ค๋ช…๋„ ์ถ”๊ฐ€ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  @ApiResponse ๋ฅผ ํ†ตํ•ด ์‘๋‹ต response ์™€ ์ƒํƒœ ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.

 

 

๋‹จ, ์ด๋Ÿฐ ์ž‘์—…์„ ๊ฑฐ์น˜๋ฉด API ๋ฌธ์„œ๋Š” ์นœ์ ˆํ•ด์ง€๊ณ  ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง€์ง€๋งŒ Controller ์ฝ”๋“œ๊ฐ€ ๋งค์šฐ ์ง€์ €๋ถ„ํ•ด์ง„๋‹ค..

 

๊ทธ๋ž˜์„œ, ์ด ๋ฌธ์ œ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค!

 


 

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostApiController {

    private final PostService postService;

    @Tag(name = "Post", description = "๊ฒŒ์‹œ๊ธ€ ๊ด€๋ จ API")
    @Operation(summary = "๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ", description = "๐Ÿ’ก์นดํ…Œ๊ณ ๋ฆฌ์™€ ์ƒ๊ด€์—†์ด ๋ชจ๋“  ๊ฒŒ์‹œ๊ธ€์„ ํŽ˜์ด์ง€๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "โญ• SUCCESS", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                    examples = {@ExampleObject(value = "{\"message\":\"SUCCESS\",\"result\":{\"content\":[{\"postId\":1,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/04/27 21:14\",\"lastModifiedDate\":\"2023/04/27 21:14\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0},{\"postId\":2,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/05/13 00:15\",\"lastModifiedDate\":\"2023/05/13 00:15\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0}],\"pageable\":{\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"offset\":0,\"pageNumber\":0,\"pageSize\":20,\"unpaged\":false,\"paged\":true},\"totalPages\":1,\"totalElements\":2,\"last\":true,\"size\":20,\"number\":0,\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"numberOfElements\":2,\"first\":true,\"empty\":false}}")}, schema = @Schema(implementation = Response.class))),
    })
    @GetMapping
    public ResponseEntity<Response<Page<PostGetResponse>>> getAll(Pageable pageable) {
        Page<PostGetResponse> response = postService.getAllPosts(pageable);
        return ResponseEntity.ok(Response.success(response));
    }
}

 

์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด๊ฒ ๋‹ค.

 

 

public interface PostApiSpecification {

    @Tag(name = "Post", description = "๊ฒŒ์‹œ๊ธ€ ๊ด€๋ จ API")
    @Operation(summary = "๊ฒŒ์‹œ๊ธ€ ์กฐํšŒ", description = "๐Ÿ’ก์นดํ…Œ๊ณ ๋ฆฌ์™€ ์ƒ๊ด€์—†์ด ๋ชจ๋“  ๊ฒŒ์‹œ๊ธ€์„ ํŽ˜์ด์ง€๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "โญ• SUCCESS", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                    examples = {@ExampleObject(value = "{\"message\":\"SUCCESS\",\"result\":{\"content\":[{\"postId\":1,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/04/27 21:14\",\"lastModifiedDate\":\"2023/04/27 21:14\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0},{\"postId\":2,\"title\":\"title\",\"content\":\"content\",\"category\":\"category\",\"userId\":1,\"nickName\":\"nickName\",\"userName\":\"userName\",\"createdDate\":\"2023/05/13 00:15\",\"lastModifiedDate\":\"2023/05/13 00:15\",\"imageUrl\":\"imageUrl\",\"view\":0,\"totalNumOfComments\":0,\"totalNumOfLikes\":0}],\"pageable\":{\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"offset\":0,\"pageNumber\":0,\"pageSize\":20,\"unpaged\":false,\"paged\":true},\"totalPages\":1,\"totalElements\":2,\"last\":true,\"size\":20,\"number\":0,\"sort\":{\"empty\":true,\"sorted\":false,\"unsorted\":true},\"numberOfElements\":2,\"first\":true,\"empty\":false}}")}, schema = @Schema(implementation = Response.class))),
    })
    @GetMapping
    ResponseEntity<Response<Page<PostGetResponse>>> getAll(Pageable pageable);

}

 

์œ„์™€ ๊ฐ™์€ API ๋ช…์„ธ๋ฅผ ์œ„ํ•œ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•œ๋‹ค.

 

 

@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostApiController implements PostApiSpecification {

    private final PostService postService;

    @GetMapping
    public ResponseEntity<Response<Page<PostGetResponse>>> getAll(Pageable pageable) {
        Page<PostGetResponse> response = postService.getAllPosts(pageable);
        return ResponseEntity.ok(Response.success(response));
    }
}

 

๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด ์ฝ”๋“œ์˜ ์Šค์›จ๊ฑฐ ๊ด€๋ จ ์–ด๋…ธํ…Œ์ด์…˜์€ ์ง€์šฐ๊ณ 

 

์ •์˜ํ•œ PostApiSpecification ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ implements ํ•ด์ค€๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด, API ๊ด€๋ จ ์–ด๋…ธํ…Œ์ด์…˜ ์„ค๋ช…์€ ์ธํ„ฐํŽ˜์ด์Šค ํ•œ ๊ณณ์— ๋ชฐ์•„์„œ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด ๊ธฐ์กด ์ฝ”๋“œ์™€ ๋ถ„๋ฆฌ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค!

 


์ž๋ฐ”์˜ ์ƒ์†๊ณผ ๋‹คํ˜•์„ฑ ๊ทธ๋ฆฌ๊ณ  Spring์˜ ์›๋ฆฌ๋งŒ ์•Œ๋ฉด ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด์—ˆ๋Š”๋ฐ,

 

๋’ค๋Šฆ๊ฒŒ ๋ฐฉ๋ฒ•์ด ๋– ์˜ฌ๋ผ์„œ ์ ์šฉํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

 

 

 

    'Learned/Spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
    • Spring Boot Github ์†Œ์…œ ๋กœ๊ทธ์ธ ๊ตฌํ˜„ํ•˜๊ธฐ [ RestTemplate ยท WebClient ยท FeignClient ๋ฅผ ๋น„๊ตํ•ด๋ณด์ž ]
    • Dispatcher Servlet์€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ?
    • Dto Validation ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์— AOP๋ฅผ ์ ์šฉํ•ด๋ณด์ž!
    • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€ ๋ถ„์„ ๋„๊ตฌ Jacoco๋ฅผ ์ ์šฉํ•ด๋ณด์ž!
    ์šฐ๊ทœ์ด์ธ์šฐ์œค
    ์šฐ๊ทœ์ด์ธ์šฐ์œค
    ๊ฐœ๋ฐœ์ž ๊ฟˆ๋‚˜๋ฌด

    ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”

    ๋‹จ์ถ•ํ‚ค

    ๋‚ด ๋ธ”๋กœ๊ทธ

    ๋‚ด ๋ธ”๋กœ๊ทธ - ๊ด€๋ฆฌ์ž ํ™ˆ ์ „ํ™˜
    Q
    Q
    ์ƒˆ ๊ธ€ ์“ฐ๊ธฐ
    W
    W

    ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€

    ๊ธ€ ์ˆ˜์ • (๊ถŒํ•œ ์žˆ๋Š” ๊ฒฝ์šฐ)
    E
    E
    ๋Œ“๊ธ€ ์˜์—ญ์œผ๋กœ ์ด๋™
    C
    C

    ๋ชจ๋“  ์˜์—ญ

    ์ด ํŽ˜์ด์ง€์˜ URL ๋ณต์‚ฌ
    S
    S
    ๋งจ ์œ„๋กœ ์ด๋™
    T
    T
    ํ‹ฐ์Šคํ† ๋ฆฌ ํ™ˆ ์ด๋™
    H
    H
    ๋‹จ์ถ•ํ‚ค ์•ˆ๋‚ด
    Shift + /
    โ‡ง + /

    * ๋‹จ์ถ•ํ‚ค๋Š” ํ•œ๊ธ€/์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž๋กœ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ‹ฐ์Šคํ† ๋ฆฌ ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.