인:향 - 중복 코드 줄이기

김준석·2023년 12월 20일
0

향수 추천 서비스

목록 보기
18/21
post-thumbnail

문제 1

컨트롤러에 응집된 API 문서화 코드가 가독성과 확장성 측면에서 문제를 지니고 있다고 판단하였습니다.

@Slf4j
@RestController
@RequestMapping("/perfume")
public class PerfumeController implements PerfumeControllerDocs {

    private final PerfumeService perfumeService;

    private final FeatureService featureService;

    public PerfumeController(PerfumeService perfumeService, FeatureService featureService) {
        this.perfumeService = perfumeService;
        this.featureService = featureService;
    }

    @Operation(summary = "이름으로 향수 찾기")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공"),
            @ApiResponse(responseCode = "404", description = "해당 향수 찾을 수 없음")
    })
    @PostMapping("/find-by-name")
    public ResponseEntity<List<Perfume>> findByPerfumeName(@RequestBody final PerfumeRequestDto perfumeRequestDto) {
        log.info("향수 이름: {} 의 향수 조회", perfumeRequestDto.getPerfumeName());
        return ResponseEntity.ok(perfumeService.findPerfumeListByName(perfumeRequestDto));
    }
    ... 중략

문서화를 Controller에 직접적으로 할 시에 컨트롤러에 방대한 양의 코드가 추가됩니다. 추가적인 API 생성 시에도 소요되는 시간 또한 증가되어 개발 속도가 저하될 것이라고 생각하였습니다.

따라서 API 문서화를 따로 담당하는 interface가 필요하다고 느꼈고, 바로 적용하였습니다. 인터페이스를 구현함으로써 작성할 API를 강제한다는 점에서도 효과적이라고 생각하였습니다.

해결 1

@Tag(name = "향수 데이터 컨트롤러")
public interface PerfumeControllerDocs {

    @Operation(summary = "이름으로 향수 찾기")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공"),
            @ApiResponse(responseCode = "404", description = "해당 향수 찾을 수 없음")
    })
    ResponseEntity<List<Perfume>> findByPerfumeName(@Parameter(description = "향수 이름") PerfumeRequestDto perfumeRequestDto);

    @Operation(summary = "브랜드로 향수 찾기")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공"),
            @ApiResponse(responseCode = "404", description = "해당 향수 찾을 수 없음")
    })
    ResponseEntity<List<Perfume>> findByBrandName(@Parameter(description = "브랜드 이름") PerfumeRequestDto perfumeRequestDto);

    @Operation(summary = "모든 향수 데이터 조회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공"),
            @ApiResponse(responseCode = "404", description = "향수 데이터가 없음")
    })
    ResponseEntity<List<Perfume>> showAllData();

    @Operation(summary = "향수 id로 조회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "성공"),
            @ApiResponse(responseCode = "404", description = "해당 데이터 없음")
    })
    ResponseEntity<FeatureResponseDto> showPerfumeDetails(@Parameter(description = "향수 id") Long id);

    @Operation(summary = "향수 image 조회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "조회 성공", headers = @Header(name = "Authorization", description = "Access Token")),
            @ApiResponse(responseCode = "401", description = "유효하지 않은 사용자 접근")
    })
    ResponseEntity<PerfumeResponseDto> showPerfumeImage(@Parameter(description = "향수 이름") String perfumeName);

}

문제 2

Interface를 통해 문서화 코드를 별도로 관리함으로써 컨트롤러의 무게가 저하되었습니다. 다만, 현재 서버의 api 개수가 약 50개정도 있는데, 대부분의 api에서 200, 404 Response를 하고있었습니다. 추가적으로 생성될 api에서도 대부분 200번과 404번 응답을 사용할 것이라고 판단되었고 이를 더 효과적으로 관리하기 위해 Custom Annotation을 적용하였습니다.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface APICommonResponse {

    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "조회 성공"),
            @ApiResponse(responseCode = "404", description = "해당 데이터 없음")
    }) ApiResponse[] responses() default {};
}

해결 2

@Tag(name = "향수 데이터 컨트롤러")
public interface PerfumeControllerDocs {

    @Operation(summary = "이름으로 향수 찾기")
    @APICommonResponse
    ResponseEntity<List<Perfume>> findByPerfumeName(
            @RequestBody(description = " 향수 이름") @Parameter(description = "향수 이름") PerfumeRequestDto perfumeRequestDto);

    @Operation(summary = "브랜드로 향수 찾기")
    @ApiResponses
    ResponseEntity<List<Perfume>> findByBrandName(
            @Parameter(description = "브랜드 이름") PerfumeRequestDto perfumeRequestDto);

    @Operation(summary = "모든 향수 데이터 조회")
    @APICommonResponse
    ResponseEntity<List<Perfume>> showAllData();

    @Operation(summary = "향수 id로 조회")
    @APICommonResponse
    ResponseEntity<FeatureResponseDto> showPerfumeDetails(@Parameter(description = "향수 id") Long id);

    @Operation(summary = "향수 image 조회")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "조회 성공", headers = @Header(name = "Authorization", description = "Access Token")),
            @ApiResponse(responseCode = "401", description = "유효하지 않은 사용자 접근")
    })
    ResponseEntity<PerfumeResponseDto> showPerfumeImage(@Parameter(description = "향수 이름") String perfumeName);
}

Custom Annotation을 활용하여 코드가 더욱 간결해졌다고 느꼈습니다. 또한, 새로운 api가 생성될 때 문서화에 쓰는 시간을 줄일 수 있게 되었습니다! 200번과 404가 아닌 추가적인 응답코드를 정의할 때에는 따로 정의해주면 될 것 같습니다.

profile
기록하면서 성장하기!

0개의 댓글