Shooot-PJT / shooot

0 stars 0 forks source link

[Mock/Feat/#40] service worker κ΅¬ν˜„ - [merged] #163

Closed yh-project closed 6 days ago

yh-project commented 2 weeks ago

Merges feat/40-request-mocking -> mock

πŸ₯₯ Contents

service worker κ΅¬ν˜„

μ‚¬μš©μžμ˜ http μš”μ²­μ„ λ°›μ•„μ„œ mocking 처리λ₯Ό ν•˜κΈ° μœ„ν•œ service worker 의 κΈ°λŠ₯을 κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€. fetch 이벀트λ₯Ό λ“±λ‘ν•˜μ—¬ λΈŒλΌμš°μ €μ—μ„œ λ™μž‘ν•˜κ³  μžˆλŠ” service worker κ°€ μš”μ²­μ„ μΈμ‹ν•˜μ—¬ 처리λ₯Ό ν•˜κ²Œ λœλ‹€.

μ‚¬μš©μž μš”μ²­ κ°€λ‘œμ±„κΈ° ```javascript // shooot-mock/service-worker.js if (projectName === undefined || !projectName.length) { console.error("[Fetch Event]: projectName 을 λ“±λ‘ν•΄μ£Όμ„Έμš”"); } else { const { request } = event; const url = new URL(request.url); if (!url.origin.split(/[/.]/).includes(projectName)) { console.error("[Fetch Event]: μ„œλΉ„μŠ€μ—μ„œ μ œκ³΅λ˜λŠ” λ„λ©”μΈμœΌλ‘œμ˜ μš”μ²­λ§Œ mocking λ©λ‹ˆλ‹€"); } else { ... } } ``` μš”μ²­μ„ λ°›μœΌλ©΄ service worker κ°€ ν•  일은 μš”μ²­μ„ 관리해도 λ˜λŠ”μ§€μ— λŒ€ν•œ μ—¬λΆ€ νŒŒμ•…μ΄λ‹€. - projectName(우리 μ„œλΉ„μŠ€μ— λ“±λ‘ν•œ μ„œλΉ„μŠ€μ˜ 영문λͺ…)을 μ„€μ •ν•˜μ§€ μ•Šμ€ 경우 - μ„€μ •ν•œ projectName 이 도메인에 ν¬ν•¨λœ μš”μ²­μ˜ 경우
데이터 κ°€κ³΅ν•˜κΈ° ```javascript // shooot-mock/service-worker.js event.respondWith( (async () => { const origin = url.origin; // origin === domain const method = request.method; // request method const headers = [...request.headers.entries()]; // request header const params = Object.fromEntries(url.searchParams.entries()); // request parameters, path variables const requestParameters = {}; const pathVariables = {}; Object.keys(params).forEach((key) => { // request parameter, path variable 가곡 κ³Όμ • }); let body = {}; if (["POST", "PUT", "PATCH"].includes(method)) { // request body 가곡 κ³Όμ • } const postData = { // express 에 전달할 λ°μ΄ν„°λ‘œ λ³€ν™˜ }; let result; try { const response = await fetch("express endpoint", { // post 둜 데이터 전달 }); console.log(`[Fetch Event]: from express ${result}`); return new Response(JSON.stringify(await response.json()), { status: 200 }); } catch (error) { console.error(`[Fetch Event]: from express error`, error); return new Response(JSON.stringify({ error: "[Fetch Event]: server internal error" }), { status: 500, }); } })() ); ``` - ν˜„μž¬ μ‚¬μš©μžμ˜ request μ—μ„œ μ‚¬μš©ν•˜λŠ” μ •λ³΄λŠ” μœ„μ—μ„œ λ³΄μ—¬μ§€λŠ” 6가지(origin, method, headers, request parameters, path variables, body) - 뽑아낸 데이터λ₯Ό ν•˜λ‚˜μ˜ 객체(postData)둜 κ°μ‹Έμ„œ express μ„œλ²„λ‘œ 전달 - express 의 응닡을 μ‚¬μš©μžμ—κ²Œ 전달
message 이벀트 ```javascript // shooot-mock/service-worker.js self.addEventListener("message", async function (event) { console.log("[ν”„λ‘œμ νŠΈ μ„€μ •]=========="); if (event.data && event.data.type === "SET_CONFIGS") { projectName = event.data.projectName; delay = event.data.delay; console.log(`[ν”„λ‘œμ νŠΈ μ„€μ •]: 정보 μ„€μ • μ™„λ£Œ (${projectName}, ${delay}ms)`); } }); ``` - μœ„μ—μ„œ μ–ΈκΈ‰λœ projectName 은 service worker κ°€ μ•„λ‹Œ λΌμ΄λΈŒλŸ¬λ¦¬κ°€ μ œκ³΅ν•˜λŠ” object 의 λ©”μ†Œλ“œκ°€ λ°›μ•„μ„œ μžμ›μœΌλ‘œ μ €μž₯ - object κ°€ 값을 μ „λ‹¬ν•΄μ€˜μ•Ό ν•˜κ³ , κ·Έ 과정을 service worker κ°€ λ³΄μœ ν•˜κ³  μžˆλŠ” postMessage λ©”μ†Œλ“œμ™€ message 이벀트둜 관리 - λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œμ˜ κ΅¬ν˜„ μ½”λ“œλŠ” λ‹€μŒ merge requeset μ—μ„œ 확인이 κ°€λŠ₯ν•˜κ³  μ—¬κΈ°μ„œλŠ” message μ΄λ²€νŠΈμ—μ„œ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” κ³Όμ •λ§Œ μ†Œκ°œ

라이브러리 κ΅¬ν˜„

우리의 μ΅œμ’… λͺ©ν‘œλŠ” service worker κ°€ λ™μž‘ν•˜λŠ” 라이브러리λ₯Ό κ΅¬ν˜„ν•˜μ—¬ κ°œλ°œμžλ“€μ΄ μ“Έ 수 μžˆλ„λ‘ ν•˜λŠ” 것이닀.

λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ μ œκ³΅ν•˜λŠ” object - 관리 μžμ› ```javascript // shooot-mock/index.js shooot.projectName = ""; shooot.delay = 0; shooot.axios = {}; ``` - 3개의 μžμ›μ„ 관리 - ν”„λ‘œμ νŠΈ 도메인 νŒλ³„μ„ μœ„ν•œ projectName, μš”μ²­ delay μ‹œκ°„ - path variables λ₯Ό 직접 객체 ν˜•νƒœλ‘œ μ •μ˜ν•˜κΈ° μœ„ν•΄ κ°€κ³΅ν•œ axios ```javascript // shooot-mock/index.js /** * Custom GET request * ν…œν”Œλ¦Ώ, λ§€κ°œλ³€μˆ˜, λ°˜ν™˜κ°’μ— λŒ€ν•œ μ •μ˜ */ shooot.axios.get = async function (url, config = {}, pathVariables = {}) { let to = url; if (pathVariables) { // 객체 ν˜•νƒœλ‘œ 받은 path variable 을 url 에 ν¬ν•¨μ‹œν‚€λŠ” κ³Όμ • Object.keys(pathVariables).forEach((k) => (to += `/${pathVariables[k]}`)); } const swState = await checkServiceWorker("Axios-Get"); // service worker κ°€ ν™œλ™ν•˜κ³  μžˆλŠ”κ°€ const domainState = checkDomain(to, shooot.projectName); // μ‚¬μš©ν•  수 μžˆλŠ” projectName 인가 const newConfig = swState && domainState ? getAxiosConfigs(pathVariables, config) : { ...config, params: { ...config.params } }; // λ‘˜λ‹€ true 라면 mocking 을 μœ„ν•œ config 둜, μ•„λ‹ˆλΌλ©΄ κΈ°λ³Έ axios μš”μ²­μ„ μœ„ν•œ config 둜 return axios.get(to, newConfig); // κ°€κ³΅ν•œ μ •λ³΄λ‘œ axios μš”μ²­ }; ``` - 기쑴의 axios μ—μ„œλŠ” path variables λ₯Ό 객체 ν˜•νƒœλ‘œ μ„ μ–Έν•˜μ§€ λͺ»ν•¨ - 객체 ν˜•νƒœλ‘œ μ„ μ–Έν•  수 μžˆλ„λ‘ 가곡 -> mocking ν•  λ•Œ μžμ›μ„ 가곡할 수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•¨ - mocking ν•΄μ•Ό ν•˜λŠ” μš”μ²­μΈμ§€ νŒλ³„ν•˜μ—¬ 상황에 λ§žλŠ” config 둜 가곡 - 기쑴의 axios λ₯Ό ν˜ΈμΆœν•˜μ—¬ μš”μ²­μ„ 보내어 mocking 이라면 service worker κ°€, μ•„λ‹ˆλΌλ©΄ κΈ°μ‘΄ μš”μ²­μ΄ κ·ΈλŒ€λ‘œ 진행 - 이와 같은 λ°©μ‹μœΌλ‘œ get, post, put, patch, delete κ΅¬ν˜„
λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ μ œκ³΅ν•˜λŠ” object - λ©”μ†Œλ“œ ```javascript // shooot-mock/index.js shooot.register = async function () { console.log("[Register]=========="); if ("serviceWorker" in navigator) { try { await navigator.serviceWorker.register("/service-worker.js"); console.log(`[Register]: service worker 등둝 μ™„λ£Œ`); } ... } ... }; shooot.unregister = async function () { console.log("[UnRegister]=========="); if ("serviceWorker" in navigator) { try { const registration = await navigator.serviceWorker.getRegistration(); if (registration) { await registration.unregister(); console.log("[UnRegister]: service worker 등둝 ν•΄μ œν•˜μ˜€μŠ΅λ‹ˆλ‹€"); } ... } ... } ... }; shooot.controller = async function (mode) { if (process.env.NODE_ENV === (mode ? mode : "development")) { await this.register(); } else { await this.unregister(); } }; ``` - λΈŒλΌμš°μ €μ— service worker λ₯Ό λ“±λ‘ν•˜λŠ” register - λΈŒλΌμš°μ €μ—μ„œ service worker λ₯Ό 등둝 ν•΄μ œν•˜λŠ” unregister - λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ” mode κ°€ μžˆλ‹€λ©΄ mode or development λͺ¨λ“œμΌ λ•Œ register, 아닐 λ•Œμ—λŠ” unregister ```javascript // shooot-mock/index.js shooot.setConfigs = async function (projectName, delay) { const projectNameRegex = /^[a-z0-9-]{1,20}$/; console.log("[Set-Config]=========="); if (!projectName) { // projectName 이 μž…λ ₯λ˜μ—ˆλŠ”κ°€ } else if (!projectName.trim().length || !projectNameRegex.test(projectName.trim())) { // μ‚¬μš© κ°€λŠ₯ν•œ projectName 인가} else { // λ“±λ‘λœ ν”„λ‘œμ νŠΈμΈμ§€ 확인 const pn = projectName.trim(); const canUse = await checkProjectName(pn); // λ“±λ‘λœ ν”„λ‘œμ νŠΈ if (canUse === "AVAILABLE") { const checkServiceWorkerResult = await checkServiceWorker("Set-Config"); if (checkServiceWorkerResult) { shooot.projectName = projectName; shooot.delay = delay; const message = { type: "SET_CONFIGS", projectName: shooot.projectName, delay: shooot.delay, }; checkServiceWorkerResult.active.postMessage(message); console.log(`[Set-Config]: message λ₯Ό μ „λ‹¬ν–ˆμŠ΅λ‹ˆλ‹€`); } } else if (canUse === "UNAVAILABLE") { ... // λ“±λ‘λ˜μ§€ μ•Šμ€ ν”„λ‘œμ νŠΈ } } }; ``` - μ‚¬μš©κ°€λŠ₯ν•œ projectName 인지 μš°μ„  νŒλ³„ - service worker κ°€ λ™μž‘ 쀑인지 확인 - service worker μ—κ²Œ μ„€μ • κ°’ 전달
λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ μ œκ³΅ν•˜λŠ” util λ©”μ†Œλ“œ, type ```javascript // shooot-mock/utils.js export const checkProjectName = async (projectName) => { ... // express μ—μ„œ λ“±λ‘λœ projectName 인지 확인 }; export const checkServiceWorker = async (keyword) => { ... // service worker κ°€ λ™μž‘ 쀑이면 true λ₯Ό, μ•„λ‹ˆλ©΄ false λ₯Ό }; export const checkDomain = (to, domain) => { ... // μš”μ²­μ˜ url μ—μ„œ domain(=projectName) 이 ν¬ν•¨λ˜μ–΄μžˆλŠ”μ§€ 확인 }; export const getAxiosConfigs = (pathVariables, config) => { ... // mocking 을 μœ„ν•œ axios config 둜 가곡 }; ``` ```javascript // shooot-mock/index.d.ts export interface Configs { // μ„€μ •κ°’ projectName: string; delay?: number; } export interface Shooot extends Configs { axios: Omit & { get, D = any>( url: string, config?: AxiosRequestConfig, pathVariables?: object ): Promise; // get κ³Ό 같은 ν˜•νƒœλ‘œ post, put, patch, delete 도 등둝 }; setConfigs: (projectName: string, delay?: number) => Promise; controller: (mode?: string) => Promise; register: () => Promise; unregister: () => Promise; } ``` - νƒ€μž…μŠ€ν¬λ¦½νŠΈ ν™˜κ²½μ—μ„œ 라이브러리λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•œ νƒ€μž… λͺ¨λ“ˆ 파일 μ„ μ–Έ

라이브러리 μ‚¬μš©

init script ```bash npx shooot init ``` κ΅¬ν˜„ν•œ 라이브러리λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ init script νŒŒμΌμ„ μž‘μ„±ν•˜μ˜€λ‹€. ν•΄λ‹Ή script λ₯Ό ν†΅ν•΄μ„œ service-worker.js νŒŒμΌμ„ λ³ΈμΈλ“€μ˜ ν”„λ‘œμ νŠΈμ— μ μš©μ‹œμΌœ λΈŒλΌμš°μ €μ—μ„œ λ™μž‘μ‹œν‚€λ„λ‘ ν•˜κΈ° μœ„ν•¨μ΄λ‹€.

express μ„œλ²„ κ΅¬ν˜„

λ“±λ‘λœ projectName 인가 ```javascript // shooot-express/server.js app.get("/projects/search", (req, res) => { const { projectName } = req.query; const query = `SELECT * FROM project WHERE english_name = "${projectName}"`; db.execute(query, (err, results) => { ... // κ²°κ³Ό λ°˜ν™˜ }); }); ``` - express μ„œλ²„μ—μ„œ 우리 μ„œλΉ„μŠ€μ— λ“±λ‘λœ projectName 인지 ν™•μΈν•˜μ—¬ κ²°κ³Ό λ°˜ν™˜
service worker λ‘œλΆ€ν„° 받은 κ°€κ³΅λœ 데이터 ```javascript // shooot-express/server.js app.post("/mock/data", (req, res) => { const { projectName, url, origin, method, headers, requestParameters, pathVariables, body } = req.body; console.log("[Received data from Service Worker]"); ... // λ‹¨μˆœνžˆ μ½˜μ†”μ— 둜그 남기기 res.json({ message: "데이터λ₯Ό μ •μƒμ μœΌλ‘œ λ°›μ•˜μŠ΅λ‹ˆλ‹€" }); }); ``` - service worker λ‘œλΆ€ν„° 전달 받은 κ°€κ³΅λœ 데이터λ₯Ό ν˜„μž¬λŠ” λ‹¨μˆœνžˆ μ½˜μ†”μ— 둜그만 남김 - 후에 api docs μͺ½ κΈ°λŠ₯이 μ™„μ„±λ˜λ©΄ 남은 κΈ°λŠ₯ κ΅¬ν˜„ μ˜ˆμ •

πŸ“Έ Screenshot

βš“ Related Issue

  • 40

πŸ“š Reference

chatGPT κ³ λ§ˆμ›Œμš©

yh-project commented 2 weeks ago

requested review from @ABizCho

yh-project commented 2 weeks ago

added 1 commit

  • a6d132b7 - mock/fix: λΆˆν•„μš” 파일 제거

Compare with previous version

yh-project commented 2 weeks ago

added 1 commit

  • 5181bf76 - mock/fix: return type μˆ˜μ •

Compare with previous version

yh-project commented 2 weeks ago

@altys31 @ABizCho

컀밋을 μ§€λΌμ„œ mr μ“°λŠ” 방법을 λͺ°λΌμ„œ ν•œλ²ˆμ— μ˜¬λ ΈμŒλ‹€. μ΅œλŒ€ν•œ λ‹¨μˆœν•˜κ²Œ μ μ–΄λ΄€λŠ”λ° λ‚΄μš©μ΄ κ·Έλž˜λ„ 쫌 λ§Žμ•„μ—¬ γ… γ… ... 빨리 ν•΄μ•Όν•˜λŠ”κ±° μ•„λ‹ˆλ‹ˆκΉŒ 천천히 μ½μ–΄λ³΄μ‹œκ³  내일 μ˜€μ „ κΉŒμ§€λ§Œ 리뷰해주십셩.

yh-project commented 2 weeks ago

In GitLab by @ABizCho on Nov 11, 2024, 09:43

approved this merge request

yh-project commented 2 weeks ago

In GitLab by @ABizCho on Nov 11, 2024, 09:47

Commented on shooot-mock/src/index.d.ts line 38

Axios νƒ€μž… μ •μ˜λ₯Ό λ³΄λŠ”κ²ƒλ„ μ‹ μ„ ν•˜κ³ , 이참에 λ©€λ¦¬ν–ˆλ˜ νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ œλ„€λ¦­ λ¬Έμ„œλ₯Ό μ°Ύμ•„λ΄μ„œ 도움이 λμŠ΅λ‹ˆλ‹€.

yh-project commented 2 weeks ago

In GitLab by @ABizCho on Nov 11, 2024, 09:47

Commented on shooot-mock/src/index.js line 31

ν•¨μˆ˜ν˜• 처리둜 pathVariable μ²˜λ¦¬λ°©μ‹μ΄ κ°„κ²°ν•΄μ„œ μ’‹μ•„μš”

yh-project commented 2 weeks ago

In GitLab by @ABizCho on Nov 11, 2024, 09:47

ꡉμž₯히 도전적인 νŒŒνŠΈμ˜€λŠ”λ°, μ½”λ“œλ„ κΉ”λ”ν•˜κ³  λͺ¨ν‚Ήμ΄λΌλŠ” 핡심 κΈ°λŠ₯κ³Ό 저희 μ„œλΉ„μŠ€μ™€μ˜ 접점을 μ–΅μ§€μŠ€λŸ½μ§€ μ•Šμ€ 쒋은 λ°©λ²•μœΌλ‘œ λ„ˆλ¬΄ 잘 μ—°κ²°ν•˜μ‹  것 κ°™μ•„μš”. λ¬Έμ„œλ„ 이해가 잘 κ°€λ„€μš” 고생 λ§ŽμœΌμ…¨μ–΄μš”!

yh-project commented 2 weeks ago

In GitLab by @altys31 on Nov 11, 2024, 10:29

각 μ„Ήμ…˜λ³„λ‘œ 잘 μ •λ¦¬ν•΄μ£Όμ…”μ„œ μ‰½κ²Œ 이해할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. πŸ‘πŸ‘πŸ‘
λ³΅μž‘ν•œ 섀정없이 npx shooot init만으둜 μ‚¬μš© κ°€λŠ₯ν•΄μ„œ 초기 μ„€μ • 뢀담이 μ—†μ–΄μ„œ 쒋은것 κ°™μŠ΅λ‹ˆλ‹€.
수고 λ§ŽμœΌμ…¨μ–΄μš” 😸