개발/spingboot

Springboot Controller 에서 OpenAPI 분리하기

ikkison 2026. 2. 11. 19:12

Springboot Controller 에서 Request Body/Parameter 와 많은 Response, GET 요청의 필터들까지 포함하여 OpenAPI 를 작성하다보면 OpenAPI Annocation 의 라인수가 상당히 길어져 가독성이 떨어지고 화면이 넘어가 관리하기 힘든 상황이 발생한다.

 

Controller 소스에서 API 기능과 OpenAPI 문서를 분리하여 가독성을 높이고 관리하기 용이하게 변경해보자.

 

AS-IS

우선, 기존의 Controller 가 아래와 비슷한 형태일 것이다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/comment/like")
public class BookmarkAddController {
    private final BookmarkAddService bookmarkAddService;

    @PostMapping
    @Operation(
            summary = "북마크 추가 API",
            description = """
            사용자가 게시물에 북마크를 합니다.
            - 사용자는 MemberEntity 의 id 로 BookmarkEntity 의 memberId 와 ID Reference 관계입니다.
            """
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = """
                    북마크 처리 성공
                    - 처리 성공 시나리오를 반환함.
                    - 북마크가 된 경우 EnvelopeResponse 의 Data 에 isExists 값이 true 로 반환 
                    - 이미 북마크가 되어있는 경우 EnvelopeResponse 의 Data 에 isExists 값이 false 로 반환 
                    """,
                    content = @Content(
                            schema = @Schema(implementation = BookmarkAddResponse.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "인증 실패",
                    content = @Content(schema = @Schema(implementation = Error.class)) // 공통 에러 객체 연결 가능
            ),
            @ApiResponse(
                    responseCode = "404",
                    description = "게시물이 존재하지 않음"
            )
    })
    public ResponseEntity<BookmarkAddResponse> addBookmark(
            @RequestBody BookmarkAddRequest request
    ) {
        BookmarkAddResDto resDto = bookmarkAddService.addBookmark(request.toDto());
        return ResponseEntity
                .status(resDto.result().isExists() ? HttpStatus.CREATED : HttpStatus.OK)
                .body(BookmarkAddResponse.from(resDto));
    }
}

 

실제 Controller 의 소스는 8 라인밖에 없지만 해당 Controller 소스파일의 반이상이 OpenAPI 문서작성으로 차지하고 있다.

 

해당 예제소스가 Toggle Action 형태의 Bookmark 예제 라서 이 정도로 끝나지만, Member, Board 도메인의 Controller 를 제대로 만들어 Request Body, Pageable, Response 등을 OpenAPI에 제대로 작성하다보면 계속 스크롤을 위아래로 흔들고 있을것이다.

마우스 스크롤도 딸깍 하지않고 오래 긁어야해서 상당히 피곤해진다.

 

TO-BE

@Operation 과 @ApiResponses 등의 OpenAPI 어노테이션을 분리해서 Controller 에서 등록만하도록 변경해보자.

 

아래와 같이 Controller 함수에 대응되는 Custom Annotation 을 만든다.

Operation Annotion 를 위한 Controller 함수와 1대1 대응되게 분리한다.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Operation(
        summary = "북마크 추가 API",
        description = """
            사용자가 게시물에 북마크를 합니다.
            - 사용자는 MemberEntity 의 id 로 BookmarkEntity 의 memberId 와 ID Reference 관계입니다.
            """
)
@ApiResponses(value = {
        @ApiResponse(
                responseCode = "200",
                description = """
                    북마크 처리 성공
                    - 처리 성공 시나리오를 반환함.
                    - 북마크가 된 경우 EnvelopeResponse 의 Data 에 isExists 값이 true 로 반환 
                    - 이미 북마크가 되어있는 경우 EnvelopeResponse 의 Data 에 isExists 값이 false 로 반환 
                    """,
                content = @Content(
                        schema = @Schema(implementation = BookmarkAddResponse.class)
                )
        ),
        @ApiResponse(
                responseCode = "401",
                description = "인증 실패",
                content = @Content(schema = @Schema(implementation = Error.class)) // 공통 에러 객체 연결 가능
        ),
        @ApiResponse(
                responseCode = "404",
                description = "게시물이 존재하지 않음"
        )
})
public @interface BookmarkAddOperation {
}

 

아래와 같이 생성한 OpenAPI 용 Custom Annotation 인 @BookmarkAddOperation 을 추가하여 Controller 코드에서는 RestAPI 에 집중하도록 한다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/comment/like")
public class BookmarkAddController {
    private final BookmarkAddService bookmarkAddService;

    @PostMapping
    @BookmarkAddOperation
    public ResponseEntity<BookmarkAddResponse> addBookmark(
            @RequestBody BookmarkAddRequest request
    ) {
        BookmarkAddResDto resDto = bookmarkAddService.addBookmark(request.toDto());
        return ResponseEntity
                .status(resDto.result().isExists() ? HttpStatus.CREATED : HttpStatus.OK)
                .body(BookmarkAddResponse.from(resDto));
    }
}

 

'개발 > spingboot' 카테고리의 다른 글

Springboot 설정(application.yaml) 우선순위  (0) 2026.02.19
H2 Datasource 설정 - File Based 방식  (0) 2026.02.19