ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [appling] Controller 추가하기 (Api Annotation 추가, Swagger 적용)
    appling 프로젝트 2024. 9. 8. 13:10
    728x90
    반응형

    🔴 Controller 만들기

    지금까지 Service만 작성하고 실제로 Controller는 작성하지 않았다. 이제 Controller를 적용해보자.

    🟠 Controller 적용하기

    🟢 기존 Controller 쓰기

    @RestController
    @RequestMapping("/api/v1")
    public class ProductController {
    }

    RestController를 적용하고 기본 값으로 /api/v1을 붙여주려고 한다. 근데 만드는 Controller마다 붙이는게 너무 귀찮을거 같아 Annotation으로 설정하려고 하다.

    🟢 @ApiController 만들기

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @RestController
    @RequestMapping("/api/v1")
    public @interface ApiController {
    }

    @ApiController를 Annotation으로 정의하고

    @ApiController
    public class ProductController {
    }

    기존에 Controller 적용 대신 다음과 같이 적용해준다.

    🟠 Swagger 적용하기

    🟢 Controller 처리

    @ApiController
    @RequiredArgsConstructor
    @Tag(name = "Product API", description = "Product API Documentation")
    public class ProductController {
        private final ProductService productService;
    
        @PostMapping("/product")
        @Operation(summary = "상품 등록", description = "상품 등록 api")
        @ApiResponses(value = {
                @ApiResponse(responseCode = "201", description = "정상", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)),
                @ApiResponse(responseCode = "500", description = "서버 에러", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)),
        })
        public ResponseEntity<ResponseData<PostProductResponse>> product(@RequestBody @Validated PostProductRequest productRequest, BindingResult bindingResult) {
            if (bindingResult.hasErrors()) {
                Map<String, String> errors = new HashMap<>();
                bindingResult.getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage()));
                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                        .body(ResponseData.from(ResponseDataCode.SUCCESS, errors));
            }
            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(ResponseData.from(ResponseDataCode.SUCCESS, productService.createProduct(productRequest)));
        }
    }

    상품 Controller에 @Tag()를 추가해주고 각 Url에 대한 설명을 위해 @Operation() @ApiResponse()를 추가해준다.

    그리고 Controller에 추가적으로 @ValidatedBindingResult를 추가로 적용하여 유효성 검사를 추가해준다.

    @Getter
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class PostProductRequest {
    
        @NotNull(message = "상품명을 입력해 주세요.")
        @JsonProperty("product_name")
        @Schema(description = "상품명", example = "아리수")
        private String productName;
        @NotNull(message = "상품 무게를 입력해 주세요.")
        @JsonProperty("product_weight")
        @Schema(description = "상품 무게", example = "5")
        private int productWeight;
        @NotNull(message = "상품 타입을 입력해 주세요. ex) 사과는 11과")
        @JsonProperty("product_type")
        @Schema(description = "상품 타입", example = "11과")
        private String productType;
        @NotNull(message = "상품 가격을 입력해 주세요.")
        @JsonProperty("product_price")
        @Schema(description = "상품 가격", example = "100000")
        private int productPrice;
        @NotNull(message = "상품 수량을 입력해 주세요.")
        @JsonProperty("product_stock")
        @Schema(description = "상품 수량", example = "100")
        private int productStock;
    
        ...
    }
    

    Request에 Swagger에 대한 추가 설정을 한다.

    Swagger 추가 설정을 통해 테스트 시 기본 값과 API의 요청 Schema에 대한 설명을 추가해두었다.

    @Entity
    @Table(name = "product")
    @AllArgsConstructor(access = AccessLevel.PROTECTED)
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @Builder
    @Getter
    public class ProductEntity extends CommonEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long productId;
        private String productName;
        private int productWeight;
        private String productType;
        @Enumerated(EnumType.STRING)
        private ProductStatus productStatus;
        private int productPrice;
        private int productStock;
    
        ...
    }

    추가로 Entity에 @Enumerated(EnumType.STRING)를 추가해두었다.

    🟢 반환 데이터 정의하기

    @ApiController
    @RequiredArgsConstructor
    @Tag(name = "Product", description = "Product API Documentation")
    public class ProductController {
        private final ProductService productService;
    
        @PostMapping("/product")
        @Operation(summary = "상품 등록", description = "상품 등록 api")
        @ApiResponses(value = {
                @ApiResponse(responseCode = "201", description = "정상", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PostProductResponse.class))),
                @ApiResponse(responseCode = "500", description = "서버 에러", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)),
        })
        public ResponseEntity<ResponseData<PostProductResponse>> product(@RequestBody @Validated PostProductRequest productRequest) {
            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(ResponseData.from(ResponseDataCode.CREATE, productService.createProduct(productRequest)));
        }
    
        ...
    }

    기존 Controller에서 반환할때 데이터도 추가하려고 한다.

    @ApiResponse(responseCode = "201", description = "정상", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = PostProductResponse.class))),

    해당 부분의 schema 데이터를 추가하여 주면 반환하는 데이터의 형식도 예시로 보여줄 수 있다.

    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
    @Builder
    @Schema(description = "상품 등록 반환 데이터")
    public record PostProductResponse(
        @Schema(description = "상품번호", example = "1") Long productId
    ) {
        public static PostProductResponse createFrom(ProductEntity saveProduct) {
            return PostProductResponse.builder()
                    .productId(saveProduct.getProductId())
                    .build();
        }
    }

    반환되는 class도 다음과 같이 정의해주면 된다.

    그러면 반환 시 데이터 예시를 정의해 둘수 있고

    schema를 클릭하면 반환 데이터의 설명도 확인해 볼 수 있다.

    나머지 get, put도 수정해두었는데 해당 부분은 깃 소스를 통해 확인해주시면 된다!

    product response git source

    728x90
    반응형
Designed by Juno.