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

🎉 Nabi 77 fetch 관련 설정 #23

Merged
merged 5 commits into from
Nov 1, 2023
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NEXT_PUBLIC_API_ADDRESS=http://localhost:3000
NEXT_PUBLIC_API_MOCKING=enabled # or disabled
CHROMATIC_TOKEN=your-token
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"@tanstack/eslint-plugin-query": "^5.0.0",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/node": "^20",
"@types/react": "^18",
"@types/react": "^18.2.33",
"@types/react-dom": "^18",
"@types/tailwindcss": "^3.1.0",
"chromatic": "^7.5.4",
Expand All @@ -66,7 +66,7 @@
"prettier": "^3.0.3",
"storybook": "7.5.0",
"storybook-dark-mode": "^3.0.1",
"typescript": "^5",
"typescript": "^5.2.2",
"vitest": "^0.34.6"
},
"lint-staged": {
Expand Down
1,595 changes: 866 additions & 729 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions src/app/(root)/(routes)/(home)/components/TestBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client'

import React, { useEffect } from 'react'
// import { useApiClient } from '@/contexts/FetchContext'
import { getTest } from '@/services/test/test'

// async function getTestValue() {
// const res = await getTest()
// const data = await res.json()
// return data
// }

export default function TestBlock() {
// const data = await getTestValue()
// console.log(data.message)

useEffect(() => {
async function fetchData() {
const data = await getTest()
console.log(await data.json())
}
fetchData()
}, [])

return <div>{'index '}</div>
}
2 changes: 2 additions & 0 deletions src/app/(root)/(routes)/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { DarkModeButton } from '@/components/ui/DarkModeButton'
import TestBlock from './components/TestBlock'

export default function HomePage() {
return (
<main className="flex flex-col items-center justify-between min-h-screen p-24 text-4xl font-bold text-text-color bg-background-color">
hi
<TestBlock />
<DarkModeButton />
</main>
)
Expand Down
35 changes: 17 additions & 18 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@ import { Suspense } from 'react'
import type { Metadata } from 'next'
import Head from 'next/head'
import Header from '@/components/domain/Header'
import { Environment } from '@/config/environment'
import { FetchProvider } from '@/contexts/FetchContext'
import MSWWrapper from '@/contexts/MSWWrapper'
import TanstackQueryContext from '@/contexts/TanstackQueryContext'
import ThemeProviderContext from '@/contexts/ThemeProviderContext'
import { initMockApi } from '@/lib/msw/initMockApi'
import '@/styles/globals.css'

export const metadata: Metadata = {
title: '나비장터',
description: '물물교환 플랫폼 나비장터입니다.',
}

if (Environment.apiMocking() === 'enabled') {
console.log('Mocking enabled')
initMockApi()
}

export default function RootLayout({
children,
authModal,
Expand All @@ -31,17 +26,21 @@ export default function RootLayout({
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</Head>
<body>
<TanstackQueryContext>
<ThemeProviderContext>
<Suspense>
<div className="centered-content">
<Header isLogin={false} />
{children}
{authModal}
</div>
</Suspense>
</ThemeProviderContext>
</TanstackQueryContext>
<FetchProvider>
<TanstackQueryContext>
<MSWWrapper>
<ThemeProviderContext>
<Suspense>
<div className="centered-content">
<Header isLogin={false} />
{children}
{authModal}
</div>
</Suspense>
</ThemeProviderContext>
</MSWWrapper>
</TanstackQueryContext>
</FetchProvider>
</body>
</html>
)
Expand Down
6 changes: 6 additions & 0 deletions src/config/apiEndPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const ApiEndPoint = {
login: () => '/login',
test: () => '/test',
} as const

export default ApiEndPoint
3 changes: 1 addition & 2 deletions src/config/environment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { assertValue } from '@/utils/assertValue'

export const Environment = {
apiAddress: () => process.env.NEXT_PUBLIC_API_ADDRESS,
apiMocking: () =>
process.env.NEXT_PUBLIC_API_MOCKING === 'enabled' ? 'enabled' : 'disabled',
}
26 changes: 26 additions & 0 deletions src/contexts/FetchContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client'

import React, { ReactNode, createContext, useContext, useMemo } from 'react'
import FetchAPI from '@/lib/fetchAPI'

const FetchContext = createContext<{ api: FetchAPI }>({ api: {} as FetchAPI })

export const FetchProvider = ({ children }: { children: ReactNode }) => {
const api = FetchAPI.getInstance()

const contextValue = useMemo(() => ({ api }), [api])

return (
<FetchContext.Provider value={contextValue}>
{children}
</FetchContext.Provider>
)
}

export const useApiClient = () => {
const context = useContext(FetchContext)
if (!context) {
throw new Error('useApiClient must be used within a FetchProvider')
}
return context
}
34 changes: 34 additions & 0 deletions src/contexts/MSWWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client'

import { useState, type PropsWithChildren, useEffect } from 'react'
import { Environment } from '@/config/environment'

const isMockingMode = Environment.apiMocking() === 'enabled'

const MSWComponent = ({ children }: PropsWithChildren) => {
const [mswReady, setMSWReady] = useState(() => !isMockingMode)

useEffect(() => {
const init = async () => {
if (isMockingMode) {
const initMocks = await import('../lib/msw/initMockApi').then(
(res) => res.initMockApi,
)
await initMocks()
setMSWReady(true)
}
}

if (!mswReady) {
init()
}
}, [mswReady])

if (!mswReady) {
return null
}

return <>{children}</>
}

export default MSWComponent
88 changes: 88 additions & 0 deletions src/lib/fetchAPI.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Environment } from '@/config/environment'

class FetchAPI {
private baseURL: string
private headers: { [key: string]: string }

private static instance: FetchAPI

private constructor() {
this.baseURL = Environment.apiAddress() ?? ''
this.headers = {
'Content-Type': 'application/json',
}
}

public static getInstance(): FetchAPI {
if (!FetchAPI.instance) {
FetchAPI.instance = new FetchAPI()
}
return FetchAPI.instance
}

public setBaseURL(url: string): void {
this.baseURL = url
}

public setDefaultHeader(key: string, value: string): void {
this.headers[key] = value
}

public async get(
endpoint: string,
nextInit: RequestInit = {},
customHeaders: { [key: string]: string } = {},
): Promise<any> {
const response = await fetch(`${this.baseURL}${endpoint}`, {
method: 'GET',
headers: { ...this.headers, ...customHeaders },
...nextInit,
})
return response
}

public async post(
endpoint: string,
body: any,
nextInit: RequestInit = {},
customHeaders: { [key: string]: string } = {},
): Promise<any> {
const response = await fetch(`${this.baseURL}${endpoint}`, {
method: 'POST',
headers: { ...this.headers, ...customHeaders },
body: JSON.stringify(body),
...nextInit,
})
return response
}

public async put(
endpoint: string,
body: any,
nextInit: RequestInit = {},
customHeaders: { [key: string]: string } = {},
): Promise<any> {
const response = await fetch(`${this.baseURL}${endpoint}`, {
method: 'PUT',
headers: { ...this.headers, ...customHeaders },
body: JSON.stringify(body),
...nextInit,
})
return response
}

public async delete(
endpoint: string,
nextInit: RequestInit = {},
customHeaders: { [key: string]: string } = {},
): Promise<any> {
const response = await fetch(`${this.baseURL}${endpoint}`, {
method: 'DELETE',
headers: { ...this.headers, ...customHeaders },
...nextInit,
})
return response
}
}

export default FetchAPI
5 changes: 4 additions & 1 deletion src/lib/msw/mocks/testHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { rest } from 'msw'
import { Environment } from '@/config/environment'

const baseUrl = Environment.apiAddress()

export const testHandlers = [
rest.get('/test', async (_req, res, ctx) => {
rest.get(`${baseUrl}/test`, async (_req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
Expand Down
15 changes: 15 additions & 0 deletions src/services/auth/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import ApiEndPoint from '@/config/apiEndPoint'
import { Environment } from '@/config/environment'

const postLogin = async () => {
const response = await fetch(Environment.apiAddress() + ApiEndPoint.login(), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
})
const data = await response.json()
return data
}

export { postLogin }
16 changes: 16 additions & 0 deletions src/services/test/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ApiEndPoint from '@/config/apiEndPoint'
import FetchAPI from '@/lib/fetchAPI'

const getTest = async () => {
const api = FetchAPI.getInstance()
const response = await api.get(
ApiEndPoint.test(),
{ next: { revalidate: 10 } },
{
'Content-Type': 'application/json',
},
)
return response
}

export { getTest }