API 모킹으로 개발 생산성 높이기 with Next.js API Routes

Next.js API Routes로 모킹 서버를 만들어 프론트엔드 개발을 진행했던 경험을 정리했습니다.

2023년 09월 13일| by유연

Mock API

API 모킹의 필요성

그동안 참여했던 프로젝트들을 돌이켜보면 대부분 아래와 같은 프로세스로 진행되곤 했습니다.

여기서 눈여겨봐야 할 부분은 프론트엔드의 개발 영역이 둘로 나뉜다는 것입니다. 한 부분은 API와 크게 관련 없는 UI 요소 개발이고, 나머지 한 부분은 API를 활용해야 하는 부분의 개발입니다. 예상대로 잘 프로젝트가 진행된다면 큰 문제가 없겠지만, 아래와 같은 상황이 된다면 조금 타격이 생깁니다.

프론트엔드 혹은 백엔드 둘 중 한 군데라도 계획과 달리 개발이 늦어지게 되면, 자연스럽게 상대는 할 일이 없게 되는, 즉 작업 지연이 발생하게 됩니다. 작업 지연이 발생하게 되면, 자연스럽게 프론트와 백 모두 순수한 작업 시간이 늘어나 QA에 할애할 수 있는 시간도 줄어들게 됩니다.

위 그림들에서 또 중요한 부분은 API 설계 오류 피드백이 개발이 절반 정도 진행된 시점, 심지어는 백엔드 입장에서는 개발이 모두 완료된 이후에 가능하다는 것입니다. 만약 API 설계에 심각한 문제가 있다면, 그동안의 코드를 모두 갈아엎어야 하는 대참사가 발생할 수도 있고, 그 작업은 배포 거의 직전에 수행되어야만 하는 산 넘어 산의 상황이 됩니다.

놀랍게도 프론트엔드 작업이 늦어지는 문제, 백엔드 작업이 늦어지는 문제, 그리고 API 설계에 심각한 문제가 있었던 경우 모두 42gg 3기 작업을 하면서 겪었던 상황입니다. 순수 개발 시간이 길어짐 → QA 기간이 줄어듦 → 충분한 테스트를 못 해봄에서 오는 불안으로 인해서 스트레스를 받았기 때문에 다음 기수에는 반드시 이 문제를 해결하겠다고 다짐했습니다. 그리고 고민의 결과, Mock API를 활용하기로 했습니다.

Mock API?

mock은 “가짜의, 모조의”라는 뜻을 가지고 있습니다. 즉, Mock API란 가짜 API를 의미합니다. 백엔드 API 개발이 완료되기 전, 가짜 API를 만들어서 실제 API를 활용한 것처럼 개발하는 것입니다.

Mock API를 활용하게 되면, 아래와 같은 프로세스로 개발할 수 있습니다.

위의 그림들과 비교해 보면 다른 점이 몇 가지 보입니다.

첫 번째로 API 모킹 작업이 추가되었습니다. 백엔드가 실질적인 개발에 돌입하기 전에 마련해 둔 API 스펙을 기반으로 가짜 API를 만드는 작업입니다. 가짜 API를 만드는 방법은 실제로 개발 서버를 간단히 만들어 볼 수도 있겠고, (아주 간단한 방법으로 json-server를 활용할 수도 있을 것 같습니다.), MSW와 같은 API 모킹 라이브러리를 활용할수도 있겠지만, 저희는 빈 폴더로 남아 있던 Next.js의 API Routes를 이용하여 API를 만들었습니다. API 모킹은 해당 API를 사용하는 기능을 개발하기 직전에 진행되었고, 그림 상에서는 개발의 볼륨과 API 모킹의 볼륨이 비슷해 보이지만 어디까지나 가짜 API를 만드는 것이기 때문에 사실 굉장히 간단한 과정입니다. (모킹에 큰 비용이 들게 되면 개발 비용 줄이기라는 본래의 목적에 맞지 않게 되기도 하고요!)

두 번째로는 초록색 블록의 이름이 UI 요소가 아닌 개발이 되었다는 점입니다. 앞서서 모킹한 API를 이용해서, 마치 실제 완성된 API를 호출하는 것처럼 개발하는 과정입니다. 저희는 Mock API URL에 특정한 prefix를 붙여서 테스트 API의 경우에는 다른 axios instance로 호출하는 식으로 Mock API를 호출했습니다.

세 번째로는 API 설계에 대한 피드백 과정이 앞으로 당겨졌다는 점입니다. 프론트엔드에서 API를 모킹하며 API 스펙에 대한 1차 점검을 하고, Mock API를 실제 서비스에 적용하면서 API 설계의 피드백을 주고받을 수 있게 된 것입니다. 피드백을 주고받는 시점이 프론트엔드도, 백엔드도 개발이 많이 진행되기 전이기 때문에 설계 변경에 대한 리스크가 줄어들게 되었습니다.

마지막으로는 API 연결 작업이 짧아졌다는 것입니다. Mock API는 API URL에 특정한 prefix가 붙는다는 점을 빼면 미리 정해둔 API 스펙과 동일하게 만들어졌습니다. 따라서 실제 API를 연결하기 위해서는 큰 작업 없이 API URL에 prefix만 빼 주면 되고, 저희의 경우에는 별도로 Mock API를 호출하는 axios instance를 만들어서 사용했기 때문에 Mock API를 호출하는 axios instance를 실제 API를 호출하는 instance로 교체하기만 하면 API 연결 작업이 마무리됩니다.

API 모킹하기

들어가기에 앞서 작업 당시에는 API Routes를 이용한 모킹이 불러올 수도 있는 문제점을 전혀 모르고 진행했다는 점을 알립니다…

API Routes를 이용하면 Next.js를 이용해서 API를 만들 수 있습니다.

pages/api 폴더 안에 있는 모든 파일은 /api/* URL에 매치되는 API로 취급됩니다.

1
// pages/api/hello.ts
2
import type { NextApiRequest, NextApiResponse } from "next";
3
4
export default function handler(req: NextApiRequest, res: NextApiResponse) {
5
res.status(200).json({ message: "Hello from Next.js!" });
6
}

위 파일은 /api/hello로의 모든 요청을 처리하는 API입니다. 아래와 같이 특정 HTTP 메서드를 처리하도록 만들 수도 있습니다.

1
// pages/api/hello.ts
2
import type { NextApiRequest, NextApiResponse } from "next";
3
4
export default function handler(req: NextApiRequest, res: NextApiResponse) {
5
if (req.method === "POST") {
6
// Process a POST request
7
} else if (req.method === "GET") {
8
// Process a GET request
9
} else {
10
// Handle any other HTTP method
11
}
12
}

저희가 활용한 Mock API 코드 중 하나는 이렇게 생겼습니다.

1
// pages/api/users/image.ts
2
import type { NextApiRequest, NextApiResponse } from "next";
3
4
export default function handler(req: NextApiRequest, res: NextApiResponse) {
5
const { method, body } = req;
6
if (method === "POST") {
7
// console.log(body);
8
res.status(201).end();
9
// res.status(400).end();
10
}
11
}

이 API는 사용자의 프로필 이미지를 바꾸는 API를 모킹한 것입니다. POST /api/users/image 요청이 왔을 때 201 응답을 보내도록 했는데요. 주석을 풀어 요청 body로 전달된 image content를 확인 할 수도 있고, 201 응답 대신 400 응답을 보내서 에러 상황에 대해서도 테스트 해 볼 수 있습니다.

Mock API 활용 후기

개선된 점 (체감)

기획 단계에서 프론트엔드 팀원들과 백엔드 팀원들이 모두 참여하긴 했지만 이후 API 설계 과정에는 온전히 백엔드 팀원들만 참여했기 때문에 작성된 API 명세가 프론트엔드의 사정과 맞지 않는 경우가 종종 있었습니다.

앞서 밝혔던 3기 개발 과정 중에도 프론트엔드에 당연히 가지고 있을 거라고 생각한 데이터가 사실은 없는 데이터라 백엔드에서 일부 API의 수정이 있었던 적이 있었는데요. 이번 4기 작업에서는 백엔드에서는 API 구현 전에 먼저 명세를 명확히 작성하고, 프론트엔드에서는 그 명세에 맞게 API를 모킹하면서 바로 설계에 대해 피드백할 수 있었습니다.

백엔드 분들에게 직접 여쭤보진 못했지만, 개인적으로는 백엔드 쪽에서도 피드백을 바로 받을 수 있어 불필요한 수정 작업을 덜 할 수 있었다는 장점이 있지 않았을까 하는 생각이 듭니다.

개선된 점 (객관적으로)

위 내용은 제가 개인적으로 개선되었다고 느꼈던 부분이고, 객관적인 데이터로도 생산성이 향상했는지를 확인해 보고 싶어서 비교할 수 있는 데이터들을 찾아보았는데요.

3기 작업에서는 프로젝트를 시작한 지 한 달 만에 첫 API 연결 작업을 시작했고, 그 후 36일이 지난 뒤에 API 관련 마지막 버그 해결 PR이 올라왔습니다. 그동안 머지되었던 API 관련 PR은 23개입니다.

4기 작업에서는 기능 개발과 함께 Mock API 추가 작업을 진행했습니다. 실제로 백엔드 API와 연결하는 작업은 저번 기수와 거의 동일하게 한 달 뒤에 첫 API 연결 작업을 시작했고, 26일이 지난 뒤에 API 관련 마지막 버그 해결 PR이 올라왔습니다. 그동안 머지되었던 API 관련 PR은 16개입니다.

기수API 연결 시작까지API 연결 끝까지API 관련 PR 개수
3기1개월36일23개
4기1개월26일16개

객관적인 비교 기준이 될지는 잘 모르겠지만 3기와 4기 구성 인원들이 거의 동일하고, 4기에는 조금 쉬엄쉬엄 (ㅎㅎ..) 작업했다는 것을 감안하면 객관적으로 봤을 때도 꽤 생산성이 높아졌다고 판단할 수 있을 것 같습니다.

아쉬운 점

꽤 시간이 지난 뒤에 추가된 내용입니다.

저희가 MSW 같은 좋은 Mocking 라이브러리를 사용하지 않은 이유는 MSW를 접해보지 않은 팀원들이 빡빡한 개발 기간 내에 Service Worker를 공부하고 잘 사용하기에는 무리가 있다고 생각했기 때문입니다.

하지만 제가 간과했던 것은 API Route는 Production 환경에도 여전히 남아있다는 사실입니다… 즉 테스트 코드들이 Production 환경에 그대로 남아있습니다. 이것은 빌드된 파일이 커질 뿐 아니라 Production 환경에서도 테스트 데이터들을 확인할 수 있는... 허술한 프로그램이 되었다는 뜻이죠…

제가 왜 이 생각을 못했었는지 아쉬움이 남지만 그래도 Mocking을 이용한 생산성 향상을 마음으로 이해하고 머리로 이해할 수 있게 되어서 소득이 꽤 높았다고 생각합니다!

기회가 된다면 남아 있는 API Routes 코드를 제거하고 MSW에 대한 간단한 가이드북을 만들어서 다음 기수 팀원들이 큰 러닝 커브 없이 MSW를 이용해서 좀 더 멋진 Mock API를 활용할 수 있게 하고 싶다는 생각이 듭니다...