-
Nextjs ์์ fetch ์ปค์คํ ํ์ฌ ์ฌ์ฉํ๊ธฐ(feat. ์ด์ค ํ์ดํ ํจ์)์นดํ ๊ณ ๋ฆฌ ์์ 2024. 2. 4. 19:50
์ง๋๋ฒ์ https://minju-k.tistory.com/15 ์์ Next.js ๋ ๋ค์ํ ์บ์ฑ ์ต์ ์ ๊ฐ์ง๊ณ ์๋ fetch๋ฅผ ์ด์ฉํ๊ธธ ๊ถ์ฅํ๋ ๊ฒ์ ์์๋ณด์๋ค.
์ด ๊ธ์์๋ ๊ทธ๋์ ์ด๋ป๊ฒ ํ๋ก์ ํธ์ fetch๋ฅผ ์ฌ์ฉํ๋ฉด์๋ axios๋ฅผ ์ธ ๋์ฒ๋ผ์ ์ด์ ์ ๋๋ฆฌ๋๋ก ํ๋์ง ๊ธ์ ์จ๋ณด๊ธฐ๋ก ํ๋ค. ๋ง๋ค ๋์๋ ์๋์ ๋ ํผ๋ฐ์ค๋ก ๊ฑธ์ด๋์ return-fetch ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐธ๊ณ ํ์๋ค.
axios๋ fetch์ ๋ฐํด ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ ๊ฐ์ง๊ณ ์๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณ๋ serialize, deserialize ํ ํ์๊ฐ ์์ด ๊ฐ์ฒด ํํ๋ก ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ค.
- baseUrl์ ์ค์ ํ ์ ์๋ค.
- default header๋ฅผ ์ค์ ํ ์ ์๋ค.
- request, response interceptor๋ฅผ ํ์ฉํ ์ ์๋ค.๋ด๊ฐ ๋งก์ ์๋ก์ด ํ๋ก์ ํธ์์๋ baseUrl์ ์ด 4๊ฐ๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์,
axios๋ฅผ ์ฌ์ฉํ ๋ ๊ธฐ๋ณธ ์ต์ ๋ค์ ์ง์ ํ์ฌ ์ฌ๋ฌ ๊ฐ์ axiosClient๋ฅผ ๋ง๋ค๋ฏ์ด, fetch๋ ๊ธฐ๋ณธ ์ต์ ์ ๋ค๋ฅด๊ฒ ์ค์ ํ ํธ์ถ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ํ์ฉํ๊ณ ์ถ์๋ค.
๋ฐ๋ผ์ ์ฐ์ DefaultOption์ ์ง์ ํ ์ ์๋๋ก ๋ง๋ค์๋ค. ๊ธฐ๋ณธ์ ์ธ ํํ๋ axiosClient๋ฅผ ๋ง๋ค๋์ ๊ฐ๋ค.
import Cookies from 'js-cookie'; interface IRequestOptions { method?: string; headers?: Headers; mode?: string; cache?: string; body?: Record<string, any> | string; } interface IInterceptors { request: (args: IRequestOptions) => void; } interface IDefaultOptions { baseURL: string | undefined; headers: { 'Content-type': string; 'X-SNAPS-CHANNEL': string; }; mode: string; interceptors: IInterceptors; } const defaultOptions: IDefaultOptions = { baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { 'Content-type': 'application/json; charset=UTF-8', 'X-SNAPS-CHANNEL': 'DA_WEB', }, mode: 'cors', interceptors: { request: (args: IRequestOptions) => { const token = Cookies.get('OH_PRINT_ME_GROUND_USER_TOKEN'); if (!token) return; if (token) { if (!args.headers) { args.headers = new Headers(); } args.headers.set('X-SNAPS-TOKEN', token); } }, }, };
๊ทธ๋ฆฌ๊ณ ๋ DefaultOption์ ๊ธฐ๋ณธ์ผ๋ก ๋ฐ๊ณ , url๊ณผ option์ ๋ฐ์ API๋ฅผ ํธ์ถํ ํ ์๋ต์ ๋ฆฌํดํด์ฃผ๋ fetcherํจ์๋ฅผ ๋ง๋ค์๋ค.
defaultOption๋ง ๋ค๋ฅด๊ฒ ๋ง๋ค์ด์ฃผ๋ฉด, baseUrl์ด๋ ๊ธฐ๋ณธ์ ์ธ interceptor ์ฌ์ฉ ์ต์ ๋ค์ด ๋ฌ๋ผ์ง๋ค ํด๋ ๊ณ์ ์ด fetcher ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
์ด ๋ ์ด์ค ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด๋ ๊ฒ ํ๋ฉด fetcher๋ฅผ ์ฌ์ฉํ ๋๋ง๋ค defaultOption์ ์ธ์๋ก ๋ณด๋ด์ฃผ์ง ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฅผ ํ์ฉํ ๋ด๋ถ ํจ์๋ฅผ ๋ฐํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
const fetcher = (defaultOptions: IDefaultOptions) => async (url: string, options: IRequestOptions = {}) => { const requestOptions: IRequestOptions = { method: options.method || 'GET', headers: new Headers(defaultOptions.headers), mode: defaultOptions.mode, ...options, }; defaultOptions.interceptors.request(requestOptions); const fullURL = defaultOptions.baseURL + url; if (requestOptions.method === 'POST' && requestOptions.body !== undefined) { requestOptions.body = JSON.stringify(requestOptions.body); } try { const response = await fetch(fullURL, requestOptions); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error(error); throw error; } };
์ฌ๋ฌ ๊ฐ์ ํ์ดํ ํจ์
์ฌ๋ฌ ๊ฐ์ ํ์ดํ ํจ์๋ฅผ ๋ ํ์คํ๊ฒ ์ดํดํ๊ณ ๊ฐ์.
const add = a => b => { return a + b; } const add = a + b => { return a + b; }
์ด ๋ ํจ์์ ์ฐจ์ด์ ์, ์ฒซ ๋ฒ ์งธ๋ a๋ฅผ ์ธ์๋ก ๋ฐ๋ ์ธ๋ถํจ์๊ฐ b๋ฅผ ์ธ์๋ก ๋ฐ๋ ๋ด๋ถ ํจ์์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฆฌํดํ๋ ํํ์ด๊ณ , ๋ ๋ฒ์งธ๋ a,b๋ฅผ ๋์์ ์ธ์๋ก ๋ฐ์์ ๊ทธ ํฉ๊ณ๋ฅผ ๋ฆฌํดํ๋ ํํ๋ผ๋ ๊ฒ์ด๋ค.
์ฒซ ๋ฒ์งธ ํจ์๋ฅผ ์ฐ๋ฆฌ์๊ฒ ์ต์ํ ํํ๋ก ํ์ด์ ์์ฑํด ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
function(a){ function(b){ return a + b; } } function(a,b){ return a + b; }
์ด๋ ๊ฒ ์ด์ค ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ ๋์ ์ฅ์ ์ a๋ฅผ ๋งค๋ฒ ์ธ์๋ก ๋๊ฒจ์ฃผ์ง ์์๋, ์ด a๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ํ์ฉํ ํจ์๋ฅผ ๋ง๋ค ์ ์๋ค๋ ๊ฒ์ด๋ค. ์๋๋ ์ด๋ฅผ ํ์ฉํ์ฌ ์ด๋ค ๊ฐ์ ๋ฃ๋๋ผ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ์๋ก ๋๊ฒจ๋์ 2๋ฅผ ๋ํด์ฃผ๋ ํจ์์ด๋ค.
const add = a => b => a + b; const add2 = add(2); console.log(add2) // b => 2 + b; add2(3); // 5 add2(4); // 6
๋ฐ๋ผ์ fetcher๋ defaultOption์ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฉํ ๋ด๋ถ ํจ์๋ฅผ ๋ฐํํ๋๋ก ๋ง๋ ๊ฒ์ด๋ค.
์ต์ข ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ customFetcher ํจ์๋ฅผ ๋ง๋ค๊ณ , ํธ์ถ ์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋๋ก ํ๋ค.export const customFetcher = fetcher(defaultOptions); // ์์ add2์ ๊ฐ์ ํจ์๋ฅผ ๋ง๋ ๊ฒ์ด๋ค!! // method ์ต์ ์ ๋ณ๋๋ก ๋๊ธฐ์ง ์์์ ๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก GET์ ์ฌ์ฉํ๋๋ก ๋์ด ์์ผ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ณตํต SWRConfig๋ฅผ ๋ง๋ค์ด์ฃผ์๋ค. export default function SWRConfigContext({ children }: Props) { return ( <SWRConfig value={{ refreshInterval: 3000, fetcher: (url: string) => customFetcher(url), }} > {children} </SWRConfig> ); } // POST๋ cache option ์ฌ์ฉ์ const data = await customFetcher(url, { method: 'POST', body: { loginId, password }, method : 'no-store' });
์ด์ axios์์ axiosClient๋ฅผ ํธ์ถํด์ ์ฌ์ฉํ๋ฏ์ด customFetcher๋ฅผ ์ฌ์ฉํด์๋ axios์ฒ๋ผ ๊ฐ๋จํ APIํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ ์ ์๊ฒ ๋์๋ค.
์ด์ค ํจ์๋ฅผ ์ฌ์ฉํด์ ์ฌ๋ฌ๋ฒ ์ฌํ์ฉ ํ ์ ์๋ ํจ์๋ฅผ ๋ง๋๋ ๊ณผ์ ์ด ์ฐธ ์ฌ๋ฏธ์์๊ณ , ์์ผ๋ก๋ ์ฌ๋ฌ๊ฐ์ ๊ธฐ๋ณธ ํจ์๋ฅผ ๋ง๋ค์ด์ผ ํ ๋์๋ ์ด๋ฅผ ํ์ฉํ๋ฉด ๋ ๊ฒ ๊ฐ๋ค.