Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: set MSW for browser & server #14 #15

Merged
merged 2 commits into from
Jan 4, 2024

Conversation

NamgungJongMin
Copy link
Member

개요

백엔드 완성 이전 api 명세에 따라 api 요청과 응답 결과를 확인하기 위해 MSW 설정.
.env 설치 / development 모드일 때만 msw 작동하도록 설정.

스크린샷

주요 내용

  • base interceptor public에 생성
  • 브라우저 및 노드 환경에서 적용될 handler 파일 생성 및 템플릿 작성
  • worker 설정 및 등록
  • jest 테스트에 msw 적용을 위한 파일 생성 및 수정

테스트 결과

  • 브라우저 환경 테스트 이상 무 (test api 연결 후 쿠키 및 response 정상 응답 확인)
  • 노드 환경 테스트 안함. jest 설정 건드려보려다 성훈님이 설정한 것과 충돌할까봐 일단 냅둠. 이후에 jest 테스트 시 문제 있으면 수정하겠음.

closes #14

Copy link

vercel bot commented Jan 3, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
trip-vote ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 4, 2024 1:13am

@NamgungJongMin
Copy link
Member Author

github actions 확인 요망

@HOOOO98
Copy link
Contributor

HOOOO98 commented Jan 4, 2024

@NamgungJongMin

이전 PR 확인 바랍니다. #13

Copy link
Member

@Yamyam-code Yamyam-code left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

msw 세팅 감사해요

Copy link
Contributor

@HOOOO98 HOOOO98 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코멘트 참고하셔서 읽으시면 될 거 같습니다.
(더 정확히 파악하고 싶으시면 검색하시면서 읽으시면 될 거 같습니다.)
+공식문서..영어던데 감사합니다ㅎㅎ

Comment on lines +15 to +17
self.addEventListener("install", function () {
self.skipWaiting();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설치

Comment on lines +19 to +21
self.addEventListener("activate", function (event) {
event.waitUntil(self.clients.claim());
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실행

Comment on lines +136 to +154
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId);

if (client?.frameType === "top-level") {
return client;
}

const allClients = await self.clients.matchAll({
type: "window",
});

return allClients
.filter((client) => {
return client.visibilityState === "visible";
})
.find((client) => {
return activeClientIds.has(client.id);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

탭이나 창 기준으로 클라이언트를 식별하기 때문
-> 개발 중인 탭 찾기

Comment on lines +239 to +252
async function respondWithMock(response) {
if (response.status === 0) {
return Response.error();
}

const mockedResponse = new Response(response.body, response);

Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
value: true,
enumerable: true,
});

return mockedResponse;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모의 응답 함수

Comment on lines +156 to +218
async function getResponse(event, client, requestId) {
const { request } = event;

const requestClone = request.clone();

function passthrough() {
const headers = Object.fromEntries(requestClone.headers.entries());

delete headers["x-msw-intention"];

return fetch(requestClone, { headers });
}

if (!client) {
return passthrough();
}

if (!activeClientIds.has(client.id)) {
return passthrough();
}

const mswIntention = request.headers.get("x-msw-intention");
if (["bypass", "passthrough"].includes(mswIntention)) {
return passthrough();
}

const requestBuffer = await request.arrayBuffer();
const clientMessage = await sendToClient(
client,
{
type: "REQUEST",
payload: {
id: requestId,
url: request.url,
mode: request.mode,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: requestBuffer,
keepalive: request.keepalive,
},
},
[requestBuffer],
);

switch (clientMessage.type) {
case "MOCK_RESPONSE": {
return respondWithMock(clientMessage.data);
}

case "MOCK_NOT_FOUND": {
return passthrough();
}
}

return passthrough();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> 모의 응답 있으면 모의 응답해주고 없으면 실제 응답으로 토스

Comment on lines +23 to +85
self.addEventListener("message", async function (event) {
const clientId = event.source.id;

if (!clientId || !self.clients) {
return;
}

const client = await self.clients.get(clientId);

if (!client) {
return;
}

const allClients = await self.clients.matchAll({
type: "window",
});

switch (event.data) {
case "KEEPALIVE_REQUEST": {
sendToClient(client, {
type: "KEEPALIVE_RESPONSE",
});
break;
}

case "INTEGRITY_CHECK_REQUEST": {
sendToClient(client, {
type: "INTEGRITY_CHECK_RESPONSE",
payload: INTEGRITY_CHECKSUM,
});
break;
}

case "MOCK_ACTIVATE": {
activeClientIds.add(clientId);

sendToClient(client, {
type: "MOCKING_ENABLED",
payload: true,
});
break;
}

case "MOCK_DEACTIVATE": {
activeClientIds.delete(clientId);
break;
}

case "CLIENT_CLOSED": {
activeClientIds.delete(clientId);

const remainingClients = allClients.filter((client) => {
return client.id !== clientId;
});

if (remainingClients.length === 0) {
self.registration.unregister();
}

break;
}
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메세지에 이벤트 리스너 달고,
클라이언트 ID가 맞는 것만 탐색,
클라이언트 종료 및 시작에 따라 id 제거 및 추가
-> 특정 상태에 따라 모의 기능 활성화 및 종료

Comment on lines +87 to +134
self.addEventListener("fetch", function (event) {
const { request } = event;

if (request.mode === "navigate") {
return;
}

if (request.cache === "only-if-cached" && request.mode !== "same-origin") {
return;
}

if (activeClientIds.size === 0) {
return;
}

const requestId = crypto.randomUUID();
event.respondWith(handleRequest(event, requestId));
});

async function handleRequest(event, requestId) {
const client = await resolveMainClient(event);
const response = await getResponse(event, client, requestId);

if (client && activeClientIds.has(client.id)) {
(async function () {
const responseClone = response.clone();

sendToClient(
client,
{
type: "RESPONSE",
payload: {
requestId,
isMockedResponse: IS_MOCKED_RESPONSE in response,
type: responseClone.type,
status: responseClone.status,
statusText: responseClone.statusText,
body: responseClone.body,
headers: Object.fromEntries(responseClone.headers.entries()),
},
},
[responseClone.body],
);
})();
}

return response;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네트워크 요청을 가로채서 처리(navigate 제외)
요청이 캐싱만 하는 경우? 와 동일출처가 아닌 경우 요청 처리 안함.
요청에 고유 아이디 만들어서 응답 반환
-> 요청 가로채서 응답반환

Comment on lines +220 to +237
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel();

channel.port1.onmessage = (event) => {
if (event.data && event.data.error) {
return reject(event.data.error);
}

resolve(event.data);
};

client.postMessage(
message,
[channel.port2].concat(transferrables.filter(Boolean)),
);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메세지 채널 생성
메세지 수신 대기
응답 전송(에러도)
-> 요청 응답 처리 양방향

Comment on lines +10 to +13
async function enableMocking() {
if (import.meta.env.MODE !== "development") {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별다른 환경변수를 만들지 않아도 된다고 합니다.ㅎㅎ
vite에서 제공해 주는 환경변수라고 합니다.

https://ko.vitejs.dev/guide/env-and-mode.html#modes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

환경변수 만든거 없습니다. 기본 vite에서 제공하는 mode에요

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? 뭔가 오해 하신거 같은데ㅎㅎ
다른 분들께 환경변수파일 안만들어도 된다는 의미였어요ㅋㅋㅋㅋ

Copy link
Contributor

@SKY-PEY SKY-PEY left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안그래도 목서버+테스트가 걱정이었는데 종민님 덕에 제대로 공부해보네요! 고생많으셨구 감사히 잘쓰겠습니다~!👍👍

@NamgungJongMin NamgungJongMin merged commit 28a6f1c into dev Jan 4, 2024
3 checks passed
@NamgungJongMin NamgungJongMin deleted the 14-feat-set-msw-for-browser-server branch January 4, 2024 05:31
HOOOO98 pushed a commit that referenced this pull request Jan 9, 2024
…-server

Feat: set MSW for browser & server #14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants