1. 오늘의 과업 리스트 📋

2. MSW 이해
1) 왜 모킹이 필요한가?
개발자는 다양한 역할의 팀원들과 협업하게 된다. 제한된 일정 때문에 백엔드 개발과 프론트엔드 개발을 동시에 진행하는 경우가 생긴다. 프론트엔드에서는 백엔드가 제공하는 API를 호출해야 하는데, 서버가 없거나 아직 API가 완성되지 않은 경우, 혹은 가상의 API를 호출해 데이터 입출력을 테스트하고 싶을 때 모킹을 할 수 있다. 때로는 하드코딩을 통해 직접 가상의 데이터로 화면을 구성하지만 서버를 거쳐서 내려오는 데이터와 같은 형식이라고 보장하기 어렵다. 이런 불편함을 해소하기 위해 나온 것이 모킹이라는 개념이다.
2) 모킹이란?
모의 데이터를 뜻하는 Mock을 만들어 활용하는 것이다. 애플리케이션의 개발과 테스트를 위해 실제 객체를 모방하거나 흉내 낸 가짜 객체를 생성할 때 사용한다.
- 네트워크의 요청을 가로채 서버로 보내지 않고 자체적으로 설정한 목(Mock) 데이터를 돌려줌
- 클라이언트는 서버에서 받은 데이터와 똑같은 데이터를 모킹을 통해 받을 수 있음
- 실제 API와 동일한 기능을 하는 모킹 API를 통해 에러가 발생하는 것을 확인할 수 있음
3) MSW 모킹 서비스
Mock Service Worker 프레임워크 : 자바스크립트를 위한 API 모킹 서비스
- 환경, 프레임워크, 도구에 구애받지 않음
- 모킹을 위한 코드를 별도의 소스로 관리하기 때문에 웹 애플리케이션 코드 안에 모킹 데이터를 위한 코드가 추가 되지 않음
➡️모킹 API 사용 후 실제로 개발된 API로 대체할 때 별다른 추가 작업이 필요 없음
- 스토리북이나 E2E 테스트, 통합 테스트에 걸쳐 동일한 목업 API를 사용할 수 있기 때문에 한 번 작성해놓으면 재사용하기 좋음
* E2E: End to End 테스트로 애플리케이션의 흐름을 처음부터 끝까지 테스트 하는 것을 의미
4) MSW 네트워크 흐름


5) MSW 시작하기
프로젝트의 터미널에서 다음 명령어 실행
npm install msw
- npm을 활용할 수 없는 환경이라면 CDN을 통한 사용방법도 제공


- 브라우저에 개발자 도구로 확인 시 실제 API를 호출한 것과 똑같은 방식으로 작동
- Status Code에 "From ServiceWorker"로 표기해서 MSW를 통한 모킹이라는 것을 알려줌
📍 서비스워커, 핸들러 생성
- 서비스워커 생성 명령어
npx msw init public/ --save
- 핸들러에서 가상으로 내려줄 목 데이터를 생성, API주소 설정
- 가상의 테스트용 API 서버 기능을 통해 테스트 도구를 사용할 수 있는 환경 제공
📍리스폰스 패칭(Response patching)
실제 API 데이터와 설정한 목데이터를 조합해서 응답 받을 수 있음
💯 MSW 퀴즈


📑 오답 검토

서비스워커와 핸들러의 의미를 헷갈린 것 같다.
3. Recoil을 활용한 상태 관리 및 API Mocking 실습
먼저 실습을 위해 새 리액트 앱을 만들었다.


1) Recoil과 MSW 라이브러리 설치

- Recoil 설치
Recoil
A state management library for React.
recoiljs.org
터미널에 다음 명령어를 입력한다.
npm install recoil

- MSW 설치
Mock Service Worker
API mocking library for browser and Node.js
mswjs.io
터미널에 다음 명령어를 입력한다.
npm install msw@latest --save-dev

2) Recoil을 이용해 간단한 문자열 카운터 앱 만들기
https://recoiljs.org/ko/docs/introduction/getting-started/
Recoil 시작하기 | Recoil
React 애플리케이션 생성하기
recoiljs.org
📍RecoilRoot
recoil 상태를 사용하는 컴포넌트는 부모 트리 어딘가에 나타나는 RecoilRoot 가 필요하다. 'index.js'파일을 다음과 같이 변경한다.

📍Atom
Atom은 상태(state)의 일부를 나타낸다. Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다.
➡️ atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트가 재 렌더링 된다.

📍CharacterCounter 컴포넌트
[components] 폴더를 만들고 CharacterCounter.js 파일을 생성한다. 다음은 CharacterCounter 컴포넌트 코드의 일부이다.

📍Selector
CharacterCounter 컴포넌트에서 selector를 만든다. Selector는 파생된 상태(derived state)의 일부를 나타낸다. 파생된 상태는 상태의 변화다. 파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물로 생각할 수 있다.

📍App.js
위에서 만든 문자열 카운터 컴포넌트를 랜더링한다.

💻결과 확인

트러블 슈팅💥
1) npm start 명령어 실행 실패

node_modules 확인, react-scripts 설치 오류, npm 캐시 문제의 경우를 확인해보았으나 모두 문제가 없었다.
🔎문제 원인
원인은 내가 만든 테스트 앱의 폴더 구조였다. C드라이브에서 새 폴더를 만들고 그 폴더에서 리액트 앱을 생성했다. 문제는 내가 폴더와 리액트 앱의 이름을 동일하게 생성하여 헷갈렸던 것이다.
➡️ C:\testapp 안에 또 다른 testapp 폴더가 있고, 실제 React 앱은 그 하위 폴더(C:\testapp\testapp) 안에 있다!
✅ 해결 방법
명령어의 올바른 실행 경로는 [C:\testapp\testapp] 이므로 다음 명령어로 폴더를 이동하여 npm start를 실행한다.
cd testapp
2) 리액트 앱이 빈 화면을 렌더링
다음 몇가지 사항을 확인했다.
1️⃣ 루트 컴포넌트에 아무것도 렌더링 되지 않은 경우
✔ App.js에서 CharacterCounter 컴포넌트를 제대로 리턴하고 있고, index.js에서 App 을 제대로 렌더링하고 있으며 RecoilRoot로 감싸고 있는 것을 확인했다.
2️⃣ CharacterCounter 컴포넌트 내부 오류
✔ console.log()로 테스트 메시지가 올바르게 출력됨을 확인했다.
3️⃣ atom.js의 경로 오류
✔ atom.js의 위치가 src/recoil/atom.js인 것을 확인했다.
🔎문제 원인
콘솔 에러를 제대로 확인해보니 Invalid hook call 에러를 발견했다. 흔히 Hook이 잘못된 위치에서 호출되었을 때 발생한다. 이 에러 메시지에 따르면, 주로 다음과 같은 이유로 발생할 수 있다.
1️⃣ React와 React DOM 버전 불일치
2️⃣ Hooks 규칙 위반
React와 React DOM의 버전을 확인하고, 동일한 버전이 설치되어 있는지 확인한다.
나는 해결 과정에서 다음 명령어를 통해 React와 Recoil을 최신 버전으로 업데이트했다.
npm install react@latest react-dom@latest recoil@latest
그런데도 해결이 되지 않아 에러 메시지를 분석해보니 다음과 같은 내용이었다.

npm ls react 명령어로 React가 중복 설치된 경우를 확인한다.

✅ 해결 방법

React를 공식적으로 지원하는 18.x 버전으로 다운그레이드하고, Recoil을 최신 버전으로 업데이트한다.
npm install react@18 react-dom@18
npm install recoil@latest
3) MSW를 이용해서 login, user API mocking을 설정 후 테스트


📍핸들러 설정

📍서비스워커 설정

서비스 워커를 등록하고 활성화하는 worker.start()를 호출하여 워커를 시작해야 한다 . 또한, 프로덕션 트래픽에 영향을 주지 않도록 개발 환경에서만 API 모킹을 활성화한다.

📍실습 코드 작성
handler.js에서 http.post와 http.get을 이용하여 '/login', '/user' 로 각각 login API와 user의 데이터를 mocking 해서 가져오는 코드를 작성한다. 그리고 [components] 폴더에 LoginTest.js 파일을 생성하고 로그인 페이지에 대한 테스트 코드를 작성한다.
import { http, HttpResponse } from 'msw'
export const handlers = [
http.post('/login', async ({request})=>{
const {username, password} = await request.json();
if (username==='test' && password==='1234') {
return HttpResponse.json({message: '로그인 성공', token:'fake-jwt-token'});
}
return HttpResponse.json({message:'로그인 실패'},{status:401});
}),
http.get('/user', ({request})=>{
const authHeader = request.headers.get('Authorization');
if (authHeader === 'Bearer fake-jwt-token') {
return HttpResponse.json({
username: 'test',
id: 1,
password: 1234
});
}
return HttpResponse.json({message: 'Unauthorized'}, {status:401})
})
]
💻결과 확인




4) 슈킹 프로젝트에 실습

1️⃣ Recoil 상태 정의 (Atom & Selector 만들기)
(Recoil 설치 후, index.js에서 RecoilRoot로 앱 감싸기)
📍src/recoil/products.js

➡️ 상품 개수 표시에 Recoil 값 사용: App.js에서 상품 개수를 useRecoilValue로 가져와 표시한다.

- atom은 기본 상태(상품 목록)를 정의한다.
- selector는 파생 상태(상품 개수)를 계산한다.
📍ProductList.js
현재는 상품 목록 데이터가 임시로 하드코딩 된 상태이다.
import { useEffect, useState } from "react";
import { useRecoilState } from "recoil";
import { productListState } from "../recoil/products";
import Product_card from "./Product_card";
import { Col, Row } from "react-bootstrap";
const ProductList = ({ setCartCount }) => {
const [products, setProducts] = useRecoilState(productListState);
useEffect(()=>{
const tempProducts = [
{ id: 1, name: 'nike', price: 15900 },
{ id: 2, name: 'adidas', price: 14900 },
{ id: 3, name: 'newbalance', price: 11900 }
];
setProducts(tempProducts);
}, [setProducts]);
return (
<Row>
{products.map((product)=>(
<Col xs={6} md={6} lg={4} key={product.id}>
<Product_card product={product} setCartCount={setCartCount} />
</Col>
))}
</Row>
)
}
export default ProductList;
npm start로 프로젝트를 실행해보면, 내가 임시로 등록한 상품 목록이 화면에 나타나는 것을 볼 수 있다.

2️⃣ msw 설정 – 상품 API 핸들러 정의
(현재 mockServiceWorker.js 인식 오류로 진행 보류)
✒️컨텐츠 피드백 및 진행 소감
지난 시간에 배운 Recoil을 이용한 상태관리 방법을 공식 페이지의 실습과 슈킹 프로젝트에 적용해보면서 테스트 할 수 있었다. 다른 프로젝트에서도 적용시켜보면서 더 공부해야겠다. msw는 슈킹 앱에 적용하는데 성공하지는 못했지만 login과 user 정보를 불러오는 과정으로라도 실습해보았으니 다행이다. 배운 내용을 직접 구현해보는 것이 생각보다 너무 어렵고 힘들었다. 계속 연습하고 기록하는 과정을 통해 새로운 기능들을 열심히 익혀야겠다.
https://www.interninmeta.or.kr/
인턴IN메타
인턴IN메타(인턴인메타)는 인턴체험서비스를 제공하는 메타버스 직업체험관입니다.
www.interninmeta.or.kr

'인턴IN메타 - 응용SW개발' 카테고리의 다른 글
| [Week6 - Day26] 대인관계능력 / 회의 - 회의 준비와 회의록 작성 (2) | 2025.05.13 |
|---|---|
| [Week5 - Day25] 시간 관리, 비즈니스 매너, 주간 회고 (3) | 2025.05.10 |
| [Week5 - Day23] RESTful API와 HTTP 개념, Recoil 상태 관리 (1) | 2025.05.08 |
| [Week5 - Day22] 결제 모듈에 대한 고객사 대상 리뷰 및 프로젝트 회고, 장바구니 페이지 요구사항 분석 (0) | 2025.05.08 |
| [Week5 - Day21] GitHub Actions을 활용한 CI/CD 구축, 결제 모듈 문서화 (0) | 2025.05.07 |