appling 프로젝트

[appling] Controller 추가하기 (Api Annotation 추가, Swagger 적용)

studyingJuno 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
반응형