본문 바로가기
우아한테크코스/Level3

생각하기 전에 행동하면 어떻게 될까? (부제: 스레독)

by kriorsen 2024. 8. 11.

📌  코딩하는 것보다 생각하는 게 더 귀찮다구요


"말하기 전에 생각했나요?"
"얕보지 마시죠. 저는 말보다 행동이 앞섭니다."

 

레벨2에 기술적 의사 결정을 어떻게 해야 할지 깨달았다며 기세등등하게 블로그 포스팅을 해 놓고 실천 하나도 안 한 김모씨(945세). 코딩 테스트 풀 때 시간 복잡도 계산 안 하고 냅다 제출할 때부터 알아봤다 진짜.

 

 

사건은 이 디스코드 메시지로부터 시작되었다. 노션에 작성된 API 문서를 제때제때 수정하지 않은 탓에, 함께 개발하는 프론트 크루들이 문서에 대해 질문하는 일이 잦아졌다. 백엔드 팀원들도 이를 인지하여 다음날 바로 노션 문서를 전체적으로 수정했고, 나는 동기화가 잘 이루어질 수 있는 문서의 도입이 필요하다는 것을 깨달았다.

 

 

📌  Spring REST Docs & Swagger


프론트엔드 크루들이 바로 요청을 보낼 수 있는 Swagger 문서를 더 선호해서 초기에는 노션과 Swagger를 함께 사용하려고 했다. 그러나 스웨거 사용 시 프로덕션 코드에 문서를 위한 코드가 추가된다는단점으로 인해 결국에는 REST Docs로 변경할 것 같다는 이야기가 나왔다. 굳이 일을 두 번 하는 것보다야 처음부터 REST Docs를 적용하는 것이 낫겠다고 판단하여 스웨거에 대한 학습을 아예 하지 않고 REST Docs로 작업을 했다.

 

📌  Non-딸깍 태스크  


행동대장 백엔드 단어 사전
딸깍: 아무 생각 없이 공식 문서에 나온 튜토리얼 따라서 하면 금방 끝나는 작업 (ex. 도커 설치, ssl 인증서 적용)

 

레벨2에 레독 적용하려고 주말 하나 통으로 날려서 삽질을 했었던 터라 이번에는 아주아주 수월하게 작업할 수 있을 거라고 생각했다. 말 그대로 딸깍 태스크라고 생각했는데, RestAssured에서 MockMvc로 좀 바뀌었다고 어버버버버버버

 

어찌저찌 Spring 공식 문서에서 하라는 대로 build.gradle 파일 구성하고 테스트를 작성하기 시작했다. 문서화를 하려면 테스트에 문서 관련 요청, 응답 필드 그리고 그에 대한 설명을 코드로 일일이 추가해야 해서, 아예 테스트 코드만 작성된 테스트와 문서화를 위한 테스트를 분리하기로 했다. 그렇게 탄생한 RestDocsSupprot 클래스

@ExtendWith({RestDocumentationExtension.class})
abstract class RestDocsSupport {

    protected MockMvc mockMvc;

    protected ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    void setUp(RestDocumentationContextProvider restDocumentation) {
        this.mockMvc = MockMvcBuilders.standaloneSetup(initController())
                .apply(documentationConfiguration(restDocumentation))
                .build();
    }

    protected abstract Object initController();
}

 

MockMvcBuilder는 다음과 같은 두 개의 정적 팩토리 메서드가 제공된다.

 

public final class MockMvcBuilders {
    private MockMvcBuilders() {
    }

    public static DefaultMockMvcBuilder webAppContextSetup(WebApplicationContext context) {
        return new DefaultMockMvcBuilder(context);
    }

    public static StandaloneMockMvcBuilder standaloneSetup(Object... controllers) {
        return new StandaloneMockMvcBuilder(controllers);
    }
}

 

webAppContextSetup의 경우 아예 애플리케이션 컨텍스트 전체를 받고, standaloneSetup은 테스트에서만 사용하는 특정 객체들을 받아서 조금 더 가볍다. 기존 컨트롤러 테스트가 모두 서비스를 모킹하는 방식으로 작성되어서 후자를 선택해 컨트롤러만 넘겨 주도록 작성했다.

 

📌  REST Docs 사용 자격 요건


 

 

성실함과 꼼꼼함을 겸비한 사람만 사용하세요... 전 정말 말렸습니다.

 

 

이런 식으로 reports라는 배열 안에 name과 price를 속성으로 가지는 객체를 저장하여 반환하는 API에 대한 테스트에서 document()를 다음과 같이 작성하면 오류가 발생한다.

document("getMemberBillReports",
    preprocessRequest(prettyPrint()),
    preprocessResponse(prettyPrint()),
    pathParameters(
        parameterWithName("eventId").description("행사 ID")
    ),
    responseFields(
        fieldWithPath("reports").type(JsonFieldType.ARRAY).description("전체 정산 현황 목록"),
        fieldWithPath("reports[0].price").type(JsonFieldType.NUMBER).description("참여자 정산 금액")
))

 

 

첫 번째 줄 읽어 보면 알 수 있듯 문서화가 되지 않은 부분이 있는 게 문제라는 건데, 누락된 reports[].name 부분을 추가하면 테스트가 문제 없이 잘 돌아간다.

 

처음 저 오류를 마주했을 때는 성가시고 귀찮다고 생각했지만, 글을 작성하고 있는 이 시점에 다시 생각해 보니 실수 없이 문서를 작성하도록 해 주니 꽤 큰 장점이라고 느껴지기도 한다.

 

📌  충격적 비주얼의 소유자


"감자 크롬 다크모드 꺼 봐."
"아... 꺼도 똑같은데? 근데 문서가 꼭 예뻐야 되나? ㅎㅎ"

 

몇 시간 걸려서 열심히 API 설명 적어 놨더니 결과물이...

 

디자인 스펠링도 모르는 백호마저 고개를 절레절레 젓게 만든 똥색 문서가 탄생했다. 다행히 빌드 후 생성된 index.html은 그나마 가독성이 조금 더 낫다.

 

 

📌  이제 그만 괴롭힐 때도 됐잖아


TMI - 감자는 귀찮아서 아직까지도 크롬 다크모드를 다시 켜지 않았다.

 

마크다운은 include를 사용할 수 없어서 프로젝트에 asciidoctor를 적용했다. adoc 파일을 통해 변환된 html은 build/docs/asciidoc 경로에 저장된다. 서버 애플리케이션 실행 시 이를 정적 자원으로 제공하려면 src/main/resource/static/docs 아래에 해당 html들을 옮겨놔야 한다.

 

build.gradle에 파일을 옮겨 주는 copyApiDocuments라는 작업을 추가하고 bootJar와 build가 해당 작업에 의존하도록 설정하여 다음과 같은 action 실행 결과를 얻었다.

 

asciidoctor -> copyApiDocuments -> bootJar 순서로 잘 실행하고 있는데 도커 이미지의 jar를 압축 해제해서 확인을 하면 resources에서 index.html을 찾을 수 없었다. 🥲

 

gradle이 빌드 최종 결과물인 jar 파일을 생성할 때 build 경로 아래 파일을 기준으로 하기 때문에 /src/main/resources가 아닌 /build/resources/main/static 하위에 저장해야 원하는 결과를 얻을 수 있다.

 

📌  왜 기술적 의사 결정을 제대로 안 했다고 했냐면요


REST Docs 문서 작업을 하면서도 계속 문서화 도구에 대한 논의가 이어졌다. 노션을 제대로 관리한다면 불필요한 작업이기도 하고, 기능 구현이 끝나기 전에도 프론트에게 문서가 제공되어야 하기 때문에 결국 노션을 항상 사용해야 한다. 결국은 노션만 있으면 되는 거 아닌가? 하는 생각이 계속 들기도 했고, 와중에 Swagger도 테스트 코드 기반 문서 생성이 가능하다는 사실을 알게 되어서 좀 허무했다.

 

현재 방식에 치명적인 단점이 있는 건 아니어서 유지하기로 했지만, 또 이 정도의 공수를 들일 만큼 유의미한 이점을 가져온다고도 선뜻 말할 수도 없을 것 같다. 실행하기 편하고 UI가 깔끔한 Swagger와 비교했을 때 Rest Docs의 장점으로 가장 크게 와닿았던 건 유연한 커스터마이징이다. 이 API가 이전에는 어떤 형식이었고, 어떤 점을 고려해서 이렇게 설계되었고, 해당 API를 사용할 때 어떤 사항들을 주의해야 하는지와 같은 내용들도 자유롭게 작성할 수 있다. 팀에서 문서화 항목을 조금 더 구체적으로 잡으면 REST Docs의 이점을 제대로 느낄 수 있을 것 같다.

 

'우아한테크코스 > Level3' 카테고리의 다른 글

CORS에서 감자로 살아남기 🥔  (1) 2024.07.29