[Spring] API 문서화

zini9188·2023년 3월 8일
0

Spring

목록 보기
33/33

API 문서화

클라이언트가 REST API 백엔드 애플리케이션에 요청을 전송하기 위해서 알아야 되는 요청 정보(URL, URI, Reqeust Body, Query Parameter)를 문서로 잘 정리하는 것을 의미한다.

API 문서 생성의 자동화가 필요한 이유

API 문서를 수기로 작성하는 것은 비효율적이고, 누락되는 기능이 생길 수 있다.

문서 자동화를 통해 작업 시간을 단축하고, 애플리케이션의 완성도가 높아질 수 있다.

Swagger

Java 기반의 애플리케이션에서는 전통적으로 Swagger라는 API 문서 자동화 오픈 소스를 많이 사용해왔다고 한다.

@ApiOperation(value = "회원 정보 API", tags = {"Member Controller"})
@RestController
@RequestMapping("/v11/swagger/members")
@Validated
@Slf4j
public class MemberControllerSwaggerExample{
	private final MemberService memberService;
    private final MemberMapper mapper;
    public MemberControllerSwaggerExample(MemberService memberService, MemberMapper mapper) {
        this.memberService = memberService;
        this.mapper = mapper;
    }
    @ApiOperation(value = "회원 정보 등록", notes = "회원 정보를 등록합니다.")
    
    @ApiResponses(value = {
    		@ApiResponse(code = 201, message = "회원 등록 완료"),
            @ApiResponse(code = 404, message = "Member not found")
    })
    @PostMapping
    public ResponseEntity postMember(@Valid @RequestBody MemberDto.Post memberDto) {
        Member member = mapper.memberPostToMember(memberDto);
        member.setStamp(new Stamp()); // homework solution 추가

        Member createdMember = memberService.createMember(member);

        return new ResponseEntity<>(
                new SingleResponseDto<>(mapper.memberToMemberResponse(createdMember)),
                HttpStatus.CREATED);
    }  
}

이외에도 여러 기능, Request Body, Response Body같은 DTO 클래스들에 대해 무수히 많은 애너테이션을 애플리케이션 코드에 추가해야 한다.

기능이 늘어날수록 API 문서를 위한 코드가 늘어나 가독성이 안좋아지게 된다.

하지만 이런 과정을 모두 통과하고 Swagger를 구현하는데 성공한다면 Postman과 같은 API 요청 툴로써의 기능을 사용할 수 있고, API 문서를 만들 수 있다는 장점이 있다.

Spring Rest Docs

Spring Rest Docs는 Swagger와 달리 애플리케이션 기능 구현과 관련된코드에는 API 문서 생성을 위한 애너테이션 같은 어떠한 정보도 추가되지 않는다.

그 대신 테스트 클래스에 API 문서를 위한 정보를 추가하게 된다.

문서 생성 흐름

스니핏은 문서의 일부 조각을 의미하며, 테스트 케이스 하나 당 하나의 스니핏이 생성된다. 여러개의 스니핏을 모아 하나의 API 문서를 생성할 수 있다.

설정

  • AsciiDoc 문서를 생성해주는 Asciidoctor를 위한 플러그인 설정
id "org.asciidoctor.jvm.convert" version "3.3.2"
  • API 문서 스니핏이 생성될 경로를 지정
ext {
	set('snippetsDir', file("build/generated-snippets"))
}
  • AsciiDoctor에서 사용되는 의존 그룹을 지정하여 :asciidoctor task가 실행되면 내부적으로 asciidoctorExtensions라는 그룹을 지정
configurations {
	asciidoctorExtensions
}
  • spring-restdocs-core, spring-restdocs-mockmvc, spring-restdocs-asciidoctor의 의존 라이브러리 추가
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
  • :test task실행 시, API 문서 생성 스니핏 디렉토리 경로를 설정
tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}
  • :asciidoctor task 실행 시, Asciidoctor 기능을 사용하기 위한 asciidoctorExtensions설정
tasks.named('asciidoctor') {
	configurations "asciidoctorExtensions"
	inputs.dir snippetsDir
	dependsOn test
}
  • :copyDocument task가 수행되면 index.html파일이 copy되며, API 문서를 파일 형태로 외부에 제공하기 위해 사용
task copyDocument(type: Copy) {
	dependsOn asciidoctor
	from file("${asciidoctor.outputDir}") 
	into file("src/main/resources/static/docs")
}
  • :build 실행 전에 :copyDocument가 먼저 수행되도록 설정
build {
	dependsOn copyDocument
}
  • 애플리케이션 실행 파일이 생성하는 :bootJar task 설정으로 Asciidoctor 실행으로 생성되는 index.html 파일을 jar 파일 안에 추가
bootJar {
	dependsOn copyDocument    
	from ("${asciidoctor.outputDir}") { 
		into 'static/docs'     
	}
}

전체 설정

plugins {
	id "org.asciidoctor.jvm.convert" version "3.3.2"   
}

ext {	
	set('snippetsDir', file("build/generated-snippets"))
}

configurations {
	asciidoctorExtensions
}

dependencies {
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'  
	asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
}

tasks.named('test') {
	outputs.dir snippetsDir
	useJUnitPlatform()
}

tasks.named('asciidoctor') {
	configurations "asciidoctorExtensions"
	inputs.dir snippetsDir
	dependsOn test
}

task copyDocument(type: Copy) {
	dependsOn asciidoctor            
	from file("${asciidoctor.outputDir}") 
	into file("src/main/resources/static/docs")   
}

build {
	dependsOn copyDocument  
}

bootJar {
	dependsOn copyDocument    
	from ("${asciidoctor.outputDir}") {  
		into 'static/docs'     
	}
}

사용하기

then 절에서 andDo를 통해 API 문서화 코드를 시작할 수 있다.

actions.andExpect(status().isCreated())
	   .andExpect(header().string("Location", is(startsWith("/v11/members/"))))
       .andDo(document("post-member",
       			getRequestPreProcessor(),
                getResponsePreProcessor(),
                requestFields(
                		List.of(
                        		fieldWithPath("email").type(JsonFieldType.STRING).description("이메일"),
                                fieldWithPath("name").type(JsonFieldType.STRING).description("이름"),
                                fieldWithPath("phone").type(JsonFieldType.STRING).description("휴대폰 번호")
                        )
                 ),
                 responseHeaders(
                 		headerWithName(HttpHeaders.LOCATION).description("Location header. 등록된 리소스의 URI")
                 )
         ));

장점

  • 테스트가 성공적이지 않으면 문서의 생성이 완료되지 않는다.

  • 이는 API 스펙 정보와 API 문서 정보의 불일치를 방지할 수 있다는 의미이다.

단점

  • 테스트 케이스를 일일이 작성해야 한다.

  • 모든 테스트 케이스가 통과되어야 한다.

profile
백엔드를 지망하는 개발자

0개의 댓글