'Markdown'에 해당하는 글 2건

Spring Boot REST API 프로젝트에 API 문서 자동화 툴로 뭘 써야 하나...

 

 

Swagger vs Spring rest docs

 

  • Swagger - 적용 난이도 쉬움, 이쁜 UI, API 테스트 기능, 코드 기반
  • Spring rest docs - 적용 난이도 보통, 단순 UI, 테스트 기반

 

장단점이 분명하지만, 내부용으로 후딱 만들어 쓸 것이라면 Swagger. 외부에 제공해야 한다면 정갈해 보이는 Spring rest docs 이 아닐까 싶다.

 

 

문서화를 위한 gradle 구성

 

  • Step 1. 코드 테스트 -> 에러가 발생하지 않으면 해당 api 내용(snippets: asciidoc 파일) 생성.
  • Step 2. Test 로부터 생성된 snippets 을 import 하도록 api 문서 템플릿(asciidoc 파일) 을 생성하고 빌드하여 API 문서(html) 완성.
  • Step 3. 생성된 html 파일을 외부에 노출하도록 static 디렉토리에 복제.

 

plugins {
    id 'org.springframework.boot' version '2.5.6'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    // gradle 7 이상은 org.asciidoctor.convert 대신 org.asciidoctor.jvm.convert 사용
    id 'org.asciidoctor.jvm.convert' version '3.3.2'  // Step 1
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

bootJar {  // Step 3
    dependsOn asciidoctor
    copy {
        from "${asciidoctor.outputDir}"
        into "${sourceSets.main.output.resourcesDir}/static/docs"
    }
}

asciidoctor {  // Step 2
    sourceDir 'src/main/asciidoc'
    attributes \
        'snippets': file('build/generated-snippets')
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'  // Step 1
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

 

이 build.gradle 파일은 가장 기본적인 세팅이므로 추후 익숙해지면 task 를 통합하거나 build 경로를 수정하여 커스터마이징 해서 편하게 쓰면 되겠다. 예를 들면...

 

asciidoctor {
    dependsOn test
    sourceDir 'src/main/asciidoc'
    outputDir "${sourceSets.main.output.resourcesDir}/static/docs"
    attributes \
        'snippets': file('build/generated-snippets')
}

 

3개의 task (단위 Test - html 생성 - html 복사) 를 하나로 통합한 예...

 

아무튼 build.gradle 파일을 수정했으면 JUnit 와 MockMvc 를 사용하여 Test 를 작성한다. (model, controller 생략)
MockMvc, REST Assured 중 서버 구동없이 컨트롤러 단위 테스트가 쉬운 MockMvc 를 사용한다.

 

@WebMvcTest(TestController.class)
@AutoConfigureRestDocs(outputDir = "build/generated-snippets")
public class TestControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test() throws Exception {

        mockMvc.perform(RestDocumentationRequestBuilders.get("/book/{id}",1).accept(MediaType.APPLICATION_JSON))
                .andDo(print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(document("book",
                        pathParameters(
                                parameterWithName("id").description("book unique id")
                        ),
                        responseFields(
                                fieldWithPath("id").description("book unique id"),
                                fieldWithPath("title").description("title")
                        )
                ))
                .andExpect(jsonPath("$.id", is(notNullValue())))
                .andExpect(jsonPath("$.title", is(notNullValue())));
    }
}

 

* 아래 에러 발생시:
urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?

> pathParameters 을 사용할 때는 MockMvcBuilders.get 대신 RestDocumentationRequestBuilders.get 사용

 

Test 코드에서 document(identifier, ...) 에 기반하여 snippets 의 내용을 구성하게 되며, andExpect 에서 에러가 발생하지 않으면 outputDir 에 snippets 들이 생성된다.

 

curl-request.adoc
http-request.adoc
http-response.adoc
...

 

정상적으로 생성됐다면 이 snippets 들을 불러올 템플릿 asciidoc 파일을 생성한다. (/src/main/asciidoc/)

 

= Document Title
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toc-title: API 명세서
:toclevels: 4

[[api]]

== Book Api
api 에 관련된 설명을 이곳에 적습니다..

include::{snippets}/book/curl-request.adoc[]
include::{snippets}/book/http-request.adoc[]
include::{snippets}/book/path-parameters.adoc[]
include::{snippets}/book/http-response.adoc[]
include::{snippets}/book/response-fields.adoc[]

 

* asciidoc 파일 작성시 참고

https://docs.asciidoctor.org/asciidoc/latest/

https://narusas.github.io/2018/03/21/Asciidoc-basic.html

 

 

Asciidoc 기본 사용법

Asciidoc의 기본 문법을 설명한다

narusas.github.io

 

AsciiDoc Language Documentation :: Asciidoctor Docs

AsciiDoc is a lightweight and semantic markup language primarily designed for writing technical documentation. The language can be used to produce a variety of presentation-rich output formats, all from content encoded in a concise, human-readable, plain t

docs.asciidoctor.org

 

Spring REST Docs는 기본적으로 Asciidoctor 를 사용하지만, Markdown 을 사용할 수도 있다.

간략하게 이런식으로 작성하고 bootJar 를 실행하면 /build/docs/asciidoc/book.html 파일이 생성된 것을 확인할 수 있다.

 

spring-rest-docs

 

bootRun 을 실행하면 해당 파일이 /build/resources/main/static/docs 에 복사되며 http://localhost/docs/book.html 에서 확인 가능.

 

 


WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,



2001년 제대하고 갖은 고생을 하며 1년만에 개인홈피를 만들었고 그 한 구석에다가 공개형 일기를 써나갔다. 몇년 뒤 싸이월드에서 개인홈피는 쨉도 안되는 기능을 가진 미니홈피를 만들어 일반인들에게 일기, 사진첩, 게시판, 방명록을 제공했다. SK 인수 후 네이트온과 연동되어 친구들과 쉽게 미니홈피를 공유할 수도 있었다. 나도 그 당시 싸이를 했었지만 여친과의 결별 후 미니홈피를 잠정 비공개하는 이상한 짓거리를 반복하다가 어느 순간 접어버렸다. 미니홈피가 뜸해지니 그 다음엔 블로그가 떠오르게 됐다. 포털마다 블로그들이 난무했고 개나소나 DSLR 을 사서 블로깅을 시작했다. 2006년 나도 디자인에서 손을 떼며 세상에서 단 하나뿐인 내 개인홈피 업데이트를 중단하고 블로깅을 하기 시작했다. 그 당시 워드프레스가 세계적으로는 대세였지만 그 땐 영어를 한 자라도 덜 보고 싶었고 결국 테터툴즈 설치형을 선택했다. '내꺼' 에 집착하던 나는 내 유료공간에 내 유료이름(oops4u.com)으로 블로그를 시작했다. 이렇게 해야 뭔가 특별한 블로그 같았다. 그리고 수년만에 사람들은 내 유료이름이 아닌 포털을 통해서만 내 블로그에 들어온다는 사실을 알게 됐다. 곧 아무 의미 없는 개인 서버를 처분했다. 도메인도 처분하려고 했는데, 깜빡하다가 만료되고 나면 일단 연장하고, 만료되면 또 연장하고 하다보니 18년째다. 


도메인을 처음 샀던 18년 전, 내 개인홈피에 들어올 수 있는 방법은 브라우저에 직접 입력하는 방법 한 가지 뿐이라고 생각해서 나름 쉽게 지은거다. 학생때까지만 해도 난 보기와 다르게 이것저것 재주가 많아서 우리말 닉네임이 '깜짝' 이었다. 키는 작았지만 운동도 누구보다 잘 했고, 피아노나 컴퓨터 실력도 주변에선 제일 괜츈했고, 곱상해 보이지만 곳곳에 털도 무성했고... 저때는 '반전' 이란 말을 잘 안썼지만 약간 그런 느낌. 영어로 oops, 영문 아이디 ggamzzak... 저 도메인이 그렇게 탄생한거다. 그렇게 고민끝에 결정한 저 도메인은 18년 동안 나만 알고 있다. 요즘 세상에 도메인은 네이버랑 구글만 알면 된다. 유니크한 도메인도 이제 필요없다. 만료기한인 2021년까지만 사용하고 빠빠이 할 생각이다.


아무튼 지금은 티스토리 가입형을 사용중이며, 블로깅 14년차다. 일기로 시작했다가 개발일지 위주로 가려고 했는데, 사진에 자동차에 피아노에 지금은 완전 잡동사니. 하지만 그 속에 내 모든게 있다. 나의 과거, 나의 생활과 생각, 반성과 다짐들이 기록되어 있다. 1일 1게시물을 목표로 하였으나, 뭔가에 홀려서 하루이틀 빼먹다가 몇 달씩 방치되어 있던 적도 많았다. 1일 1게시물을 목표로 했던 것은 단지 게시물의 양만 늘리려고 했던 것은 아니다. 바빠서 녹초가 된게 아니라면 잠시라도 시간을 내서 뭐든 한 가지라도 익히고 그 흔적을 남기고자 했던 것인데 다시 리셋할 때가 되긴 했다. 지금까지의 게시물을 다시 쓱~ 훑어보니 블로거로서 공유해 온 내용의 질은 그저 그렇지만, 자기계발과 자기반성 용도로는 만족할만 하다. 앞으로도 시간내서 틈틈이 블로깅하는 것에는 조금도 이견이 없는데, 오래도록 사용해온 티스토리를 계속 유지해야 할지는 고민 중이다. 손에는 익숙하지만 새롭게 각오를 다지는 김에 스킨이라도 바꿔보고 싶은데, 스킨 바꾸면 모든 게시물의 폼이 달라질 것이고, 그럴바에는 백업해서 이 기회에 Markdown 기반 플랫폼으로 이전을 하고 싶기도 한데 이식이 잘 될지 모르겠다.


(약 1시간 정도의 서핑을 하고...)


ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 완전... 코 꼈다. 2016년인지 2017년인지부터 티스토리 백업 기능을 없애 버렸다고 한다. 어떤 식으로든 정상적인 백업은 불가능하며, 백업 프로그램이 몇개 떠돌아 다니는거 같기는허나 정상 복구 시키는건 어림없다고 본다. 서비스 종료시에는 백업이 가능하도록 하겠다니 일단 믿어야겠지만, 지금이라도 새출발을 해야 하는건지. 2천개의 게시물을 버릴 수는 없는데ㅜ. 차라리 잘됐다. 어디로 옮길까 고민 많았는데 그냥 눌러앉지 뭐. 긍정적으로 생각하며 8년만에 스킨이나 바꿔봤다. 또 여기에 적응해야지 흐규. 티스토리도 블로그로써는 꽤 괜찮은데 자꾸 불안한건 기분탓인가...




WRITTEN BY
손가락귀신
정신 못차리면, 벌 받는다.

,