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

MSW 상호 참조 에러 수정 #5

Merged
merged 2 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions apps/service/__mocks__/data/articles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
const getThumbnailUrl = (keyword: string) =>
`https://source.unsplash.com/random/1280x720/?${keyword}`

export const articles = [
{
id: 1,
thumbnailUrl: getThumbnailUrl('newsletter'),
title: '트렌드에 대한 최신 업데이트',
category: 'business',
date: '2024-03-05',
readPercentage: 0,
readingTime: 15,
isRead: false,
},
{
id: 2,
thumbnailUrl: getThumbnailUrl('tech'),
title: '블록체인 기술의 미래 전망',
category: 'technology',
date: '2024-02-20',
readPercentage: 0,
readingTime: 8,
isRead: false,
},
{
id: 3,
thumbnailUrl: getThumbnailUrl('economy'),
title: '글로벌 경제 상황과 전망',
category: 'economy',
date: '2024-03-10',
readPercentage: 0,
readingTime: 2,
isRead: false,
},
{
id: 4,
thumbnailUrl: getThumbnailUrl('ai'),
title: 'AI 기술의 현재와 미래',
category: 'technology',
date: '2024-02-29',
readPercentage: 0,
readingTime: 12,
isRead: false,
},
{
id: 5,
thumbnailUrl: getThumbnailUrl('startup'),
title: '스타트업 생태계의 동향과 분석',
category: 'business',
date: '2024-03-15',
readPercentage: 0,
readingTime: 4,
isRead: false,
},
{
id: 6,
thumbnailUrl: getThumbnailUrl('environment'),
title: '지속 가능한 환경을 위한 노력들',
category: 'society',
date: '2024-02-18',
readPercentage: 0,
readingTime: 18,
isRead: false,
},
{
id: 7,
thumbnailUrl: getThumbnailUrl('health'),
title: '건강한 생활 습관에 대한 조언',
category: 'health',
date: '2024-03-01',
readPercentage: 0,
readingTime: 5,
isRead: false,
},
{
id: 8,
thumbnailUrl: getThumbnailUrl('investment'),
title: '다양한 투자 전략과 팁',
category: 'economy',
date: '2024-02-25',
readPercentage: 0,
readingTime: 7,
isRead: false,
},
{
id: 9,
thumbnailUrl: getThumbnailUrl('data'),
title: '빅데이터의 활용과 분석 방법',
category: 'technology',
date: '2024-03-05',
readPercentage: 0,
readingTime: 13,
isRead: false,
},
{
id: 10,
thumbnailUrl: getThumbnailUrl('finance'),
title: '금융 시장의 최신 동향과 전망',
category: 'economy',
date: '2024-02-15',
readPercentage: 0,
readingTime: 20,
isRead: false,
},
{
id: 11,
thumbnailUrl: getThumbnailUrl('internet'),
title: '인터넷 보안에 대한 중요성과 대응 전략',
category: 'technology',
date: '2024-02-22',
readPercentage: 0,
readingTime: 16,
isRead: false,
},
{
id: 12,
thumbnailUrl: getThumbnailUrl('marketing'),
title: '디지털 마케팅의 효과적인 전략',
category: 'business',
date: '2024-03-10',
readPercentage: 0,
readingTime: 3,
isRead: false,
},
{
id: 13,
thumbnailUrl: getThumbnailUrl('education'),
title: '현대 교육 방식의 혁신과 도전',
category: 'society',
date: '2024-02-29',
readPercentage: 0,
readingTime: 14,
isRead: false,
},
{
id: 14,
thumbnailUrl: getThumbnailUrl('leadership'),
title: '리더십의 핵심 요소와 발전 방향',
category: 'business',
date: '2024-03-20',
readPercentage: 0,
readingTime: 11,
isRead: false,
},
{
id: 15,
thumbnailUrl: getThumbnailUrl('food'),
title: '건강한 식습관을 위한 다양한 요리 레시피',
category: 'health',
date: '2024-02-17',
readPercentage: 0,
readingTime: 19,
isRead: false,
},
{
id: 16,
thumbnailUrl: getThumbnailUrl('travel'),
title: '여행지 추천과 여행 팁',
category: 'lifestyle',
date: '2024-03-12',
readPercentage: 0,
readingTime: 10,
isRead: false,
},
{
id: 17,
thumbnailUrl: 'culture',
title: '세계 각국의 문화와 특징',
category: 'lifestyle',
date: '2024-03-07',
readPercentage: 0,
readingTime: 1,
isRead: false,
},
{
id: 18,
thumbnailUrl: 'design',
title: '디자인 트렌드와 아이디어',
category: 'design',
date: '2024-02-28',
readPercentage: 0,
readingTime: 6,
isRead: false,
},
{
id: 19,
thumbnailUrl: 'entertainment',
title: '연예계 소식과 유명인의 인터뷰',
category: 'entertainment',
date: '2024-02-21',
readPercentage: 0,
readingTime: 17,
isRead: false,
},
{
id: 20,
thumbnailUrl: getThumbnailUrl('society'),
title: '사회 문제와 대응 전략',
category: 'society',
date: '2024-03-18',
readPercentage: 0,
readingTime: 9,
isRead: false,
},
]
1 change: 1 addition & 0 deletions apps/service/__mocks__/data/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { articles } from './articles'
82 changes: 80 additions & 2 deletions apps/service/__mocks__/handlers/inbox/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,83 @@
import { HttpHandler } from 'msw'
import { HttpHandler, HttpResponse } from 'msw'
import { error, get, getParams } from '../tools'
import { articles } from '@/__mocks__/data'

const inboxHandlers: HttpHandler[] = []
const defaultPagination = {
size: 0, // 읽어온 데이터 갯수
number: 0, // size 기준 현재 페이지 (0부터 시작)
sort: { empty: true, sorted: true, unsorted: true },
numberOfElements: 0,
pageable: {
offset: 0,
sort: { empty: true, sorted: true, unsorted: true },
pageSize: 0,
paged: true,
pageNumber: 0,
unpaged: true,
},
first: true, // 첫 번째 페이지
last: true, // 마지막 페이지
empty: true,
}

type DefaultPagination = typeof defaultPagination
type Article = (typeof articles)[number]

const sorted: { [key: string]: (a: Article, b: Article) => number } = {
desc: (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
asc: (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(),
}

const inboxHandlers: HttpHandler[] = [
get('/v1/member/:memberId/articles', ({ request, params }) => {
const memberId = Number(params.memberId)
const searchParams = getParams(request.url)
const page = Number(searchParams.get('page') ?? 0)
const size = Number(searchParams.get('size') ?? 12)
const sort = searchParams.get('sort') ?? 'asc'
const query = searchParams.get('q')
const isInvalidRequest = [memberId, page, size].some(isNaN)

if (isInvalidRequest) {
return error('잘못된 요청입니다', 400)
}

const sortedArticles = [...articles].sort(sorted[sort])
const searchedArticles = !!query
? sortedArticles.filter((article) => article.title.includes(query))
: sortedArticles
const resultOfArticles = Array.from(
{ length: size },
(_, i) => searchedArticles[i],
).filter((e) => !!e)

const data: { articles: Article[] } & DefaultPagination = Object.assign(
defaultPagination,
{
articles: resultOfArticles,
size: resultOfArticles.length,
number: page,
first: page === 0,
last: page >= 2 || resultOfArticles.length < size,
},
)

return HttpResponse.json(data)
}),
get('/v1/articles/:articleId', ({ params }) => {
const articleId = Number(params.articleId)

if (isNaN(articleId)) {
return error('잘못된 요청입니다', 400)
}
if (articleId > articles.length) {
return error('요청하신 아티클을 찾을 수 없습니다', 404)
}

return HttpResponse.json({
article: articles.find((article) => article.id === articleId),
})
}),
]

export default inboxHandlers
33 changes: 2 additions & 31 deletions apps/service/__mocks__/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,9 @@
import {
HttpHandler,
type HttpResponseResolver,
HttpResponse,
http,
delay,
} from 'msw'
import { HttpHandler, HttpResponse } from 'msw'
import { get } from './tools'
import inboxHandlers from './inbox'
import mainHandlers from './main'
import mypageHandlers from './mypage'

const delayMS = 1200
const baseURL = `${process.env.API_URL}/api`

export const get = (path: string, resolver: HttpResponseResolver) =>
http.get(baseURL + path, async (info) => {
console.log(`[MSW]: ${info.request.method} ${info.request.url} ✅`)
await delay(delayMS)
return resolver(info)
})

export const post = (path: string, resolver: HttpResponseResolver) =>
http.post(baseURL + path, async (info) => {
const body = await info.request.json()
console.log(`[MSW]: ${info.request.method} ${info.request.url} ✅`)
console.log(` ᄂ ${JSON.stringify(body)}`)
await delay(delayMS)
return resolver(info)
})

export const error = (message: string, status: number) =>
HttpResponse.json({ message: `${message} (MSW)` }, { status })

export const getParams = (url: string) => new URL(url).searchParams

const handlers: HttpHandler[] = [
get('/test', () => {
return HttpResponse.json({ message: 'success!!!' })
Expand Down
25 changes: 25 additions & 0 deletions apps/service/__mocks__/handlers/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { type HttpResponseResolver, HttpResponse, http, delay } from 'msw'

const delayMS = 1200
const baseURL = `${process.env.API_URL}/api`

export const get = (path: string, resolver: HttpResponseResolver) =>
http.get(baseURL + path, async (info) => {
console.log(`[MSW]: ${info.request.method} ${info.request.url} ✅`)
await delay(delayMS)
return resolver(info)
})

export const post = (path: string, resolver: HttpResponseResolver) =>
http.post(baseURL + path, async (info) => {
const body = await info.request.json()
console.log(`[MSW]: ${info.request.method} ${info.request.url} ✅`)
console.log(` ᄂ ${JSON.stringify(body)}`)
await delay(delayMS)
return resolver(info)
})

export const error = (message: string, status: number) =>
HttpResponse.json({ message: `${message} (MSW)` }, { status })

export const getParams = (url: string) => new URL(url).searchParams
7 changes: 2 additions & 5 deletions apps/service/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import '@/public/fonts/fonts.css'
import './globals.css'
import initMSW from '@/__mocks__'

if (process.env.NODE_ENV !== 'production') {
initMSW()
}

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
Expand All @@ -22,7 +19,7 @@ export default function RootLayout({
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<body>{children}</body>
</html>
)
}