혼자 하는 Spring 프로젝트 - 8 : API 문서화

꾸준하게 달리기~·2023년 7월 20일
0

솔로 프로젝트

목록 보기
8/11
post-thumbnail

들어가기 앞서

내가 API를 작성했으면, 해당 API대로 요청이 들어와야 한다.
해당 API가 아니면, 내가 만든 프로젝트는 요청이 와도 이해할 수 없기 때문이다.

예를 들어,
멤버 등록을 /members 라고 지정했는데,
/members/create 라고 요청이 들어오면,
내 프로젝트에선
이 요청은 뭐라는거지? 메뉴판에 있지도 않은 일을 시켜버리네 ㅋㅋ 할줄 몰라!
가 되어버린다.

그래서 이번엔!
해당 API대로 주문 부탁드릴게요~! 🙏 🙏 🙏 🙏 🙏 🙏
를 실행하기 위해

남들에게 어떻게 API가 구성되어있고 어떻게 작동하는지를 알려주기 위한
API문서화를
Swagger 라이브러리를 통해 구현할거다.

Swagger

가장 먼저 swagger 의존성을 추가한다 (gradle)

implementation'io.springfox:springfox-swagger2:2.9.2' //swagger
implementation'io.springfox:springfox-swagger-ui:2.9.2' //swagger




그다음 swagger에 대한 설정을 해주는 swaggerConfig 클래스를 다음과 같이 작성했다

swaagerConfig

이해 쉽게 주석

@Configuration
@EnableSwagger2
public class SwaggerConfig {  // Swagger

    private static final String API_NAME = "soloProject API";
    private static final String API_VERSION = "0.0.1 초기버전";
    private static final String API_DESCRIPTION = "soloProject API 명세서";

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.soloProject.server"))  // Swagger를 적용할 클래스의 package명
                .paths(PathSelectors.any())  // 해당 package 하위에 있는 모든 url에 적용
                .build()
                .apiInfo(apiInfo());
    }

    public ApiInfo apiInfo() {  // API의 이름, 현재 버전, API에 대한 정보
        return new ApiInfoBuilder()
                .title(API_NAME)
                .version(API_VERSION)
                .description(API_DESCRIPTION)
                .build();
    }
}

그다음 컨트롤러에서 애너테이션으로 다음과 같이 API에 대한 설명을 붙여주면 된다.
이해가 쉽도록 주석을 달아놓았다

ProductController

@RestController
@Slf4j
@Validated
@Transactional
@RequiredArgsConstructor
@RequestMapping("/products")
@Api(tags = {"재고 API Test"})  // Swagger 최상단 Controller 명칭
public class ProductController {
    private final static String PRODUCT_DEFAULT_URL = "/products";

    @Autowired
    ProductService productService;

    @PostMapping
    @ApiOperation(value = "물건 등록", notes = "물건 등록 API")
    public ResponseEntity postProduct(@Valid @RequestBody ProductDto.Create productPostDto) {
        Product createProduct = productService.createProduct(productPostDto);
        URI location = UriCreator.createUri(PRODUCT_DEFAULT_URL, createProduct.getProductId());
        return ResponseEntity.created(location).build();
    }

    @PatchMapping("/sell/{product-id}/{member-id}/{quantity}")
    @ApiOperation(value = "재고 판매", notes = "재고 판매 API") // Swagger에 사용하는 API에 대한 간단 설명
    @ApiImplicitParams( // Swagger에 사용하는 파라미터들에 대해 설명
            {
                    @ApiImplicitParam(name = "member-id", value = "멤버 아이디"),
                    @ApiImplicitParam(name = "product-id", value = "재고 물품 아이디"),
                    @ApiImplicitParam(name = "quantity", value = "판매 재고 수량")
            })
    public ResponseEntity sellProduct(@PathVariable("member-id") @Positive long memberId,
                                              @PathVariable("product-id") @Positive long productId,
                                              @PathVariable("quantity") @Positive int quantity) {
        Product patchProduct = productService.sellProduct(memberId, productId, quantity);

        URI location = UriCreator.createUri(PRODUCT_DEFAULT_URL, patchProduct.getProductId());
        return ResponseEntity.created(location).build();
    }

    @PatchMapping("/buy/{product-id}/{member-id}/{quantity}")
    @ApiOperation(value = "재고 구매", notes = "재고 구매 API")
    @ApiImplicitParams(
            {
                    @ApiImplicitParam(name = "member-id", value = "멤버 아이디"),
                    @ApiImplicitParam(name = "product-id", value = "재고 물품 아이디"),
                    @ApiImplicitParam(name = "quantity", value = "구매 재고 수량")
            })
    public ResponseEntity buyProduct(@PathVariable("member-id") @Positive long memberId,
                                              @PathVariable("product-id") @Positive long productId,
                                              @PathVariable("quantity") @Positive int quantity) {
        Product patchProduct = productService.buyProduct(memberId, productId, quantity);

        URI location = UriCreator.createUri(PRODUCT_DEFAULT_URL, patchProduct.getProductId());
        return ResponseEntity.created(location).build();
    }


}

위와 비슷하게 모든 Controller에 설명을 붙여주고
어플리케이션을 실행했다.
그런데

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

라는 에러가 나왔다.

구글링 결과,
spring.mvc.pathmatch.matching-strategy 값이
ant_apth_matcher에서 path_pattern_parser로 변경되면서
발생하는 오류였다.

해당 오류 해결에 대한 코드는 아래의 사진과 같다.

application.yml

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

즉, 이전처럼 ant_apth_matcher으로 설정해줘야 한다.

해당 오류에 관한 설명은,
springboot의 버전이 높아지며 다양한 MVC 패턴 전략이 만들어지며,
이전엔 기본값이 AntPathMatcher(내가 직접 변경해준것) 이었지만,
지금은 기본값이 PathPatternParser(변경 안해줄시 기본값) 이라고 한다.

출처 : https://godekdls.github.io/Spring%20Boot/developing-web-applications/


API 보기

이렇게 작성을 마치고 코드를 실행시켜
http://localhost:8080/swagger-ui.html
를 들어가보면,

이런식으로 내 API에 대해 설명해주고 있다.

그다음, html 문서화를 진행하고 싶다! 라고 생각하면

아까 들어갔던
http://localhost:8080/swagger-ui.html 에서,

사진의 링크
http://localhost:8080/v2/api-docs를 들어간다.

그럼 사진과 같이 수많은 json을 볼 수 있는데,
우클릭을 통해 다른이름으로 저장하기를 클릭해 json 문서를 저장해준다.

swagger editor url로 진입 후
화면에서 상단 File -> Import file 선택 -> 미리 저장해둔 json 파일을 선택한다. (위의 사진)

로드 이후 html 파일로 출력하는게 목표 이므로 상단 Generate Client -> html2 를 선택한다. (위의 사진)

압축을 풀고, html 문서가 만들어진것을 확인할 수 있다.


마치며

이제 문서화까지 완료했다.
남은 내용은 AWS 에 관한 설정들과
CI/CD를 위한 젠킨스와의 연동

까지 남은 것 같다.
처음 ERD 설계부터, 빌드 배포 자동화까지 하려다보니 힘들지만
아는 내용은 더 확실히 더 잘 알게 되고
모르는 내용은 알게 되어가는 그런 과정인 것 같다.

profile
반갑습니다~! 좋은하루 보내세요 :)

1개의 댓글

comment-user-thumbnail
2023년 7월 20일

글이 잘 정리되어 있네요. 감사합니다.

답글 달기

관련 채용 정보