MSW를 활용한 API 모킹과 개발 환경의 분리

▪︎ API 모킹 도입의 배경

프론트엔드와 백엔드가 동시에 개발을 시작하는 프로젝트에서는 항상 한 가지 딜레마가 존재합니다. 프론트엔드 개발자는 백엔드 API가 완성되기 전까지 실제 데이터를 다루는 코드를 작성하기 어렵다는 점입니다.

image.png

우리 프로젝트는 짧은 MVP 개발 기간이라는 제약 조건 속에서 이 문제를 해결해야 했습니다. 이를 위해 다음과 같은 접근 방식을 채택했습니다.

▫︎ 선행 API 명세 작성

  • 디자인 시안 분석을 통한 필요 API 도출
  • 데이터 구조 및 엔드포인트 사전 정의
  • 팀 간 API 명세서 공유 및 합의

▫︎ 병렬 개발 전략

  • 백엔드: API 명세에 따른 실제 구현
  • 프론트엔드: 모킹된 API를 통한 선행 개발

image.png

우선 디자인 시안을 분석하여 우리 앱 개발에 필요한 API들을 분석하고 정리하여 API 명세서를 작성했습니다. 해당 문서를 통해 프론트엔드 - 백엔드 간 소통을 진행하였으며 이를 바탕으로 모킹된 API를 작성, API 관련 개발을 초기부터 진행할 수 있었습니다.

▪︎ MSW란?

MSW는 서비스 워커를 활용하여 네트워크 수준에서 API 요청을 가로채고 모의 응답을 제공하는 API 모킹 라이브러리입니다.

image.png

출처: https://www.codit.eu/blog/how-to-mock-api-requests-in-front-end-development/

  1. 브라우저에 Service Worker가 구성되어 있고, 실제 요청(Request)이 발생하면 Service Worker가 이를 가로챕니다.
  2. Service Worker는 가로챈 요청(Request)을 복사하여 MSW로 전달합니다.
  3. MSW는 가로챈 요청을 미리 정의된 모의 응답(Mock Response)과 매칭하는 작업을 수행합니다.
  4. MSW는 모의 응답을 Service Worker에 전달합니다.
  5. Service Worker는 모의 응답을 브라우저에 전달합니다.

▫︎ MSW의 주요 특징

  1. 네트워크 레벨 모킹
    • 실제 네트워크 요청을 가로채서 처리
    • 브라우저 네트워크 탭에서 요청 확인 가능
    • 실제 API와 동일한 동작 방식
  2. 개발 환경 통합
    • 별도의 서버 없이 모킹 환경 구축
    • 실제 API로의 전환이 용이
    • 테스트 코드와의 높은 호환성

▪︎ MSW를 활용한 API 모킹

우리 프로젝트에 MSW를 적용하여 백엔드 개발 이전 모킹된 API를 통한 관련 코드 작성이 가능했습니다. 뿐만아니라 테스트 코드에서도 MSW를 활용하여 API 통신과 관련된 테스트도 진행하여 보다 안정된 코드 작성이 가능했습니다.

이는 프론트엔드 개발 생산성을 크게 증대시켰습니다. 실제 API와 모킹 API 연결 간 코드의 수정이 필요가 없었기 때문에 명세서 대로 작성하고 백엔드 개발 완료 이후에는 정상적으로 동작하는지 확인만 해주면 되었습니다.

image.png

▫︎ 핸들러 구현 예시 (Auth)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
export const signup = [
/* -------------------------------- 인증 이메일 전송 ------------------------------- */
http.post<MailSendRequestBody, PathParams>(
`${process.env.NEXT_PUBLIC_BASE_URL}/auth/sign-up/emails`,
async ({ request }) => {
const { email } = await request.json();

// 이메일 중복 체크 실패 (이미 가입된 이메일)
if (email === FAKE_USER_EMAIL) {
return HttpResponse.json(
{ message: 'Email already exists' },
{ status: 400 },
);
}

// 이메일 중복 체크 성공
return HttpResponse.json(
{
message: 'Email sent',
},
{ status: 200 },
);
},
),

/* ------------------------------- 이메일 인증코드 확인 ------------------------------ */
http.post<VerifyRequestBody, PathParams>(
`${process.env.NEXT_PUBLIC_BASE_URL}/auth/emails/verify`,
async ({ request }) => {
const { verifyNumber } = await request.json();

// 이메일 인증코드 확인 실패
if (Number(verifyNumber) !== FAKE_EMAIL_CODE) {
return HttpResponse.json(
{
message: 'Invalid email code',
},
{
status: 400,
},
);
}

// 이메일 인증코드 확인 성공
return HttpResponse.json(
{
message: 'Email code verified',
verifiedToken: FAKE_VERIFIED_TOKEN,
},
{ status: 200 },
);
},
),

/* -------------------------------- 회원 가입 요청 -------------------------------- */
http.post<SignupRequestBody, PathParams>(
`${process.env.NEXT_PUBLIC_BASE_URL}/auth/sign-up`,
async ({ request }) => {
const {
email,
password,
name,
nickname,
birthDate,
contact,
verifiedToken,
} = await request.json();

// 회원 가입 성공
if (
email &&
password &&
name &&
nickname &&
birthDate &&
contact &&
verifiedToken
) {
return HttpResponse.json(
{
message: 'Sign up successful',
},
{
status: 200,
headers: {
'Set-Cookie': 'accessToken=msw-access, refreshToken=msw-refresh',
},
},
);
}
return HttpResponse.json({ message: 'Sign up failed' }, { status: 400 });
},
),
  1. 실제 API 스펙 반영
    • 동일한 엔드포인트 사용
    • 일치하는 요청/응답 구조
    • 에러 케이스 시뮬레이션
  2. 상태 관리
    • 모의 데이터베이스 구현
    • 상태 변경 추적
    • 실제 서버와 유사한 동작

▪︎ 개발 환경 분리

1
2
3
4
5
6
7
// package.json
{
"scripts": {
"dev": "cross-env NEXT_PUBLIC_MODE=production next dev -H front.we-go.world --experimental-https",
"dev-mock": "cross-env NEXT_PUBLIC_MODE=mock next dev"
}
}

개발을 진행하며 모킹된 API를 적용한 환경과 실제 API를 적용한 환경을 구별하여 개발 편의성을 늘렸습니다. npm dev-mock 명령어를 통해 우리가 작성한 모킹 API를 기반으로 한 테스트를 진행하고, 이후 npm run dev 명령어를 통해 실제 API 환경에서도 작성된 코드가 잘 동작하는지를 확인할 수 있었습니다.

백엔드 개발이 완료된다 하더라도 모킹 환경은 큰 이점이 있었습니다. 에러 케이스들을 확인해야 할 때 특히 유용하게 사용되었는데, 에러 상황을 띄우기 위해 브라우저에서 다른 동작을 해주지 않고 간단한 코드 수정만으로 해당 상황을 연출할 수 있었기 때문입니다.

▫︎ 환경별 동작 방식

  1. 실제 API 환경 (npm run dev)
    • 실제 백엔드 서버로 요청
    • 프로덕션과 동일한 동작
    • 실제 데이터로 테스트
  2. 모킹 환경 (npm run dev-mock)
    • MSW가 요청 가로챔
    • 모의 응답 반환
    • 빠른 개발 반복 가능

▫︎ 환경 분리의 이점

  1. 개발 생산성 향상
    • 백엔드 의존성 제거
    • 빠른 프로토타이핑
    • 병렬 개발 가능
  2. 코드 재사용성
    • API 관련 코드의 재사용
    • 환경 전환 시 코드 수정 최소화
    • 일관된 인터페이스 유지
  3. 유지보수 용이성
    • API 스펙 변경 시 한 곳만 수정
    • 테스트 환경 구축 용이
    • 디버깅 효율성 증가

▫︎ 실제 적용 효과

  1. 개발 프로세스 개선
    • API 명세 기반 선행 개발 가능
    • 백엔드 개발 완료 전 기능 구현
    • 빠른 피드백 루프 형성
  2. 코드 품질 향상
    • 일관된 API 인터페이스 유지
    • 엣지 케이스 테스트 용이
    • 안정적인 개발 환경 제공
  3. 팀 협업 효율화
    • 명확한 API 계약 정의
    • 의사소통 비용 감소
    • 병렬 작업 가능

▪︎ 마치며

MSW를 활용한 API 모킹과 개발 환경 분리는 현대적인 웹 개발 프로세스에서 매우 효과적인 전략입니다. 특히 짧은 개발 기간 내에 프론트엔드와 백엔드가 동시에 개발을 진행해야 하는 상황에서, 이 접근 방식은 개발 생산성을 크게 향상시키고 코드 품질을 유지하는 데 도움이 되었습니다.

이러한 방식은 단순히 개발 편의성을 넘어서, 더 나은 소프트웨어 아키텍처와 개발 프로세스를 구축하는 데 기여했습니다. API 명세를 중심으로 한 개발 방식은 프론트엔드와 백엔드 간의 명확한 계약을 형성하며, 이는 장기적으로 프로젝트의 유지보수성과 확장성을 높이는 결과로 이어진다고 생각합니다.