MSW๋ฅผ ํ™œ์šฉํ•œ API ๋ชจํ‚น๊ณผ ํ…Œ์ŠคํŠธ์ฝ”๋“œ

minjookimm
  • #MSW
  • #test

์•ˆ๋…•ํ•˜์„ธ์š”. ๋…ธ๋จธ์Šค ํ”„๋ก ํŠธ์—”๋“œ ํŒ€ ๊น€๋ฏผ์ฃผ์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” MSW๋ฅผ ๋„์ž…ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๊ณต์œ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

MSW ๋„์ž… ๋ฐฐ๊ฒฝ

MSW๋ฅผ ๋„์ž…ํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ๋Š” ๋ฉˆ์ถฐ ์žˆ๋˜ ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•˜๋ฉด์„œ์˜€์Šต๋‹ˆ๋‹ค.

์ €ํฌ ํ”„๋กœ์ ํŠธ๋Š” ์‚ฌ์šฉ์ž ๊ถŒํ•œ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ UI๊ฐ€ ๋…ธ์ถœ๋˜๊ณ , ๊ถŒํ•œ๋ณ„๋กœ ๋‹ค๋ฅธ API ํ˜ธ์ถœ์ด ๋ฐœ์ƒํ•˜๋Š” ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์ปธ๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ํ•„์ˆ˜์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด์— ์ž‘์„ฑ๋œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์žˆ๊ธด ํ–ˆ์ง€๋งŒ, ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด์„œ ๋กœ์ง์ด ์ˆ˜์ •๋˜๊ฑฐ๋‚˜ API ์‘๋‹ต์ด ๋ฐ”๋€Œ๋ฉด์„œ skip ์ฒ˜๋ฆฌ๋œ ์ฝ”๋“œ๋„ ๋งŽ์•˜๊ณ  ์ƒˆ๋กœ์šด ์ผ€์ด์Šค๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด์„œ ์ˆ˜์ •๊ณผ ๋ณด์™„์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, API ํ˜ธ์ถœ์„ ๊ฐ€๋กœ์ฑ„ ์›ํ•˜๋Š” ์‘๋‹ต์„ ๋ชจํ‚นํ•  ์ˆ˜ ์žˆ๋Š” MSW๋ฅผ ๋„์ž…ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

MSW(Mock Service Worker)๋ž€?

: MSW(Mock Service Worker)๋Š”ย ์„œ๋น„์Šค ์›Œ์ปค(Service Worker) ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ์—์„œ API ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ  ๋ชจํ‚น(mocking)ํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.

ํŠนํžˆ, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ API ์š”์ฒญ์„ ์ œ์–ดํ•˜๊ฑฐ๋‚˜, ๋กœ์ปฌ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ ์ž„์˜์˜ API ์‘๋‹ต์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

MSW ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์— ์„ค์น˜ํ•˜๊ณ  ํ•„์š”ํ•œ ์„ค์ • ํŒŒ์ผ๋“ค์„ ์„ธํŒ…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ MSW๋ฅผ ์ด์šฉํ•ด ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ •์˜

  1. ์ฑ„๋„์— ์—ฐ๊ฒฐ๋œ ๋ฉค๋ฒ„์‹ญ์ด ์—†์„ ๋•Œ
  2. ๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์—†์„ ๋•Œ
  3. ๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์žˆ์„ ๋•Œ
  • ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ํ•˜๋‚˜์ผ ๋•Œ
  • ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๋•Œ

์ด ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์€ MembershipCardList ์ปดํฌ๋„ŒํŠธ์˜ ๋™์ž‘์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ•„์ˆ˜์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

// MembershipCardContainer/index.tsx
const MembershipCardContainer =() => {
    const { memberships } = useGetMembershipUsers();

    if (!memberships) return <EmptyLinkedMembership />;
    if (!memberships.user) return <EmptyJoinedMembership />;

    return <MembershipCardListContainer />;
};

MembershipCardList ์ปดํฌ๋„ŒํŠธ๋Š” ์กฐ๊ฑด์— ๋”ฐ๋ผ ์„ธ ๊ฐ€์ง€ ์ปดํฌ๋„ŒํŠธ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ API ์‘๋‹ต๋ณ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ

ํ…Œ์ŠคํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ˆœ์„œ๋กœ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

MembershipContainer/index.test.tsx

  • API ์‘๋‹ต์— ๋”ฐ๋ผ ์กฐ๊ฑด๋ณ„๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋Š” ๊ตฌ์กฐ์ด๊ธฐ ๋•Œ๋ฌธ์—, ํ…Œ์ŠคํŠธ ์‹œ server.use()๋กœ API ๋ชจํ‚น์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์‘๋‹ต์ด ๋นˆ ๋ฐฐ์—ด์ผ ๋•Œ
    • ๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์—†์„ ๋•Œ
    • ๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์žˆ์„ ๋•Œ
// MembershipContainer/index.test.tsx
describe('MembershipContainer', () => {
    test('๋ฉค๋ฒ„์‹ญ์ด ์—†์„ ๋•Œ "๋ฉค๋ฒ„์‹ญ์ด ์—†์–ด์š”" ๋ฌธ๊ตฌ๊ฐ€ ๋…ธ์ถœ๋œ๋‹ค', async () => {
        server.use(
            http.get(`${MembershipApiBaseUrl}/membershipUsers*`, () =>
                HttpResponse.json({
                    success: true,
                    data: EmptyMemberships
                })
            )
        );
		    ...
	test('ํ•ด๋‹น ๋ฉค๋ฒ„์‹ญ์— ํŒฌ๋ค๋ช…์ด ์žˆ์„๋•Œ ํŒฌ๋ค๋ช…์ด ๋…ธ์ถœ๋œ๋‹ค', async () => {
        server.use(
            http.get(`${MembershipApiBaseUrl}/membershipUsers*`, () =>
                HttpResponse.json({
                    success: true,
                    data: { memberships: [EmptyJoinedMembershipsWithFandomName] }
                })
            )
        );
  

MembershipCardListContainer/index.test.tsx

  • ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ๋‚ด๊ฐ€ ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„์‹ญ์ด ์žˆ์„ ๋•Œ ๋ฐ˜ํ™˜๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์— props๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์•„ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฉด์„œ props ๊ฐ’๋งŒ ๋ฐ”๊พธ์–ด ๋‹ค์–‘ํ•œ UI ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋งŒ๋ฃŒ๋œ ๋ฉค๋ฒ„์‹ญ์ผ ๊ฒฝ์šฐ ๋ฉค๋ฒ„์‹ญ ๊ธฐ๊ฐ„ ๋งŒ๋ฃŒ ํ…์ŠคํŠธ ๋…ธ์ถœ
    • ๋ชจ์ง‘์ด ์ข…๋ฃŒ๋œ ๋ฉค๋ฒ„์‹ญ์ผ ๊ฒฝ์šฐ โ€œ๋ฉค๋ฒ„์‹ญ ๋ชจ์ง‘ ์ข…๋ฃŒโ€ ๋ฒ„ํŠผ ๋…ธ์ถœ ํ™•์ธ
    • โ€ฆ
// MembershipCardListContainer/index.test.tsx
describe('MembershipCardListContainer', () => {
    test('๋งŒ๋ฃŒ๋œ ๋ฉค๋ฒ„์‹ญ์ผ ๊ฒฝ์šฐ ๋ฉค๋ฒ„์‹ญ ๊ธฐ๊ฐ„ ๋งŒ๋ฃŒ ํ…์ŠคํŠธ ๋…ธ์ถœ', async () => {
        render(<MembershipCardListContainer memberships={ExpiredSingleMembership} />);
        ...
    test('๋ชจ์ง‘์ด ์ข…๋ฃŒ๋œ ๋ฉค๋ฒ„์‹ญ์ผ ๊ฒฝ์šฐ "๋ฉค๋ฒ„์‹ญ ๋ชจ์ง‘ ์ข…๋ฃŒ" ๋ฒ„ํŠผ ๋…ธ์ถœ', async () => {
        render(<MembershipCardListContainer memberships={SingleMembershipRecruitEndAt} />);
        ...

์ด๋ ‡๊ฒŒ MSW๋ฅผ ํ™œ์šฉํ•ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

MSW๋ฅผ ์‚ฌ์šฉํ•˜๋‹ˆ API ์š”์ฒญ์„ ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ์—์„œ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ์–ด ์‹ค์ œ API ํ˜ธ์ถœ๊ณผ ๋ถ„๋ฆฌ๋œ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ server.use() ๋กœ API ์‘๋‹ต์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด ๋‹ค์–‘ํ•œ ์ผ€์ด์Šค๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ๋„ ๋ชจํ‚น๋œ API ์š”์ฒญ๊ณผ response๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๋ถ€๋ถ„์€ ๋‚˜์ค‘์— API๊ฐ€ ์ค€๋น„๋˜์ง€ ์•Š์€ ์ƒํ™ฉ์—์„œ ์ž‘์—…์ด ํ•„์š”ํ• ๋•Œ ์ผ€์ด์Šค๋ณ„ UI๋ฅผ ํ™•์ธํ•ด๋ณด๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ• ๋•Œ ํฐ ์žฅ์ ์ด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

MSW ํ™œ์šฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ ๊ณ ๋ ค ์‚ฌํ•ญ ๋ฐ ๊ฐœ์„  ๋ฐฉ๋ฒ•

์ด ์™ธ์—๋„ MSW๋ฅผ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฒช์€ ์–ด๋ ค์›€๊ณผ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๋‚ด์šฉ๊ณผ ๋”๋ถˆ์–ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๋ณด๋ฉด์„œ ๋Š๊ผˆ๋˜ ๋‚ด์šฉ์„ ๊ณต์œ ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

  1. ํ•„์š”ํ•œ mockData์— ๋ชจ๋“  ์ผ€์ด์Šค๋ฅผ ํ•˜๋‚˜์”ฉ ์ •์˜ํ•˜๊ธฐ๋ณด๋‹จ ๋จผ์ € ์ •์˜ํ•ด๋‘” ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด์„œ ์žฌ์‚ฌ์šฉ โ†‘
export const SingleMembershipWithFandomName = [
    {
        ...ExpiredSingleMembership[0],
        user: undefined,
        fandomName: '๋…ธ๋จธ์Šค'
    }
];
  1. test ํŒŒ์ผ์—์„œ ์กฐ๊ฑด๋ฌธ์„ ์“ฐ๊ธฐ๋ณด๋‹ค ๊ฐ ์กฐ๊ฑด์— ๋งž๋Š” mockData ์ค€๋น„
โŒ
if(membership.name) {
    const name = await screen.findByText(membership.name);
    expect(name).toBeInTheDocument();
}

โญ•๏ธ
export const SingleMembershipWithName = [
    {
        ...ExpiredSingleMembership[0],
        user: undefined,
        name: '๋…ธ๋จธ์Šค'
    }
];

test('์ด๋ฆ„์ด ์žˆ์„๊ฒฝ์šฐ ์ด๋ฆ„ ๋…ธ์ถœ ํ™•์ธ', async () => {
    render(<MembershipCardListContainer memberships={SingleMembershipWithName} />);

    const name = await screen.findByText(SingleMembershipWithName[0].Name);
    expect(name).toBeInTheDocument();
});
  1. mockData๋Š” ์ƒ์ˆ˜์ด๋ฏ€๋กœ Pascalcase ์‚ฌ์šฉ(์ปจ๋ฒค์…˜์— ๋งž๊ฒŒ)
  2. ํ…Œ์ŠคํŠธํ•˜๊ณ ์ž ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘ render ํ•ด์„œ ํ…Œ์ŠคํŠธ ์ง„ํ–‰
โŒ
render(
    <>
        <MembershipCardEmpty />
        <MembershipJoinButton />
        <MembershipPromotion />
    </>
);

โญ•๏ธ
render(<EmptyLinkedMembership />);

: ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์€ ์ปดํฌ๋„ŒํŠธ์˜ ๊ตฌํ˜„ ๋ฐฉ์‹์ด ๋ฐ”๋€Œ์–ด๋„ ํ…Œ์ŠคํŠธ๊ฐ€ ๋™์ผํ•˜๊ฒŒ ์ž‘๋™ํ•˜๋„๋ก ๋ณด์žฅํ•ด ์ค๋‹ˆ๋‹ค.

  1. mockServiceWorker.jsํŒŒ์ผ์€ propduction์— ๋นŒ๋“œ์‹œ ์ œ์™ธ ํ•„์š”(https://github.com/mswjs/msw/issues/291)
  2. MSW๊ฐ€ 2.0์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋์œผ๋‹ˆ ํ•ด๋‹น ๋‚ด์šฉ ํ™•์ธํ•˜๊ธฐ(https://mswjs.io/blog/introducing-msw-2.0/)

์•„๋ž˜๋Š” MSW๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋งˆ์ฃผํ•œ ์—๋Ÿฌ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

์—๋Ÿฌ ์ฒ˜๋ฆฌ

  • ReferenceError: BroadcastChannel is not defined
    • ์›์ธ : BroadcastChannel API๊ฐ€ Node.js ํ™˜๊ฒฝ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต๋˜์ง€ ์•Š๊ณ  ํ•ด๋‹น ์†์„ฑ์€ Node.js 18 ๋ฒ„์ „๋ถ€ํ„ฐ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ•ด๊ฒฐ : git flow push yamlํŒŒ์ผ์— ์„ธํŒ…๋œ node version์„ 16์—์„œ 18๋กœ ์˜ฌ๋ ค์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. (์ฐธ๊ณ ํ•œ ๋งํฌ๋ฅผ ๋ณด๋ฉด jset+jsdom ํ™˜๊ฒฝ์ผ๋•Œ jest-fixed-jsdom ๋กœ๋„ ํ•ด๊ฒฐ ๊ฐ€๋Šฅํ•ด ๋ณด์ž…๋‹ˆ๋‹ค)
  • server.use()๋กœ api ๋ชจํ‚น ์ˆ˜์ •์ด ๋˜์ง€ ์•Š์„ ๋•Œ
    • ์›์ธ : react-query๋กœ api๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์ „ ๋ฐ์ดํ„ฐ๊ฐ€ ์บ์‹œ์— ๋‚จ์•„์žˆ์–ด ๋ฐœ์ƒํ–ˆ๋˜ ๋ฌธ์ œ
    • ํ•ด๊ฒฐ : setupTestํŒŒ์ผ์— React Query cache๋ฅผ resetํ•˜๋Š” ์ฝ”๋“œ ์ถ”๊ฐ€ํ•ด์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
beforeEach(() => {
    queryCache.clear();
});

or

afterEach(() => {
    server.resetHandlers();
    queryClient.clear();
});

๋…ผ์˜ ์‚ฌํ•ญ

  • MSW ํด๋” ๊ตฌ์กฐ : MSW๋ฅผ ๊ฒ€์ƒ‰ํ•ด๋ณด๋ฉด ํด๋” ๊ตฌ์กฐ๋ฅผ API์ฃผ์†Œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ณณ๋„ ์žˆ์—ˆ๊ณ , handler์™€ api๋ฅผ ๊ฐ๊ฐ ๋ถ„๋ฆฌํ•ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ณณ๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ๋ชจ๋‘ mocksํŒŒ์ผ ๋‚ด๋ถ€์— ์žˆ์—ˆ๊ณ  ์ €ํฌ ํ”„๋กœ์ ํŠธ์™€๋Š” ๋ฐฉํ–ฅ์ด ๋‹ค๋ฅด๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
    • testํŒŒ์ผ๊ณผ mockDataํŒŒ์ผ์€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์™€ ๋™์ผํ•œ ๋ ˆ๋ฒจ์— ์œ„์น˜
    • mocksํด๋” ์•ˆ์—๋Š” node settingํŒŒ์ผ๊ณผ browser settingํŒŒ์ผ๋งŒ ์œ„์น˜

์ €ํฌ๋Š” ์ด๋ ‡๊ฒŒ ๊ตฌ์กฐ๋ฅผ ์„ค์ •ํ•ด ๋ดค์Šต๋‹ˆ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์—๋Š” src/mocks/handler.js์™€ src/mocks/node.js๋งŒ ์ ํ˜€์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ์ธ์ด๋‚˜ ํšŒ์‚ฌ์— ๋งž๋Š” ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค์–ด๊ฐ€์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์ด๋ฒˆ ์ž‘์—…์„ ํ†ตํ•ด MSW๋ฅผ ๋„์ž…ํ•ด ์ฒ˜์Œ์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” MSW๋ฅผ ์ถฉ๋ถ„ํžˆ ํ™œ์šฉํ•ด๋ณด์ง€ ๋ชปํ–ˆ์ง€๋งŒ ์•ž์œผ๋กœ ์ž‘์„ฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ๋Š” MSW๋ฅผ ๋” ๋งŽ์ด ํ™œ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

MSW๋ฅผ ํ™œ์šฉํ•œ ํ…Œ์ŠคํŠธ์— ๊ด€์‹ฌ์ด ์žˆ์œผ์‹  ๋ถ„๋“ค์—๊ฒŒ ์ด ๊ธ€์ด ๋„์›€์ด ๋˜๊ธธ ๋ฐ”๋ผ๋ฉฐ ๊ธ€ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค:)


[์ฐธ๊ณ  ๋ธ”๋กœ๊ทธ]

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ