๊ฐ์๋ฅผ ๋ค์ผ๋ฉด์ ์ฒ์ ์ ํ๊ฑฐ๋ ์ดํด๊ฐ ๋์ง ์๋ ๋ถ๋ถ๋ค์ด ์์๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ ๊ณต๋ถ๊ฐ ํ์ํ๋ค. ์๋์ ๋ฐ๋ก ๊ณต๋ถํ ๊ฒ๋ค์ ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํด๋ณด์๋ค.
URLSearchParams ์ธ์คํด์ค๋ new URLSearchParams()
์ ๋ฐํ๊ฐ์ผ๋ก URLSearchParams ํธ์ถ์ ์ธ์๋ก ์ ๋ฌํ key/value ์์ ๋ฐ๋ณตํ์ฌ ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ์ฝ๊ฒ ๋ค๋ฃฐ ์ ์๋๋ก ๋๋๋ค.
interface URLSearchParams {
/** Appends a specified key/value pair as a new search parameter. */
append(name: string, value: string): void;
/** Deletes the given search parameter, and its associated value, from the list of all search parameters. */
delete(name: string): void;
/** Returns the first value associated to the given search parameter. */
get(name: string): string | null;
/** Returns all the values association with a given search parameter. */
getAll(name: string): string[];
/** Returns a Boolean indicating if such a search parameter exists. */
has(name: string): boolean;
/** Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. */
set(name: string, value: string): void;
sort(): void;
/** Returns a string containing a query string suitable for use in a URL. Does not include the question mark. */
toString(): string;
forEach(
callbackfn: (value: string, key: string, parent: URLSearchParams) => void,
thisArg?: any
): void;
}
- Now You See Me: How To Defer, Lazy-Load And Act With IntersectionObserver
- IntersectionObserver's coming into view - web.dev
new IntersectionObserver((entries: IntersectionObserverEntry) => { ... }): IntersectionObserver;
- ๋ธ๋ผ์ฐ์ ๊ฐ ์ ๊ณตํ๋ Intersection Observer API๋ ์์ ์์ ๋๋ ์ต์์ ๋ฌธ์์ viewport์ target ์์์ ๊ต์ฐจ์์ ๋ณ๊ฒฝ ์ฌํญ์ ๋น๋๊ธฐ์์ผ๋ก ๊ด์ฐฐํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
- ๋น๋๊ธฐ ํน์ฑ ๋๋ฌธ์ ์ฌ๋ฌ ๊ด์ฐฐ ํญ๋ชฉ์ด ๋์์ ์ฝ๋ฐฑ ํจ์์ ์ธ์๋ก ์ ๋ฌ๋ ์ ์์ผ๋ฉฐ, ๋๋ฌธ์ ์ฝ๋ฐฑ ํจ์์ ์ธ์๋ ๋จ์ผ ํญ๋ชฉ์ด ์๋ Array์ด๋ค.
IntersectionObserverEntry
์ ์ธ์คํด์ค๋IntersectionObserver
์ ์ธ์์ธ entries๋ก ์ ๋ฌ๋๋ฉฐ, target ์์์ ์ขํ์ ๊ต์ฐจํ๋ ๊ฒฝ๊ณ ๋ฑ ์ฌ๋ฌ ์์ฑ์ ํฌํจํ๊ณ ์๋ค.- ์ฃผ์ํ ์
- Intersection Observer API ์์ฒด๋ ๋น๋๊ธฐ๋ก ๋์ํ๋,
InterSectionObserver
์ ์ฝ๋ฐฑ ํจ์ ๋ด์์ ์คํ๋๋ ์ฝ๋๋ ๋น๋๊ธฐ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ๋ด๋ถ ์ฝ๋๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ค๋ ๋๋ฅผ blocking ์ํฌ ์ ์๋ค. InterSectionObserver
๋ฅผ ์ด๋ฏธ์ง ๋ฑ ์ปจํ ์ธ ๋ฅผ ์ง์ฐ ๋ก๋ํ ๋ ์ฌ์ฉํ ๊ฒฝ์ฐ ์์ฐ์ด ๋ก๋๋ ํ์unobserve()
๋ฉ์๋๋ฅผ ์คํํด์ผ ํ๋ค.- target ์์๊ฐ DOM tree์ ์กด์ฌํด์ผ๋ง ๊ต์ฐจ๋ฅผ ๊ฐ์งํ ์ ์๋ค. ์ฆ, target ์์๊ฐ ๋ ์ด์์์ ์ํฅ์ ๋ฏธ์ณ์ผ ํ๋ค.
display: none
์ธ target ์์๋ ๊ต์ฐจ๋ฅผ ๊ฐ์งํ ์ ์๋ค.opacity: 0
๋๋visibility: hidden
๋ก ์ธํด target ์์๊ธฐ ๋ณด์ด์ง ์์๋ ๋ ์ด์์์ ๊ฐ์ง๋ฉด(width
์height
๊ฐ ์กด์ฌํ๋ ๋ฑ) target ์์๋ฅผ ๊ฐ์งํ ์ ์๋ค.position: absolute; width: 0; height: 0
์ธ target ์์๋ ๊ฐ์งํ ์ ์์ผ๋, containing block์ ์์ญ ๋ฐ์ ์์นํ๊ณoverflow: hidden
์ ์ํด ๊ฐ๋ ค์ง target ์์๋ ๊ฐ์งํ ์ ์๋ค.
- Intersection Observer API ์์ฒด๋ ๋น๋๊ธฐ๋ก ๋์ํ๋,
๊ณผ๊ฑฐ์ target ์์์์ ๊ต์ฐจ๋ฅผ ๊ฐ์งํ๊ธฐ ์ํด์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ(scroll
)์ Element.getBoundingClientRect()
๋ก ๊ตฌํํ๋ค. ํ์ฌ๋ ์ด๋ฌํ ์ ํต์ ์ธ ๋ฐฉ๋ฒ ๋์ Intersection Observer API๋ฅผ ์ฌ์ฉํ๋๋ฐ, ๊ทธ ์ด์ ์ ๋ํด ์์๋ณด์.
getBoundingClientRect()
๋ฉ์๋๋ ํธ์ถ๋ ๋๋ง๋ค ๋ธ๋ผ์ฐ์ ๊ฐ ์ ์ฒด ํ์ด์ง๋ฅผ re-layout ํ๋๋ก ๊ฐ์ ํ์ฌ ์๋๋ฅผ ๋๋๊ฒ ํ๋ ๋ฐ๋ฉด,IntersectionObserverEntry
๋ ๋ฏธ๋ฆฌ ์ ์๋๊ณ ๋ฏธ๋ฆฌ ๊ณ์ฐ๋ ์์ฑ ์งํฉ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ํจ์จ์ ์ด๋ค.- ์ ํต์ ์ธ ๋ฐฉ๋ฒ์ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ค๋ ๋์์ ๋๊ธฐ์ ์ผ๋ก ์คํ๋๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ผ๊ธฐํ๋ ๋ฐ๋ฉด, Intersection Observer API๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ ๋์๋ ๋ณ๊ฐ์ ์ค๋ ๋(browser์ ์ค๋ ๋ ์ค ํ๋)์์ ๋น๋๊ธฐ์ ์ผ๋ก ์คํ๋์ด ์ต์ ํ๊ฐ ๊ฐ๋ฅํ๋ค.
- ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋น๋๊ธฐ๋ก ํธ์ถ๋์ง๋ง, ๊ฒฐ๋ก ์ ์ผ๋ก๋ ํ ์คํฌ ํ์ ์์ธ ๋น๋๊ธฐ ํจ์๋ค์ด ์์๋๋ก ํธ์ถ๋๋ฏ๋ก ๋ฉ์ธ ์ค๋ ๋(์๋ฐ์คํฌ๋ฆฝํธ ์ค๋ ๋)์ ์๋ต์ฑ์ ์ํฅ์ ๋ฏธ์น๋ค.
- ์คํฌ๋กค์ ์ด๋ฏธ์ง ๋๋ ๊ธฐํ ์ปจํ ์ธ ์ง์ฐ ๋ก๋
- ๋ฌดํ ์คํฌ๋กค
- ๊ด๊ณ ์์ต์ ๊ณ์ฐํ๊ธฐ ์ํ ๊ด๊ณ ์ ๊ฐ์์ฑ ๋ณด๊ณ
- ํ์ฌ ์คํฌ๋กค์ด ์์นํ ์ฝํ ์ธ ์ ๋ฐ๋ผ ๋ค๋น๊ฒ์ด์ ํ์ด๋ผ์ดํ
๊ฐ์์์ ๊ตฌํํ IntsersectionObserver์ ๋ก์ง ์ดํด๋ฅผ ์ํด ์ฐพ์๋ณด๋ค๊ฐ ์๋ ๋ฌธ์๋ค์ ์ฝ๊ฒ ๋์๋ค. ๋ฌธ์์์ ๊ตฌํํ ๋ก์ง๊ณผ ๊ฐ์์์ ๊ตฌํํ ๋ก์ง์ ๋ค๋ฅธ ํํ๋ฅผ ๋๊ณ ์๋ค. ์ถํ ๋ฆฌํฉํ ๋งํ๋ ์๊ฐ์ ๊ฐ์ง๊ธฐ ์ํด ๋ฌธ์์ ๋ด์ฉ์ ์ ๋ฆฌํ๋ค.
- How To Use an IntersectionObserver in a React Hook
- React - Intersection Observer API๋ฅผ ์ฌ์ฉํ์ฌ ์ธํผ๋ํธ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
const App = () => {
const [target, setTarget] = useState(null);
useEffect(() => {
let observer;
if (target) {
observer = new IntersectionObserver(onIntersect, { threshold: 0.5 });
observer.observe(target);
}
return () => observer && observer.disconnect();
}, [target]);
// ...
return (
...
<div ref={setTarget} />
);
}
useState
๋ฅผ ์ฌ์ฉํ์ฌIntersectionObserver
์ callback์์ ์คํ๋๋ ํญ๋ชฉ์ ์ ๊ณตํ ์ ์๋ค.- ์๋ฅผ ๋ค์ด,
entry.isIntersecting
์ ๊ฐ์ ๋ฐ๋ผ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๊ฑฐ๋ view๊ฐ ๋ฌ๋ผ์ง๋ค๋ฉดentry.isIntersecting
์ ๊ฐ์ state(์ํ)๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
- ์๋ฅผ ๋ค์ด,
useEffect
๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ ๋ ๋๋ง์ ๊ฒฐ๊ณผ์ ๋๊ธฐํ๋ Intersection Observer API๋ฅผ ์คํํ ์ ์๋ค.useEffect
๋ React์๊ฒ ์ธ๋ถ API(Intersection Observer API)๊ฐ ํ๋ ์ผ์ ์๋ฆฌ์ง ์์ ์ ์์ผ๋ฉฐ, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋๊ณ ๋ ์ดํ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ๋ ์ํ๋ DOM ๋ฑ React๋ฅผ ์ธ๋ถ ์์คํ ๊ณผ ๋๊ธฐํํ ์ ์๋ค.
useState
๋ฅผ ์ด์ฉํ์ฌ target node๋ฅผ ์ ์ฅํ๊ณIntersectionObserver
๊ฐ target node๋ฅผ ๊ด์ฐฐํ ์ ์๋๋กobserve()
๋ฉ์๋์ ์ ๋ฌํ๋ค.- target node์
ref
์์ฑ์ callback์ผ๋กuseState
์setState
๋ฅผ ์ ๋ฌํ๋ฉดref callback
์ด ํธ์ถ๋๋ฉด์state
์ target node๊ฐ ๋ฑ๋ก๋๋ค. observe()
๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ์๋useEffect
๋ฉ์๋์ ์์กด์ฑ ๋ฐฐ์ด์useState
์ ๊ฐ์ ์ถ๊ฐ ํ์ฌ target node๊ฐ ๋ณ๊ฒฝ๋ ๋๋งuseEffect
๊ฐ ํธ์ถ๋ ์ ์๊ฒ ํด์ผ ํ๋ค.- ๋จ, target node๊ฐ ๋ณ๊ฒฝ๋์์ ๋
IntersectionObserver
๊ฐ ๊ด์ฐฐํ๊ณ ์๋ ๋์์ ๊ธฐ์กด์ ๊ด์ฐฐ ๋์์ ์ถ๊ฐํ๋ ค๋ ๊ฒ ์๋๋ผ ๊ด์ฐฐ ๋์์ ๊ต์ฒดํ๋ ๊ฒ์ด๋ผ๋ฉดuseEffect
์disconnect()
๋ฉ์๋๋ฅผ ํธ์ถํ๋cleanup function
์ ์์ฑํด์ผ ํ๋ค. useRef
๋ฅผ ์ด์ฉํ์ฌ target node๋ฅผ ์ ์ฅํ๋ ๋ฐฉ๋ฒ๋ ์์ง๋ง,ref
๊ฐ์ฒด๋ ๊ฐ์ด ๋ณ๊ฒฝ๋์ด๋ ๋ ๋๋ง์ ์ผ์ผํค์ง ์๊ธฐ ๋๋ฌธ์useEffect
์ ํธ์ถ์ ์ ์ดํ ์ ์๋ค. (useEffect
๋ ์ปดํฌ๋ํธ ๋ ๋๋ง ์ดํ์ ํธ์ถ๋๋ค.)
- target node์
-
์์ฑ๋ Custom Hook ์ฝ๋
/** * @param onIntersect IntersectionObserver callback: (entries, observer) => { ... } * @param options IntersectionObserver options: { root = null, threshode = 0, rootMargin = 0 }) * @return [target, setTarget]: target node์ target node๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ ํจ์ */ const useIntersect = (onIntersect, options) => { const [target, setTarget] = useState(null); /** * - ์ธ๋ถ์์ ์ธ์๋ก ์ ๋ฌ๋ฐ์ onIntersect ํจ์๋ฅผ ์ด์ฉํ์ฌ useIntersect ํธ์ถ์ IntersectionObserver์ callback์ผ๋ก ์ ๋ฌ๋ ํจ์ ์์ฑ. * - useCallback ๋ฉ์๋๋ฅผ ์ด์ฉํด์ useIntersect ํจ์๊ฐ ์ฒ์ ํธ์ถ๋์์ ๋๋ง checkIntersect ํจ์๋ฅผ ์ ์ํ ์ ์๋๋ก ์ต์ ํ */ const checkIntersect = useCallback(([entry], observer) => { // entries ๋ฐฐ์ด์ ์ฒซ๋ฒ์งธ entry์ isIntersecting ๊ฐ์ ํ๋จํ์ฌ ์ธ๋ถ์์ ์ ๋ฌ๋ฐ์ onIntersect ํธ์ถ if (entry.isIntersecting) { onIntersect(entry, observer); } }, []); /** * - target, options๊ฐ ๋ณ๊ฒฝ๋๋ฉด useEffect ์ฝ๋ฐฑ ์คํ * - target๊ฐ ์์ผ๋ฉด: observer๋ฅผ ์์ฑํ๊ณ observer๊ฐ target๋ฅผ ๊ด์ฐฐํ๋๋ก ์ฐ๊ฒฐ * - useEffect๊ฐ ์ธ๋ง์ดํธ ๋ ๋ observer๊ฐ ์์ผ๋ฉด: observer๊ฐ ๊ด์ฐฐํ๊ณ ์๋ ๋์๊ณผ์ ์ฐ๊ฒฐ์ ํด์ง */ useEffect(() => { let observer; if (target) { observer = new IntersectionObserver(checkIntersect, { ...option, }); observer.observe(target); } return () => observer && observer.disconnect(); }, [ target, options.root, options.threshold, options.rootMargin, checkIntersect, ]); // [target, setTarget]๋ฅผ ๋ฐํํ์ฌ useIntersect ์ปค์คํ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ณณ์์ target๋ฅผ ์กฐ์ํ ์ ์๋๋ก ํจ return [target, setTarget]; };
-
์ฌ์ฉ ์์
const App = () => { const [_, setTarget] = useIntersect(async (entry, observer) => { observer.unobserve(entry.target); await fetchItems(); observer.observe(entry.target); }, {}); return ( ... <div ref={setTarget}>more</div> ) }
<div ref={(node) => console.log(node)} />
ref callback
๋ ์ด์ ๋ ๋๋ง์์์node
๋ฅผ ์ธ์๋ก ๋ฐ์ref
attribute์ ๊ฐ์ผ๋ก ์์ฑ๋ ํจ์๋ฅผref callback
(ref
์ ์ฝ๋ฐฑํจ์)์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.- React๋ ๋ณ๊ฒฝ๋
ref callback
์ ์ ๋ฌ ๋ฐ์ ๋๋ง๋คref callback
์ ํธ์ถํ๋ค.- React๋ ์ด์ ์ ์ฌ์ฉ๋
ref
๋ฅผ ์ ๊ฑฐํ๊ณ ์ref
๋ฅผ ์ค์ ํ๊ธฐ ์ํด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋ ๋ ๋ง์ดํธ๊ฐ ํด์ ๋ ํจ์๋null
์ ์ธ์๋ก ํ์ฌ ํธ์ถํ๊ณ ๋ง์ดํธ๋๋ ํจ์๋DOM node
๋ฅผ ์ธ์๋ก ํ์ฌ ํธ์ถํ๋ค. ์ฆ, ์ด 2๋ฒ ํธ์ถ๋๋ค.
- React๋ ์ด์ ์ ์ฌ์ฉ๋
null
์ ์ธ์๋ก ๋ฐ์ ํธ์ถ๋ref
๋ ์ฐธ์กฐ๊ฐ์ ์ญ์ ํ๋ค.- ๋งค๊ฐ๋ณ์:
node
DOM node
๋๋null
๋ฅผ ๊ฐ์ผ๋ก ๊ฐ์ง๋ค.- ๋ชจ๋ ๋ ๋๋ง์์ ref ์ฝ๋ฐฑ์ ๋ํด ๋์ผํ ํจ์ ์ฐธ์กฐ๋ฅผ ์ ๋ฌํ์ง ์๋ ํ ์ฝ๋ฐฑ์ ๊ตฌ์ฑ ์์๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ๋์ ์ผ์์ ์ผ๋ก ๋ถ๋ฆฌ๋๊ณ (์ธ๋ง์ดํธ) ๋ค์ ์ฐ๊ฒฐ๋๋ค.
routes.tsx์์ route ๊ฒฝ๋ก์ ํด๋นํ๋ ํ์ด์ง๋ค์ import ๋ฐ์์ฌ ๋ React.lazy()
๋ฅผ ์ด์ฉํ์ฌ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฌ์ค๋๋ฐ, ๊ทธ๋ ๋ค๋ฉด React.lazy()
๋ ๋ญ๊น?
lazy()
๋ React์์ ์ปดํฌ๋ํธ๋ฅผ ๋์ ์ผ๋ก ๋ก๋ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ํจ์๋ก, ์ฝ๋ ๋ถํ ์ ์ฃผ๋ก ์ฌ์ฉ๋๋ค.- ์ฝ๋ ๋ถํ (Code Splitting): ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ํฅ์์ํค๊ธฐ ์ํด ๋ฒ๋ค ํ์ผ์ ์์ ์กฐ๊ฐ์ผ๋ก ๋ถํ ํ๊ณ ํ์ํ ์ฝ๋ ์กฐ๊ฐ๋ง ํ์ํ ์์ ์ ๋์ ์ผ๋ก ๋ก๋ฉํ๋ ๊ธฐ์ .
โ ๏ธ ESModule์์๋ ์ฝ๋ ๋ถํ ์ ์ง์ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ฝ๋ ๋ถํ ์ ์ํด์๋ผ๋ฉด lazy ํจ์๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์๋ค. (๋๋ฌธ์ ์ถํ ๋ณธ ๊ฐ์์์ vite-plugin-next-react-router๋ฅผ ์ฌ์ฉํ์ง ์๊ณ route๋ฅผ ์ง์ ๊ตฌํํ ๋๋ lazy ํจ์๋ฅผ ์ง์ ๋ค.)
- ์ปดํฌ๋ํธ ๋ด๋ถ์์ lazy ํจ์๋ฅผ ํธ์ถํ๋ฉด ์๋๋ค. ๋ฆฌ์กํธ๋ lazy ํจ์๋ฅผ ํด๋น ์ปดํฌ๋ํธ๊ฐ ํ์ํ ์๊ฐ ์ด๊ธฐ์ ํ๋ฒ๋ง ๋ ๋๋ง์ ํ๋๋ก ๋์ด ์๋๋ฐ, ์ปดํฌ๋ํธ ๋ด๋ถ์์ lazy ํจ์๋ฅผ ํธ์ถํ๋ฉด state๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ฏ๋ก ์ข์ง ์์ ์ฑ๋ฅ์ ๊ฐ์ ธ์จ๋ค.
- lazy ํจ์๋ฅผ
<Suspense>
์ ํจ๊ป ์์ฑํ๋ฉด lazy ํจ์์ ์ํด ์ง์ฐ ๋ก๋๋๋ ์ปดํฌ๋ํธ๊ฐ ๋ก๋๋๋ ๋์ Suspense ํจ์์ fallback ํ๋กํผํฐ์ ์์ฑ๋ ๊ฐ์ ์ถ๋ ฅํด์ค๋ค. - ๋คํธ์ํฌ ์ํ์ ๋ฐ๋ผ ์ง์ฐ ๋ก๋๋ ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ์๋ ์๋ค. ์ด๋ด ๊ฒฝ์ฐ Error Boundaries(์๋ฌ ๊ฒฝ๊ณ) ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ์ฌ ์๋ฌ๊ฐ ์์ธก๋๋ ์ปดํฌ๋ํธ๋ฅผ wrappingํ๋ฉด ์๋ฌ๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ๋ฉ์ถ๊ฒ ํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์๋ค.
lazy(loadFunction);
loadFunction
: Promise ๋๋ thenable(Promise์ ๋น์ทํ ์ญํ ์ ํ๋ ๊ฐ์ฒด)๋ฅผ ๋ฐํํ๋ฉฐ, ๋งค๊ฐ๋ณ์๋ ์์ฑํ ์ ์๋ค.- ์์
const ProductPage = lazy(() => import("./src/product/index"));
<Suspense>
๋children
์ปดํฌ๋ํธ๊ฐ ๋ก๋๋ฅผ ์๋ฃํ ๋๊น์งfallback
ํ๋กํผํฐ์ ์์ฑ๋ ๋์ฒด ui๋ฅผ ์ถ๋ ฅํ๋ค.<Suspense fallback={<Loading />}>children */</Suspense>
children
: ํ์ ์ปดํฌ๋ํธ ๋ฑ ์ง์ฐ ๋ก๋๋ฅผ ํฌํจํ๊ณ ์๋ ์ค์ ui.fallback
: ๋ก๋๋๋ ๋์ ๋์ฒด ์ถ๋ ฅ๋ ui.
- children์์ ๋ณด๋ค ๋น ๋ฅด๊ฒ ๋ก๋๋๋ ์ปดํฌ๋ํธ๊ฐ ์๋๋ผ๋ chilren์ด ๋ชจ๋ ๋ก๋๋ ๋๊น์ง ui๋ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
- ๋ณด๋ค ๋นจ๋ฆฌ ๋ก๋๋๋ ์ฝํ ์ธ ๋ฅผ ๋ฏธ๋ฆฌ ๊ณต๊ฐํ๊ณ , ๋ชจ๋ ๋ก๋๋์ด๋ ๋ฏธ๋ฆฌ ๊ณต๊ฐ๋์๋ ์ฝํ ์ธ ๋ฅผ ์จ๊ธฐ๊ณ ์ถ์ง ์๋ค๋ฉด startTransition ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
<Suspense>
๊ฐ ์ค์ฒฉ๋ ๊ตฌ์กฐ๋ก ์์ฑ๋์๋ค๋ฉด ์ง์ฐ ๋ก๋๋๋ ์ปดํฌ๋ํธ์ ๊ฐ์ฅ ๊ฐ๊น์ด<Suspense>
๋ง ๋์ํ๋ค.- fallback์ผ๋ก ์ ํ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ์ถ๋ ฅํ๋ ๊ฒ์ ํ์ด์ง๊ฐ ๋ก๋ฉ๋๋ ์ค์ UI์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ๊ฒฝํ์ ๋จ์ด๋จ๋ฆฌ๋ฏ๋ก ์ฌ์ฉ์ ์ง์ํด์ผ ํ๋ค.
- ์ฝํ ์ธ ๊ฐ ๋ก๋๋๋ ๋์ ๋์ฒด ํ์ (์คํผ๋, ์ค์ผ๋ ํค ๋ฑ ์์ฌ์ด ๊ตฌํ)
- ์ฝํ ์ธ ๋ฅผ ํ ๋ฒ์ ๊ณต๊ฐ
- ๋ก๋๋ ๋ ์ค์ฒฉ๋ ์ฝํ ์ธ ํ์
- ์๋ก์ด ์ฝํ ์ธ ๊ฐ ๋ก๋๋๋ ๋์ ์ค๋๋ ์ฝํ ์ธ ํ์
- ์ด๋ฏธ ๊ณต๊ฐ๋ ์ฝํ ์ธ ๊ฐ ์จ๊ฒจ์ง๋ ๊ฒ์ ๋ฐฉ์ง
- ์ ํ์ด ์ผ์ด๋๊ณ ์์์ ๋ํ๋ ๋๋ค.
- ๋ด๋น๊ฒ์ด์ ์์ ์์คํ์ค ๊ฒฝ๊ณ ์ฌ์ค์
- ์๋ฒ ์ค๋ฅ ๋ฐ ์๋ฒ ์ ์ฉ ์ฝํ ์ธ ์ ๋ํ ๋์ฒด ์ ๊ณต
export function createPortal(
children: ReactNode,
container: Element | DocumentFragment,
key?: null | string
): ReactPortal;
createPortal(children, domNode);
createPotal
๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์ DOM ๊ณ์ธต ๊ตฌ์กฐ ๋ฐ์ ์๋ DOM ๋ ธ๋(domNode
)๋กchildren
์ ๋ ๋๋งํ๋ Potal์ ๊ฐ๋ฅํ๊ฒ ํ๋ ๋ฉ์๋์ด๋ค. ์ฆ, ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ์๋ ๋ค๋ฅธ ๊ณณ์children
์ ์ถ๋ ฅํ ํ์๊ฐ ์์ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋์ด๋ค. (์: modal)children
์๋ element, string, fragment ๋ฑ react๊ฐ ๋ ๋๋งํ ์ ์๋ ๋ชจ๋ ์์๋ฅผ ์ ๋ฌํ ์ ์๋ค.domNode
๋children
์ด ๋ ๋๋ง๋๋ DOM Element๋ฅผ ๋งํ๋ฉฐ, potal์ด ํธ์ถ๋๊ธฐ ์ domNode
๊ฐ ๋ฐ๋์ ์กด์ฌํด์ผ ํ๋ค.- potal์ ์ถ๋ ฅ๋ DOM(
children
)์ ๋ฌผ๋ฆฌ์ ์์น๋ง ๋ณ๊ฒฝํด์ฃผ๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ์ ํ ๋ฑ์ React Tree ์์ ์์น๋ฅผ ๋ฐ๋ฅธ๋ค.- ์ฆ,
children
์ ์ถ๋ ฅ๋๋ ์์น๊ฐ ์๋ ์ ์ธ๋ ์์น(๋ถ๋ชจ ํธ๋ฆฌ)์์ ์ ๊ณตํ๋ ์ปจํ ์คํธ๋ฅผ ๊ณต์ ํ๋ค. - ์ด๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์
children
์ ์ด๋ฒคํธ ์ ํ๋ฅผ ๊ฐ์งํ๋ ๋ฑ์ ๊ด์ฌ๊ฐ ๊ฐ๋ฅํจ์ ๋งํ๋ฉฐ, portal์ ์์กดํ์ง ์๋ ์ ์ฐํ ๊ฐ๋ฐ์ ๊ฐ๋ฅํ๊ฒ ํ๋ค.
- ์ฆ,
useCallback(fn, dependencies): fn;
useCallback
์ด๋ ๋ฆฌ๋ ๋๋ง ๊ฐ์ ํจ์ ์ ์๋ฅผ ์บ์ํ ์ ์๋ ๋ฉ์๋์ด๋ค.- ์ฆ, ์ผ๋ฐ ํจ์์ฒ๋ผ ์ปดํฌ๋ํธ๊ฐ ํธ์ถ๋ ๋๋ง๋ค ๋งค๋ฒ ํน์ ํจ์๋ฅผ ์๋กญ๊ฒ ์ ์ํ๋ ํ์ง ์๊ณ , ํน์ ํจ์๋ฅผ ์๋ก ๋ง๋ค์ง ์๊ณ ์ฌ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค.
- ์ด๋ ๋งค๋ฒ ํจ์๋ฅผ ์ ์ํ์ง ์๊ธฐ ๋๋ฌธ์ ์ต์ ํ์ ์ ๋ฆฌํ๋ค.
- React์์ ์ ๊ณตํ๋
useMemo
์ ๋น์ทํ๋, ํจ์๋ฅผ ํธ์ถํ์ง ์๊ณ ํจ์ ๊ฐ์ฒด๋ฅผ ์บ์ํ ๋ค ํจ์ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ ํจ์ ํธ์ถ ์์ ์ ์ง์ ํ ์ ์๋ค.useMemo
: ์ธ์๋ก ์ ๋ฌ๋ ํจ์๋ฅผ ํธ์ถํ๊ณ ํจ์๊ฐ ์คํ๋ ๊ฒฐ๊ณผ๊ฐ์ ์บ์ํ๋ ๋ฉ์๋์ด๋ค.
- ์ธ์
fn
- ์บ์ํ๋ ค๋ ํจ์ ๊ฐ์ฒด์ด๋ค.
- React๋ ์ด๊ธฐ ๋ ๋๋ง ์ค์ ํจ์ ๊ฐ์ฒด๋ฅผ ์บ์ํ ํ, ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ ๋
dependencies
(์์กด์ฑ ๋ฐฐ์ด)์ด ์ด์ ๋ ๋๋ง๊ณผ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ๋์ง ์์์ ๊ฒฝ์ฐ ๋์ผํ ํจ์๋ฅผ ์ ๊ณตํ๋ค. ๋ณ๊ฒฝ๋์์ผ๋ฉด ํ์ฌ ๋ ๋๋ง ์ค์ ํด์๋๋fn
ํจ์๋ฅผ ์ ๊ณตํ ๋ค ์ฌ์ฌ์ฉํ ์ ์๋๋ก ์บ์ํ๋ค. useCallback
์fn
์ ํธ์ถํ์ง ์๋๋ค. ๋ฐ๋ผ์ ๋ฐํ๋fn
์ผ๋ก ์ฌ์ฉ์๊ฐ ํธ์ถ ์์ ์ ๊ฒฐ์ ํ ์ ์๋ค.
dependencies
: ์์กด์ฑ ๋ฐฐ์ด๋ก, ๋ฐฐ์ด์ ๊ฐ์ ์ด์ ๋ ๋๋ง๊ณผ ๋น๊ตํ์ฌ ์บ์ํfn
์ ๊ฒฐ์ ํ๋ค.
- ๋ฐํ๊ฐ:
fn
ํจ์ ๊ฐ์ฒด
- ํ์ ๊ฒฝ๋ก์ ์๋ ํ์ด์ง๋ค์ router๋ก ์ ๊ณตํ๊ธฐ ์ํด์๋ react-router์์ ์ ๊ณตํ๋
<Outlet>
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ๋๋ฌธ์ _layout.tsx์์<Outlet />
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ Layout ํจ์ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ ๋ค. - routes.tsx์์
GlobalLayout
(=Layout)์ ๋ถ๋ฌ์ routes์ element๋ก ์ ๋ฌํ์ฌ routes ๊ตฌ์กฐ๋ฅผ ๋ง๋ ๋ค. - ์ด๋ฅผ ํ๋ก์ ํธ์ ์ง์
์ ํ์ผ์ธ app.tsx์
App
ํจ์ ์ปดํฌ๋ํธ์์useRoutes(routes)
๋ฅผ ํธ์ถํ์ฌ route ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๋ค.
- vite-plugin-next-react-router๋ ๋ผ์ฐํธ ํด๋ ๊ตฌ์กฐ๋ฅผ next์ ๋์ผํ๊ฒ ๊ฐ์ ธ๊ฐ ์ ์๋๋ก ๋์์ฃผ๋ third-party library์ด๋ค.
- vite.config.ts ํ์ผ์์
defineConfig
์ธ์๋ก ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ ๋ plugins ํ๋กํผํฐ์ ๋ฐฐ์ด ๋ด๋ถ์reactRouterPlugin()
์ ์ ๋ฌํ๋ฉด ํ๋ก์ ํธ root ํด๋์ routes.tsx ํ์ผ์ ์๋ ์์ฑํด์ฃผ๋ฉฐ, route ๊ฒฝ๋ก์ ํด๋นํ๋ ํ์ด์ง๋ค ๋ํ ์๋์ผ๋ก routes.tsx ํ์ผ์ ์ถ๊ฐํด์ค๋ค.
- ์์น๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ชจ๋
<Route>
๋ฅผ ์ดํด๋ณด๊ณ ์ผ์นํ๋ ํญ๋ชฉ์ ์ฐพ์ ํด๋น๋๋ UI๋ฅผ ๋ ๋๋งํ๋ค. <Route>
์ props๋ก๋path
,element
๊ฐ ์๋ค.- ์ค์ฒฉ ๋ผ์ฐํ
๊ตฌํ์ ์ํด
<Route>
๋ด๋ถ์<Route>
๋ฅผ ์์ฑํ ์ ์์ผ๋ฉฐ, ๋ด๋ถ์ ์์ฑ๋<Route>
์ UI๋<Outlet />
์ ์ํด ํ์ถ๋ ์ ์๋ค.
- ํ์ route element๊ฐ ๋ ๋๋ง๋๋ ๋ถ๋ถ์ผ๋ก, ํ์ route element๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํด์๋ ์์ route element์
<Outlet />
์ ์ ๋ฌํด์ผ ํ๋ค. - ์ค์ฒฉ ๋ผ์ฐํ ์ ๊ฐ๋ฅํ๊ฒ ํ์ฌ, ํ์ด์ง ๊ฐ์ ๊ณตํต์ ์ผ๋ก ๋ ๋๋ง๋์ด์ผ ํ๋ ๊ฒ์ด ์์ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ค.
declare function useRoutes(
routes: RouteObject[],
location?: Partial<Location> | string;
): React.ReactElement | null;
<Routes>
์ ๋์ผํ ๊ธฐ๋ฅ์ผ๋ก, JSX๊ฐ ์๋ javascript๋ก ์ฌ์ฉํ ์ ์๋๋ก ๋ง๋ค์ด์ก๋ค.- ๊ฐ์ฒด์ ํ๋กํผํฐ๋ก๋
<Route>
์์ ์ฌ์ฉํ๋ props์ ๋์ผํ๊ฒpath
,element
,children
๊ฐ ์๋ค.chldren
์ ํตํด ํ์ ๊ฒฝ๋ก, ์ฆ ์ค์ฒฉ๋ผ์ฐํ ์ ๊ตฌํํ ์ ์๋ค.
- v6๋ถํฐ ์ฌ์ฉ ๊ฐ๋ฅํ hook์ผ๋ก, ์ด์ ์๋ react-router-config ํจํค์ง์
renderRoutes
๋ก ๊ตฌํํ๋ค. - ๋ฐํ๊ฐ
ReactElement
: ์ธ์๋ก ์ ๋ฌ๋ routes ๊ฐ์ฒด์ ๋ฐฐ์ด์ ํด์ํ์ฌ ํ์ฌ ์์น์ path๊ฐ ์ผ์นํ๋ReactElement
๋ฅผ ๋ฐํํ๋ค.null
: ์ผ์นํ๋ path๊ฐ ์์ ๊ฒฝ์ฐ.
<Link>
๋ react-router์์ ์ ๊ณตํ๋ ์ปดํฌ๋ํธ๋ก, ์ฌ์ฉ์์ ์ก์ ์ผ๋ก ์ธํ ํ์ด์ง ์ด๋์ ์ํด ์ฌ์ฉ๋๋ฉฐ ๋ธ๋ผ์ฐ์ ์ ์ถ๋ ฅ์<a>
ํ๊ทธ๋ก ๋ณํ๋์ด ์ถ๋ ฅ๋๋ค.- ํด๋ฆญ์ ๋ฐ๋ก ํ์ด์ง ์ด๋์ด ์ผ์ด๋๋, ์ฆ
<a>
ํ๊ทธ์ ๋์ผํ ์ญํ ์ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค. <a>
ํ๊ทธ์ ๋ฌ๋ฆฌ ์ํ๊ฐ์ ์ ์ฅํ๊ณ ์ด๋ํ ํ์ด์ง๋ก ์ ๋ฌํ ์ ์๋ค.
declare function useNavigate(): NavigateFunction;
interface NavigateFunction {
(
to: To,
options?: {
replace?: boolean;
state?: any;
relative?: RelativeRoutingType;
}
): void;
(delta: number): void;
}
-
useNavigate
๋ ์ผ๋ฐ์ ์ธ ํ์ด์ง ์ด๋์ด ์๋, ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ํ์ด์ง ์ด๋์ด ํ์ํ๊ฑฐ๋ ์ถ๊ฐ ๋ก์ง์ด ํ์ํ ๊ฒฝ์ฐ ์ฌ์ฉํ๋ hook์ด๋ค. -
๋ฐํ๊ฐ์ ํจ์๋ก, ์ ๋ฌํ๋ ์ธ์์ ๋ฐ๋ผ ๋์ํ๋ ํํ๊ฐ ๋ค๋ฅด๋ค.
-
์ฒซ๋ฒ์งธ ์ธ์์ ํ์ด์ง ์ด๋ ๊ฒฝ๋ก๋ฅผ ์์ฑํ๋ฉด, ์์ฑํ ๊ฒฝ๋ก๋ก ํ์ด์ง ์ด๋์ ํด์ฃผ๋
<Link to={path}>
์ ๋์ผํ ๋์์ ํ๋ฉฐ, ๋ ๋ฒ์งธ ์ธ์์๋replace
,state
๋ฑ ๋ถ๊ฐ ์ต์ ์ ์ ๋ฌํ ์ ์๋ค.replace
์ ๊ธฐ๋ณธ๊ฐ์false
๋ก,true
๋ฅผ ์ ๋ฌํ๋ฉด react-router๋ ์ด๋ํ url์ history์ ์๋กญ๊ฒ push ํ๋ ๋์ ํ์ฌ history๋ฅผ ์ด๋ํ url๋ก replaceํ๋ค. ๋๋ฌธ์NavigateFunction
์ ์ด์ฉํ์ฌ ํ์ด์ง๊ฐ ์ด๋ํ ๋replace: true
์ผ ๊ฒฝ์ฐ ๋ค๋ก ๊ฐ๊ธฐ ๋ฒํผ์ ์ด์ฉํ์ฌ ๊ธฐ์กด์ ํ์ด์ง๋ก ์ ๊ทผํ ์ ์๋ค.
-
์ฒซ๋ฒ์งธ ์ธ์์ ์ซ์๋ฅผ ์ ๋ฌํ๋ฉด ๋ธ๋ผ์ฐ์ history ์ด๋์ด ๊ฐ๋ฅํ๋ค. ์๋ฅผ ๋ค์ด, ์๋์ ๊ฐ์ด useNavigate๊ฐ ๋ฐํํ ํจ์์ ์ซ์ -1์ ์ ๋ฌํ๋ฉด ๋ธ๋ผ์ฐ์ ์ ๋ค๋ก ๊ฐ๊ธฐ ๋ฒํผ์ ํด๋ฆญํ ๊ฒ๊ณผ ๋์ผํ ๋์์ ํ๋ค.
const navigate = useNavigate(); navigate(-1);
-
type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
redirect
์ loaders๋ actions์์ ์๋ฒ์ ์๋ต์ ๋ฐํ ๋ฐ์์ ํ์ด์ง ์ด๋์ ์ํํ ๋ ์ฌ์ฉํ๋ ํจ์์ด๋ค.- ๋ฐ๋ผ์ ๋ฆฌ๋๋ ์
์ด ๋ฐ์ดํฐ์ ๋ํ ์๋ต์ธ ๊ฒฝ์ฐ
useNavigate
๋์ loader ๋ฐ action ํจ์์์redirect
์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
์ฐธ๊ณ ๋ฌธ์: [๋ฒ์ญ] #10: ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ ์ํ ๊ด๋ฆฌ์๋ค
- ์บ์๋ฅผ ์ด์ฉํ์ฌ ์๋ฒ ์ํ๋ฅผ ์ฝ๊ฒ ๊ด๋ฆฌํ ์ ์๋๋ก ๋์์ฃผ๋ React ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๋น๋๊ธฐ ์ํ ๊ด๋ฆฌ์๋ก, ์ด๋ค ํํ์ ๋น๋๊ธฐ ์ํ๋ ๊ด๋ฆฌํ ์ ์๋ค. ๋๋ถ๋ถ data fetching์ ํตํด
Promise
๋ฅผ ๋ฐํ ๋ฐ๊ณ ์๋ค. - React Query๋ ๋ฐ์ดํฐ๋ฅผ refetch ํด์ค๋ ์ ๋ต ์งํ๋ฅผ ์ ๊ณตํ๋ค. (
refetchOnMount
,refetchOnWindowFocus
,refetchOnReconnect
๋ฑ)- ์๋์ผ๋ก refetch ์์ ์ ์ง์ ํ๋ ๊ฒ ์ธ์
queryClient.invalidateQueries
๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฌดํจํํ๋ ํ ์ ์๋ค.
- ์๋์ผ๋ก refetch ์์ ์ ์ง์ ํ๋ ๊ฒ ์ธ์
- fetching ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ
- ์ ํ๋ฆฌ์ผ์ด์
์ ๋ง์ดํธ ์์ ์ data๋ฅผ fetchํ๊ณ ์ ์ญ ์ํ๋ก ์ ์ฅํ์ฌ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ data๋ฅผ ์ฐธ์กฐํ ์ ์๋๋ก ํ๋ค.
- ์ด ๋ฐฉ๋ฒ์ fetch๋ ๋ฐ์ดํฐ๋ฅผ ์์ฃผ ์ ๋ฐ์ดํธ ํ๊ธฐ ์ด๋ ต๋ค.
- ์ ์ญ ์ํ๊ฐ ๋๋ฌด ๋ง์ ๊ฒ์ ๊ด๋ฆฌํ๊ณ ์๋ค๋ ๋จ์ ์ด ์๋ค.
- data๊ฐ ํ์ํ ์ปดํฌ๋ํธ์ ๋ง์ดํธ ์์ ์ data๋ฅผ fetchํ๊ณ ์ง์ญ ์ํ๋ก ๊ด๋ฆฌํ๋ค.
- ์ปดํฌ๋ํธ๊ฐ ์์ฃผ ๋ง์ดํธ๋ ๊ฒฝ์ฐ ์ฆ์ ํต์ ์ด ๋ฐ์๋๋ค๋ ๋จ์ ์ด ์๋ค.
์ฐธ๊ณ ๋ฌธ์: Inside React Query ํ๊ธ ๋ฒ์ญ
- ์ ํ๋ฆฌ์ผ์ด์
์์์
QueryClient
์ ์ธ์คํด์ค๋ฅผ ์์ฑํ์ฌQueryClientProvider
๋ก ์ปดํฌ๋ํธ์ ๋ฐฐ๋ถํ๋ฉด React Query ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. QueryClient
๋ก ์์ฑ๋ ์ธ์คํด์ค๋QueryCache
,MutationCache
์ ์ปจํ ์ด๋๋ก,query
์mutation
์ ์กฐ์ํ๋ ๋ฉ์๋์ ์บ์ ์์ ์ด ๊ฐ๋ฅํ๋ค.- ์บ์(cache): ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ณต์ฌํด๋๋ ์์ ์ฅ์๋ก, ์บ์์ ์ ๊ทผํ๋ ์๊ฐ์ ๋นํด ์๋ณธ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆด ๊ฒฝ์ฐ ์๊ฐ์ ์ ์ฝํ๊ธฐ ์ํด ์์ฃผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
QueryCache
๋ ์์ ์ ์ด๊ณ ์ง๋ ฌํ๋ queryKeys(queryKeyHash) ๋ฒ์ ์ key์Query
ํด๋์ค์ ์ธ์คํด์ค์ด์ ๋ฉ๋ชจ๋ฆฌ ๋ด ๊ฐ์ฒด(in-memory object)์ธ value๋ก ์ด๋ค์ ธ์๋ค.- React Query๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์๋ง ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ค๋ฅธ ๊ณณ์๋ ์ ์ฅํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ์บ์๊ฐ ์ฌ๋ผ์ง๋ค. ์ด๋ฅผ ์์น ์์ผ๋ฉด ๋ค๋ฅธ ์ธ๋ถ ์ ์ฅ์์ ์ ์ฅํ๋
persistQueryClient
๋ฅผ ์ด์ฉํ ์ ์๋ค. - ์บ์์๋ ์ฟผ๋ฆฌ๋ค์ด ์๋๋ฐ ์ด ์ฟผ๋ฆฌ์์ ๋๋ถ๋ถ์ ๋ก์ง๋ค์ด ์คํ๋๋ค. ์ฌ๊ธฐ์๋ ์ฟผ๋ฆฌ์ ๋ํ ๋ชจ๋ ์ ๋ณด(๋ฐ์ดํฐ, ์ํ ํ๋, ๋ง์ง๋ง fetching์ด ๋ฐ์๋์์ ๋ ๋ฑ์ ๋ฉํ ์ ๋ณด)์ ์ฟผ๋ฆฌ ํจ์๋ฅผ ์คํํ๊ณ ์ฌ์๋, ์ทจ์, ์ค๋ณต ์ ๊ฑฐ๋ฅผ ํ๋ ๋ก์ง๋ ํฌํจ๋๋ค.
- React Query๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์๋ง ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๋ค๋ฅธ ๊ณณ์๋ ์ ์ฅํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ์บ์๊ฐ ์ฌ๋ผ์ง๋ค. ์ด๋ฅผ ์์น ์์ผ๋ฉด ๋ค๋ฅธ ์ธ๋ถ ์ ์ฅ์์ ์ ์ฅํ๋
Query
๋Observer
๋ฅผ ํตํด ๋๊ฐ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋์ง ์๊ณ , ํด๋น ๊ด์ฐฐ์์๊ฒ ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ ์๋ฆด ์ ์๋ค.Observer
๋useQuery
๋ฅผ ํธ์ถํ ๋ ์์ฑ๋๋ฉฐuseQuery
์queryKey
๋ก ์ ๋ฌ๋ ๋จ ํ๋์ ์ฟผ๋ฆฌ๋ง์ ๊ตฌ๋ ํ๋ค. ์ฆ,Observer
๋QueryCache
์ ์๋query
๋ฅผ ๊ตฌ๋ ํ๋ค.Observer
๋Query
์ ๋ฐ์ดํธ๋ฅผ ์ปดํฌ๋ํธ์๊ฒ ์๋ ค์ผ ํ๋์ง ๊ฒฐ์ ํ๋ค.Observer
๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ ์ค์ธ query์ ์์ฑ๋ค์ ์๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ด๋ จ ์๋ ๋ณ๊ฒฝ ์ฌํญ์ ์๋ฆด ํ์๊ฐ ์๋ค. ์๋ฅผ ๋ค์ด, ๋ฐ์ดํฐ ํ๋๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๋ฐฑ๊ทธ๋ผ์ด๋ refetch์์ isFetching์ด ๋ณ๊ฒฝ๋ ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ ํ์๊ฐ ์๋ค. ์ด๋ ๋ฏObserver
๋ ๋ง์ ์์ ์ ์ํํ๋ฉฐ, ๋๋ถ๋ถ์ ์ต์ ํ๊ฐ ์ด๋ค์ง๋ ๊ณณ์ด๊ธฐ๋ ํ๋ค.
Observer
๊ฐ ์๋ ์ฟผ๋ฆฌ๋ฅผ ๋นํ์ฑ ์ฟผ๋ฆฌ๋ผ๊ณ ํ๋ค. ์ด ์ฟผ๋ฆฌ๋ ์ฌ์ ํ ์บ์์ ์์ง๋ง ์ปดํฌ๋ํธ์์ ์ฌ์ฉ๋๊ณ ์์ง ์๋ ๊ฒ์ ๋งํ๋ค. React Query ๊ฐ๋ฐ ๋๊ตฌ์์๋ ์ฟผ๋ฆฌ๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋Observer
์ ๊ฐ์๋ฅผ ํ๊ธฐํ๊ณ , ๋นํ์ฑ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ ํ์์ผ๋ก ํ์ํด๋์๋ค.
์ฐธ๊ณ ๋ฌธ์: Breaking change - React Query v4
-
react-query package ์ด๋ฆ์ด ๋ณ๊ฒฝ๋์ด ์ค์น์ ๋ช ๋ น์ด์ import์ ๊ฒฝ๋ก๊ฐ ๋ณ๊ฒฝ๋จ
#๊ธฐ์กด npm i react-query #ํ์ฌ npm i @tanstack/react-query
// ๊ธฐ์กด import { useQuery } from "react-query"; // ํ์ฌ import { useQuery } from "@tanstack/react-query";
-
devtools ์ฌ์ฉ์ ์ํ๋ฉด package ์ค์น๋ฅผ ํด์ผ ํ ์ ์๊ฒ ๋ณ๊ฒฝ๋จ
npm i @tanstack/react-query-devtools
// ๊ธฐ์กด import { ReactQueryDevtools } from "react-query/devtools"; // ํ์ฌ import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
- react-query์์ ์ ๊ณตํ๋ api๋ก ์บ์์ ์ํธ ์์ฉํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, react query ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ง์ ์ ์ผ๋ก ๋ณผ ์ ์๋ค.
QueryCache
์MutationCache
์ ์ปจํ ์ด๋๋ก, ๋ชจ๋query
๋ฐmutation
์ ๋ํด ์ค์ ํ ์ ์๋ ๋ช ๊ฐ์ง ๊ธฐ๋ณธ๊ฐ์ ์์ ํ๊ณ ์์ผ๋ฉฐ ์บ์ ์์ ์ ์ํ ํธ๋ฆฌํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค. (๋๋ถ๋ถ์ ๊ฒฝ์ฐ cache์ ์ง์ ์ ๊ทผํ์ง ์๊ณQueryClient
๋ฅผ ํตํด ์ ๊ทผํ๋ค.)- query๋ฅผ fetch ๋ฐ๊ฑฐ๋ ์บ์ํ๊ณ ์ ๋ฐ์ดํธ ํ๋ ๋ฑ ๋ค์ํ ๋ฉ์๋๋ฅผ ๊ฐ์ง ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ ํด๋์ค๋ก ๋ง๋ค์ด์ ธ ์์ด new ์ฐ์ฐ์์ ํจ๊ป ํธ์ถํ ์ ์๋ค.
QueryClientProvider
๋ฅผ ์ด์ฉํ๋ฉด ReactContext
๋ฅผ ์ด์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์QueryClient
๋ฅผ ๋ฐฐ๋ถํ ์ ์๋ค.
QueryClient๋ฅผ ํธ์ถํ ๋ ์ธ์์ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ์ฌ ์ต์
๊ฐ์ ์ค์ ํ ์ ์๋ค. (queryCache
, mutationCache
, defaultOptions
)
queryCache
queries
staleTime
(๊ธฐ๋ณธ๊ฐ: 0)- ๋ฐ์ดํฐ๊ฐ ์บ์์ ์ ์ฅ๋ ์ดํ ๋ค์ ์์ฒญ์ ๋ณด๋ผ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ์๊ฐ(๋ฐ๋ฆฌ์ด).
- ์ผ๋ง๋ ์ง๋์ผ ๋ฐ์ดํฐ๋ฅผ staleํ๋ค๊ณ ํ๋จํ ์ง ๊ฒฐ์ ํ๋ ์ต์ ์ผ๋ก, ์ง์ ํ ์๊ฐ ์ ๊น์ง๋ staleํ ๋ฐ์ดํฐ๊ฐ ์๋๋ค.
- stale data: ์ ์ ํ์ง ์์, ์ฆ ์ ๋ฐ์ดํธ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋งํ๋ค. === ์บ์๊ฐ ๋ง๋ฃ๋ ๋ฐ์ดํฐ
query
๊ฐ ์ต์ ์ํ์ธ ๋ฐ์ดํฐ๋ ํญ์ ์บ์์์๋ง ์ฝํ๋ฉฐ ๋คํธ์ํฌ ์์ฒญ์ด ๋ฐ์ํ์ง ์๋๋ค.- stale ๋ฐ์ดํฐ ๋ํ ์บ์์์ ๊ฐ์ง์ค์ง๋ง, ํน์ ์กฐ๊ฑด์์ refetch๊ฐ ์ผ์ด๋ ์ ์๋ค.
cacheTime
(๊ธฐ๋ณธ๊ฐ: Infinity)- ์บ์์ ์ ์ฅ๋ ๋ฐ์ดํฐ์ ์ ํจ ์๊ฐ(๋ฐ๋ฆฌ์ด). ์ฆ, ๋นํ์ฑ ์ฟผ๋ฆฌ๊ฐ ์บ์์์ ์ ๊ฑฐ๋ ๋๊น์ง์ ์๊ฐ์ด๋ค.
query
๋ ๋ฑ๋ก๋ ๊ด์ฐฐ์(Observer
)๊ฐ ์์ ๊ฒฝ์ฐ ๋ฐ์ดํฐ๋ ์ฆ์ ๋นํ์ฑ ์ํ๋ก ์ ํ๋๋ค. ์ด๋ ํด๋น ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ๊ตฌ์ฑ ์์๋ ์ธ๋ง์ดํธ(unmounted)๋๋ค.cacheTime
์ด ์ง๋๋ฉดquery
๋ ๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์ญ์ ๋๋ค.
refetchInterval
(๊ธฐ๋ณธ๊ฐ: false): ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ค๋ ์๊ฐ(๋ฐ๋ฆฌ์ด).refetchIntervalInBackground
(๊ธฐ๋ณธ๊ฐ: false): ์ฐฝ์ด ๋นํ์ฑํ๋์์ ๋์๋ refetchInterval์ ๊ณ์ ์คํํ ์ง ์ฌ๋ถ.refetchOnWindowFocus
(๊ธฐ๋ณธ๊ฐ: true): ์ฐฝ์ด ํ์ฑํ๋์์ ๋์๋ refetchInterval์ ๊ณ์ ์คํํ ์ง ์ฌ๋ถ.refetchOnMount
(๊ธฐ๋ณธ๊ฐ: true): ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ๋ ๋๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ฌ์ง ์ฌ๋ถ.refetchOnReconnect
(๊ธฐ๋ณธ๊ฐ: true): ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ๋ค์ ํ์ฑํ๋ ๋๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ฌ์ง ์ฌ๋ถ.retry
(๊ธฐ๋ณธ๊ฐ: 3): ์์ฒญ์ด ์คํจํ ๊ฒฝ์ฐ ์ต๋ ์ฌ์๋ ํ์.retryDelay
(๊ธฐ๋ณธ๊ฐ: (attempt) => Math.min(1000 * 2 ** attempt, 30000)): ์ฌ์๋ ๊ฐ๊ฒฉ์ ๊ณ์ฐํ๋ ํจ์.retryOnMount
(๊ธฐ๋ณธ๊ฐ: true): ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง์ดํธ๋ ๋๋ง๋ค ์์ฒญ์ ๋ค์ ์๋ํ ์ง ์ฌ๋ถ.retryOnWindowFocus
(๊ธฐ๋ณธ๊ฐ: true): ์ฐฝ์ด ํ์ฑํ๋์์ ๋์๋ ์์ฒญ์ ๋ค์ ์๋ํ ์ง ์ฌ๋ถ.suspense
(๊ธฐ๋ณธ๊ฐ: false): ์ปดํฌ๋ํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋๊น์ง ๋๊ธฐํ๋ ๋์ , Suspense๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋ฉ ์ํ๋ฅผ ์ฒ๋ฆฌํ ์ง ์ฌ๋ถ.useErrorBoundary
(๊ธฐ๋ณธ๊ฐ: false): ErrorBoundary๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ด ์คํจํ์ ๋ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ ์ง ์ฌ๋ถ.queryFnParamsFilter
(๊ธฐ๋ณธ๊ฐ: undefined): ์ฟผ๋ฆฌ ํจ์์ ์ ๋ฌ๋๋ ์ธ์๋ฅผ ํํฐ๋งํ๋ ํจ์.
mutationCache
mutations
mutateOptions
(๊ธฐ๋ณธ๊ฐ: {}): mutate() ํจ์์ ์ ๋ฌ๋๋ ์ต์ .throwOnError
(๊ธฐ๋ณธ๊ฐ: false): ์๋ฒ ์ค๋ฅ ๋ฐ์ ์ ์์ธ๋ฅผ ๋์ง์ง ์ฌ๋ถ.useErrorBoundary
(๊ธฐ๋ณธ๊ฐ: false): ErrorBoundary๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ด ์คํจํ์ ๋ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ ์ง ์ฌ๋ถ.
defaultOptions
queries
: queryKey์ ๋ํ ๊ธฐ๋ณธ ์ต์ ์ด๋ค.staleTime
(๊ธฐ๋ณธ๊ฐ: 0): ๋ฐ์ดํฐ๋ฅผ ๊ฐฑ์ ํ๊ธฐ ์ ์ ๋ง๋ฃ๋์ด์ผ ํ๋ ์๊ฐ (๋ฐ๋ฆฌ์ด ๋จ์)์ ์ง์ ํ๋ค.cacheTime
(๊ธฐ๋ณธ๊ฐ: 0): ๋ฐ์ดํฐ๋ฅผ ์บ์์ ์ ์ฅํ ์๊ฐ (๋ฐ๋ฆฌ์ด ๋จ์)์ ์ง์ ํ๋ค.retry
(๊ธฐ๋ณธ๊ฐ: true): ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์๋์ผ๋ก ์ฌ์๋ ์ฌ๋ถ๋ฅผ ์ง์ ํ๋ค.retryDelay
(๊ธฐ๋ณธ๊ฐ: attempt => Math.min(attempt _ 1000, 30 _ 1000)): ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ ์ฌ์๋ ๊ฐ๊ฒฉ์ ์ง์ ํ๋ค.refetchOnWindowFocus
(๊ธฐ๋ณธ๊ฐ: true): ์๋์ฐ ํฌ์ปค์ค๊ฐ ๋๋ฉด ์๋ก๊ณ ์นจ ์ฌ๋ถ๋ฅผ ์ง์ ํ๋ค.refetchInterval
(๊ธฐ๋ณธ๊ฐ: false): ์ฃผ๊ธฐ์ ์ผ๋ก ์๋ก๊ณ ์นจ ํ ์ง ์ฌ๋ถ๋ฅผ ์ง์ ํ๋ค. ๊ฐ์ด false์ด๋ฉด ์ฃผ๊ธฐ์ ์ผ๋ก ์๋ก๊ณ ์นจํ์ง ์๋๋ค. number ํ์ ์ ๊ฐ์ด๋ฉด ํด๋น ๊ฐ(๋ฐ๋ฆฌ์ด)๋ง๋ค ์ฃผ๊ธฐ์ ์ผ๋ก ์๋ก๊ณ ์นจ ํ๋ค.queryFnParamsFilter
(๊ธฐ๋ณธ๊ฐ: params => params): queryFn์ ์ ๋ฌ๋ ๋งค๊ฐ ๋ณ์๋ฅผ ํํฐ๋งํ๋ ํจ์๋ฅผ ์ง์ ํ๋ค. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ ํจ์์ ํ์ํ ๋งค๊ฐ ๋ณ์๋ง ์ ๋ฌํ ์ ์๋ค.
mutations
: mutation์ ๋ํ ๊ธฐ๋ณธ ์ต์ ์ด๋ค.retry
(๊ธฐ๋ณธ๊ฐ: true): ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์๋์ผ๋ก ์ฌ์๋ ์ฌ๋ถ๋ฅผ ์ง์ ํ๋ค.retryDelay
(๊ธฐ๋ณธ๊ฐ: attempt => Math.min(attempt _ 1000, 30 _ 1000)): ์๋ฒ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ํ ์ฌ์๋ ๊ฐ๊ฒฉ์ ์ง์ ํ๋ค.onError
(๊ธฐ๋ณธ๊ฐ: error => console.error(error)): ์ค๋ฅ๊ฐ ๋ฐ์ํ์ ๋ ํธ์ถํ ํจ์๋ฅผ ์ง์ ํ๋ค.
const data = queryClient.getQueryData(queryKey);
- ๊ธฐ์กด ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๋๊ธฐ ํจ์๋ก, ์ฟผ๋ฆฌ๊ฐ ์กด์ฌํ์ง ์์ผ๋ฉด
undefined
๋ฅผ ๋ฐํํ๋ค. - ํ๋ฒ์ ์ฌ๋ฌ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ค๋ฉด
getQueriesData()
๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. - ์ต์
filters?: QueryFilters
: Query filter๋ฅผ ํ์ฉํ๋ ํ๋กํผํฐ
- ๋ฐํ๊ฐ:
data: TQueryFnData | undefined
queryClient.setQueryData(queryKey, updater);
setQueryData()
๋ ๋น๋๊ธฐ๋ก ๋์ํ๋fetchQuery()
์ ๋ฌ๋ฆฌ ์ฟผ๋ฆฌ์ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ๋๋ฐ ์ฌ์ฉํ ์ ์๋ ๋๊ธฐ ํจ์๋ก, ์ฟผ๋ฆฌ๊ฐ ์์ผ๋ฉด ์์ฑ๋๋ค.- ๊ธฐ๋ณธ
cacheTime
5๋ถ ๋์ Query Hook์์ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ฟผ๋ฆฌ๊ฐ ๊ฐ๋น์ง ์์ง๋๋ค. - ํ๋ฒ์ ์ฌ๋ฌ ์ฟผ๋ฆฌ๋ฅผ ์
๋ฐ์ดํธํ๊ณ Query key๋ฅผ ๋ถ๋ถ์ ์ผ๋ก ์ผ์น์ํค๋ ค๋ฉด
setQueriesData()
๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. updater
๊ฐundefined
์ผ ๊ฒฝ์ฐ ์ฟผ๋ฆฌ ๋ฐ์ดํฐ๋ ์ ๋ฐ์ดํธ๋์ง ์๋๋ค.setQueryData()
๋ด์์onSuccess
๋ ๋ฌดํ๋ฃจํ๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ํธ์ถํ ์ ์๋ค. (v4 ๋ง์ด๊ทธ๋ ์ด์ )setQueryData()
๋ ์์ํด์ผ ํ๋ค. ์ฆ, ๋ด๋ถ์์getQueryData()
๋ฅผ ํธ์ถํ์ฌ ์ฆ๊ฐ์ ์ผ๋ก ๊ฐ์ ๋ณ๊ฒฝ์ํค๋ฉด ์๋๋ค.- ์ต์
queryKey: QueryKey
: Query keyupdater: TQueryFnData | undefined | ((oldData: TQueryFnData | undefined) => TQueryFnData | undefined)
: updater๋ก ํจ์๊ฐ ์๋ ๊ฐ์ด ์ ๋ฌ๋๋ฉด ํด๋น ๊ฐ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ๋๊ณ , ํจ์๊ฐ ์ ๋ฌ๋๋ฉด ์ด์ ๋ฐ์ดํฐ ๊ฐ์ ์์ ํ๊ณ ์๋ก์ด ๊ฐ์ ๋ฐํํ๋ค.
- ํน์ querykey์ ํด๋นํ๋ ๋ค๋ฅธ ์์ฒญ์ ๋ฌด์ํ ์ ์๋๋ก ํ๋ api
- Optimistic Update์์ ๋ฐ์ดํฐ refetch๋ฅผ ์ทจ์ํ๋๋ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ค.
<QueryClientProvider>
๋ QueryClient ์ปดํฌ๋ํธ provider๋ก, ํ์ ํด๋ผ์ด์ธํธ์๊ฒ QueryClient ์ปดํฌ๋ํธ๋ฅผ JSX๋ก ์ ๊ณตํ ์ ์๋ค.client
(ํ์): ์ ๊ณตํ QueryClient์ ์ธ์คํด์คcontextSharing
(๊ธฐ๋ณธ๊ฐ: false): context๋ฅผ ๊ณต์ ํ ๊ฒ์ธ์ง๋ฅผ ์ ํํ๋ ์ต์
- useQuery๋ react-query์์ ์ ๊ณตํ๋ api๋ก, query๋ฅผ ์๋ฒ๋ก๋ถํฐ GET ๋ฐ์ ๋ ์ฌ์ฉํ๋ค.
useQuery
๋ ๋น๋๊ธฐ๋ก ๋์ํ๋ค.useQuery
๋ ๋ ๋๋ง ์์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋น๋๊ธฐ ํจ์์ด๋ค. ๋ฐ๋ผ์ ์ฒซ ๋ ๋๋ง์๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํ๊ณ ,undefined
๋ฅผ ๋ฐํํ ์ ์๋ค.
useQuery
๋ ์ฒซ ๋ ๋๋ง ๋ ๋ฐ์ดํฐ๋ฅผ ์บ์ํ๊ณ , ์ดํ์๋ ์บ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ค. ์ฒซ ๋ ๋๋ง ์ดํ์useQuery
์ ๋น๋๊ธฐ ํจ์๊ฐ ์คํ๋๊ณ ๋ฐ์ดํฐ๋ฅผ ์๋กญ๊ฒ ์บ์ํ๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์๋๋ฉฐ,data
์๋ ์บ์๋ ๋ฐ์ดํฐ์์ ๊ฐ์ ์ฝ์ด์จ๋ค.- ์ฟผ๋ฆฌ๋ ์๋์ผ๋ก ์ํ๋๋ค.
- ์ข ์์ฑ์ ์ ์ํ์ง๋ง, React Query๋ ์ฆ๊ฐ์ ์ธ ์ฟผ๋ฆฌ ์คํ์ด๋ ์ ๋ฐ์ดํธ๊ฐ ํ์ํ๋ค๊ณ ํ๋จ๋๋ ๊ฒฝ์ฐ ์๋์ผ๋ก background update๋ฅผ ์ํํ๋ค.
- ๋ฐฑ์๋์ ์ค์ ๋ฐ์ดํฐ์ ํ๋ฉด์ ํ์๋๋ ๋ด์ฉ์ ๋๊ธฐํ ํ๋ ๊ฒ์ ์ ํฉํ๋ค.
- React Query๋ ๋์์ ๋ฐ์ํ๋
useQuery
์์ฒญ์ ์ค๋ณต์ ์ ๊ฑฐํ๋ค. ๋ฐ๋ผ์ ๋์ผํQueryClientProvider
๋ด๋ถ์์ ๋์ผํ ๋ ๋ ์ฃผ๊ธฐ์ ํธ์ถ๋๋ ๋์ผํ data fetching์ ๋ฌด์๋์ด ํ๋ฒ์ ๋คํธ์ํฌ ์์ฒญ๋ง ์ผ์ด๋๋ค. queryKey
๋ฅผ ๊ณ ์ ํ๊ฒ ์๋ณํ๊ธฐ ๋๋ฌธ์ ๋์ผํQueryClientProvider
๋ด๋ถ์์useQuery()
๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ค ์ปดํฌ๋ํธ๋ผ๋ ๋์ผํ data๋ฅผ fetch ๋ฐ์ ์ ์๋ค.- ์ฆ, ์ปดํฌ๋ํธ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํ ์ ์๋ค.
- ๋งค๋ฒ
useQuery()
ํ๋ data fetch ํจ์์ ์ ๊ทผํ๋ ๊ฒ๋ณด๋ค custom hooks๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ๊ฒ์ด ํจ์จ์ ์ด๋ค.
useQuery(queryKey, queryFunction): UseQueryResult;
queryKey: (string, number, object)[]
:query
๋ฅผ ๊ด๋ฆฌํ๋๋ฐ ์ฌ์ฉ๋๋ unique keyqueryFunction
: api ํธ์ถ์ ํ๋ promise ํจ์
useMutation
์mutation
์ํ๋ฅผ ์ถ์ ํ๋ API๋ก, ๋น๋๊ธฐ๋ก ๋์ํ๋ค.- loading, error, status field๋ฅผ ์ ๊ณตํ์ฌ ์ฌ์ฉ์์๊ฒ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ์ ๊ณตํ๋ค.
useQuery
์ ์ฝ๋ฐฑ๊ณผ ๋์ผํ๊ฒonSuccess
,onError
,onSettled
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.useQuery
์ ๋ฌ๋ฆฌ ์ปดํฌ๋ํธ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํ์ง ์์ผ๋ฉฐ, mutation(๋ณํ)์ด ์๋์ผ๋ก ์ํ๋์ง ์๋๋ค.- refetch๋๋ ์์ ๋ง๋ค mutation ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ฉด ์๋๊ธฐ ๋๋ฌธ (ex, ๋ธ๋ผ์ฐ์ ์ฐฝ์ ์ด์ ์ ๋ง์ถ ๋๋ง๋ค ์๋ก์ด todo๊ฐ ์์ฑ๋จ)
- mutation์ ์๋์ผ๋ก ์ํํ๋ ๋์ , ๋ณํ์ ํ๊ณ ์ถ์ ๋๋ง๋ค ํธ์ถํ ์ ์๋ ํจ์(
mutate
)๋ฅผ ์ ๊ณตํ๋ค.
useMutation
๊ฐ ๋ฐํํ๋mutate
์mutateAsync
๋ ๋น์ทํ์ง๋งmutate
๋ ๋ฐํ๊ฐ์ด ์๊ณ ,mutateAsync
๋ Promise๋ฅผ ๋ฐํํ๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.- ๋๋ถ๋ถ์ ๊ฒฝ์ฐ
mutate
๋ฅผ ์ฌ์ฉํ๊ณ mutation์ ์๋ต์ ์ ๊ทผํด์ผ ํ๋ ๊ฒฝ์ฐ์๋งmutateAsync
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. mutateAsync
๋ promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์try/catch
๋then/catch
์ฒด์ด๋์ ์ด์ฉํ ์๋ฌ ํธ๋ค๋ง์ด ๊ฐ๋ฅํ๋ค.
- ๋๋ถ๋ถ์ ๊ฒฝ์ฐ
useMutation
์ ์ฝ๋ฐฑ์mutate
์ ์ฝ๋ฐฑ ํจ์๋ณด๋ค ๋จผ์ ์คํ๋๊ธฐ ๋๋ฌธ์useMutation
์ ์ฝ๋ฐฑ์ UI์ ๊ด๋ จ๋ ๋ก์ง์ ์คํํ๋ค๋ฉดmutate
์ ์ฝ๋ฐฑ์ด ์คํ๋๊ธฐ ์ ์ ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธ ๋ ์ ์๋ค. ๋ฐ๋ผ์useMutation
์ ์ฝ๋ฐฑ์๋ ์ฟผ๋ฆฌ ๋ฌดํจํ ๋ฑ ํ์์ ์ด๊ณ ๋ ผ๋ฆฌ์ ์ธ ์์ ์ ์ํํ๊ณ ,mutate
์ ์ฝ๋ฐฑ์๋ ๋ฆฌ๋ค์ด๋ ํธ๋ ํ ์คํธ ์๋ฆผ ๊ฐ์ UI ๊ด๋ จ ์์ ์ ์ํํ๋ ๊ฒ์ด ์ข๋ค.- custom hook์ ์ฌ์ฉํ ๋์๋ ์ฝ๋ฐฑ์ ์ญํ ๋ถ๋ฆฌ๊ฐ ์ ๋์ด์์ผ๋ฉด custom hook์ ์ฌ์ฌ์ฉ์ฑ์ ๋์ผ ์ ์๋ค.
onMutate?: (variables: unknown, mutation: Mutation) => Promise<unknown> | unknown
- mutation์ด ์์๋๊ธฐ ์ ์ ๋๊ธฐ์ ์ผ๋ก ํธ์ถ๋๋ ํจ์๋ก, ์๋ฒ์์ ํต์ ์์ด ์ด๋ค์ง๋ค.
onMutate
์์ ๋ฐํํ๋ ๊ฐ์onSuccess
,onError
,onSettled
์ ๋งค๊ฐ๋ณ์context
๋ก ์ ๋ฌ๋๋ค.
onSuccess?: (data: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown
: ์ผ๋ถ mutate๊ฐ ์ฑ๊ณตํ๋ฉด ํธ์ถ๋๋ ํจ์๋ก, mutate ์ดํ ์๋ฒ์์ ๋ฐํํ๋ ๊ฐ์ด ์ฒซ๋ฒ์งธ ๋งค๊ฐ๋ณ์์ธ data๋ก ๋ค์ด์จ๋ค.onError?: (error: unknown, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown
- ์ผ๋ถ mutate๊ฐ ์คํจํ๋ฉด ํธ์ถ๋๋ ํจ์
- Optimistic Update ๊ตฌํ์
onMutate
์์ ๋ฐํํcontext
๋ฅผ ์ด์ฉํ์ฌ ์ด์ ๊ฐ์ผ๋ก ๋๋ฆฌ๋ ๋กค๋ฐฑ ๋ก์ง์ ๊ตฌํํ ์ ์๋ค.
onSettled?: (data: unknown | undefined, error: unknown | null, variables: unknown, context: unknown, mutation: Mutation) => Promise<unknown> | unknown
: ์ผ๋ถ mutate๊ฐ settled, ์ฆ ์ฑ๊ณต์ด๋ ์คํจ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ฉด ํธ์ถ๋๋ ํจ์
mutation์ query์ ์ง์ ์ ์ผ๋ก ์ฐ๊ฒฐ๋์ง ์๋๋ค. ๋ฐ๋ผ์ mutation์ด query์ ๋ํด ๋ณ๊ฒฝํ ๋ด์ฉ์ ๋ฐ์ํ๊ธฐ ์ํด์๋ 2๊ฐ์ง ๋ฐฉ๋ฒ์ด ํ์ํ๋ค.
-
invalidateQueries()
์ ์ฌ์ฉํ์ฌ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ stale data๋ก ๋ณ๊ฒฝํ๊ณ refetch ๋ฐ๋๋ค. -
invalidateQueries ํธ์ถ ๊ฒฐ๊ณผ
- ๊ธฐ์กด์ ์ฟผ๋ฆฌ๋ฅผ stale data๋ก ๋ณ๊ฒฝํ๋ค.
- ํด๋น ์ฟผ๋ฆฌ๊ฐ
useQuery
๋ฅผ ํตํด ๋ ๋๋ง๋๊ฑฐ๋ ๋น์ทํ Hooks๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด ๋ฐ์ดํฐ๋ฅผ refetchingํ๋ค.
-
ํด๋ผ์ด์ธํธ์์ ์ฌ์ฉ์์ ์ก์ ์ ์ํด ์ด๋ค ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์๋ฒ ๋ฐ์ดํฐ๋ฅผ ๋๊ธฐํํ ํ์๊ฐ ์๋๋ฐ, ์ด๋ฐ ๊ฒฝ์ฐ์ ๋ง์ด ์ฌ์ฉํ๋ค.
-
์ธ์๋ก
QueryFilters
๋ฅผ ์ ๋ฌํ์ฌ query ์์ฒญ์ ์กฐ๊ฑด์ ์ง์ ํ ์ ์๋ค.queryClient.invalidateQueries({ queryKey: [QueryKeys.PRODUCTS], exact: false, refetchType: "all", });
exact?: boolean
: ์ฟผ๋ฆฌ ํค๋ก ์ฟผ๋ฆฌ๋ฅผ ๊ฒ์ํ ๋, ํฌ๊ด์ ์ธ ๊ฒ์์ ์ํ๋ฉดfalse
๋ฅผ ์ ๋ฌํ๊ณ ์ ํํ ์ฟผ๋ฆฌ ํค๋ฅผ ๊ฒ์ํ๋ ค๋ฉดtrue
๋ฅผ ์ ๋ฌํ๋ค.refetchType?: 'active' | 'inactive' | 'all' | 'none'
: ๊ธฐ๋ณธ๊ฐ์active
๋ก, ์ด๋ค ํ์ ์ ์ฟผ๋ฆฌ๋ฅผ ๋ค๋ฃฐ ๊ฒ์ธ์ง ์ง์ ํ ์ ์๋ค.
setQueryData()
๋ฅผ ์ฌ์ฉํ๋ฉด ์ฟผ๋ฆฌ ์บ์๋ฅผ ์ง์ ์ ๋ฐ์ดํธ ํ ์ ์๋ค.setQueryData()
๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ง์ ์บ์์ ๋ฃ์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์๋ฒ์์ ๋ฐํ๋ ๊ฒ์ฒ๋ผ ๋์ํ๋ฏ๋ก ํด๋น ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ค.- ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ fetch ๋ฐ์์ค๊ณ ์ถ์ง ์์ ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ผ๋ก, mutation์ด ์๋ฒ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ชจ๋ ๋ฐํํ ๋ ์ฌ์ฉํ๋ค.
- ์ง์ ์ ๋ฐ์ดํธ๋ ์๋ฒ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๊ฐ ๋ณ๊ฒฝ๋์์ ์ํ์ด ์๊ธฐ ๋๋ฌธ์ ๋น๊ต์ ์์ ํ์ง ์์ ์ ๊ทผ ๋ฐฉ์์ผ ์ ์๋ค.
- Optimistic Update๋ ์ฌ์ฉ์์ ์ก์
์ ์ํด ํน์ ์๋ฒ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ด ๋ฐ์๋์์ ๋, ์ค์ ์๋ฒ๋ก๋ถํฐ ์ฑ๊ณต ์๋ต์ ๋ฐ๊ธฐ ์ ์ ์ฑ๊ณตํ ๊ฒ์ด๋ผ๋ ๋๊ด์ ์ธ ๋ง์ธ๋๋ก ์ฑ๊ณตํ์ ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ํ๋ฉด์ ๊ตฌํํ๋ค.
- ๋น๊ด์ ์ ๋ฐ์ดํธ(Pessimistic Update): ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์๋ฒ ํต์ ์ผ๋ก, ์ฌ์ฉ์์ ์ก์ ์ผ๋ก ์๋ฒ ์์ฒญ์ด ๋ฐ์๋๊ณ ์๋ต์ ๋ฐ์ผ๋ฉด UI๊ฐ ์ ๋ฐ์ดํธ ๋๋ ๋ก์ง์ด๋ค.
- Optimistic Update๋ ๋ณด๋ค ๋น ๋ฅธ UI ์ ๋ฐ์ดํธ๋ก ์ฌ์ฉ์์ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ , ๋น ๋ฅธ ํผ๋๋ฐฑ์ ์ ๊ณตํ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
- ์๋ฒ์ ์๋ต์ด ์ฑ๊ณต์ ์ด์ง ์์ ๊ฒฝ์ฐ ์ด์ ์ํ๋ก ๋์๊ฐ๊ธฐ ๋๋ฌธ์ ์ ๋ฐ์ดํธ ๋กค๋ฐฑ ๋ก์ง์ ํจ๊ป ๊ตฌํํด์ผ ํ๋ค๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค.
- ์๋ฒ์ ๋ฐ์ดํฐ๊ฐ ๋ฐ์๋๊ธฐ ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ข ๋ฃ๋๊ฑฐ๋ ๋คํธ์ํฌ์ ๋ฌธ์ ๊ฐ ์๊ธด๋ค๋ฉด ๋ฐ์ดํฐ ์์ค์ ์ํ์ด ์์ผ๋ฏ๋ก, ๊ณผ๋ํ ์ฌ์ฉ์ ์ง์ํด์ผ ํ๋ค.
- ์ฃผ๋ก, ์ข์์ ๋ฒํผ ๊ฐ์ ๊ฐ๋ฒผ์ด ์๋ฒ ํต์ ์ ์ฌ์ฉ๋๋ค.
useMutation
์onMutate
์ฝ๋ฐฑ์ ์ด์ฉํ์ฌ Optimistic Update๋ฅผ ์ ๊ณตํ๊ณ ,onError
,onSettled
์ฝ๋ฐฑ์ ์ด์ฉํ์ฌ ๋กค๋ฐฑ ๋ก์ง์ ๊ตฌํํ ์ ์๋ค.- Optimistic Update ๊ตฌํ ์ ๋ฐ์ดํฐ ๊ผฌ์ ๋ฐฉ์ง:
cancelQueries() ์ฌ์ฉ
- React Query๊ฐ ์ ๊ณตํ๋
refetchOnMount
์ต์ ์ ๊ธฐ๋ณธ๊ฐ์true
์ด๊ธฐ ๋๋ฌธ์, React Query๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ๋ฐ์ดํฐ๋ฅผ ์ต์ ์ผ๋ก ์ ๋ฐ์ดํธ ํด์ฃผ๊ธฐ ์ํด refetch๋ฅผ ํ๋ค. ์ด๋ refetch ์์ ์ ์ ํํ๊ฒ ์ ์ ์๊ธฐ ๋๋ฌธ์ ํ์ด๋ฐ์ด ๊ผฌ์ด๋ฉด optimistic update ๋ฐ์ดํฐ๊ฐ ๋จผ์ ๋ณด์ด๊ณ , ๋์ค์ ์๋ต๋ refetch ๋ฐ์ดํฐ(์์ ๋ฐ์ดํฐ)๊ฐ ์ด๋ฅผ override ๋์ด ํ๋ฉด์๋ ์์ ๋ฐ์ดํฐ๊ฐ ๊ทธ๋๋ก ๋ฟ๋ ค์ง๋ ํ์์ด ์ผ์ด๋ ์ ์๋ค. ์ด๋ฅผ ๋ง๊ธฐ ์ํด์๋cancelQueries()
๋ฅผ ์ด์ฉํ์ฌ refetch๋ฅผ ์ทจ์ํด์ผ ํ๋ค.
- React Query๊ฐ ์ ๊ณตํ๋
- Optimistic Update ๊ตฌํ ํ ์์ ํ ์ฒ๋ฆฌ๋ฅผ ํ๋ ๋ฐฉ๋ฒ
onMutate
๋ด๋ถ์ ๊ตฌํํ ๋ก์ง์ ๋ณ๊ฒฝ ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ฐ์ดํธ๋ฅผ ํ๊ณ UI์ ์ถ๋ ฅํ๋ค. ์ด ์์ ์ ์๋ฒ์์ ํต์ ์์ด ์ด๋ค์ง๊ธฐ ๋๋ฌธ์ ์๋ฒ์ ์ต์ข ์๋ต๊ณผ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ค ์ ์์ด ์์ ํ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.- ์๋ฒ ๋ฐ์ดํฐ์์ ๋๊ธฐํ์ ์์ ์ฑ์ ์ฃผ๊ธฐ ์ํด์๋
onMutate
์ดํonSuccess
์ฝ๋ฐฑ์ ์๋ก์ด ๋ฐ์ดํฐ๋ก ์บ์๋ฅผ ์ ๋ฐ์ดํธํ๋ ์์ ์ด ํ์ํ๋ค. onSuccess
์ฝ๋ฐฑ์ ์๋ฒ๋ก๋ถํฐ ์ค์ ์๋ต์ ๋ฐ์์ ๋, ์ฆ Optimistic Update๋ฅผ ์ํํ๊ณ ๋ ๋ค ์๋ฒ์ ๋๊ธฐํ๋ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ UI๋ฅผ ์ ๋ฐ์ดํธํ ๋ ์ฌ์ฉํ๋ค.onSuccess
์ฝ๋ฐฑ์ ์ฌ์ฉํ์ง ์๊ณonMutate
๋ง ์์ฑํ์ ๊ฒฝ์ฐ, ์๋ฒ ์๋ต์ด ์คํจํ๋ค๋ฉด UI์ ์ค์ ๋ฐ์ดํฐ๊ฐ ๋๊ธฐํ๋์ง ์์ ์ ์๋ค. ๋ฐ๋ผ์onSuccess
๋ด๋ถ์setQueryData()
๋ฅผ ์ด์ฉํ์ฌ ์๋ก์ด ๋ฐ์ดํฐ๋ก ์บ์๋ฅผ ์ ๋ฐ์ดํธ ํด์ฃผ๋ ๊ฒ์ด ์ข๋ค.
function useInfiniteQuery<
TQueryFnData = unknown,
TError = unknown,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey
>({
queryKey,
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
...options,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
}): UseInfiniteQueryResult<TData, TError>;
useInfiniteQuery
๋ ํด๋น ์ฟผ๋ฆฌ์ ๋ํ ๋ฐ์ดํฐ ์์ (useQuery
)์ ํ๋ผ๋ฏธํฐ ๊ฐ๋ง ๋ณ๊ฒฝํ์ฌ ๋ฌดํ์ ํธ์ถํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋์ด๋ค.- ์์ ๋ ๋ฐ์ดํฐ๋ ํ์ด์ง ๋จ์๋ก ์์ /๊ด๋ฆฌํ๋ฉฐ,
getNextPageParam
,getPreviousPageParam
์ผ๋ก ๋ค์ ์์ ์ ํ์ํ ํ๋ผ๋ฏธํฐ ๊ฐ์ ์ ๋ฌํ ์ ์๋ค.
- Recoil์ Facebook์์ ๋ง๋ React๋ฅผ ์ํ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, npm์ ์ด์ฉํ์ฌ ์ค์นํ ์ ์๋ค.
<RecoilRoot>
๋ ํ์ ์ปดํฌ๋ํธ์๊ฒ recoil ์ํ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ณตํ๋ ์ปดํฌ๋ํธ์ด๋ค.<RecoilRoot>
๋ recoil ์ํ๋ฅผ ๊ณต์ ํ๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ค์ ์กฐ์ ์ปดํฌ๋ํธ์ฌ์ผ ํ๋ค. -> ํ๋ก์ ํธ์ root ์ปดํฌ๋ํธ์ ์ ๊ณตํ๋ ๊ฒ์ด ํจ์จ์ ์ผ ์ ์๋ค.- ์ฌ๋ฌ ๊ฐ์ root๊ฐ ๊ฐ์ด ์กด์ฌํ ์ ์์ผ๋ฉฐ ๊ฐ๊ฐ์ root๋ ๋ณ๊ฐ์ ์ํ๋ฅผ ๊ฐ์ง์ง๋ง,
<RecoilRoot>
๊ฐ ์ฌ๋ฌ ๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ ์ํ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ค๋ฅธ root์ ๊ณต์ ๋๋ ๋ฑ ์๊ธฐ์น ์์ ๋์์ด ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์ ํ๋์ root๋ง ์ฌ์ฉํ ๊ฒ์ ๊ถ์ฅํ๋ค.selector
์บ์๊ฐ์ ์บ์๋ค์ root ์ฌ์ด์์ ๊ณต์ ๋ ์ ์๋ค. ์ด๋selector
ํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ด๋ฏธ ์์ฑ๋ ์บ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ ์ฑ๋ฅ ํฅ์์ ์ฅ์ ์ด ์์ง๋ง, root๋ฅผ ๊ณต์ ํ ์ ์๋ค๋ ์์ธก ๋ถ๊ฐ๋ฅ์ฑ์ด ์๋ค.- root๋ค์ด ์ค์ฒฉ๋ ๊ตฌ์กฐ๋ก ์์ฑ๋์ด ์๋ค๋ฉด ์ตํ์์ root๊ฐ ์ต์์์ root๋ฅผ override ํ ์ ์๋ค.
- ๊ธฐ๋ณธ๊ฐ์
true
๋ก,override
์์ฑ์ดtrue
์ผ ๊ฒฝ์ฐ ํด๋น root๋ ์๋ก์ด recoil scope๋ฅผ ์์ฑํ๋ค. override
์์ฑ์ดfalse
์ผ ๊ฒฝ์ฐ- ์ผ๋ฐ์ ์ผ๋ก
override
์์ฑ์false
๋ก ํ๋ ๊ฒ์ recoil์์ ์ ๊ณตํ๋ ๋๋ฒ๊น ๋ฐ ๊ฐ๋ฐ ๋๊ตฌ๊ฐ ๋นํ์ฑํ๋๋ฏ๋ก ๊ถ์ฅ๋์ง ์๋๋ค. - ์ฅ์
- Recoil ๊ฐ๋ฐ ๋๊ตฌ๋ Recoil์ ์ํ ๋ฐ ์ก์
๋ก๊ทธ๋ฅผ ๋ธ๋ผ์ฐ์ ์ฝ์์ ํ์ํ๋๋ฐ, ๋ฐฐํฌ ์ ๋ณด์ ์ด์๊ฐ ๋ฐ์๋ ์ ์๋ค. ์ด๋ด ๊ฒฝ์ฐ
override
์์ฑ์false
๋ก ํด์ recoil ๊ฐ๋ฐ ๋๊ตฌ๋ฅผ ๋นํ์ฑํ ํ ์ ์๋ค.
- Recoil ๊ฐ๋ฐ ๋๊ตฌ๋ Recoil์ ์ํ ๋ฐ ์ก์
๋ก๊ทธ๋ฅผ ๋ธ๋ผ์ฐ์ ์ฝ์์ ํ์ํ๋๋ฐ, ๋ฐฐํฌ ์ ๋ณด์ ์ด์๊ฐ ๋ฐ์๋ ์ ์๋ค. ์ด๋ด ๊ฒฝ์ฐ
- ๋จ์
- ์ํ ๋ณ๊ฒฝ ์๋ฆผ ์์:
atom
๋๋selector
์ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ์ํ ๋ณ๊ฒฝ ์๋ฆผ์ด ํ์๋์ง ์๋๋ค. - ๋๋ฒ๊น ๋ชจ๋ ์ ํ: ๊ฐ๋ฐ์ ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ํ ๋๋ฒ๊น , ์ค๋ ์ท ๋ฑ์ ๊ธฐ๋ฅ์ด ์ ํ๋๋ค.
- ์ก์ ๋ก๊น ์ ํ: ๊ฐ๋ฐ์ ๋๊ตฌ์์ ์ ๊ณตํ๋ ์ก์ ๋ก๊น ๊ธฐ๋ฅ์ด ์ ํ๋๋ค. (์ก์ ๋ก๊น ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐ์ํ๋ ๋ชจ๋ ์ํ ๋ณ๊ฒฝ ์์ ์ด ๋ก๊น ๋๋ฏ๋ก ๋ฌธ์ ํด๊ฒฐ์ ๋์์ด ๋๋ค.)
- ์ฑ๋ฅ ์ ํ: ๊ฐ๋ฐ์ ๋๊ตฌ์ ๊ด๋ จ๋ ๋ถ๊ฐ์ ์ธ ์์ ์ด ์ํ๋์ง ์๊ธฐ ๋๋ฌธ์ ์ผ๋ถ ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์๋ ์ ์๋ค.
- ์ํ ๋ณ๊ฒฝ ์๋ฆผ ์์:
- ์ผ๋ฐ์ ์ผ๋ก
export type RecoilRootProps =
| {
initializeState?: (mutableSnapshot: MutableSnapshot) => void;
override?: true;
children: React.ReactNode;
}
| {
override: false;
children: React.ReactNode;
};
/**
* Root component for managing Recoil state. Most Recoil hooks should be
* called from a component nested in a <RecoilRoot>
*/
export const RecoilRoot: React.FC<RecoilRootProps>;
atom
์ ์ ์ญ ์ํ๋ก, ์ด๋ค ์ปดํฌ๋ํธ์์๋ ์ฐธ์กฐํ๊ณ ์ฌ์ฉํ ์ ์๋ค.- ์ปดํฌ๋ํธ๊ฐ
atom
์ ์ฐธ์กฐํ๋ ์๊ฐ๋ถํฐ ์ปดํฌ๋ํธ๋atom
์ ๊ตฌ๋ ํ๊ณ ์๋ ๊ฒ์ผ๋ก,atom
๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉดatom
์ ๊ตฌ๋ ํ๊ณ ์๋ ๋ชจ๋ ์ปดํฌ๋ํธ๋ ๋ฆฌ๋ ๋๋ง ๋๋ค. atom
์ ๋ฐํ๊ฐRecoilState
์ด๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ๊ฐatom
์ ์ฐธ์กฐํ ๋๋useRecoilState
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ธ์์atom
์ผ๋ก ์ ์ธ๋ state๋ฅผ ์ ๋ฌํด์ผ ํ๋ค.
interface AtomOptionsWithoutDefault<T> {
key: NodeKey;
effects?: ReadonlyArray<AtomEffect<T>>;
effects_UNSTABLE?: ReadonlyArray<AtomEffect<T>>;
dangerouslyAllowMutability?: boolean;
}
interface AtomOptionsWithDefault<T> extends AtomOptionsWithoutDefault<T> {
default: RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T> | T;
}
export type AtomOptions<T> =
| AtomOptionsWithoutDefault<T>
| AtomOptionsWithDefault<T>;
/** ๊ธฐ๋ณธ atom: RecoilState */
export function atom<T>(options: AtomOptions<T>): RecoilState<T>;
/** ๋จ์ํ ๊ฐ์ ๊ฐ์ธ๋๋ฐ ์ฌ์ฉ๋๋ atom: WrappedValue */
export namespace atom {
function value<T>(value: T): WrappedValue<T>;
}
import { atom, useRecoilState } from "recoil";
const atomState = atom<defaultValueType>({
key: "uniqueKey",
default: "initValue",
});
const [state, setState] = useRecoilState(atomState);
selector
๋ Derived state๋ฅผ ๊ณ์ฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋ฉ์๋๋ก, ๋ค๋ฅธatom
์ด๋selector
๋ฅผ ์ฝ์ด๋ค์ฌ ์๋ก์ด ๊ฐ์ ๊ณ์ฐํ๊ณ ์ด ๊ฐ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋๋ก ํด์ค๋ค.- Derived state: ๋ค๋ฅธ ์ํ(state)๋ค๋ก ๊ณ์ฐํด์ ์ป์ด๋ด๋ ์๋ก์ด ์ํ๊ฐ์ผ๋ก, props์ ์์กด์ฑ์ด ์๋ state์ด๋ค.
- ์ฃผ์ด์ง ์ข ์์ฑ ๊ฐ ์งํฉ์ ๋ํด ํญ์ ๋์ผํ ๊ฐ์ ๋ฐํํ๋ ์์ํจ์๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
selector
๋ ์ผ๋ฐ์ ์ธ ์ํ๊ฐ์ด ์๋, ์ฝ๊ธฐ ์ ์ฉ(read-only)๋ก ์ฌ์ฉ๋๋ค.selector
ํจ์๊ฐ ์คํ๋ ๋ ๋ง๋คselector
์บ์๊ฐ ์์ฑ๋๋ฉฐ ์ด ์บ์๋ ๋ฉ๋ชจ๋ฆฌ์ ๋ณด๊ด๋์ด ์ฌ์ฌ์ฉ๋๋ค. ์ด๋selector
ํจ์๋ฅผ ์คํํ ๋๋ง๋ค ์๋ก์ด ์ธ์คํด์ค๋ฅผ ๋ง๋ค ํ์๊ฐ ์์ด์ ธ ์ฑ๋ฅ ํฅ์์ ๋์์ด ๋๋ค.- return ๊ฐ์ด
RecoilValueReadOnly
์ผ ๋selector
๊ฐ์ ์ฐธ์กฐํ๊ธฐ ์ํด์๋useRecoilValue
๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
export interface ReadOnlySelectorOptions<T> {
key: string;
get: (opts: {
get: GetRecoilValue;
getCallback: GetCallback;
}) => Promise<T> | RecoilValue<T> | Loadable<T> | WrappedValue<T> | T;
dangerouslyAllowMutability?: boolean;
cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
}
/** ์ฝ๊ธฐ ์ ์ฉ(get)์ผ๋ก ์์ฑ๋์์ ๋์ ๋ฐํ๊ฐ: RecoilValueReadOnly */
export function selector<T>(
options: ReadOnlySelectorOptions<T>
): RecoilValueReadOnly<T>;
/** ์ฝ๊ธฐ์ ์ฐ๊ธฐ(get, set)๊ฐ ๋ชจ๋ ํ์ฉ๋์์ ๋์ ๋ฐํ๊ฐ: RecoilState */
export function selector<T>(
options: ReadWriteSelectorOptions<T>
): RecoilState<T>;
/** ๋จ์ํ ๊ฐ์ ๊ฐ์ธ๋๋ฐ ์ฌ์ฉ๋๋ ๋ฐํ๊ฐ: WrappedValue */
export namespace selector {
function value<T>(value: T): WrappedValue<T>;
}
selector
์ ์ธ์๋กkey
ํ๋กํผํฐ์get
๋ฉ์ธ๋๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ๋ค.- recoil์
get
๋ฉ์๋๋ ๋ฉ์๋ ์ถ์ฝํํ์ ์ฌ์ฉํ์ง ์๊ณ ํ์ดํ ํจ์๋ก ์์ฑํ๋ค. selector
๊ฐ์ ์ฐธ์กฐํ๊ธฐ ์ํด์๋useRecoilValue
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
import { selctor, useRecoilValue } from "recoil";
const mySelector = selector({
key: "mySelector",
get: ({ get }) => {
const value1 = get(myAtom1);
const value2 = get(myAtom2);
return value1 + value2;
},
});
export function Component() {
const derivedValue = useRecoilValue(mySelector);
return <div>derivedValue</div>;
}
selectorFamily
๋ฉ์๋๋key
ํ๋กํผํฐ์get
,set
๋ฉ์๋๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ์ ๋ฌํ๋ฉดselector
๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์ด๋ค.selectorFamily
๋ ๊ฒฐ๊ณผ๊ฐ์ดRecoilState
์ด๊ธฐ ๋๋ฌธ์useRecoilState
๋ฉ์๋๋ฅผ ์ด์ฉํ์ฌselector
์ ๊ฐ์ ์ฐธ์กฐํด์ผ ํ๋ค.selectorFamily
๋ ์ธ์ ์์ฑ ํ์ ์ดReadWriteSelectorFamilyOptions
์ผ๋กget
,set
ํ๋กํผํฐ๋ฅผ ์์ฑํ ๋(param) => (options) => returnValue
๋ฐฉ์์ผ๋ก ์์ฑํ๋ค.
export interface ReadWriteSelectorFamilyOptions<
T,
P extends SerializableParam
> {
key: string;
get: (
param: P
) => (opts: {
get: GetRecoilValue;
getCallback: GetCallback;
}) => Promise<T> | Loadable<T> | WrappedValue<T> | RecoilValue<T> | T;
set: (param: P) => (
opts: {
set: SetRecoilState;
get: GetRecoilValue;
reset: ResetRecoilState;
},
newValue: T | DefaultValue
) => void;
cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
dangerouslyAllowMutability?: boolean;
}
export function selectorFamily<T, P extends SerializableParam>(
options: ReadWriteSelectorFamilyOptions<T, P>
): (param: P) => RecoilState<T>;
import { selectorFamily } from "recoil";
const mySelector = selectorFamily({
key: "mySelector",
get: ({ get }) => {
// ...
},
set: ({ get, set }, newValue) => {
// ...
},
});
function useRecoilState<T>(
recoilState: RecoilState<T>
): [T, SetterOrUpdater<T>];
- ์ ์ญ ์ํ์ธ
atom
์ ์ ๊ทผํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ฉ์๋ (์ฝ๊ณ ์ฐ๊ธฐ๊ฐ ๊ฐ๋ฅ) atom
์ ๊ฐ์ ๊ตฌ๋ ํ์ฌ ์ ๋ฐ์ดํธํ ์ ์๋ hook์ผ๋ก, useState์ ๋์ผํ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.atom
์ ์ปดํฌ๋ํธ๋ฅผ ๋ฑ๋กํ๋ hook์ด๋ค.- ๋ฐํ๊ฐ์
useState
์ ๋น์ทํ๊ฒ[recoilState, setRecoilState]
์ด๋ค.
function useRecoilValue<T>(recoilValue: RecoilValue<T>): T;
- setter ํจ์ ์์ด atom์ ๊ฐ์ ๋ฐํํ๋ค. ์ฆ,
atom
์ ์ฝ๊ธฐ๋ง ํ ๋ ์ฌ์ฉํ๋ค. atom
์ ์ปดํฌ๋ํธ๋ฅผ ๋ฑ๋กํ๋ hook์ด๋ค. (atom์ด ๋ณ๊ฒฝ๋๋ ๊ฒ์ ๊ตฌ๋ ํ๋ค.)
function useSetRecoilState<T>(recoilState: RecoilState<T>): SetterOrUpdater<T>;
- setter ํจ์๋ง ๋ฐํํ๋ค. ์ฆ,
atom
๊ฐ์ ๋ณ๊ฒฝํ๋ ๋ฑ ์ฐ๊ธฐ๋ง ํ ๋ ์ฌ์ฉํ๋ค.
function useResetRecoilState(recoilState: RecoilState<any>): Resetter;
atom
์ ์ด๊น๊ฐ์ผ๋ก ์ด๊ธฐํํ ๋ ์ฌ์ฉํ๋ค.
function useRecoilCallback<Args extends ReadonlyArray<unknown>, Return>(
fn: (interface: CallbackInterface) => (...args: Args) => Return,
deps?: ReadonlyArray<unknown>
): (...args: Args) => Return;
- ์ปดํฌ๋ํธ๋ฅผ
atom
์ ๋ฑ๋กํ์ง ์๊ณ ๊ฐ์ ์ฝ์ด์ผ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค. - ์ด๋ฒคํธ์ ๋ํ ์๋ต์ผ๋ก Recoil state์ ์ ๊ทผํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
- Mocking์ผ๋ก ์์ฐ์ฑ๊น์ง ์ฑ๊ธฐ๋ FE ๊ฐ๋ฐ - Kakao Tech
- Mocking์ผ๋ก ํ๋ก ํธ์๋ DX๋ฅผ ๋์ฌ๋ณด์ - ํํดํ
- MSW๋ก API ๋ชจํนํ๊ธฐ - ์ฝด๋ค ํ๋ธ๋ก๊ทธ
์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค ๋ ํ๋ก ํธ์๋๋ ๋ฐฑ์๋์ API๋ฅผ ํ์ฉ์ด ํ์์ ์ด์๋ค. ๋๋ฌธ์ ๋ฐฑ์๋์ ๊ตฌํ์ด ๋๋๊ณ ํ๋ก ํธ์ ๊ฐ๋ฐ์ด ์์๋๋ฉด ๊ฐ์ฅ ์ด์์ ์ด๊ฒ ์ง๋ง, ์์ฐ์ฑ ์ธก๋ฉด์์ ์๊ฐ์ด ๋๋ฌด ๋ง์ด ์์๋๋ ๋จ์ ์ด ์๋ค. ๋ฐ๋ผ์ ์์ฐํ๋ ๋ฐ ์์๋๋ ์๊ฐ์ ์ค์ด๊ธฐ ์ํด ๋ฐฑ์๋์ ํ๋ก ํธ์ ๊ฐ๋ฐ์ด ๋์์ ์งํ๋๋ ๊ฒฝ์ฐ๊ฐ ์ฆ๋นํ๋ฐ, ์ด๋ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ด Mocking์ด๋ค.
์ํค ๋ฐฑ๊ณผ์์๋ Mock์ ์๋ฏธ๋ฅผ ์๋์ ๊ฐ์ด ์ ์ํ๋ค.
"๋ชจ์ ๊ฐ์ฒด(Mock Object) ๋ ์ฃผ๋ก ๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์ผ๋ก ๊ฐ๋ฐํ ํ๋ก๊ทธ๋จ์ ํ ์คํธ ํ ๊ฒฝ์ฐ ํ ์คํธ๋ฅผ ์ํํ ๋ชจ๋๊ณผ ์ฐ๊ฒฐ๋๋ ์ธ๋ถ์ ๋ค๋ฅธ ์๋น์ค๋ ๋ชจ๋๋ค์ ์ค์ ์ฌ์ฉํ๋ ๋ชจ๋์ ์ฌ์ฉํ์ง ์๊ณ ์ค์ ์ ๋ชจ๋์ "ํ๋ด"๋ด๋ "๊ฐ์ง" ๋ชจ๋์ ์์ฑํ์ฌ ํ ์คํธ์ ํจ์ฉ์ฑ์ ๋์ด๋๋ฐ ์ฌ์ฉํ๋ ๊ฐ์ฒด์ด๋ค. ์ฌ์ฉ์ ์ธํฐํ์ด์ค(UI)๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์คํธ ๋ฑ๊ณผ ๊ฐ์ด ์๋ํ๋ ํ ์คํธ๋ฅผ ์ํํ๊ธฐ ์ด๋ ค์ด ๋ ๋๋ฆฌ ์ฌ์ฉ๋๋ค."
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์คํธ : ์๋ฃ์ ๋ณ๊ฒฝ์ ์๋ฐํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ํ ์์ ์ ํ ์คํธ ํ๋ ๊ฒฝ์ฐ ํ ์คํธ ์ํ ํ ๋งค๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ฃ๋ฅผ ์๋๋๋ก ๋๋ ค๋์ผ ํ๋๋ฐ ์ด๋ด ๊ฒฝ์ฐ ๋ชจ์ ๊ฐ์ฒด๋ฅผ ์ด์ฉํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ต์ ํ๋ด๋ด์ด ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์์ด ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
์ฆ, ํ๋ก ํธ์๋์ Mocking์ด๋ ๋ฐฑ์๋ API์ ๋ํ ์์กด์ฑ์ ๋ฎ์ถ๊ณ ์ API๊ฐ ํ์ํ ์์ ์ ์ค์ API๊ฐ ์๋ ๋ชจ์ API๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ์ด๋ค.
-
ํ๋ฉด์ ํ์ํ ๋ฐ์ดํฐ ์ํ๋ฅผ ๋ด๋ถ ๋ก์ง์ ์ง์ Mocking ์์ฑ
- ์ฅ์ : ๊ตฌํ์ด ์ฌ์ ๋น ๋ฅด๊ฒ ์ ์ฉํ ์ ์์
- ๋จ์ : ์ค์ API๊ฐ ๋ค์ด์ฌ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋น์ค ๋ก์ง์ ๋ค์ ์์ ํด์ผ ํ๊ณ , ์ค์ API์ ํ๋ฆ๊ณผ ๋ค๋ฅผ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ ๋๋ฌธ์ HTTP ๋ฉ์๋์ ๋คํธ์ํฌ ์๋ต ์ํ์ ๋์ํ๊ธฐ ์ด๋ ค์
-
Mock ์๋ฒ๋ฅผ ๋ณ๋๋ก ๊ตฌํ
- ์ฅ์ : ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋น์ค ๋ก์ง์ ์์ ํ์ง ์์๋ ๋๊ณ , HTTP ๋ฉ์๋์ ๋คํธ์ํฌ ์๋ต ์ํ์ ๋์ํ ์ ์์
- ๋จ์ : ์๋ฒ๋ฅผ ๊ตฌํํด์ผ ํ๊ณ , ๋ก์ปฌ์ด ์๋ ๋ค๋ฅธ ๊ณณ์ ๊ณต์ ๋ฅผ ํด์ผ ํ๋ค๋ฉด ์ถ๊ฐ ํ๊ฒฝ ๊ตฌ์ฑ ์์ ๋ฑ ๋น์ฉ๊ณผ ๊ณต์๊ฐ ๋ง์ด ๋ค์ด๊ฐ.
-
Mocking ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
๊ฐ์์์๋ 3๋ฒ์ ๋ฐฉ๋ฒ์ ์ฑํํ์ฌ, MSW(Mock Service Worker)์ GraphQl์ ์ฌ์ฉํด์ Mocking์ ๊ตฌํํ๋ค.
- MSW๋ Service Worker๋ฅผ ํตํด ์๋ฒ์ ์ค์ ๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ์ ๋ชจ์ ์๋ต(Mocked response)์ ๋ณด๋ด์ฃผ๋ API Mocking library๋ก, Mock ์๋ฒ๋ฅผ ๊ตฌ์ถํ์ง ์์๋ API๋ฅผ ๋คํธ์ํฌ ์์ค์์ Mocking ํ ์ ์๋ค.
- Service Worker๋ ์น ์ ํ๋ฆฌ์ผ์ด์ , ๋ธ๋ผ์ฐ์ ๋ฐ ๋คํธ์ํฌ(์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ) ์ฌ์ด์ ์๋ ํ๋ก์ ์๋ฒ ์ญํ ๋ก, ๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , ๋คํธ์ํฌ ์ฌ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ์ ๋ฐ๋ผ ์ ์ ํ ์กฐ์น๋ฅผ ์ทจํ๊ณ , ์๋ฒ์ ์์ฃผํ๋ ์์ฐ์ ์ ๋ฐ์ดํธํ๊ธฐ ์ํ API์ด๋ค.
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฉ์ธ ์ค๋ ๋์ ๋ถ๋ฆฌ๋ ๋ณ๋์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋(worker context)์์ ์คํ์ํฌ ์ ์๋ ๊ธฐ์ ๋ก, DOM์ ์ ๊ทผ ๊ถํ์ด ์๊ณ JavaScript์ ๋ค๋ฅธ ์ค๋ ๋์์ ์คํ๋์ด UI Blocking ์์ด ์ฐ์ฐ์ ์ฒ๋ฆฌํ ์ ์๋ค. (MSW๊ฐ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํ๋ ์์ํฌ์ ์ข ์์ ์ด์ง ์๊ณ ํธํ์ฑ ๋๊ฒ ์๋ํ๋ ์ด์ )
- ์ด๋ฒคํธ ๊ธฐ๋ฐ์ ์์ ์์ด๋ฉฐ, ๋น๋๊ธฐ์์ผ๋ก ์ค๊ณ๋์ด ์์
- ์ฃผ๋ก ์ฌ์ฉ๋๋ ๊ณณ
- ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ๊ธฐ๋ฅ
- ํธ์ ๋ฉ์ธ์ง์ ๋ฐ์
- ๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ๋ ํ์: Service Worker๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์๋ฒ ์ฌ์ด์์ Request๋ฅผ ๊ฐ๋ก์ฑ ์ง์ Fetch์ ๋ํ ์ปจํธ๋กค์ ํ ์ ์์ (HTTP Request, Response๋ฅผ ๋ณด๊ณ ์บ์ฑ ์ฒ๋ฆฌ, ๋ก๊น ๋ฑ)
- ์ฌ์ฉ์ด ์ ํ๋๋ ๊ฒฝ์ฐ
- IE์ ๊ฐ์ ์ผ๋ถ ๋ธ๋ผ์ฐ์
- ์ค๊ฐ์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ๊ฐ๋ก์ฑ๊ณ ์กฐ์ํ ์ ์๋ ๊ธฐ๋ฅ์ ๊ฐ์ก๊ธฐ ๋๋ฌธ์ ๋ณด์์์ ์ด์ ๋ก localhost๊ฐ ์๋ ํ๊ฒฝ์ด๋ผ๋ฉด HTTPS ๋ณด์ ํ๋กํ ์ฝ ํ๊ฒฝ์์๋ง ๋์ํจ (FireFox ์ ์ธ)
- App์์ Service Worker API ์ค์น
- Service Worker ์ค์น ์๋ฃ
- App์์ Service Worker์ ์ค์ ์์ฒญ์ ์ ๋ฌ
- Service Worker๊ฐ ์ค์ ์์ฒญ์ ๋ณต์ฌํด์ MSW์ ์ ๋ฌ
- MSW๋ ์์ฒญ์ ๋ํ ๋ชจ์ ์๋ต์ ์์ฑํ์ฌ Service Worker์ ์๋ต ์ ๊ณต
- Service Worker๊ฐ ์ต์ข ์ ์ผ๋ก App์ ๋ชจ์ ์๋ต ์ ๋ฌ
- Service Worker๋ browser ํ๊ฒฝ์์๋ง ๋์ํ๋ค. ๋ฐ๋ผ์ browser ํ๊ฒฝ์์ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฐ๊ณผ ๋๋ฒ๊น ์ ํ๊ธฐ์ ํธ๋ฆฌํ๋ค.
- node ํ๊ฒฝ์์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์ผ๋ฐ์ ์ผ๋ก ํ ์คํธ ํ๊ฒฝ์์ ์ฌ์ฉํ ๋์ด๋ฉฐ, Jest๋ก ํ ์คํธ๊ฐ ๊ฐ๋ฅํ๋ค.
graphql-tag
: Apollo GraphQL ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ ํจํค์ง๋ก, GraphQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. GraphQL ์ฟผ๋ฆฌ๋ฅผ ๋ฌธ์์ด๋ก ์์ฑํ๋ ๋์ ํ๊ทธ๋ ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด(gql
)์ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๋ค. -> ์ฝ๋ ๊ฐ๋ ์ฑ์ ๋์graphql-request
: ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก GraphQL ์์ฒญ์ ๋ณด๋ด๋ ๋ฐ ์ฌ์ฉ๋๋ ํจํค์ง๋ก, fetch API๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋์ด ์์ผ๋ฉฐ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ด๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ ํจ์๋ฅผ ์ ๊ณตํ๋ค.- ๋ ํจํค์ง์ ์์ ๊ฐ์ ํน์ง์ผ๋ก
graphql-tag
์graphql-request
๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด GraphQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค. - ๋ค๋ฅธ ํจํค์ง๋ฅผ ์ฌ์ฉํ๋ฉด ์ถ๊ฐ์ ์ธ ์ค์ ๊ณผ ๊ตฌ์ฑ์ด ํ์ํ ์ ์์ผ๋ฉฐ MSW์ ํธํ์ฑ์ด ๋ณด์ฅ๋์ง ์์ ์ ์๋ค.
MSW๋ GraphQL API์ ๋ํ ์์ฒญ์ ์บก์ณํ๊ธฐ ์ํ Request handlers(query
, mutation
)์ utilities(operation
, link
)๋ฅผ ์ ๊ณตํ๋ค. ์ด๋ฅผ ์ด์ฉํ์ฌ ํด๋ผ์ด์ธํธ์์ ์๋ฒ API๋ฅผ ํธ์ถํ ๋ ๊ฐ์ง ์๋ต์ ๋ฐํํ ์ ์๋ค.
- ์ข
๋ฅ
query()
: GraphQL query์ ๋ํ ๊ฐ์ง ์๋ต์ ๋ฐํํ๋ ์๋ต ํธ๋ค๋ฌ๋ก,graphql.query(queryName, callbackFunction)
์ผ๋ก ์์ฑํ๋ค.mutation()
: GraphQL mutation์ ๋ํ ๊ฐ์ง ์๋ต์ ๋ฐํํ๋ ์๋ต ํธ๋ค๋ฌ๋ก,graphql.mutation(queryName, callbackFunction)
์ผ๋ก ์์ฑํ๋ค.
- ์ธ์ ์ค๋ช
(๋ ๋ฉ์๋ ๋ชจ๋ ๊ณตํต๋จ)
queryName
: ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก, ์๋ฒ์์ ์ ์ํ GraphQL query name ๋๋ mutation name๊ณผ ์ผ์นํด์ผ ํ๋ค.callbackFunction
: ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋๋ ์ฝ๋ฐฑ ํจ์์ด๋ค. ์ด ํจ์๋req
,res
,ctx
๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์(req, res, ctx) => { return res( /* ๋ด์ฉ */ ) }
๋ก ์์ฑํ๋ค.req
: ์์ฒญ ๊ฐ์ฒด๋ก, GraphQL query ๋๋ mutation, ๋ณ์ ๋ฐ ๊ธฐํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๋ค.res
: ์๋ต ๊ฐ์ฒด๋ก, ํด๋ผ์ด์ธํธ์ ๋ฐํํ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ๋ ๋ฉ์๋๋ฅผ ํฌํจํ๋ค.ctx
: ์ปจํ ์คํธ ๊ฐ์ฒด๋ก, ์๋ต ๊ฐ์ฒด ๋ฐ ์์ฒญ ๊ฐ์ฒด๋ฅผ ์กฐ์ํ๋ ๋ฐ ์ฌ์ฉํ๋ค.
operation()
- GraphQL query๋ฅผ ํ์ฑํ๊ณ ํด๋น operation ํ์
์ ์๋ณํ์ฌ MSW ์์ฒญ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ๋ ์ญํ ์ ํ๋ค. ์ฆ,
query()
,mutation()
๋ฑ ๋ชจ๋ operation ํ์ ์ ์ฒ๋ฆฌํ ์ ์์ด ์ ์ฐํ๋ค. graphql.operation(operationType, queryName, callbackFunction)
: ์ฒซ ๋ฒ์งธ ์ธ์๋กoperationType
์ ์ถ๊ฐ ์ ๋ฌํ๋ ๊ฒ ์ธ์๋ ๋ค๋ฅธ ์์ฒญ ํธ๋ค๋ฌ์ ์์ฑ ๋ฐฉ๋ฒ์ด ๋์ผํ๋ค.
- GraphQL query๋ฅผ ํ์ฑํ๊ณ ํด๋น operation ํ์
์ ์๋ณํ์ฌ MSW ์์ฒญ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ๋ ์ญํ ์ ํ๋ค. ์ฆ,
link()
์ฐธ๊ณ ๋ฌธ์: GraphQL ๊ฐ๋ ์ก๊ธฐ - ์นด์นด์ค Tech
- GraphQL์ API๋ฅผ ์ฝ๊ฒ ์ค๊ณํ๊ณ ํธ์ถํ๋ ๋ฐ ์ฌ์ฉ๋๋ ์ฟผ๋ฆฌ ์ธ์ด๋ก, REST API๋ฅผ ๋์ฒดํ๊ธฐ ์ํ ๋ชฉ์ ์ผ๋ก ๋ง๋ค์ด์ก๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ ์ํ ์ธ์ด์ธ sql๊ณผ ๋ฌ๋ฆฌ gql์ ์น ํด๋ผ์ด์ธํธ๊ฐ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ๋ก ๋ถํฐ ํจ์จ์ ์ผ๋ก ๊ฐ์ ธ์ค๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค. ๋๋ฌธ์ sql ๋ฌธ์ฅ์ ๋ฐฑ์๋์์ ์์ฑํ๊ณ ํธ์ถํ๋ ๋ฐ๋ณ, gql ๋ฌธ์ฅ์ ์ฃผ๋ก ํด๋ผ์ด์ธํธ ์์คํ ์์ ์์ฑํ๊ณ ํธ์ถํ๋ค.
- ์์ฒญํ๋ ์ฟผ๋ฆฌ๋ฌธ์ ๊ตฌ์กฐ์ ์๋ต ๋ด์ฉ์ ๊ตฌ์กฐ๊ฐ ๊ฑฐ์ ๋์ผํ๋ค.
์ฐธ๊ณ ๋ฌธ์: GraphQL vs REST API - Apollo Blog
- REST API๋ url, method ๋ฑ์ ์กฐํฉํ๊ธฐ ๋๋ฌธ์ ๋ค์ํ EndPoint๊ฐ ์กด์ฌํ์ง๋ง, gql์ ํ๋์ EndPoint๋ง ์กด์ฌํ๋ค. ๋ํ gql API๋ ๋ถ๋ฌ์ค๋ ๋ฐ์ดํฐ์ ์ข
๋ฅ๋ฅผ ์ฟผ๋ฆฌ ์กฐํฉ์ ํตํด ๊ฒฐ์ ํ๋ค.
- EndPoint: ์๋ฒ ์์ฒญ์ ๊ตฌ๋ถํด์ฃผ๋ url
- ์์: REST API์์๋ ๊ฐ Endpoint๋ง๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค SQL ์ฟผ๋ฆฌ๊ฐ ๋ฌ๋ผ์ง๋ ๋ฐ๋ฉด, gql API๋ gql ์คํค๋ง์ ํ์ ๋ง๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค SQL ์ฟผ๋ฆฌ๊ฐ ๋ฌ๋ผ์ง๋ค.
- REST API์ ๋ฌ๋ฆฌ ํด๋ผ์ด์ธํธ๊ฐ ํ์ํ ๋ฐ์ดํฐ๋ง ์์ฒญํ ์ ์๋ค.
- ์ด๋ ๋ฐ์ดํฐ ์ ์ก ์์ ์ค์ด๊ณ , ํด๋ผ์ด์ธํธ ์ธก์์ ํ์ํ ์ฒ๋ฆฌ๋ฅผ ์ํํ ์ ์๋๋ก ๋์์ค๋ค.
- ๋ํ, ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฌ ๋ฆฌ์์ค๋ฅผ ๋ณ๋ ฌ๋ก ์์ฒญํ ์ ์์ด ์๋ฒ์์ ๋ณ๋ชฉ ํ์์ด ๋ฐ์ํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์๋ค.
GraphQL์ ์ฌ์ฉํ๋ฉด ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฌ ๋ฆฌ์์ค๋ฅผ ๋ณ๋ ฌ๋ก ์์ฒญํ ์ ์๋ ์ด์ ?
GraphQL์ ์ฌ๋ฌ ํ๋๋ฅผ ๊ฐ์ง๋ ๋จ์ผ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋ผ์ด์ธํธ๊ฐ ์ฌ๋ฌ ๋ฆฌ์์ค๋ฅผ ํ๋ฒ์ ์์ฒญํ ์ ์๋ค. ์์๋ฅผ ํตํด ์ดํด๋ณด์.
// GraphQL ์คํค๋ง
type User {
id: ID!
name: String!
email: String!
}
type Post {
id: ID!
title: String!
body: String!
author: User!
}
type Query {
posts: [Post!]!
user(id: ID!): User!
}
์ ์์ ์ ๊ฐ์ ์คํค๋ง์์ ํด๋ผ์ด์ธํธ๋ posts
ํ๋๋ฅผ ์ ํํ์ฌ ๋ชจ๋ ๊ฒ์๋ฌผ๊ณผ ํด๋น ๊ฒ์๋ฌผ์ ์์ฑ์๋ฅผ ํ ๋ฒ์ ์์ฒญํ ์ ์์ผ๋ฉฐ, user
ํ๋๋ฅผ ์ ํํ์ฌ ํน์ ์ฌ์ฉ์์ ์ด๋ฆ, ์ด๋ฉ์ผ, ์ฌ์ฉ์๊ฐ ์ฌ๋ฆฐ ๋ชจ๋ ๊ฒ์๋ฌผ์ ์์ฒญ ํ ์ ์๋ค.
// ์ฌ๋ฌ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ ์ ์๋ ๋จ์ผ ์ฟผ๋ฆฌ
query {
posts {
id
title
author {
name
}
}
user(id: "123") {
name
email
posts {
title
}
}
}
์ ์ฟผ๋ฆฌ๋ ์ฌ๋ฌ ํ๋๋ฅผ ํฌํจํ๊ณ ์๊ธฐ ๋๋ฌธ์ ํ๋ฒ์ ๋ค์ํ ๋ฆฌ์์ค๋ฅผ ์์ฒญํ ์ ์๋ค. ๋๋ถ์ด ์ ์ฟผ๋ฆฌ์์ ํ์ํ ํ๋๋ง ์ ํํ ์๋ ์์ด ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์๋ค.
- ์คํค๋ง๋ GraphQL API๊ฐ ์ ๊ณตํ๋ ๋ฐ์ดํฐ ํ์ ์ ์ ์ํ๋ ๊ตฌ์กฐ์ฒด(structure)๋ก, gql ์ฟผ๋ฆฌ์ ์ง์ ์ (EndPoint)์ด๋ค.
- GraphQL์ ์คํค๋ง๋ก ํ์
์์คํ
์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ์ ์ํ๋ค.
- GraphQL ์ฟผ๋ฆฌ์์ ํ์ฉ๋๋ ๋ชจ๋ ๋ฐ์ดํฐ ์ ํ๊ณผ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉฐ, ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ ์ํ ์ฟผ๋ฆฌ๋ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ธฐ ์ํ ๋ฎคํ ์ด์ ๋ฑ์ ์ ์ํ๋ค.
- ์ด๋ ํด๋ผ์ด์ธํธ์๊ฒ API์ ๊ตฌ์กฐ์ ์ฌ์ฉ ๊ฐ๋ฅํ ์ฟผ๋ฆฌ์ ๋ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
- ํด๋ผ์ด์ธํธ๋ ์ด ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฒญ์ ์์ฑํ ์ ์๋ค.
- GraphQL์ ๋ฐํ์์์ ์ฟผ๋ฆฌ์ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ , ํ์คํ๋ JSON ํ์์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
- ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์ ์ก๋๋ GraphQL ์์ฒญ์ ์ ํ์ ๋งํ๋ฉฐ,
query
,mutation
,subscription
์ด ์๋ค.- query๋ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ๋ฐ ์ฌ์ฉํ๋ ์์ ์ผ๋ก, REST API์์์ GET ๋ฉ์๋์ ํด๋นํ๋ค. (CRUD์ R)
- mutation์ด๋ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฑฐ๋ ์์ฑํ๋ ๋ฐ ์ฌ์ฉํ๋ ์์ ์ผ๋ก, REST API์์์ POST, PATCH, DELETE ๋ฉ์๋์ ํด๋นํ๋ค. (CRUD์ CUD)
subscription
: ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์ฌํญ์ ๊ตฌ๋ ํ๋ ์์ ์ผ๋ก, ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- operation์ ์๋ฒ์์ ์์ฒญํ๋ ์์ ์ ์ ํ์ ๋ช ํํ๊ฒ ๋ํ๋ด๋ฏ๋ก, ํด๋ผ์ด์ธํธ์ ์๋ฒ ๊ฐ์ ํต์ ์ ํจ์จ์ ์ผ๋ก ๋ง๋ ๋ค.
- request ๋ฉ์๋: GraphQL์ query๋ mutation์ ์ง์ ํ HTTP EndPoint(url)๋ก POST ์์ฒญ(๋ณ๊ฒฝ ๊ฐ๋ฅ)์ ๋ณด๋ด๋ ๋ฉ์๋์ด๋ค.
- request ๋ฉ์๋๋ Promise ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์
then
์ฒด์ด๋์ด๋async/await
์ ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค. - request ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ GraphQL์์๋ query์ variables๋ฅผ ๋ชจ๋ JSON ํํ๋ก ๋ณํํ์ฌ ์ ๋ฌํ๋ค.
request(url, query, variables);
-
url
: GraphQL์ EndPoint === ์์ฒญ์ด ์ ์ก๋ ์๋ฒ -
query
: ์๋ฒ์์ ์๋ตํ query ํํ -
variables
- ๊ฐ์ฒด ํํ๋ก,
method
,headers
,body
๋ฅผ ํฌํจํ ์ ์๋ค. method
: ์์ฑํ์ง ์์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ POST์ด๋ฉฐ, GET, PUT, PATCH, DELETE์ ์์ฑํ ์ ์๋ค.body
: method, headers์ ๋ฌ๋ฆฌ body๋ผ๋ ์ด๋ฆ์ ํ๋กํผํฐ๋ฅผ ์์ฑํ์ง ์๊ณ , body๋ก ์ ๋ฌํ ํ๋กํผํฐ key/value ์์ ๊ฐ์ฒด๋ก ์์ฑํ๋ค. -> GraphQL์ด ์๋ฒ์ ์์ฒญํ ๋ ์๋ฌต์ ์ผ๋ก ๊ฐ์ฒด ๋ด์ฉ์ HTTP ์์ฒญ ๋ฐ๋์ ๋ด๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.variables
์ ์์ฑํ ํ๋กํผํฐ๋ query๋ฌธ์์$
๋ฅผ ๋ถ์ฌ ๋ณ์๋ช ์ผ๋ก ์ฐธ์กฐํ ์ ์๋ค.
- ๊ฐ์ฒด ํํ๋ก,
import { gql } from 'graphql-tag';
import { request } from 'graphql-request';
const query = gql`
query GET_VALUE($id: string) {
key1: value1,
key2: value2,
}
`;
const variables = {
method: 'POST',
headers: {
Authorization: 'Bearer MY_TOKEN',
},
{ id: 'ID' },
};
request('/graphql', query, variables);
- UUID(Universally Unique IDentifier, ๋ฒ์ฉ ๊ณ ์ ์๋ณ์)๋ ๋คํธ์ํฌ ์์์ ๊ณ ์ ์ฑ์ ๋ณด์ฅํ๋ ID๋ฅผ ๋ง๋ค๊ธฐ ์ํ ํ์ค ๊ท์ฝ์ผ๋ก, GUID(Globally Unique IDentifier)๋ผ๊ณ ๋ ๋ถ๋ฆฐ๋ค.
- UUID๋ 32๊ฐ์ 16์ง์ ์ซ์๊ฐ ํ์ดํ(
-
)์ผ๋ก ๊ตฌ๋ถ๋ 5๊ฐ์ ๊ทธ๋ฃน์ผ๋ก ํ์๋๋ฉฐ, ์ด 36์์ ๋ํด8-4-4-4-12
ํ์์ผ๋ก ํํ๋๋ค.- ์์:
123e4567-e89b-12d3-a456-426614174000
- ์์:
- ๋ ธ๋ ํ๊ฒฝ์์๋ UUID๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ณตํ์ฌ, ์์ฝ๊ฒ ์ค์นํ๊ณ ์ฌ์ฉํ ์ ์๋ค.
UUID๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- uuid ์ค์น
npm i uuid
- TypeScript์์ uuid ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด uuid์ ๋ํ ํ์
์ ์๊ฐ ๋ ํจํค์ง๋ฅผ ์ถ๊ฐ๋ก ์ค์นํด์ผ ํ๋ค.
npm i --save @types/uuid
export const NIL: NIL;
export const parse: parse;
export const stringify: stringify;
export const v1: v1;
export const v3: v3;
export const v4: v4;
export const v5: v5;
export const validate: validate;
export const version: version;
uuid.v1()
: ํ์ฌ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก UUID๋ฅผ ์์ฑํ๋ฉฐ, ์ด๋ uuid๊ฐ ์์ฑ๋ ์๊ฐ๊ณผ MAC์ฃผ์๋ก uuid๋ฅผ ์ ์ถํ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ์ฑ์ด ๋จ์ด์ง๋ค๋ ๋จ์ ์ด ์๋ค.uuid.v3()
: MD5 ํด์ ๊ธฐ์ค์ผ๋ก UUID๋ฅผ ์์ฑํ๋ค.uuid.v4()
: ๋๋ค๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก UUID๋ฅผ ์์ฑํ๋ฉฐ, ๋ง์ ์ฌ๋๋ค์ด ์ฃผ๋ก ์ฌ์ฉํ๋ ๋ฒ์ ์ด๋ค.uuid.v5()
: SHA-1 ํด์ ๊ธฐ์ค์ผ๋ก UUID๋ฅผ ์์ฑํ๋ค.
import { v4 as uuidv4 } from "uuid"; // as๋ import ๋ฐ์ ์์์ ๋ณ์นญ์ ์ ํ๋ ๊ฒ์ผ๋ก ๋ค๋ฅธ ๋ค์ด๋ฐ์ ์์ฑํด๋ ๋๋ค. -> ex: { v4 as uuid }
const productItem = {
id: uuidv4(), // import ๋ฐ์ uuidv4 ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด import ๋ฐ์ ๋ฒ์ ์ ๋ง๋ UUID๋ฅผ ์์ฑํด์ค๋ค.
};
- ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋๋ง๋ค uuid๊ฐ ํธ์ถ๋์ด uuid๋ ๋ ์๋ก์ด id๊ฐ์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ๊ธฐ์กด์ id๊ฐ์ ์ ์งํ๊ธฐ ์ด๋ ต๋ค. (์์: ์ํ id๋ฅผ ์ด์ฉํ์ฌ ์๋ฒ์ ์ํ ์ ๋ณด๋ฅผ GET ์์ฒญํ๋ ๋ก์ง์ด๋ผ๋ฉด, ํด๋น ํ์ด์ง์์ ์๋ก๊ณ ์นจ์ ํ์ ๊ฒฝ์ฐ uuid๊ฐ ์์ฑํ ์ํ id๊ฐ์ด ๋ณ๊ฒฝ๋๊ธฐ ๋๋ฌธ์ ๋์ผํ ์ํ id๋ฅผ ์๋ฒ์์ ์ฐพ์ง ๋ชปํ๋ค.)
- TypeScript ํํธ์์ ๋ฏธ์์ฑ๋ ๋ด์ฉ์ Learning TypeScript ์ฑ ์ ์๋ ํ ๋ค ์์ฑํ๊ฒ ์
type
์์ ํ๋กํผํฐ๋ค์ ๋ฝ์ ์งํฉkeys
๋ฅผ ์ ํํ์ฌ type ์ ํ์ ๊ตฌ์ฑํ ์ ์๋ค.keys
๋type
์ ์์ฑ๋ ํ๋กํผํฐ ํค๋ฅผ ๋ฌธ์์ด๋ก ์์ฑํ๊ณ|
๋ฅผ ์ด์ฉํ์ฌ ๋์ดํ๋ค.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
};
RequestInit ์ธํฐํ์ด์ค๋ fetch() ํจ์๋ฅผ ํธ์ถํ ๋ ์ ๋ฌํ ์ ์๋ ๊ฐ์ฒด์ ํ์ ์ ์ ์ํ๋ค. ์ด ๊ฐ์ฒด๋ HTTP ์์ฒญ์ ๊ตฌ์ฑํ๋ ์ฌ๋ฌ๊ฐ์ง ์์ฑ์ ๊ฐ์ง๊ณ ์๋ค.
- body (BodyInit | null): HTTP ์์ฒญ ๋ฐ๋์ ๋ด์ฉ์ ๋ด๋ BodyInt ํ์ ์ ๊ฐ์ฒด์ด๋ค. ์ด ์์ฑ์ ์๋ต ๊ฐ๋ฅํ๋ฉฐ ์๋ตํ ๊ฒฝ์ฐ HTTP ์์ฒญ์๋ ๋ฐ๋๊ฐ ์์์ ๋ํ๋ธ๋ค.
- cache (RequestCache): HTTP ์์ฒญ์ ๋ํ ์บ์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๋ํ๋ธ๋ค. RequestCache ์ด๊ฑฐํ ๊ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- credentials (RequestCredentials): HTTP ์์ฒญ ์ ํจ๊ป ๋ณด๋ผ ์ธ์ฆ ์ ๋ณด๋ฅผ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. RequestCredentials ์ด๊ฑฐํ ๊ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- headers (HeadersInit): HTTP ์์ฒญ ํค๋๋ฅผ ๋ํ๋ด๋ HeadersInit ํ์ ์ ๊ฐ์ฒด์ด๋ค. ๊ฐ์ฒด ๋ฆฌํฐ๋ด, Headers ๊ฐ์ฒด, ๋๋ ๋ ํญ๋ชฉ ๋ฐฐ์ด์ ๋ฐฐ์ด๋ก ์ ๋ฌํ ์ ์๋ค.
- integrity (string): ์์ฒญ ๋ฆฌ์์ค์ ๋ฌด๊ฒฐ์ฑ์ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. ๋๊ฐ SHA-256 ํด์ ๊ฐ์ผ๋ก ํํ๋๋ค
- keepalive (boolean): HTTP keep-alive ๊ธฐ๋ฅ ์ฌ์ฉ ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ๋ถ๋ฆฌ์ธ ๊ฐ์ด๋ค.
- method (string): HTTP ์์ฒญ ๋ฉ์๋๋ฅผ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. ๊ธฐ๋ณธ๊ฐ์ "GET"์ด๋ค.
- mode (RequestMode): HTTP ์์ฒญ์ CORS ์ฒ๋ฆฌ ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. RequestMode ์ด๊ฑฐํ ๊ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- redirect (RequestRedirect): HTTP ์์ฒญ ์ค ๋ฆฌ๋๋ ์ ์ฒ๋ฆฌ ๋ฐฉ๋ฒ์ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. RequestRedirect ์ด๊ฑฐํ ๊ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- referrer (string): HTTP ์์ฒญ์์ Referrer ์ ๋ณด๋ฅผ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. ๋ณดํต ์ด์ ํ์ด์ง URL์ ์ ๋ฌํ๋ค.
- referrerPolicy (ReferrerPolicy): HTTP ์์ฒญ์์ Referrer ์ ๋ณด์ ์ ๋ฌ ๋ฐฉ๋ฒ์ ๋ํ๋ด๋ ๋ฌธ์์ด์ด๋ค. ReferrerPolicy ์ด๊ฑฐํ ๊ฐ ์ค ํ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- signal (AbortSignal | null): ์์ฒญ์ ์ทจ์ํ ์ ์๋ AbortSignal ๊ฐ์ฒด๋ฅผ ๋ํ๋ธ๋ค.
- window (null): HTTP ์์ฒญ์ ์คํํ Window ๊ฐ์ฒด์ด๋ค. ์ผ๋ฐ์ ์ผ๋ก null ๊ฐ์ ๊ฐ์ง๋๋ค.
interface RequestInit {
/** A BodyInit object or null to set request's body. */
body?: BodyInit | null;
/** A string indicating how the request will interact with the browser's cache to set request's cache. */
cache?: RequestCache;
/** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
credentials?: RequestCredentials;
/** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
headers?: HeadersInit;
/** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
integrity?: string;
/** A boolean to set request's keepalive. */
keepalive?: boolean;
/** A string to set request's method. */
method?: string;
/** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
mode?: RequestMode;
/** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
redirect?: RequestRedirect;
/** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
referrer?: string;
/** A referrer policy to set request's referrerPolicy. */
referrerPolicy?: ReferrerPolicy;
/** An AbortSignal to set request's signal. */
signal?: AbortSignal | null;
/** Can only be null. Used to disassociate request from any Window. */
window?: null;
}
CartItem ์ปดํฌ๋ํธ์์ input
์ onChange
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋, input
์ ์์ฑ๋ amount ๊ฐ์ ์ฝ๊ธฐ ์ํด ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ด๋ถ์์ ์ด๋ฒคํธ๋ฅผ ์ผ์ผํจ ๋์(e.target
)์ ์ฐธ์กฐํด์ผ ํ๋ค. ์ด๋, ๋งค๊ฐ๋ณ์๋ก ์์ฑ๋ e
์ ํ์
์ React.SyntheticEvent
์ผ๋ก ์์ฑํ๋๋ฐ SyntheticEvent
๊ฐ ์ด๋ค ํ์
์ธ์ง ๋ด๋ถ๋ฅผ ์ดํด๋ณด์.
SyntheticEvent
ํ์ ์BaseSyntheticEvent
ํ์ ์ ํ์ฅํ ๊ฒ์ผ๋ก,BaseSyntheticEvent
์๋currentTarget
,target
,preventDefault()
๋ฑ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋งค๊ฐ๋ณ์event
๊ฐ ์ ๊ณตํ๋ ํ๋กํผํฐ์ ๋ฉ์๋์ ๋ํ ํ์ ์ด ์ ์๋์ด ์๋ค.
interface BaseSyntheticEvent<E = object, C = any, T = any> {
nativeEvent: E;
currentTarget: C;
target: T;
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
persist(): void;
timeStamp: number;
type: string;
}
/**
* currentTarget - a reference to the element on which the event listener is registered.
*
* target - a reference to the element from which the event was originally dispatched.
* This might be a child element to the element on which the event listener is registered.
* If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/11508#issuecomment-256045682
*/
interface SyntheticEvent<T = Element, E = Event>
extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
src/queryClient.tsx์์ restfetcher ํจ์๋ฅผ ๋ง๋ค ๋ fetchOptions์ body์ Access-Control-Allow-Origin
ํ๋กํผํฐ๋ฅผ ์์ฑํ๋ค. Access-Control-Allow-Origin
๊ฐ ๋ญ๊น?
- HTTP ํต์ ์ผ๋ก ์น์ฌ์ดํธ์ ๋ฆฌ์์ค์ ์ ๊ทผํ ๋ ๋๋ฉ์ธ์ด ๋ค๋ฅผ ๊ฒฝ์ฐ ๋ณด์์์ ์ด์ ๋ก ์๋ฒ ์ ๊ทผ์ ์ ํํ๋๋ฐ,
Access-Control-Allow-Origin
๋ ์ด ๊ถํ์ ๋ค๋ฃจ๋ HTTP ํค๋์ด๋ค. - ์ด ํค๋๋ ์๋ฒ ์๋ต์ ํฌํจ๋์ด ํด๋น ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ ๋๋ฉ์ธ์ ํ์ํ๋ค.
- ์ฆ,
Access-Control-Allow-Origin
ํค๋์ ๋ช ์๋ ๋๋ฉ์ธ๊ณผ ์น ํ์ด์ง์ ํธ์คํธ๊ฐ ์ผ์นํด์ผ ๋ฆฌ์์ค ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
- *: ๋ชจ๋ ๋๋ฉ์ธ์์ ์ ๊ทผ ํ์ฉํ๋ค๋ ๋ป์ผ๋ก, ์๋ฒ๋ ๋ชจ๋ ๋๋ฉ์ธ์์์ ์์ฒญ์ ๋ํด ๋ฆฌ์์ค์ ๋ํ ์๋ต์ ๋ฐํํ๋ค.
- ํน์ ๋๋ฉ์ธ๋ช : Access-Control-Allow-Origin ํค๋์ ํด๋น ๋๋ฉ์ธ๋ช ์ด ํฌํจ๋์ด ์์ผ๋ฉด, ํด๋น ๋๋ฉ์ธ์์๋ง ์ ๊ทผ์ด ํ์ฉ๋๋ค.
- null: ๋ธ๋ผ์ฐ์ ๊ฐ CORS ํ๋กํ ์ฝ์ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ, ํด๋น ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ค. ์ด ๊ฒฝ์ฐ, ์๋ฒ๋ Access-Control-Allow-Origin ํค๋๋ฅผ ์๋ตํ์ง ์์ผ๋ฉฐ, ๋ธ๋ผ์ฐ์ ๋ ์๋์ผ๋ก null์ ์ง์ ํ๋ค.
- GET, DELETE: body๋ฅผ ์์ฑํด์ ์๋ฒ์ ์์ฒญ์ ๋ฃ์ด๋ body๊ฐ์ ๋ฌด์๋๋ค.
- POST, PUT, PATCH: body๋ฅผ ์์ฑํด์ผ ํ๋ฉฐ, ๊ฐ์ JSON ํํ๋ก ์ ๋ฌํ๋ค. (
JSON.stringify()
์ฌ์ฉ)
- New Suspense SSR Architecture in React 18
- ๋ชจ๋ ํ๋ก ํธ์๋ ํ ์คํธ ์ ๋ต - 1ํธ(Testing Overview) - ์ฝด๋ค ํ๋ธ๋ก๊ทธ
- Inside React Query
- My๊ตฌ๋ ์ React Query ์ ํ๊ธฐ - Kakao Tech
- ์นด์นด์คํ์ด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ค์ด React Query๋ฅผ ์ ํํ ์ด์ - Kakao Tech
- [React Query] useQuery ๋์์๋ฆฌ(1)