[React] MSW (Mock Service Worker)

    728x90
    반응형

     

     

    MSW (Mock Service Worker)

    Mock Service Worker의 줄임말로, MSW(Mock Service Worker)는 서비스 워커(Service Worker)를 사용하여 네트워크 호출을 가로채는 API 모킹(mocking) 라이브러리입니다.

    실제 백엔드 서버 없이도 모의 응답(Mock Response)을 반환할 수 있게 해줍니다

     

    *mock은 모조품, 가짜란 뜻
    *서비스 워커(Service Worker)는 웹 페이지와 별개로 백그라운드에서 스크립트를 실행할 수 있는 웹 브라우저의 기능입니다. 웹 애플리케이션의 성능을 향상시키기 위해 주로 오프라인 경험, 네트워크 요청 가로채기 및 처리, 푸시 알림 및 백그라운드 동기화를 처리하는 데 사용됩니다. 서비스 워커는 웹 애플리케이션에 프록시 서버와 유사한 역할을 하여, 네트워크 요청을 중간에서 가로채고 캐싱하는 등의 작업을 수행할 수 있습니다.

    즉, 웹페이지와 별개의 백그라운드에서 스크립트를 실행하여 가짜를 만든다.

    그런데 api호출을 가로채서 만든다. 라는 뜻으로 해석할 수 있을듯 합니다.

     

     

     

    장점

    • 이전에는 개발환경의 주소, 실제환경의 주소 따로 넣어줘서 분기처리해줘야했다. (process.env.NODE_ENV)
      하지만 msw는 실제주소(운영용 주소)로만 데이터를 보내면되고 개발서버에서만 msw활성화시켜서
      서버요청을 가로채면된다. 즉 주소들을 개발/운영 따로 분기해주지 않아도 된다.
    • 기존 백엔드가 있어도 백엔드를 수정하지 않고 개발/테스트용 백엔드를 추가할 수 있다. (로그인 상태를 유지하거나 에러발생 같은 현상 재현할 수 있고 필요에 따라 테스트 가능하다)
    • 서버 개발이 완료되지 않았거나 외부 API에 접근할 수 없는 상황에서도 프론트엔드 개발을 계속 진행할 수 있습니다.
    • 실제 서버의 영향을 받지 않고 일관된 결과를 반환하는 모의 응답을 활용하여, 네트워크 요청의 다양한 시나리오를 테스트할 수 있습니다.
    • 실제 서버가 없이도 클라이언트 애플리케이션의 네트워크 통신을 시뮬레이션할 수 있어, 개발과 테스트가 용이

     

     

    세팅

    파일 설치

    // msw 설치
    npm install msw@latest --save-dev
    
    // API를 네트워크 선에서 차단하기 위해, 서비스 워커를 등록해야한다.
    // 해당 작업을 친절하게도, MSW에서 제공해준다.
    // /public폴더에 mockServiceWorker.js 파일 생성
    npx msw init public/ --save
    
    // 추가로 필요한 라이브러리들
    npm i -D @mswjs/http-middleware cors  @types/cors express @types/express

     


    Next 프로젝트는 클라이언트에서도 동작하고 서버에서도 동작한다. 그래서 MSW가 클라이언트, 서버에서 동작해야한다.

    서버쪽에서 msw를 자연스럽게 돌리는 방식이 아직 없다.

    그래서 임시로 node서버를 활용할 것이다. (node express)

     

     

     

    세팅 파일 생성

    /src/mocks

    📦mocks
     ┣ 📜browser.ts // 클라이언트에서의 요청 위해서
     ┣ 📜handlers.tsx // 모킹 데이터 내려주기 위해서
     ┗ 📜http.ts // 서버(next server)에서의 요청 위해서

     

     

    클라이언트의 mocking을 위해서

    /src/app/_component/MSWComponent.tsx

    📦_component
     ┗ 📜MSWComponent.tsx // 클라이언트에서의 요청 위해서

     

     

     

    /src/mocks/browsers.ts

    import { setupWorker } from 'msw/browser'
    import { handlers } from './handlers';
     
    // This configures a Service Worker with the given request handlers.
    const worker = setupWorker(...handlers);
    
    export default worker;

     

     

    모킹데이터 내려주기 위해

    /src/mocks/handlers.ts

    import { HttpResponse, http } from 'msw';
    
    export const handlers = [
      http.post('/api/login', () => {
        return HttpResponse.json(
          {
            userId: 1,
            nickname: '찜찜',
            id: 'zzimzzim',
            image: '/5Udwvqim.jpg'
          },
          {
            headers: {
              // http해더로 쿠키 설정하는 방법
              'Set-Cookie': 'connect.sid=msw-cookie;HttpOnly;Path=/'
            }
          }) 
      }),
      http.post('/api/logout', () => {
        return new HttpResponse(null, {
          headers: {
            // http해더로 쿠키 지우는 방법
            'Set-Cookie': 'connect.sid=;HttpOnly;Path=/;Max-Age=0'
          },
        })
      })
    ]

     

     

    /src/mocks/http.ts

    import { createMiddleware } from '@mswjs/http-middleware';
    import express from 'express';
    import cors from 'cors';
    import { handlers } from './handlers';
    
    const app = express();
    const port = 9090; // 서버코드는 9090,  클라이언트는 3001
    
    app.use(cors({ origin: 'http://localhost:3001', optionsSuccessStatus: 200, credentials: true }));
    app.use(express.json());
    app.use(createMiddleware(...handlers));
    
    app.listen(port, () => console.log(`Mock server is running on port: ${port}`));

     

     

    /src/app/_component/MSWComponent.tsx

    'use client';
    
    import { useEffect } from 'react';
    
    export const MSWComponent = () => {
      useEffect(() => {
      	// 브라우저에서만 동작하는 것을 확신하기 위해서
        if (typeof window !== 'undefined') {
          // 환경변수로 구분 (개발일때만 실행되도록)
          if (process.env.NEXT_PUBLIC_API_MOCKING === 'enabled') {
            require('@/mocks/browser');
          }
        }
      }, [])
    
      return null;
    }

     

     

     

    /src/app/layout.tsx (rootLayout.tsx)

    import type { Metadata } from 'next';
    import { Inter } from 'next/font/google';
    
    import './globals.css'; // 전체 공통 스타일
    import styles from './page.module.scss'; // 특정 페이지용
    import { MSWComponent } from '@/app/_component/MSWComponent';
    
    const inter = Inter({ subsets: ['latin'] });
    
    export const metadata: Metadata = {
        title: 'Create Next App',
        description: 'Generated by create next app',
    };
    
    export default function RootLayout({
    	children,
    }: Readonly<{
    	children: React.ReactNode;
    }>) {
        return (
            <html lang='en'>
                <body className={inter.className}>
                    {/* 클라이언트 mocking위해 컴포넌트 적용 */}
                    <MSWComponent />
                    {/* 루트 레이아웃 */}
                    <div>{children}</div>
                </body>
            </html>
        );
    }

     

     

     

     

    동작

    MSWComponent에서 browser가 require해주면 된다.(msw동작)

    기본적으로는 는 mockServiceWorker.js가 api(요청)를 가로채서, http.ts(서버)로 보낸다.

    그리고 handlers.ts가 실행

    next server에서 요청받은 것은(ssr) createMiddleware에서 받아서 handlers로 보내고

    클라이언트에서 요청받은 것은 MSWComponent에서 받아서, brwoser로 보낸다음에 handlers로 보낸다

    그런데 지금 next와 msw 연동 문제로 다 백엔드 msw 서버( createMiddleware )에서 처리

     

    원래는

    • ms- express 서버는 next server에서 요청보내는 걸 모킹 (가짜 api 응답 만든다)
    • MSWComponent는 브라우저에서 요청을 보내는 걸 모킹  (가짜 api 응답 만든다)

    지금은

    • ms- express 서버는 next server, 브라우저에서 요청보내는 걸 모두 모킹 (가짜 api 응답 만든다)

     

    ** .env.local 파일에서 NEXT_PUBLIC_API_MOCKING=enabled 에따라서 MSWComponent에서 msw동작하게 한다.

    여기서 NEXT_PUBLIC으로 시작하는 환경변수는 브라우저에서 접근가능한 환경변수가 된다.

    소스코드에서 접근가능 NEXT_PUBLIC이 없다면 서버에서만 접근가능한 환경변수가 된다. 

    (브라우저에서 접근가능하냐? 안하냐? 구분) 개발자도구에서 유출가능한 내용만 브라우저에서 접근할수있게 해야한다.

    NEXT_PUBLIC으로 msw의 동작 유무를 구분한 이유는 require('@/mocks/browser')이게 브라우저에서 실행되기 때문이다.

    그래서 브라우저에서 접근간으할 수 있도록 환경변수 설정

     

     

     

     

     

     

     

     

     

     

    참조!

    https://www.inflearn.com/course/next-react-query-sns%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard

     

    Next + React Query로 SNS 서비스 만들기 | 조현영 - 인프런

    조현영 | 리액트18 & 넥스트14 & 리액트쿼리5 & Next Auth5 & MSW2 & socket.io4 & zustand 스택으로 트위터(X.com)와 유사한 SNS 서비스를 만들어봅니다. 끝으로 검색엔진 최적화를 위한 S...

    www.inflearn.com

     

    728x90
    반응형

    댓글