Mocking APIs#
목 API(Mocking API)는 소프트웨어 개발 과정에서 실제 API를 대체하여 테스트, 개발, 디버깅을 용이하게 하는 가상의 API이다.
목 API(Mocking API)는 현대 소프트웨어 개발에서 필수적인 도구로, 개발 속도를 높이고 의존성을 줄이며 다양한 시나리오를 테스트할 수 있게 해준다. 실제 API와의 일관성을 유지하고, 다양한 상황을 시뮬레이션하며, 체계적인 전환 전략을 갖추면 목 API(Mocking API)는 개발 프로세스의 효율성을 크게 향상시킬 수 있다.
목 API(Mocking API)를 효과적으로 활용하면 프론트엔드와 백엔드 개발을 병렬화하고, 품질은 높이면서도 개발 시간은 단축할 수 있다. 다만 실제 API와의 차이를 인식하고, 최종적으로는 실제 환경에서의 철저한 테스트가 필요함을 기억해야 한다.
목 API(Mocking API)의 기본 개념#
목 API(Mocking API)는 실제 API의 동작을 시뮬레이션하는 가상의 인터페이스이다.
실제 서버나 데이터베이스에 연결하지 않고도 API 응답을 모방할 수 있어, 개발자가 의존성 없이 코드를 테스트하고 개발할 수 있게 해준다.
목킹(Mocking)이란 단어는 ‘모방하다’라는 의미로, 프로그래밍에서는 실제 객체나 서비스의 행동을 흉내 내는 것을 의미한다. API 목킹은 이러한 모방 기술을 API에 적용한 것이다.
목 API(Mocking API)를 사용하는 이유#
- 의존성 제거: 외부 서비스나 API에 의존하지 않고 독립적으로 개발할 수 있다.
- 개발 속도 향상: 백엔드 API가 완성되기 전에도 프론트엔드 개발을 진행할 수 있다.
- 테스트 안정성: 실제 API는 네트워크 지연, 서버 다운타임 등의 변수가 있지만, 목 API는 일관된 응답을 제공한다.
- 다양한 시나리오 테스트: 오류 상황, 지연 시간, 다양한 응답 형태 등을 쉽게 시뮬레이션할 수 있다.
- 비용 절감: API 호출에 요금이 부과되는 경우, 목 API를 사용하면 개발 과정에서 비용을 절약할 수 있다.
- 오프라인 개발: 인터넷 연결이 없는 환경에서도 개발을 계속할 수 있다.
목 API의 종류#
로컬 목 서버#
자체 개발 환경에서 실행되는 서버로, 실제 API의 엔드포인트와 응답을 모방한다.
주로 Express(Node.js), Flask(Python) 등을 사용하여 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // Express를 사용한 간단한 목 서버 예제
const express = require('express');
const app = express();
// 사용자 목록을 반환하는 목 API 엔드포인트
app.get('/api/users', (req, res) => {
// 실제 데이터베이스 대신 하드코딩된 응답 제공
const mockUsers = [
{ id: 1, name: '김철수', email: 'kim@example.com' },
{ id: 2, name: '이영희', email: 'lee@example.com' }
];
res.json(mockUsers);
});
app.listen(3000, () => {
console.log('Mock API server is running on port 3000');
});
|
서비스형 목 API (Mock as a Service)#
클라우드 기반의 목 API 서비스로, 웹 인터페이스를 통해 목 API를 생성하고 관리할 수 있다.
대표적인 서비스로는 Mockoon, Postman, MockAPI 등이 있다.
라이브러리 기반 목킹#
테스트 코드 내에서 HTTP 요청을 가로채고 목 응답을 반환하는 라이브러리를 사용한다.
Jest, Nock, MSW(Mock Service Worker) 등이 대표적이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // Jest와 axios-mock-adapter를 사용한 API 목킹 예제
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
// axios 인스턴스에 대한 목 어댑터 생성
const mock = new MockAdapter(axios);
// GET 요청에 대한 목 응답 설정
mock.onGet('/api/users').reply(200, [
{ id: 1, name: '김철수', email: 'kim@example.com' },
{ id: 2, name: '이영희', email: 'lee@example.com' }
]);
// 이제 axios.get('/api/users')는 위의 목 데이터를 반환합니다
|
브라우저 서비스 워커 기반 목킹#
브라우저의 서비스 워커를 사용하여 네트워크 요청을 가로채고 목 응답을 제공한다.
MSW(Mock Service Worker)가 대표적이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // MSW를 사용한 브라우저 목킹 예제
import { setupWorker, rest } from 'msw';
// 목 핸들러 정의
const handlers = [
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json([
{ id: 1, name: '김철수', email: 'kim@example.com' },
{ id: 2, name: '이영희', email: 'lee@example.com' }
])
);
})
];
// 브라우저에서 서비스 워커 설정
const worker = setupWorker(...handlers);
worker.start();
|
효과적인 목 API 구현 방법#
실제 API와 일치하는 구조 유지#
목 API는 실제 API와 동일한 엔드포인트, 요청/응답 구조, 데이터 형식을 가져야 한다.
이는 나중에 실제 API로 전환할 때 코드 변경을 최소화한다.
다양한 시나리오 지원#
성공 케이스뿐만 아니라 오류 상황, 지연 응답, 부분 데이터 등 다양한 시나리오를 시뮬레이션할 수 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 다양한 상황을 시뮬레이션하는 목 API 예제
app.get('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
// 지연 시뮬레이션
setTimeout(() => {
// 특정 ID에 대한 오류 시뮬레이션
if (userId === 999) {
return res.status(500).json({ error: '서버 오류' });
}
// 존재하지 않는 사용자 시뮬레이션
if (userId > 10) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
}
// 정상적인 응답
return res.json({ id: userId, name: `사용자${userId}`, email: `user${userId}@example.com` });
}, userId === 5 ? 2000 : 100); // ID가 5인 경우 지연 시간 증가
});
|
상태 관리 기능 구현#
필요한 경우, 목 API에서도 데이터 생성, 수정, 삭제 등의 상태 변경을 유지할 수 있어야 한다.
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
| // 상태를 유지하는 목 API 예제
const express = require('express');
const app = express();
app.use(express.json());
// 메모리 내 사용자 데이터 저장소
let users = [
{ id: 1, name: '김철수', email: 'kim@example.com' },
{ id: 2, name: '이영희', email: 'lee@example.com' }
];
// 사용자 목록 조회
app.get('/api/users', (req, res) => {
res.json(users);
});
// 사용자 추가
app.post('/api/users', (req, res) => {
const newUser = {
id: users.length + 1,
...req.body
};
users.push(newUser);
res.status(201).json(newUser);
});
// 사용자 삭제
app.delete('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const userIndex = users.findIndex(user => user.id === userId);
if (userIndex === -1) {
return res.status(404).json({ error: '사용자를 찾을 수 없습니다' });
}
users = users.filter(user => user.id !== userId);
res.status(204).end();
});
|
문서화와 공유#
팀 내에서 목 API의 사용법과 기능을 명확히 문서화하고 공유하여, 모든 팀원이 일관된 개발 환경을 갖도록 한다.
주요 목 API 도구와 라이브러리#
독립형 도구
- Mockoon: 데스크톱 애플리케이션으로, GUI를 통해 목 API를 쉽게 생성하고 관리할 수 있다.
- Postman: API 테스트 도구로, 목 서버 기능도 제공한다.
- JSON Server: JSON 파일을 기반으로 전체 가짜 REST API를 생성한다.
- WireMock: Java 기반의 강력한 목 서버로, 다양한 응답 패턴을 지원한다.
프로그래밍 언어별 라이브러리
- JavaScript/Node.js
- Nock: HTTP 서버 목킹 라이브러리
- MSW (Mock Service Worker): 서비스 워커를 통한 네트워크 요청 가로채기
- Mirage JS: 클라이언트 사이드 API 목킹 라이브러리
- Python
- responses: requests 라이브러리를 위한 목킹 도구
- HTTPretty: HTTP 요청 목킹 라이브러리
- Java
- MockMvc: Spring 애플리케이션에서 컨트롤러 테스트를 위한 도구
- Mockito: Java 목킹 프레임워크
실제 API로의 전환 전략#
목 API에서 실제 API로 원활하게 전환하려면 다음과 같은 전략이 필요하다:
1. 환경별 설정#
개발, 테스트, 프로덕션 환경에 따라 다른 API 엔드포인트를 사용하도록 설정한다.
1
2
3
4
| // 환경에 따른 API 엔드포인트 설정 예제
const API_BASE_URL = process.env.NODE_ENV === 'development'
? 'http://localhost:3000/api' // 개발 환경: 목 API
: 'https://api.example.com/api'; // 프로덕션 환경: 실제 API
|
점진적 전환#
전체 API를 한 번에 전환하는 대신, 엔드포인트별로 점진적으로 실제 API로 전환한다.
기능 플래그 사용#
코드 내에서 기능 플래그를 사용하여 목 API와 실제 API 사이를 쉽게 전환할 수 있게 한다.
1
2
3
4
5
6
7
8
9
10
| // 기능 플래그를 사용한 API 전환 예제
const useMockApi = config.features.useMockApi;
async function fetchUsers() {
if (useMockApi) {
return mockUserService.getUsers();
} else {
return realApiService.getUsers();
}
}
|
목 API의 한계와 주의사항#
실제 환경과의 차이
목 API는 실제 API의 모든 측면(성능, 오류 패턴, 시간 지연 등)을 완벽히 재현할 수 없다.
유지보수 부담
실제 API가 변경될 때마다 목 API도 업데이트해야 하므로 유지보수 부담이 있다.
통합 문제
목 API에서는 잘 작동하던 코드가 실제 API와 통합할 때 문제가 발생할 수 있다.
과도한 의존성
목 API에 너무 의존하면 실제 통합 테스트가 부족해질 수 있다.
목 API를 활용한 개발 워크플로우#
이상적인 개발 워크플로우는 다음과 같다:
- API 설계: 백엔드와 프론트엔드 팀이 함께 API 스펙을 정의한다.
- 목 API 구현: API 스펙을 기반으로 목 API를 구현한다.
- 병렬 개발: 프론트엔드 팀은 목 API를 사용해 개발하고, 백엔드 팀은 실제 API를 구현한다.
- 통합 테스트: 실제 API가 준비되면 점진적으로 전환하고 통합 테스트를 수행한다.
- 배포: 모든 통합 테스트가 통과하면 전체 시스템을 배포한다.
OpenAPI 스펙과 목 API#
OpenAPI(Swagger) 스펙은 목 API 생성을 크게 개선할 수 있다:
- 스펙 기반 자동 생성: OpenAPI 스펙을 기반으로 목 서버를 자동 생성할 수 있다.
- 일관성 보장: API 문서와 목 서버가 항상 일치하게 된다.
- 도구 지원: Swagger UI, Prism 등의 도구를 통해 쉽게 목 서버를 생성할 수 있다.
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
| # OpenAPI 스펙 예제
openapi: 3.0.0
info:
title: 사용자 API
version: 1.0.0
paths:
/users:
get:
summary: 사용자 목록 조회
responses:
'200':
description: 성공
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
|
용어 정리#
참고 및 출처#