이전에 포스팅한 내용보다 훨씬 상세할 것이다.
전체 개발 프로세스 중 요구 사항 분석 및 기획, 백엔드 개발, 프론트엔드 개발에 이르는 각각의 개발 과정은 크게 겹치지 않고 진행되는 것이 이상적이다. 각 개발 과정에서 도출되는 요구 사항이나 사용 가능한 interface에 변경이 있으면 각 단계별 의존성에 따라 다시 작업해야 하는 경우가 발생하기 때문.
하지만 현실적으로, 프론트엔드와 백엔드를 병행하여 개발하게 되는 경우가 많다.
이때 프론트엔드에서 백엔드의 API를 활용해야하는 것 처럼, 백엔드에 종속적인 부분이 있다면 해당 부분이 완성되기 전까지는 프론트엔드에서 개발을 할 수가 없고, 진행 이후에 개발이 가능하다. 심지어 추가적인 수정 사항이 발생할 경우 이런 비효율적인 과정을 반복해야한다.
예상한 기간보다 API 개발에 시간이 더 필요해진 경우, 프론트엔드는 개발 진행을 못하는 상황이 생겨난다.
대기 시간을 줄이고, 백엔드 개발과 최대한 병행해 개발을 진행할 수 있는 방법은 Mocking을 활용하는 방법이다.
MSW(Mock Service Worker)는 API Mocking 라이브러리로, 서버향의 네트워크 요청을 가로채서 모의 응답(Mocked response)을 보내주는 역할을 한다.
따라서 Mock Service Worker 라이브러리를 통하면 Mock 서버를 구축하지 않아도 API를 네트워크 수준에서 모킹할 수 있다!
이런 역할을 할 수 있는 이유는 Service Worker를 통해 HTTP 요청을 가로채기 때문이다.
Service Worker는 웹 애플리케이션의 메인 스레드와 분리된 별도의 백그라운드 스레드에서 실행시킬 수 있는 기술 중 하나이다. 브라우저와 웹 애플리케이션 사이의 중간 계층 역할을 한다.
Service worker 덕분에 애플리케이션의 UI Block 없이 연산을 처리할 수 있다.
UI Block
백엔드 개발자가 API를 제공하면, 프론트엔드 개발자는 별다른 작업 없이 MSW를 스위치 오프만 하면 Production으로 배포할 수 있는 형태의 개발 과정을 통해 개발을 진행할 수 있다!
추가 장점
특정 API 응답을 기준으로 에러가 발생해 디버깅이 필요한 상황이라면, 기존 서비스 로직을 전혀 건드리지 않고 오로지 MSW에서 Mocking을 만들어 내서 쉽게 디버깅할 수 있다.
기획자 등의 다른 누군가에게 각 화면을 공유하고 피드백을 받아야 하는 상황이 발생했을 때에도, 추가적으로 MSW에서 해당 상황을 만들어낼 수 있도록 작업을 해 둔다면 별다른 서비스 로직의 수정 없이도 MSW를 통해 제공이 가능하다.
프로젝트는 Create-React-App
을 기반으로 구성했고 한 번도 써본 적 없기에 설치 했다.
MSW 공식 홈페이지의 Get started를 보며 차근차근 따라갔다!
npm install msw --save-dev
# or
yarn add msw --dev
src/mocks
파일을 생성한다handlers.js
파일도 생성한다.나는 REST API를 선택했다. GraphQL API도 가능하다.
handlers.js
파일에 rest
를 가져온다.
REST API를 선택했기 때문에 MSW 모듈의 rest
객체를 사용한다.
// src/mocks/handlers.js
import { rest } from 'msw'
가짜 API를 구현하려면 요청이 들어왔을 때 임의의 응답을 해주는 핸들러 코드를 작성해야한다.
// src/mocks/handlers.js
import { rest } from 'msw'
export const handlers = [
rest.post('/login', (req, res, ctx) => {
// Persist user's authentication in the session
sessionStorage.setItem('is-authenticated', 'true')
return res(
// Respond with a 200 status code
ctx.status(200),
)
}),
rest.get('/user', (req, res, ctx) => {
// Check if the user is authenticated in this session
const isAuthenticated = sessionStorage.getItem('is-authenticated')
if (!isAuthenticated) {
// If not authenticated, respond with a 403 error
return res(
ctx.status(403),
ctx.json({
errorMessage: 'Not authorized',
}),
)
}
// If authenticated, return a mocked user details
return res(
ctx.status(200),
ctx.json({
username: 'admin',
}),
)
}),
]
여기서 public은 react app 설치시 자동으로 세팅되는 root에 존재하는 폴더임
MSW 모듈에서 제공하는 setupWorker()
함수를 이용해서 browser.js
에 서비스 워커를 생성한다. 위에 작성한 Request handler 코드를 불러와서 그대로 setupWorker()
함수의 인자로 넘겨주면 된다.
서비스 워커를 구동하는 코드를 애플리케이션의 진입 시점(entrypoint)에 삽입을 해보겠다.
예를 들어, Create React App으로 만든 애플리케이션의 경우, src/index.js
파일을 다음과 같이 수정해주면 된다.
상단 import 영역은 MSW 공식 홈페이지와 조금 다른데 그 이유는 react app이 업데이트 되어서 업데이트 된 경로대로 변경하여 적용해주었다. 안그러면 콘솔에 오류가 뜬다.
*일반적으로 개발 환경에서 가짜 API를 사용하므로 환경 변수를 체크하여 선택적으로 서비스 워커가 구동되도록 지정해준다.
(추후에 공부할 환경 변수에서 다시 복습 예정..)
애플리케이션을 구동 후에 (npm start
) 브라우저에서 열고 콘솔을 확인하면 MSW 모킹이 활성화되었다는 메시지가 출력된다.
fetch()
함수를 통해 GET /user
를 요청해보겠다.
인증되지 않은 요청의 경우로 세팅해놓은 403 status가 잘 뜨는 것을 확인할 수 있다.
열어보면,
body
에 설정된 {errorMessage: 'Not authorized'}
도 확인된다.
// src/mocks/handlers.js
import { rest } from 'msw';
const users = [
{
balance: '$3,946.45',
picture: 'http://placehold.it/32x32',
age: 23,
name: 'Bird Ramsey',
gender: 'male',
company: 'NIMON',
email: 'birdramsey@nimon.com',
},
{
balance: '$2,499.49',
picture: 'http://placehold.it/32x32',
age: 31,
name: 'Lillian Burgess',
gender: 'female',
company: 'LUXURIA',
email: 'lillianburgess@luxuria.com',
},
{
balance: '$2,820.18',
picture: 'http://placehold.it/32x32',
age: 34,
name: 'Kristie Cole',
gender: 'female',
company: 'QUADEEBO',
email: 'kristiecole@quadeebo.com',
},
{
balance: '$3,277.32',
picture: 'http://placehold.it/32x32',
age: 30,
name: 'Leonor Cross',
gender: 'female',
company: 'GRONK',
email: 'leonorcross@gronk.com',
},
{
balance: '$1,972.47',
picture: 'http://placehold.it/32x32',
age: 28,
name: 'Marsh Mccall',
gender: 'male',
company: 'ULTRIMAX',
email: 'marshmccall@ultrimax.com',
},
];
export const handlers = [
// 사용자 목록
rest.get('/users', (req, res, ctx) => {
return res(ctx.status(200), ctx.json(users));
}),
// 사용자 추가
rest.post('/users', (req, res, ctx) => {
users.push(req.body);
return res(ctx.status(201));
}),
];
구글링으로 임의의 사용자 데이터가 있는 users.json
파일을 구했다.
데이터가 잘 불러와지는 것을 확인할 수 있다.
API 요청을 잘하기 위해,, 얼른 스킬업이 필요할 듯하다.
그래도 잘 작동하게끔 구축하다니 뿌듯.