Easyfetch ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ตฌํ๊ธฐ
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ง๋ ๊ณ๊ธฐ
Next App Router๋ฅผ ์ด์ฉํด์ ์ฒ์ ํํ๋ก์ ํธ๋ฅผ ์งํํ๋๋ฐ ๊ธฐ์ ์คํ์ ๊ดํด์ ํ์๋ค๊ณผ ๋ ผ์๋ฅผ ํ๋ ์ค ์ด์ React๋ก๋ง ์์ ์ ํ์ ๋์ ๊ฐ์ด Axios๋ฅผ ํ์ฉํ๋ ค ํ๋๋ฐ Next App Router์์ ์ ์ฉํ๋ ค๊ณ ๋ณด๋ ๋ฌธ์ ์ ์ด ์์๋ค. ๋ฐ๋ก Next App Router๋ ์์ฒด์ ์ผ๋ก fetch๋ฅผ ํ์ฅ์์ ์ฐ๊ณ ์๋๋ฐ ๊ทธ ์์๋ ๋ค์๊ณผ ๊ฐ๋ค.
const data = await fetch('~~', {
next: {revalidate: 300}
})
์์ ๊ฐ์ Next ์บ์ฑ์๋ฆฌ๊ฐ ๊ถ๊ธํ๋ค๋ฉด ์ด์ ์ ์ด ๊ธ์ธ ์บ์ฑ์๋ฆฌ ๋ธ๋ก๊ทธ๋ฅผ ์ฐธ์กฐํ๋ฉด ์ข๋ค.
์ผ๋จ ๊ทธ๋๋ ํ๋ก์ ํธ ๊ตฌํ์ด ์ฐ์ ์ด์๊ธฐ์ ํ์ ๋ชจ๋ fetch๋ฅผ ์ฐ๊ธฐ๋ก ํ๋ค.
ํ๋ก์ ํธ๋ฅผ ๊ตฌํํ๋ฉด์ fetch๋ฅผ ์ฌ์ฉํ๋ค๋ณด๋ ๋ค์๊ณผ ๊ฐ์ ๋ถํธํจ์ด ์กด์ฌํ๋ค.
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ interceptor ๊ธฐ๋ฅ์ ๋ถ์ฌ
- res.json(), JSON.stringify ์ง๋ ฌํ ์ญ์ง๋ ฌํ๋ฅผ ๋งค๋ฒ ์ฌ์ฉํ๋ ๊ฒ
- 400๋ฒ ์ด์์ ์๋ฌ๋ res.ok๋ก ๋ถ๊ธฐ ์ฒ๋ฆฌ
- fetch์์๋ ์ฌ์ฉํ ์ ์๋ axios.post์ ๊ฐ์ API ๋ฉ์๋
interceptor ๊ธฐ๋ฅ์ ์ ์ธํ๊ณ ์์ ๊ฐ์ ๋ถํธํ ์ฌํญ๋ค์ ์ถฉ๋ถํ ์ฝ๋์์์ ํจ์๋ก ๋ก์ง์ ๋ถ๋ฆฌํด์ ํด๊ฒฐํ ์ ์๋ค. ์ผ๋จ์ ์์ ๊ฐ์ ๋ถํธํจ์ ๋ค๋ก ํ ์ฑ ํ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ๋ค ๊ฐ์ ํ์๋ค๋ผ๋ฆฌ ๋ฆฌํฉํ ๋ง ๊ธฐ๊ฐ์ ํ๋ฌ ๊ฐ๊ธฐ๋ก ํ๊ณ ๋ค์ ์ถ๊ฐ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ๋ก ํ๋ค.
ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋๋ ๋๋ ํ์๋ค์ ๋์์ ๋ฐ์ผ๋ฉด์ ๊ฐ์ฅ ๋ฉ์๊ณ ์ฝ๋ฉ์ ๋งค๋ ฅ์ ๋๋์ ์ด ๋ฑ ํ๊ฐ์ง ์๋ค. ๋ด ์ฝ๋๊ฐ ๊ฐ๊ฒฐํด์ง๋ ๊ฒ์ด๋ค. ๋๊ตฐ๊ฐ ๋๋ ๋ด ์์ ์ด ๋ณต์กํ ๋ก์ง์์ ๋ณด๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ๋ง๋ค์์ ๋ ๊ฐ์ฅ ์๊ทน์ ๋ฐ๊ณ ์ฝ๋ฉ์ ๋งค๋ ฅ์ ๋๋ผ๋ ๊ฒ ๊ฐ๋ค. ์ฆ ๋ถํธ์ ํด์ํ์๋ ๋ผ๊ณ ๋ ๋งํ ์ ์๊ฒ ๋ค.
๊ทธ๋์ ์ด๋ฒ ๋ฆฌํฉํ ๋ง ๊ธฐ๊ฐ ํ๋ฌ๋์ ์ฐ๋ฆฌํ์ ๋ถํธ์ ํด์ํ๊ณ ํ๋จ๊ณ ์ฑ์ฅํ๊ณ ์ถ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ๋ก ๊ฒฐ์ฌํ๋ค.
์๋ฃ์กฐ์ฌ
์ผ๋จ ๋ด๊ฐ ๋ญ ๋ง๋ค๊ณ ์ถ์์ง ๋ช ํํ ์ ํด์ผ ํ๋ค.
Next App Router์์ Axios๊ณผ ๊ฐ์ด ์ฌ์ฉํ๋ Fetch ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ง๋ค๊ธฐ
๋ช
ํํ ์ ํ์ผ๋ ์ด์ ๋น์ทํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋๊ฐ ๋ง๋ค์๋์ง ์ฐพ์๋ดค๋ค.
2๊ฐ์ง๊ฐ ์์๋๋ฐ ํ๋๋ ์ฐ๋ฆฌ๊ฐ ์๋ Axios๊ณ ๋๋จธ์ง ํ๋๋ ๋ฐ๋ก return-fetch์ด๋ค. ์์งํ return-fetch๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๋ฉด์ axios.get, axios.post์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ ์ธํ๋ฉด ๋ด๊ฐ ์๊ฐํ๋ ๋ฌธ์ ๋ค์ด ํด๊ฒฐ๋ ์ ์์๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋๋ ค๋ฉด ์ด์ ๋๊น์ง ๊ตฌํ์ ํด์ผํ๊ตฌ๋ ํ๋ฉด์ ๋๋จํ์ ๋ถ๋ค์ ์ ๋ง ๋ง๋ค๋๊ฒ์ ์๊ฐํ๋ฉฐ ์ด์ง ๋ฒฝ์ ๋๋ผ๊ธฐ๋ ํ๋ค. ํ์ง๋ง ์ด๋ฐ๋ฐ์๋ถํฐ ๋ง ์ฝํด์ง๋ฉด ๋์ ์ฑ์ฅ์ ๋๋ ๊ฒ ๊ฐ์๊ธฐ ๋๋ฌธ์ ๋ค์ ์ ์ ์ ์ฐจ๋ ธ๋ค!!! ์ด๋ฏธ ๋๊ฐ ๊ธฐ๋ฅ์ ๊ตฌํํด๋ ๊ฑฐ๊ธฐ์์ ๋์์ ๋ฐ์ ๋ ๋๋ง์ ๋ฐฉ์์ผ๋ก ์ฝ๋๋ฅผ ๊ตฌ์ฑํ๋ฉด ๊ทธ๊ฒ ๋ํ ๋์์ด ๋ ๊ฒ ๊ฐ์์ ๋ณธ๊ฒฉ์ ์ผ๋ก Axios, return-fetch, fetch, promise๋ฅผ ๊ณต๋ถํด ๋๊ฐ๋ค.
๊ณผ์
์ด๋ฒ ์ฝ๋๋ฅผ ์์ฑํ ๋๋ Class๋ฅผ ์ด์ฉํ๋ค.๊ทธ ์ด์ ๋ ์ฝ๋๋ฅผ ํจ์๋ฅผ ๊ตฌ์ฑํ์ ๋ ๋ณด๋ค ์ฝ๋์ ํ๋ฆ์ด ์ง๊ด์ ์ผ ๊ฒ ๊ฐ์์ ์ ํ์ ํ๋ค.
์ด๋ค ํจ์๋ฅผ ํธ์ถํ๋ฉด ํด๋น ๊ธฐ๋ฅ์ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํด์ฃผ๋ ํจ์๋ฅผ ๋ง๋๋ ค๊ณ ํ๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๋ฆ์ easyFetch์ด๋ฏ๋ก ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค.
const easy = easyFetch({
baseUrl: 'https://attraction/'
headers: {}
})
easy.get('api/v1/user')
easy.post('api/v1/user')
easyFetch๋ผ๋ ํจ์๋ default ์ค์ ์ ์ธ์๋ก ๋ฐ์ ์ธ์คํด์ค์๊ฒ ์ ๋ฌํด์ฃผ๋ ์ญํ ์ ํ๋ค.
axios.get๊ณผ ๊ฐ์ API๋ฉ์๋ ๊ตฌํ๊ธฐ
๋๋ ์์งํ ์ด ๋ถ๋ถ์์๋ ๋ณ๊ฑฐ ์์ ๊ฒ ๊ฐ์๋๋ฐ ๋๊ฒ ๊ด์ฐฎ์ ๊ตฌํ๋ฐฉ๋ฒ์ ๋ฐ๊ฒฌํ๊ฒ ๋๋ค. ์๋ ๊ธฐ๋ณธ ์๊ฐ์ ๋ค์๊ณผ ๊ฐ์๋ค.
class EasyFetch{
#baseUrl: string| URL | undefined;
#headers: HeadersInit | undefined;
constructor() {
this.#baseUrl = defaultConfig?.baseUrl;
this.#headers = defaultConfig?.headers;
}
get() {}
delete() {}
post() {}
patch() {}
put() {}
}
ํ์ง๋ง Axios ์คํ์์ค ์ฝ๋๋ฅผ ๋ถ์ํด๋ณธ ๊ฒฐ๊ณผ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ๋ฐ๊ฒฌํ ์ ์์๋ค.
์ผ๋จ ์ค๋ช ํ๊ธฐ์ ์ ์คํ์์ค์ฝ๋ ๋ถ์์ด ์ฒ์์ธ ๋์๊ฒ ๋๊ตฐ๊ฐ์ ์ฝ๋๋ฅผ ์ฝ๋๋ค๋ ๊ฒ์ด ์ฝ์ง ์์๋๋ฐ ์ด๋ฒ ๊ธฐํ๋ฅผ ํตํด ์คํ์์ค ์ฝ๋๋ค์ ๋ถ์ํ๋ฉด์ ์ฝ๋๋ฅผ ์ฝ๋ ๊ฒ์ด ์ฌ์์ก๊ณ ๋ฌด์๋ณด๋ค ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์ ํ๋ฉด์ ๋ฐฉ๊ตฌ์์์๋ง ์ฝ๋ฉ์ ํด๋ ์ฝ๋๋ฅผ ์๊ฐํ๋ ์์ผ๊ฐ ๋์ด์ก๋ค.
๊ฐ๋จํ ์ค๋ช ํ์๋ฉด axios.get ๊ณผ ๊ฐ์ ์ฝ๋๋ค์ ํด๋์ค์ ๋ฉ์๋๋ก ์ง์ ๋ช ์๋๋๊ฒ์ด ์๋ prototype์ ์ฌ์ฉํด์ ๋์ ์ผ๋ก ๋ฉ์๋ ์ฝ๋๋ค์ ๊ฐํธํ๊ฒ ์์ฑํ๊ณ ์๋ค.
ํ๋กํ ํ์ ์ JS์์๋ ๋ชจ๋ ๊ฐ์ฒด๋ค์ ๋ถ๋ชจ๊ฐ์ฒด์ ์ฐ๊ฒฐ๋์ด์ ธ์๋๋ฐ ๊ฐ์ฒด ์งํฅ์์์ ์์๊ณผ ๊ฐ์ด ๋ถ๋ชจ์ ๋ฉ์๋๋ฅผ ์์๋ ์ฌ์ฉํ ์ ์๋ค. ์ด๋์ ๋ถ๋ชจ๊ฐ์ฒด๋ฅผ ํ๋กํ ํ์ ์ด๋ผ๊ณ ํ๋ค. ๊ทธ๋ฆผ์ผ๋ก ๋ณด๋ฉด ๋ ์ฝ๋ค.
const easy = new EasyFetch()
easy๋ผ๋ ๊ฐ์ฒด์ console์ ์ฐ์ด๋ณด๋ฉด Protoype์ด๋ผ๋ ์ฌ๋กฏ์ ํ์ธํ ์ ์๋ค. ์ด๋ฌํ ์ฌ๋กฏ์ด ์์์ ๋ถ๋ชจ ํ๋กํ ํ์ ์ ์ฐ๊ฒฐ์์ผ์ฃผ๊ณ ์ด๊ฒ์ ํ๋กํ ํ์ ์ฒด์ด๋์ด๋ผ๊ณ ํ๋ค. ์ด๋ฌํ ๊ฐ๋ ์ ์์๋๊ณ Axios์ฝ๋๋ก ๋์๊ฐ๋ณด๋ฉด ๊ทธ๋ฆผ๊ณผ ๋๊ฐ์ ๋ฌธ๋ฒ์ ํ๋ ์ฐพ์๋ณผ ์ ์๋ค. ๋ฐ๋ก Easyfetch.prototype์ด๋ค. ์ด๊ฒ์ ํ์ฉํด์ ํ๋กํ ํ์ ์ ๋ฉ์๋๋ค์ ํ์ฅํด์ฃผ๋ฉด ๊ตณ์ด class์์ ๋ฉ์๋๋ค์ ์ ์๋ฅผ ์ํด๋ easy๋ผ๋ ๊ฐ์ฒด๋ ์ฌ์ฉํ ์ ์๋ค. ์๋ํ๋ฉด easy ๊ฐ์ฒด Prototype ์ฌ๋กฏ์ EasyFetch.Prototype์ด ๋ฐ์ธ๋ฉ์ด ๋์ด์๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์ ๊ธฐ๋ณธ์ด ์ค์ํ๋ค๋๊ฒ์ ๋ค์ ํ๋ฒ ๋ผ์ ๋ฆฌ๊ฒ ๋๋๋ค
์ ์ฉ
๋ฐ๋ก ์ ์ฉ์ ํด๋ดค๋๋ ๊ทธ๋ผ ๊ทธ๋ ์ง ์ํ์น ์์์ค ์์๋ค. ๋ฐ๋ก path,put,post,get,deleteํ๋กํผํฐ๋ EasyFetch ํ์ (์ ํํ ๋งํ๋ฉด EasyFetch ์ธ์คํด์ค ํ์ )์ ํ ๋นํ ์ ์๋ค๋ ๊ฒ์ด๋ค. ์ด์ฐ๋ณด๋ฉด ๋น์ฐํ๊ฒ์ด ts์์๋ class ๋ฌธ๋ฒ์ ์ฌ์ฉํ๋ฉด EasyFetch ํด๋์ค์ ์ธ์คํด์ค ํ์ ์ constructor์ ์ ์๋ ํ๋กํผํฐ์ static์ด ์๋ class ๋ฉ์๋๋ค๋ก ์ด๋ฃจ์ด์ง ์ธ๋ฑ์ค ํ์ ์ด๋ค. ๊ทธ๋ฐ๋ฐ ํ์ฌ ๋๋ ์ง์ ์ ์ผ๋ก path,put,post,get,delete๋ฅผ ํด๋์ค ๋ด๋ถ์ ๋ฉ์๋๋ก ์ค์ ํ๊ณ ์์ง ์์์ ์์๊ฐ์ ํ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ์ด๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํ์
๋จ์ธ
๊ณผ ์ธ๋ฑ์ค ์๊ทธ๋์ฒ
๋ฅผ ์ฌ์ฉํด์ ํด๊ฒฐ์ ํ๋ค.
const METHOD_WITHOUT_BODY = ['get', 'delete'] as const;
const METHOD_WITH_BODY = ['patch', 'put', 'post'] as const;
type MethodWithBodyType = (typeof METHOD_WITH_BODY)[number];
type MethodWithoutBodyType = (typeof METHOD_WITHOUT_BODY)[number];
type MethodType = MethodWithBodyType | MethodWithoutBodyType;
type EasyFetchWithAPIMethodsType = EasyFetch & {
[K in MethodType]: ???;
};
์ผ๋จ ๊ฐ์ฅ ๋จผ์ ํํธ๋ฅผ ์ป์๊ฒ์ด EasyFetch๋ ์๋ฌ๊ตฌ๋ฌธ์๋ ๋์์๋ฏ์ด ์ธ๋ฑ์ค ํ์ ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค๋ ๊ฒ์ด๋ค. key์ value๋ก ์ด๋ฃจ์ด์ง ํ์ ์ด๋ ๊ทธ์ ๋์ผํ๊ฒ ์ธ๋ฑ์ค ํ์ ์ ์์ฑํ๊ณ ์ธํฐ์น์ ํ์ ์ผ๋ก EasyFetchWithAPIMethodsType๋ฅผ ์์ฑํ๋ค. ํ์ง๋ง ๋ฌผ์ํ ๋ถ๋ถ์์ ์๋นํ ๊ณ ๋ฏผ์ ํ๋ค. get,delete๊ฐ์ ๊ฒฝ์ฐ์๋ ์ธ์์ body๊ฐ ํ์์์ง๋ง patch,put,post๊ฐ์ ๊ฒฝ์ฐ์๋ body๊ฐ ์์ด์ผํ๋ ํจ์ ํ์ ์ ์ ์ํด์ผํ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ํด๊ฒฐ๋ฒ์ด extend ํค์๋๋ฅผ ํ์ฉ์ด์๋ค.
extend ํค์๋๋ ์ ๋ค๋ฆญ ํ์ ์ ํ, ํ์ ์์, ์กฐ๊ฑด๋ถ ํ์ ์ผ๋ก ํ์ฉํ ์ ์๋๋ฐ ๊ทธ์ค ์กฐ๊ฑด๋ถ ํ์ ์ ๊ธฐ๋ฅ์ extend ํค์๋๋ก ํ์ฉํ๋ค.
type MethodFunction<T> = T extends MethodWithBodyType
? <P>(
url: string | URL,
reqBody?: object,
reqConfig?: Omit<RequestInitWithNextConfig, 'method' | 'body'>
) => Promise<EasyFetchResponse<P>>
: <P>(
url: string | URL,
reqConfig?: Omit<RequestInitWithNextConfig, 'method'>
) => Promise<EasyFetchResponse<P>>;
type EasyFetchWithAPIMethodsType = EasyFetch & {
[K in MethodType]: MethodFunction<K>;
};
export { EasyFetchWithAPIMethodsType, MethodType, MethodWithoutBodyType };
EasyFetchWithAPIMethodsType์์ ์ธ๋ฑ์ค ํ์ ์ธ K๋ path,put,post,get,delete๋ค์ด๋ฏ๋ก ์ด๊ฒ์ MethodFunction์ด๋ผ๋ ํ์ ์ ์ ๋ค๋ฆญ์ผ๋ก ์ ๋ฌํด์ฃผ๊ณ extend๋ฅผ ํ์ฉํด ๋ถ๊ธฐ์ฒ๋ฆฌํด์ ํจ์ ํ์ ์ ์ ์ ํด์ฃผ๋ ๊ฒ์ผ๋ก ์์๊ฐ์ ์๋ฌ๋ฅผ ํด๊ฒฐํ๋ค.
์์ ํจ์ ํ์ ๋ค์ ๋ณด๋ฉด RequestInitWithNextConfig์์ method๋ฅผ omitํด์ฃผ๊ณ ์๋๋ฐ ์ด๊ฒ์ ์ด์ ๋ axios๋ฅผ ์ด๊ฒ์ ๊ฒ ๋ง์ ธ๋ณด๋ค๊ฐ axios.get()์ config์์ method๋ฅผ ๋ ์ ํํ ์ ์๋ ์ค๋ฅ(?)๋ฅผ ๋ฐ๊ฒฌํด์ ํด๋น์ฌํญ์ ํผ๋์ ์ค ์ ์์ ๊ฒ ๊ฐ์์ ๋ด๊ฐ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ ์ด๋ฌํ ํ์ ์ ์ ํํ ๋ช ์ํ๋ ค๊ณ Omit์ ์ฌ์ฉํ ๊ฒ ์ด๋ค.
// RequestInitWithNextConfig.type.ts
interface NextFetchRequestConfig {
revalidate?: number | false;
tags?: string[];
}
interface RequestInitWithNextConfig extends globalThis.RequestInit {
next?: NextFetchRequestConfig | undefined;
}
Interceptor ๊ตฌํ๊ธฐ
axios์ ์ธํฐ์ ํฐ๋ฅผ ๋ค์ฌ๋ค ๋ณด๋ฉด promise ๋ํ ์ดํด๋๋ฅผ ๋์ผ ์ ์๋ค. ์ธํฐ์ ํฐ์ ํต์ฌ์ ์ธ ๊ธฐ๋ฅ๋ง ์ฝ๋๋ก ๊ฐ์ ธ์ ๋ถ์ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
axios.interceptor.request.use(fulfilled1, rejected1)
axios.interceptor.request.use(fulfilled2, rejected2)
Axios์์ ์ธํฐ์ ํฐ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ค๋ณด๋ฉด fullfilled, rejectedํจ์๋ฅผ ์ฌ์ฉํดํ๋๋ฐ ๋ด๋ถ์ ์ผ๋ก๋ ์ด๋ฌํ ํจ์๋ค์ ๋ฐฐ์ด๋ก ๊ด๋ฆฌํ๊ณ ์๋ค. ๊ทธ๋์ interceptors request์ ๋ฐฐ์ด์ forEach๋ก ๋๋ฆฌ๊ณ ์๋๋ฐ ์ด๋ฐ request ๋ฐฐ์ด์ ๋ชจ์ต์ ๋ค์๊ณผ ๊ฐ์ด ์ ์ถํด๋ณผ ์ ์๋ค.
// ์ค๋ช
์ ์ํด ๊ฐ๋ตํ๊ฒ ์ถ๋ฆฐ ๊ฒ ์
๋๋ค. ์ค์ ๋ก๋ runWhen, synchronous ํ๋กํผํฐ๋ ์์ต๋๋ค.
const request = [{fullfiled: fullfiled1, rejected: rejected1}, {fulfilled: fulfilled2, rejected: rejected2}]
์์ ๊ฐ์ด ๋๋ต์ ์ธ ๋ชจ์ต์ ๋จธ๋ฆฟ์์ ๋ฃ์ด๋๊ณ ๋นจ๊ฐ ๋ค๋ชจ๋ฐ์ค์ ์ฝ๋๋ค์ ์ดํด๋ณด๋ฉด ์ดํดํ๋๋ฐ ๋์์ด ๋ ๊ฒ์ด๋ค. ๊ทธ๋ ๋ค๋ฉด requestInterceptorChain์ ๋ชจ์ต์ ์๋ ์ฝ๋์ ๊ฐ๋ค.
// unshift ์ด๋ฏ๋ก
const requestInterceptorChain = [fulfilled2, rejected2, fullfiled1, rejected1]
responseInterceptorChain๋ requestInterceptorChain์ ๋ก์ง์ด๋ ๋น์ทํ๊ณ ๋ค๋ฅธ์ ์ unshift ์๋ push๋ฅผ ํ๋ค๋ ์ ์ด๋ค.
๊ทธ ๋ค์ ๋ถ๋ถ์ ๋ณด๋ฉด ์๋์ ๊ฐ๋ค.
์ข์ธก์ requestInterceptor๊ฐ ๋น๋๊ธฐ ์ผ๋์ ์ฝ๋์ด๊ณ ์ฐ์ธก์ requestInterceptor ์ฝ๋๊ฐ ๋๊ธฐ์ผ๋์ด๋ค.
๋น๋๊ธฐ์ผ๋์ ์ฝ๋๋ฅผ ๋ณด๋ฉด chain์ด๋ผ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ์๊น ์์ฑํ request์ response์ ์ฒด์ธ ๋ฐฐ์ด๋ค์ ํ๊ตฐ๋ฐ๋ก ํฉ์ณ์ค ๋ค์์ while๋ฌธ์ ๋๋ฉด์ then๋ฉ์๋ ์์ ์ธ์๋ก ๋ฃ๊ณ ์๋ค. while๋ฌธ์ ๋ค ๋์์ ๋์ ์ฝ๋๋ฅผ ์์ํด๋ณด๋ฉด ์๋์ ๊ฐ๋ค.
// config๋ ์ฐ๋ฆฌ๊ฐ ์์ฒญ์ ํ ๋ ์ค์ ํ config๋ค
Promise.resolve(config)
.then(requestFulfilled2, requestRejected2)
.then(requestFulfilled1, requestRejected1)
.then(dispatchRequest.bind(this), undefined)
.then(responseFullFilled1, responseRejected1)
.then(responseFullFilled2, responseRejected2)
์ด๋์ dispatchRequest return๊ฐ์ fetch์ ์๋ต๊ฐ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ํธํ๋ค.
๋๊ธฐ ์ผ๋๋ request interceptor๋ฅผ while๋ฌธ์์ ๋๊ธฐ์ ์ผ๋ก ์คํ์ํจ ๋ค์ fetch๋ฅผ ํด์ฃผ๋ dispatchRequestํจ์์ ์ ๋ฌ ํด์ฃผ๋๊ฒ๋ง ์ฐจ์ด์ ์ด๊ณ ๋๋จธ์ง response interceptor๋ก์ง์ ์์ ์ค๋ช ํ ๋ฐ์ ๊ฐ๋ค.
์ ์ฉ
์์ ๊ฐ์ ๋ฐฉ๋ฒ์ ๋ณด๋ฉด์ ๊ธฐ๋ณธ์ด ์ค์ํ๋ค๋ ๊ฒ์ ๋ค์ ๋๊ผ๋ค. ํ์ง๋ง ์์ ๊ฐ์ด ๊ทธ๋๋ก ๋ก์ง์ TS์์ ์์ฑํ ๊ฒฝ์ฐ ์๋ฌ๊ฐ ๋ ๊ฒ ๊ฐ์ ๋ถ๋ถ์ ๋ฐ๋ก ์บ์นํ๋ค. ๋ค์ ์์์ฝ๋๋ฅผ ๋ง๋ค์๋ค.
let promiseChain = Promise.resolve(config) as Promise<RequestInit>;
promiseChain = promiseChain
.then((res) => {
return res;
})
.then(async (config) => {
const sample = await fetch('http://attraction', config);
const data = await sample.json() as { data: string };
return data;
})
.then(res => res);
์ฒซ๋ฒ์งธ then์ requestInterceptor์ด๊ณ ๋๋ฒ์งธ then์ requestInterceptor์ ์ํด์ ์ ์ฉ๋ config๋ฅผ fetchํํ
์ ๋ฌํด์ฃผ๊ณ responseInterceptor๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์๋ต๊ฐ์ ๋ฆฌํด์ ํด์ฃผ๋ ๋ก์ง์ด๊ณ ๋ง์ง๋ง then์ responseInterceptor์ fulfilled ํจ์์ด๋ค.
์์ ๊ฐ์ ์ฝ๋๋ ์๊น Axios๋ ๋๊ฐ์ ๋ก์ง์ธ๋ฐ ํ์
์๋ฌ๊ฐ ๋ ์ด์ ๋ promiseChain์ ์ด๊ธฐ ํ์
์ Promise์ด๋ค. ์ฆ, ๋ค์ ์ฌํ ๋น์ ํด๋ Promise์ ๋ฐํํด์ผํ๋๋ฐ while๋ฌธ์ ์ํด์ ํ๋ฒ์ request interceptor์ response interceptor๋ฅผ ์ฒ๋ฆฌํ๋ ค๊ณ ํ๋ฉด ํ์
๋ถ์ผ์น๊ฐ ๋ฐ์ํด ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Request Interceptor์ Response Interceptor๋ฅผ ๋ถ๋ฆฌํด์ ์ฒ๋ฆฌํ๋ค.
// ์ค์ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋ ์ผ๋ถ ๋ฐ์ท
interface InterceptorCallbackType<T> {
(
onFulfilled?: (arg: T) => T | Promise<T>,
onRejected?: (err: any) => any
): void;
}
type InterceptorArgs<T> = Parameters<InterceptorCallbackType<T>>;
class Interceptor {
flushInterceptors<T>(
initVal: Promise<T>,
interceptors: InterceptorArgs<T>[]
) {
let promiseInit = initVal;
for (let i = 0; i < interceptors.length; i++) {
promiseInit = promiseInit.then(...interceptors[i]);
}
return promiseInit;
}
flushRequestInterceptors(initVal: Promise<EasyFetchRequestType>) {
return this.flushInterceptors(initVal, this.requestCbArr);
}
flushResponseInterceptors(initVal: Promise<EasyFetchResponse<any>>) {
return this.flushInterceptors(initVal, this.responseCbArr);
}
}
export default Interceptor;
flushRequestInterceptors์ flushResponseInterceptors์ ์ธ์์ let์ ์ด๊ธฐ๊ฐ์ ์ ๋ฌํจ์ผ๋ก์จ flushInterceptors ๋ฉ์๋๋ฅผ ๋ณด๋ฉด prmiseInit์ผ๋ก ํ ๋นํ๊ณ ์๋ค. ์ด๋ for๋ฌธ์ ๋๋ฉด์ promiseInit์ then์ ํ ๋นํด๋ interceptors์ ํ์ ์ธ InterceptorArgs์ ์ ๋ค๋ฆญ์ผ๋ก ์ด๊ธฐ๊ฐ์ ํ์ ์ ์ ๋ฌํด์ฃผ๋ฉด์ ํ์ ์ ์ผ์น์ํค๊ณ ์ค๋ฅ๋ฅผ ํด๊ฒฐํ ์ ์์๋ค.
ํ๋ก์ฐ ์ฐจํธ
๋ด๊ฐ ๋ง๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํ์ ์ธ ๊ธฐ๋ฅ์ ๋ธ๋ก๊ทธ๋ฅผ ์ ๋ฆฌํ๋๋ฐ ๋๋จธ์ง ๊ธฐ๋ฅ๋ค๋ ์ด๋ป๊ฒ ๊ตฌํ๋์๋์ง ๊ถ๊ธํ๋ค๋ฉด ๊นํ๋ธ์์ ํ์ธํ ์ ์๋ค.
๊ตฌํํ๋ฉด์ ๋๋์
์ด๋ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ตฌํํ๋ฉด์ ์คํ ์์ค ์ฝ๋๋ฅผ ์ง์ ๋ถ์ํ๋ฉด์ ์ ๋ณด๋ค ์ฝ๋๋ฅผ ํด์ํ๋ ๋ฅ๋ ฅ์ด ํฅ์๋์๊ณ , Promise์ then์ ํ์ฉ๋ฒ๋ ๋ช ํํ ์ดํดํ๊ฒ ๋์๋ค. JavaScript ์ฝ๋๋ฅผ TypeScript๋ก ๋ณํํ๋ ๊ณผ์ ์์ ์ ๋ค๋ฆญ ํ์ , ์ ํธ๋ฆฌํฐ ํ์ , extends ํค์๋ ๋ฑ์ ํ์ฉํ๋ฉด์, TypeScript์ ๋ํ ์๋ จ๋ ๋ํ ํ์ธต ๋ ์ฑ์ฅํ ๋๋์ด์๋ค.
ํ ์คํธ ์ฝ๋๋ฅผ ํ์ฉํด ์ฝ๋์ ์์ ์ฑ์ ๋ํ๊ณ Rollup์ ์ด์ฉํด esm, cjs์ ์ฐจ์ด๋ฅผ ์ดํดํ๊ณ ๋ ๋ชจ๋์ ๋์ํ๋๋ก Rollup์ ์ค์ ํ๋ฉด์ ์ ์ฐจ ํ๋ก ํธ ๊ณต๋ถ ์์ญ์ ๋ํ๊ฐ๊ฒ๋๋ ๊ฒฝํ์ ํ๋ค. ๋กค์ ์ ๊ดํ ์ค์ ์ ๋ค์ ๋ธ๋ก๊ทธ๋ฅผ ๋ณด๋ฉด ๋ ๊ฒ ๊ฐ๋ค.