๐ŸŒฑ <groot/>
์ง„์งœ API๋ฅผ ๊ทธ๋งŒ ๋•Œ๋ฆฌ์ž โ€” MSW๋กœ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ฐ€๋กœ์ฑ„ ๊ฐœ๋ฐœํ•˜๊ธฐ | Groot's Tech Blog
๐ŸŒWeb

์ง„์งœ API๋ฅผ ๊ทธ๋งŒ ๋•Œ๋ฆฌ์ž โ€” MSW๋กœ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ฐ€๋กœ์ฑ„ ๊ฐœ๋ฐœํ•˜๊ธฐ

2026.07.02ยท3๋ถ„ ์ฝ๊ธฐ

YouTube Playlist Generator๋ฅผ ๋งŒ๋“ค๋ฉฐ ์ œ์ผ ํฌ๊ฒŒ ๋ฐ์ธ ๊ฑด API ํ• ๋‹น๋Ÿ‰(quota) ์ด์—ˆ๋‹ค. YouTube Data API๋Š” ๊ฒ€์ƒ‰ ํ•œ ๋ฒˆ์ด 100 units์ด๊ณ  ํ•˜๋ฃจ ํ•œ๋„๊ฐ€ 10,000์ด๋ผ, ๊ฐœ๋ฐœํ•˜๋ฉฐ ์ƒˆ๋กœ๊ณ ์นจ ๋ช‡ ๋ฒˆ ํ•˜๋ฉด ํ•˜๋ฃจ์น˜ ํ• ๋‹น๋Ÿ‰์ด ์ˆœ์‹๊ฐ„์— ์ฆ๋ฐœํ–ˆ๋‹ค. "์ฝ”๋“œ ํ•œ ์ค„ ๊ณ ์น˜๊ณ  ํ™•์ธ"์„ ๋ฐ˜๋ณตํ•ด์•ผ ํ•˜๋Š” ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ์ด๊ฑด ์น˜๋ช…์ ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ๋„์ž…ํ–ˆ๋˜ ๊ฒŒ MSW(Mock Service Worker) ๋‹ค. ๊ธฐ๋Šฅ์ด ์•ˆ์ •ํ™”๋œ ๋’ค์—๋Š” ๋น„ํ™œ์„ฑํ™”ํ•ด์„œ ์ง€๊ธˆ ์ฝ”๋“œ์—๋Š” ์ฃผ์„์œผ๋กœ๋งŒ ํ”์ ์ด ๋‚จ์•„ ์žˆ์ง€๋งŒ, ์ด ๊ธ€์€ "์™œ MSW์˜€๊ณ , ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”๊ฐ€", ๊ทธ๋ฆฌ๊ณ  ๋‹น์‹œ ๋‚ด ์„ค์ •์ด ์–ด๋””๊นŒ์ง€ ๋๊ณ  ์–ด๋””์„œ ์–ด์„คํސ๋Š”์ง€๋ฅผ ์ •๋ฆฌํ•œ๋‹ค.

ํ”ํ•œ ๋ชฉํ‚น์˜ ํ•œ๊ณ„

๊ฐ€์งœ ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์€ ์›๋ž˜๋„ ์žˆ์—ˆ๋‹ค.

  • axios๋ฅผ ๊ฐ์‹ผ ํ•จ์ˆ˜๋ฅผ ํ†ต์งธ๋กœ ๊ฐˆ์•„๋ผ์šฐ๊ธฐ
  • ์ปดํฌ๋„ŒํŠธ์— if (mock) return dummy ๊ฐ™์€ ๋ถ„๊ธฐ ์‹ฌ๊ธฐ

๋ฌธ์ œ๋Š” ์•ฑ ์ฝ”๋“œ๋ฅผ ๊ฑด๋“œ๋ ค์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ. ๋ชฉํ‚น์šฉ ์ฝ”๋“œ๊ฐ€ ์‹ค์ œ ๋กœ์ง์— ์„ž์ด๊ณ , ๋ชฉ์„ ๋„๊ณ  ์ผค ๋•Œ๋งˆ๋‹ค ์ฝ”๋“œ๋ฅผ ๊ณ ์ณ์•ผ ํ•œ๋‹ค. ์‹ค์ˆ˜๋กœ ๋ชฉํ‚น ์ฝ”๋“œ๋ฅผ ๋ฐฐํฌํ•  ์œ„ํ—˜๋„ ์žˆ๋‹ค.

MSW์˜ ์•„์ด๋””์–ด: ๋„คํŠธ์›Œํฌ ๊ณ„์ธต์—์„œ ๊ฐ€๋กœ์ฑ„๊ธฐ

MSW๋Š” ์ ‘๊ทผ์ด ๋‹ค๋ฅด๋‹ค. Service Worker๋กœ ๋ธŒ๋ผ์šฐ์ €์˜ ๋„คํŠธ์›Œํฌ ์š”์ฒญ ์ž์ฒด๋ฅผ ๊ฐ€๋กœ์ฑˆ๋‹ค. ์•ฑ์€ ํ‰์†Œ์ฒ˜๋Ÿผ fetch/axios๋กœ ์ง„์งœ URL์— ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ , ๊ทธ ์š”์ฒญ์ด ๋„คํŠธ์›Œํฌ๋กœ ๋‚˜๊ฐ€๊ธฐ ์ง์ „์— MSW๊ฐ€ ๋‚š์•„์ฑ„ ๊ฐ€์งœ ์‘๋‹ต์„ ๋Œ๋ ค์ค€๋‹ค.

์•ฑ ์ฝ”๋“œ          MSW(Service Worker)         ์ง„์งœ ์„œ๋ฒ„
  โ”‚  axios.get('/search')  โ”‚                     โ”‚
  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถโ”‚  (ํ•ธ๋“ค๋Ÿฌ์— ๋งค์นญ๋จ)   โ”‚
  โ”‚โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  ๊ฐ€์งœ JSON ์‘๋‹ต      โ”‚
  โ”‚                        โ”‚  (์—ฌ๊ธฐ์„œ ๋, ์„œ๋ฒ„์—” ์•ˆ ๊ฐ)

ํ•ต์‹ฌ์€ ์•ฑ ์ฝ”๋“œ๊ฐ€ ๋ชฉํ‚น์˜ ์กด์žฌ๋ฅผ ์ „ํ˜€ ๋ชจ๋ฅธ๋‹ค๋Š” ์ ์ด๋‹ค. axios.get('/search') ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ๋œ๋‹ค. ๋ชฉ์„ ๋„๋ฉด ๊ทธ ์š”์ฒญ์ด ์ง„์งœ ์„œ๋ฒ„๋กœ ๋‚˜๊ฐ„๋‹ค. ์•ฑ ๋กœ์ง๊ณผ ๋ชฉํ‚น์ด ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋œ๋‹ค.

๋‹น์‹œ์˜ ์‹ค์ œ ์„ค์ •

git ๊ธฐ๋ก(์ปค๋ฐ‹ 90a3079 "msw ์„ค์น˜", 0d31d81 "mock ๋ฐ์ดํ„ฐ ์—ฐ๊ฒฐ")์„ ๋ณด๋ฉด, ์š”์ฒญ๋ณ„๋กœ ๊ฐ€์งœ ์‘๋‹ต์„ ์ •์˜ํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

// mocks/handlers.js
import { rest } from "msw";

const videoId = ["1EgiO-RyMGQ", "grG41kS4MUA", "HmKgFJGn46Q"];

export const handlers = [
  // ๊ฒ€์ƒ‰ ์š”์ฒญ โ†’ ๊ฐ€์งœ videoId ๋ชฉ๋ก
  rest.get("/search", (_, res, ctx) => {
    return res(ctx.status(200), ctx.json(videoId));
  }),
  // ์žฌ์ƒ๋ชฉ๋ก ์ƒ์„ฑ ์š”์ฒญ โ†’ ๊ฐ€์งœ playlist ๊ฐ์ฒด
  rest.post("/make", (_, res, ctx) => {
    return res(ctx.status(200), ctx.json(DUMMYPLAYLIST));
  }),
];

๊ทธ๋ฆฌ๊ณ  ์ด ํ•ธ๋“ค๋Ÿฌ๋“ค๋กœ ์›Œ์ปค๋ฅผ ๋งŒ๋“ค์–ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋„์šด๋‹ค.

// mocks/server.js
import { setupWorker } from "msw";
import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);

์•ฑ ์ง„์ž…์ ์—์„œ๋Š” ๊ฐœ๋ฐœ ๋ชจ๋“œ์ผ ๋•Œ๋งŒ worker.start()๋ฅผ ํ˜ธ์ถœํ–ˆ๋‹ค. ๋‹น์‹œ ์ปค๋ฐ‹์˜ ์ฝ”๋“œ ๊ทธ๋Œ€๋กœ๋‹ค.

// App.js (๋‹น์‹œ ์ปค๋ฐ‹ ๊ธฐ์ค€)
import { worker } from "./mocks/server";
if (process.env.NODE_ENV === "development") {
  worker.start();
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•ธ๋“ค๋Ÿฌ์— ๋งค์นญ๋˜๋Š” ์š”์ฒญ์€ ์ง„์งœ ์„œ๋ฒ„๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๊ณ  ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ›๋Š”๋‹ค. ํ• ๋‹น๋Ÿ‰์€ 1๋„ ์•ˆ ์“ด๋‹ค. ์ง€๊ธˆ ์ฝ”๋“œ์—์„œ๋Š” ์ด ๋ถ€๋ถ„์ด ํ†ต์งธ๋กœ ์ฃผ์„ ์ฒ˜๋ฆฌ๋ผ ์žˆ๋‹ค โ€” ๊ธฐ๋Šฅ์ด ์•ˆ์ •ํ™”๋œ ๋’ค ๋ชฉํ‚น์„ ๋ˆ ๊ฒƒ์ด๋‹ค.

๊ณ ๋ฐฑ: ๋‚ด ์„ค์ •์€ ์ด์ƒ๊ณผ ๊ฑฐ๋ฆฌ๊ฐ€ ์žˆ์—ˆ๋‹ค

์œ„์—์„œ "์•ฑ ์ฝ”๋“œ๊ฐ€ ๋ชฉํ‚น์˜ ์กด์žฌ๋ฅผ ์ „ํ˜€ ๋ชจ๋ฅธ๋‹ค"๊ณ  ํ–ˆ๋Š”๋ฐ, ์†”์งํžˆ ๋‹น์‹œ ๋‚ด ์„ค์ •์€ ๊ทธ ์ด์ƒ์— ๋ชป ๋ฏธ์ณค๋‹ค. ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์—ด์–ด๋ณด๋‹ˆ ์ด๋žฌ๋‹ค.

  • ์•ฑ์€ axios.defaults.baseURL์„ https://www.googleapis.com/youtube/v3๋กœ ๋‘๊ณ  ์ ˆ๋Œ€ URL๋กœ ์š”์ฒญํ•˜๋Š”๋ฐ, ํ•ธ๋“ค๋Ÿฌ๋Š” rest.get("/search") ๊ฐ™์€ ์ƒ๋Œ€๊ฒฝ๋กœ์˜€๋‹ค. ์ƒ๋Œ€๊ฒฝ๋กœ ํ•ธ๋“ค๋Ÿฌ๋Š” ์•ฑ ๋„๋ฉ”์ธ ๊ธฐ์ค€์ด๋ผ googleapis๋กœ ๋‚˜๊ฐ€๋Š” ์š”์ฒญ๊ณผ๋Š” ๋งค์นญ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๋‹น์‹œ์—” ๋ชฉํ‚น์šฉ์œผ๋กœ fetch("/search")๋ฅผ ๋”ฐ๋กœ ํ˜ธ์ถœํ•ด์„œ ๊ฐ€์งœ ์‘๋‹ต์„ ๋ฐ›์•˜๋‹ค โ€” ์ฆ‰ "์•ฑ ์ฝ”๋“œ ๋ฌด๋ณ€๊ฒฝ"์ด ์•„๋‹ˆ๋ผ, ๋ชฉ์— ๋งž์ถฐ ์•ฑ ์ฝ”๋“œ๋ฅผ ๊ณ ์นœ ์…ˆ์ด๋‹ค.
  • ๋ชฉ ๋ฐ์ดํ„ฐ๋„ videoId ๋ฌธ์ž์—ด ๋ฐฐ์—ด์ด๋ผ, ์‹ค์ œ API ์‘๋‹ต ๊ตฌ์กฐ (items[].id.videoId)์™€ ๋‹ฌ๋ž๋‹ค.
  • rest.post("/make") ํ•ธ๋“ค๋Ÿฌ๋Š” ๋งŒ๋“ค์–ด๋†“๊ณ  ์ •์ž‘ ๋Œ€์‘ํ•˜๋Š” ์š”์ฒญ์ด ์—†์—ˆ๋‹ค.

์ œ๋Œ€๋กœ ํ•˜๋ ค๋ฉด ํ•ธ๋“ค๋Ÿฌ URL์„ ์‹ค์ œ ์š”์ฒญ๊ณผ ๊ฐ™์€ ์ ˆ๋Œ€ URL๋กœ ๋งž์ถ”๊ณ , ๋ชฉ ๋ฐ์ดํ„ฐ๋„ ์‹ค์ œ ์‘๋‹ต ์Šคํ‚ค๋งˆ ๊ทธ๋Œ€๋กœ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ๋‹ค. MSW์˜ ์žฅ์ ("๊ฒฝ๊ณ„์—์„œ ๊ฐ€๋กœ์ฑ„๊ธฐ")์„ ์˜จ์ „ํžˆ ๋ˆ„๋ฆฌ๋ ค๋ฉด ์ด๊ฒŒ ์ „์ œ ์กฐ๊ฑด์ธ๋ฐ, ๋‹น์‹œ์—” ๊ฑฐ๊ธฐ๊นŒ์ง€ ๊ฐ€์ง€ ๋ชปํ–ˆ๋‹ค.

๊ทธ๋ž˜๋„ MSW๊ฐ€ ์ข‹์•˜๋˜ ์ 

  • ํ• ๋‹น๋Ÿ‰ ์ ˆ์•ฝ: ๊ฐœ๋ฐœ ์ค‘ ๋ฌดํ•œ ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ์ง„์งœ API๋ฅผ ์•ˆ ๋•Œ๋ฆฐ๋‹ค. ์ด๊ฒŒ ๋„์ž…ํ•œ ์ฒซ ๋ฒˆ์งธ ์ด์œ ์˜€๊ณ , ์–ด์„คํ”ˆ ์„ค์ •์œผ๋กœ๋„ ์ด ๋ชฉ์ ์€ ๋‹ฌ์„ฑํ–ˆ๋‹ค.
  • ์˜คํ”„๋ผ์ธ/๋…๋ฆฝ ๊ฐœ๋ฐœ: ์„œ๋ฒ„(๋˜๋Š” ์™ธ๋ถ€ API)๊ฐ€ ์ค€๋น„ ์•ˆ ๋์–ด๋„ ํ”„๋ก ํŠธ๋ฅผ ๋จผ์ € ๋งŒ๋“ ๋‹ค. ์‘๋‹ต ํ˜•ํƒœ๋งŒ ํ•ฉ์˜ํ•˜๋ฉด ๋ณ‘๋ ฌ๋กœ ์ง„ํ–‰ ๊ฐ€๋Šฅ.
  • ์—ฃ์ง€ ์ผ€์ด์Šค ์žฌํ˜„: 500 ์—๋Ÿฌ, ๋นˆ ๊ฒฐ๊ณผ, ๋А๋ฆฐ ์‘๋‹ต ๊ฐ™์€ ๊ฑธ ํ•ธ๋“ค๋Ÿฌ์—์„œ ctx.status(500), ctx.delay()๋กœ ๋งˆ์Œ๋Œ€๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ง„์งœ ์„œ๋ฒ„๋กœ๋Š” ์ผ๋ถ€๋Ÿฌ ๋งŒ๋“ค๊ธฐ ํž˜๋“  ์ƒํ™ฉ์ด๋‹ค.

๋˜๋Œ์•„๋ณด๋ฉฐ

๋‹น์‹œ ๋‚ด MSW ์„ค์ •์€ ์‚ฌ์‹ค ๋‹จ์ˆœํ–ˆ๋‹ค โ€” ํ•ธ๋“ค๋Ÿฌ ๋‘์–ด ๊ฐœ๊ฐ€ ์ „๋ถ€์˜€๊ณ , ์œ„์—์„œ ๊ณ ๋ฐฑํ–ˆ๋“ฏ "์•ฑ ์ฝ”๋“œ ๋ฌด๋ณ€๊ฒฝ"์ด๋ผ๋Š” MSW์˜ ์ง„์งœ ์žฅ์ ์€ ์‚ด๋ฆฌ์ง€ ๋ชปํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ "๋ชฉํ‚น์€ ์•ฑ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋„คํŠธ์›Œํฌ ๊ณ„์ธต์—์„œ ํ•˜๋Š” ๊ฒƒ"์ด๋ผ๋Š” ๋ฐœ์ƒ ์ž์ฒด๊ฐ€ ๊ทธ ๋’ค๋กœ ๊ฐœ๋ฐœ ๋ฐฉ์‹์„ ๋ฐ”๊ฟจ๋‹ค. ์™ธ๋ถ€ API์— ์˜์กดํ•˜๋Š” ์•ฑ์„ ๋งŒ๋“ค ๋•Œ, ์ง„์งœ API๋ฅผ ๊ณ„์† ๋•Œ๋ฆฌ๋Š” ๋Œ€์‹  ๊ฒฝ๊ณ„์—์„œ ๊ฐ€๋กœ์ฑ„๋ฉด ๊ฐœ๋ฐœ์ด ํ›จ์”ฌ ์ž์œ ๋กœ์›Œ์ง„๋‹ค.

๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ํ• ๋‹น๋Ÿ‰ ๊ฑฑ์ • ์—†์ด ๋งˆ์Œ๊ป ์ƒˆ๋กœ๊ณ ์นจํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค๋Š” ๊ฒƒ โ€” ์ง€๊ธˆ์€ ๊บผ๋‘” ๋„๊ตฌ์ง€๋งŒ, ๊ทธ๋•Œ์˜ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ํฌ๊ฒŒ ๋ฐ”๊ฟ”์คฌ๋‹ค.

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ
ยฉ 2026 ยท Groot's Tech Blog. Built with Gatsby ๐ŸŒฑ