TanStack Query ๊ฐœ๋ฐœ ์ค‘ ๋งˆ์ฃผ์นœ ๋ฒ„๊ทธ ๊ฐœ์„ ํ•˜๊ณ  ๊ธฐ์—ฌํ•˜๊ธฐ1

TanStack Query ๊ฐœ๋ฐœ ์ค‘ ๋งˆ์ฃผ์นœ ๋ฒ„๊ทธ ๊ฐœ์„ ํ•˜๊ณ  ๊ธฐ์—ฌํ•˜๊ธฐ1

ํ”„๋กœ์ ํŠธ ๊ฐœ๋ฐœ ๋„์ค‘ ๋งˆ์ฃผ์นœ ๋ฒ„๊ทธ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ๊ธฐ์—ฌํ•˜๋Š” ์—ํ”ผ์†Œ๋“œ๋ฅผ ์ด์•ผ๊ธฐํ•ฉ๋‹ˆ๋‹ค.

โ€ข6๋ถ„

๋“ค์–ด๊ฐ€๋ฉฐ

TanStack Query์—์„œ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ฟผ๋ฆฌ๋ฅผ ๋น„ํ™œ์„ฑํ™” ํ˜น์€ ์ผ์‹œ์ •์ง€๋ฅผ skipToken์„ ํ†ตํ•ด์„œ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”. useQueries์—์„œ skipToken๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๋ฐ˜ํ™˜๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ unknown์œผ๋กœ ํƒ€์ž… ์ถ”๋ก ์ด ์•ˆ๋˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฒ„๊ทธ๋ฅผ ๊ฐœ์„ ํ•˜์—ฌ ๊ธฐ์—ฌํ•˜๋Š” ๊ณผ์ •์„ ์ด์•ผ๊ธฐํ•˜๊ณ ์ž ํ•ด์š”.

TanStack Query๋ž€

TanStack Query ๋žœ๋”ฉ ํŽ˜์ด์ง€

ํ”„๋ก ํŠธ์—”๋“œ ๊ธฐ์ˆ ์…‹์—์„œ TanStack Query(์˜ˆ์ „์—” React Query๋ผ๊ณ  ๋ถˆ๋Ÿฌ์กŒ์Šต๋‹ˆ๋‹ค.)๋Š” ๋ฐ์ดํ„ฐ ํŒจ์น˜, ๋™๊ธฐํ™”, ์—…๋ฐ์ดํŠธ ๋“ฑ ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด์—์š”. ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์—์„œ ๊ฐ€์ ธ์˜ค๊ณ  ์ด๋ฅผ ์บ์‹ฑํ•˜๊ณ  ์ž๋™์œผ๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋“ฑ ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด์š”. ํ”„๋ŸฐํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ์•„์ฃผ ์นœ์ˆ™ํ•˜์ฃ .

skipToken์€ ๋ญ”๊ฐ€์š”

์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋„๋ก ํ•˜๋ ค๋ฉด enabled ์˜ต์…˜์ด๋‚˜ skipToken์„ ์‚ฌ์šฉํ•ด์•ผํ•ด์š”. ํŠนํžˆ skipToken์€ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ฟผ๋ฆฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•ด์š”.

// TanStack Query ๊ณต์‹ Docs์˜ ์˜ˆ์ œ ์ผ๋ถ€
function Todos() {
const [filter, setFilter] = React.useState<string | undefined>()

const { data } = useQuery({
queryKey: ['todos', filter],
// โฌ‡๏ธ ํ•„ํ„ฐ๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ๋น„์–ด ์žˆ์œผ๋ฉด ๋น„ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.
queryFn: filter ? () => fetchTodos(filter) : skipToken,
})

return (
<div>
// ๐Ÿš€ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
<FiltersForm onApply={setFilter} />
{data && <TodosTable data={data} />}
</div>
)
}

TanStack Query ๊ณต์‹ ๋ฌธ์„œ์— ์žˆ๋Š” ์˜ˆ์ œ๋ฅผ ๊ฐ€์ ธ์™”๋Š”๋ฐ์š”. ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜๋ฉด ์œ„์— ์„ ์–ธํ•œ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ผ์š”. ๋ฐ˜๋Œ€๋กœ ํ•„ํ„ฐ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ํ•ด๋‹น ์ฟผ๋ฆฌ๋Š” ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์กฐ๊ฑด์œผ๋กœ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•˜์—ฌ ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ ์ž ํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.

๊ฐœ๋ฐœ ์ค‘ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋‹ค

// ๋™์•„๋ฆฌ ์ผ์ •์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
export const useSchedule = ({ isNotAdmin }: UseScheduleParams) => {
return useQueries({
queries: [
{
queryKey: QUERY_KEY.SCHEDULE,
queryFn: () => getSchedule, // API Call
},
{
// ํ•ด๋‹น ์ผ์ • ์ •๋ณด๋Š” ์šด์˜์ง„์—๊ฒŒ๋งŒ ์ œ๊ณต ๋ฉ๋‹ˆ๋‹ค.
queryKey: QUERY_KEY.NOTICE.ADMIN,
queryFn: isNotAdmin
? skipToken // ์–ด๋“œ๋ฏผ์ด ์•„๋‹ ๊ฒฝ์šฐ ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๋น„ํ™œ์„ฑํ™”
: () => getAdminSchedule(), // API Call
},
],
});
};

// ์‚ฌ์šฉ ์˜ˆ์‹œ
const results = useSchedule({ isNotAdmin: true });

results[0].data; // โœ… ์‚ฌ์šฉ์ž ์ผ์ •, data : BaseResponse<Schedule>
results[1].data; // ๐Ÿšจ ์šด์˜์ง„ ์ผ์ •, data : unknown

useQueries๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์ฟผ๋ฆฌ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ์–ด์š”. ๋™์•„๋ฆฌ ๊ด€๋ จ ํ”„๋กœ์ ํŠธ์—์„œ ์ผ์ •์„ ์กฐํšŒํ•˜๋Š” ํ›…์—์„œ ์šด์˜์ง„(์–ด๋“œ๋ฏผ)์ผ ๊ฒฝ์šฐ ์šด์˜์ง„์—๊ฒŒ๋งŒ ํ•„์š”ํ•œ ์ผ์ •์„ ๊ฐ™์ด ์กฐํšŒ๋˜๋Š” ํ›…์—์„œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฑฐ์˜ˆ์š”. ๋ฐ˜ํ™˜๋œ ์šด์˜์ง„ ์ผ์ •์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด unknown์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ํ›…์—์„œ๋„ ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  ๋””๋ฒ„๊น… ํ•œ ๊ฒฐ๊ณผ skipToken๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐœ์ƒ๋˜๋Š” ๋ฌธ์ œ์˜€์–ด์š”. ์ฒ˜์Œ์—๋Š” ์ œ๊ฐ€ useQueries๋ฅผ ์ž˜๋ชป ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‚˜ ๊ณต์‹ ๋ฌธ์„œ๋„ ํ™•์ธํ•ด ๋ดค์ง€๋งŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์—ˆ์ฃ . ์ด ๋ฌธ์ œ๊ฐ€ ๋ฒ„๊ทธ์ธ ์ง€๋Š” ๊นƒํ—ˆ๋ธŒ(Github)์—์„œ TanStack/Query ์ด์Šˆ๋ฅผ ํ†ตํ•ด ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ ์ค‘ ๋งˆ์ฃผ์นœ ๋ฌธ์ œ๋Š” ๋ฒ„๊ทธ์˜€๋‹ค

PR#7035

useQuerirs์—์„œ skipToken์„ ์‚ฌ์šฉํ•˜๋ฉด ์ •์ƒ์ ์ธ ํƒ€์ž… ์ถ”๋ก ์ด ์•ˆ๋˜๋Š” ๋ฒ„๊ทธ๋Š” 3์›” 6์ผ ๊นƒํ—ˆ๋ธŒ ์ด์Šˆ๋ฅผ ํ†ตํ•ด ์ œ๋ณด๋œ ์ƒํƒœ์˜€์–ด์š”. ์ œ ํ™˜๊ฒฝ์—์„œ๋งŒ ๊ฒช๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ์ด์Šˆ๋ฅผ ๋ณด์ž๋งˆ์ž ๋‚ด๊ฐ€ ํ•œ ๋ฒˆ ํ•ด๊ฒฐํ•ด ๋ด์•ผ๊ฒ ๋‹ค๊ณ  ์ƒ๊ฐ๋˜์—ˆ๊ณ , ์ด์ „์— ์˜คํ”ˆ์†Œ์Šค์— ๊ธฐ์—ฌํ•œ ๊ธฐ์–ต์œผ๋กœ CONTRIBUTING.md๋„ ์‚ดํŽด๋ดค์–ด์š”. ์ด์ „์— ๊ธฐ์—ฌํ–ˆ๋˜ VitePress์™€ ํฌ๊ฒŒ ๋‹ค๋ฅธ ์ ์ด ์—†์—ˆ๊ณ  ๋ฐ”๋กœ ํฌํฌ(Fork)๋ฅผ ์ง„ํ–‰ํ•˜์—ฌ ํ™˜๊ฒฝ ์„ธํŒ…์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์—„์ฒญ๋‚œ ์ฝ”๋“œ, ์‚ฝ์งˆ์˜ ์‹œ์ž‘

tanstack-query

๋กœ์ปฌ ํ™˜๊ฒฝ์— ์„ธํŒ… ํ›„ ์ฝ”๋“œ๋ฅผ ์—ด์–ด๋ณธ ์ˆœ๊ฐ„ ๊นœ์ง ๋†€๋ž์–ด์š”. ์—„์ฒญ๋‚œ ์ฝ”๋“œ์˜ ์–‘๊ณผ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจโ€ฆ ๋ชจ๋…ธ๋ ˆํฌ(Monorepo)๋กœ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(Vue, Svelte, Solid, React, Angular)๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์—ˆ์–ด์š”.

์ฝ”๋“œ๋ฅผ ๋ณด์ž๋งˆ์ž ์–ด๋””์„œ๋ถ€ํ„ฐ ๋ณด๊ณ  ์ˆ˜์ •ํ•ด์•ผ ํ• ์ง€ ๋ง‰๋ง‰ํ–ˆ์–ด์š”. ์ด๊ฒŒ ํฐ ๊ทœ๋ชจ์˜ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ตฌ๋‚˜๋ฅผ ๋А๊ผˆ์ฃ . ์ผ๋‹จ ๋จผ์ € useQueries๊ฐ€ ์„ ์–ธ๋˜์–ด ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๊ธฐ๋กœ ํ–ˆ์–ด์š”. ํƒ€์ž… ์ถ”๋ก ์ด ์•ˆ๋˜๋Š” ๋ฒ„๊ทธ๋‹ˆ ํƒ€์ž… ์ถ”๋ก  ์ชฝ์„ ์‚ดํŽด๋ณด๋ฉด ๋˜๊ฒ ์ง€ ํ•˜๋Š” ๋ง‰์—ฐํ•œ ์ƒ๊ฐ์ด์—ˆ์–ด์š”. ์ƒ๋Œ€๋Š” ์—„์ฒญ๋‚œ ๊ทœ๋ชจ์˜ ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๋ฐ ๋ง์ด์ฃ .

// useQueries ํƒ€์ž… ์ถ”๋ก ์— ๋Œ€ํ•œ ์ฝ”๋“œ ์ผ๋ถ€
/**
* QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
*/
export type QueriesOptions<
T extends Array<any>,
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<UseQueryOptionsForUseQueries>
: T extends []
? []
: T extends [infer Head]
? [...TResults, GetUseQueryOptionsForUseQueries<Head>]
: T extends [infer Head, ...infer Tails]
? QueriesOptions<
[...Tails],
[...TResults, GetUseQueryOptionsForUseQueries<Head>],
[...TDepth, 1]
>
: Array<unknown> extends T
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
T extends Array<
UseQueryOptionsForUseQueries<
infer TQueryFnData,
infer TError,
infer TData,
infer TQueryKey
>
>
? Array<
UseQueryOptionsForUseQueries<
TQueryFnData,
TError,
TData,
TQueryKey
>
>
: // Fallback
Array<UseQueryOptionsForUseQueries>;

์–ด๋–ค๊ฐ€์š”? ๋ณด์ž๋งˆ์ž ์‰ฝ์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ์ ˆ๋กœ ๋“ค์ฃ ? ์—ฌ๊ธฐ๋ถ€ํ„ฐ ์ €์˜ ์‚ฝ์งˆ์€ ์‹œ์ž‘๋˜์—ˆ๋‹ต๋‹ˆ๋‹ค. useQueries๋ฅผ ์จ๋ณด๊ธฐ๋งŒ ํ–ˆ์ง€ ๋‚ด๋ถ€์ ์ธ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ค ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ ์ž‘๋™ํ•˜๊ณ  ํƒ€์ž…์€ ์„ ์–ธ๋˜์–ด ์ถ”๋ก  ๋˜๋Š”์ง€๋„ ๋ชจ๋ฅด๋Š” ์ œ๊ฐ€ ํ•ด๋‹น ์ด์Šˆ๋ฅผ ํ•œ ๋ฒˆ ๊ณ ์ณ๋ณด๊ฒ ๋‹ค๋Š” ๋งˆ์Œ๊ฐ€์ง์œผ๋กœ ๋ฐค์„ ์ƒˆ์šฐ๋ฉฐ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค.

useQueries๋ฅผ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € useQuery์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ–ˆ๊ณ  ์„ ์–ธ๋œ ํƒ€์ž…์„ ์–ด๋А์ •๋„ ์ˆ™์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ ๋‚ด๋ถ€์ ์ธ ํ•ต์‹ฌ ์ฝ”๋“œ๋Š” useBaseQuery์— ์ž‘์„ฑ๋˜์–ด ์žˆ์–ด์š”. ํ•ด๋‹น ๋ถ€๋ถ„์„ ์ค‘์‹ฌ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค. (ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์ฝ์–ด๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•ด์š”, ํ•ด๋‹น ์ฝ”๋“œ ๋ถ„์„์€ ์—ฌ์œ ๊ฐ€ ๋œ๋‹ค๋ฉด ๋‹ค์Œ์— ๊ฒŒ์‹œ๊ธ€๋กœ ๋‹ค๋ค„๋ณผ๊ฒŒ์š”.)

์–ด๋А ์ •๋„ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ดํ•ดํ•ด๋„ ํ•ด๋‹น ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์› ์–ด์š”. ํƒ€์ž… ์ถ”๋ก  ๊ด€๋ จ์—์„œ๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์— ๊ด€๋ จ๋œ ๋ฌธ์ œ๊ธฐ๋„ ํ•˜๊ณ ์š”. ์œ„์— ์žˆ๋Š” QueriesOptions๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํƒ€์ž… ์ถ”๋ก ์„ ํ•˜๋Š”GetUseQueryOptionsForUseQueries๋„ ๋ถ„์„ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ์ˆ˜๋งŽ์€ ์‹œ๋„๋ฅผ ํ–ˆ์ง€๋งŒ ํ•ด๊ฒฐํ•˜๊ธฐ๋Š” ์–ด๋ ค์› ์–ด์š”.

ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๋ง์ด ๋ณด์ด๋‹ค

comment-hint

ํ•ด๋‹น ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•˜๋Š” ์‹œ๋„๋Š” ์—ฌ๋Ÿฌ ์ฐจ๋ก€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. as const๋ฅผ ํ†ตํ•ด ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ํ•˜๊ฒŒ ๋˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ํƒ€์ž… ์ถ”๋ก ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋Œ“๊ธ€(Comment)๊ฐ€ ์žˆ์—ˆ๊ณ  Jaaneek ๊ฐœ๋ฐœ์ž๋‹˜๊ป˜์„œ ํ•ด๋‹น ์ด์Šˆ๋ฅผ ๊ณ ์นœ PR(#7037)์„ ๋ณด๋‚ธ ์ƒํƒœ์˜€์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ•ด๋‹น PR์€ Merge๊ฐ€ ์•„๋‹Œ Closed ์ƒํƒœ์˜€์–ด์š”.

comment-skiptoken

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ solid/vue/svelte์— ์ ์šฉํ•˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜์–ด ์ง€๊ธˆ ์ข‹์€ ํ•ด๊ฒฐ ์ฑ…์„ ์ฐพ์„ ์ˆ˜ ์—†๋‹ค๋Š” ์ด์œ ๋กœ Closed ์ƒํƒœ๊ฐ€ ๋œ ๊ฒƒ์ด์—์š”.

ํ•˜์ง€๋งŒ ์ €๋Š” ๊ธฐ์กด PR๊ณผ ๋Œ“๊ธ€์—์„œ ํžŒํŠธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. as const, ์ฆ‰ readonly ์ƒํƒœ์ผ ๊ฒฝ์šฐ ์ •์ƒ์ ์ธ ํƒ€์ž… ์ถ”๋ก ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ readonly ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด์„œ ํ•˜๋ฉด ๋˜์ง€ ์•Š์„๊นŒ๋ผ๋Š” ์ƒ๊ฐ์— ๋‹ค์‹œ ์‹œ๋„ํ•ด๋ณผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

/**
* QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
*/
export type QueriesOptions<
T extends Array<any>,
TResults extends Array<any> = [],
TDepth extends ReadonlyArray<number> = [],
> = TDepth['length'] extends MAXIMUM_DEPTH
? Array<UseQueryOptionsForUseQueries>
: T extends []
? []
: T extends [infer Head]
? [...TResults, GetUseQueryOptionsForUseQueries<Head>]
: T extends [infer Head, ...infer Tails]
? QueriesOptions<
[...Tails],
[...TResults, GetUseQueryOptionsForUseQueries<Head>],
[...TDepth, 1]
>
: ReadonlyArray<unknown> extends T // ๐Ÿ› ๏ธ Readonly !
? T
: // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!
// use this to infer the param types in the case of Array.map() argument
T extends Array<
UseQueryOptionsForUseQueries<
infer TQueryFnData,
infer TError,
infer TData,
infer TQueryKey
>
>
? Array<
UseQueryOptionsForUseQueries<
TQueryFnData,
TError,
TData,
TQueryKey
>
>
: // Fallback
Array<UseQueryOptionsForUseQueries>;

ํƒ€์ž… ์ถ”๋ก ์—์„œ readonly๋กœ ์„ ์–ธ์ด ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„์„ ์ˆœ์ฐจ๋ ฅ์œผ๋กœ ์ž…๋ ฅํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋””๋ฒ„๊น…ํ•˜๋ฉฐ ์‹œ๋„ํ•œ ๋์— QueriesOptions์—์„œ Array<unknown> extends T๋ฅผ ReadonlyArray<unknown> extends T๋กœ readonly ํƒ€์ž…์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด as const๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ์ •์ƒ์ ์œผ๋กœ ์ถ”๋ก ์ด ๋˜๋Š” ์‚ฌ์‹ค์„ ํ™•์ธํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (ํƒ€์ž… ์ถ”๋ก ๊ณผ ๊ด€๋ จํ•˜์—ฌ dev-tool์ด ์—†์–ด ํƒ€์ž… ์ถ”๋ก ์ด ์ž˜ ์œ ๋„๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ฐจ๋ก€๋Œ€๋กœ ์ˆ˜์ •ํ•˜์—ฌ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ• ๋ฐ–์— ์—†์—ˆ์–ด์š”. ๊ด€๋ จํ•˜์—ฌ ์ข‹์€ dev-tool์ด๋‚˜ ํŒ์ด ์žˆ๋‹ค๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”.)

ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ๊ธฐ์กด์— Jaaneek ๊ฐœ๋ฐœ์ž๋‹˜์˜ PR ์ฝ”๋“œ์—์„œ๋„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด์—์š”. ๋‹ค๋ฅธ ์ˆ˜๋งŽ์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜์–ด Closed ๋˜์—ˆ๋‹ค๋Š” ๋ฐฉ๋ฒ•์ด์ฃ . ๊ทธ๋ž˜์„œ ์ €๋Š” RadonlyArray๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐœ์ƒ๋˜๋Š” ์—๋Ÿฌ๋ฅผ ๊ณ ์น˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์‹œ๋„ํ–ˆ์–ด์š”.

๋Œ์•„๋ณด๋ฉด ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ

// useQueries.ts
const defaultedQueries = React.useMemo(() =>
queries.map((opts) => {
const defaultedOptions = client.defaultQueryOptions(
opts as QueryObserverOptions<unknown, Error, unknown, unknown, QueryKey>,
);
}),
);

ReadonlyArray๋กœ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด 5๊ฐœ ์ •๋„ ํƒ€์ž… ์˜ค๋ฅ˜๊ฐ€ ์ถ”๊ฐ€๋กœ ๋ฐœ์ƒ๋˜๋Š”๋ฐ ์ด๋Š” ReadonlyArray๋กœ ์ธํ•ด ํƒ€์ž… ์ง€์ •์ด ์•ˆ๋˜๋Š” ์—๋Ÿฌ์˜€์–ด์š”. ์ฐจ๋ก€๋Œ€๋กœ ํƒ€์ž…์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ๊ฐœ์„ ํ•ด๋‚˜๊ฐ”๋Š”๋ฐ, ์ตœ์ข…์ ์œผ๋กœ defaultedQueries์— ์žˆ๋Š” opts์˜ ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋‹ค๋ฅธ ๋ชจ๋“  ์—๋Ÿฌ๊ฐ€ ํ•ด๊ฒฐ๋˜๋Š” ๋ฌธ์ œ์˜€์–ด์š”.

// useQeries์˜ skipToken ํƒ€์ž… ์ถ”๋ก  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
it('TData should have correct type when conditional skipToken is passed', () => {
const queryResults = useQueries({
queries: [
{
queryKey: ['withSkipToken'],
queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5),
},
],
});

const data = queryResults[0].data;

expectTypeOf(data).toEqualTypeOf<number | undefined>();
});

์ตœ์ข…์ ์œผ๋กœ ํ•ด๋‹น ์ด์Šˆ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ์ž‘์„ฑํ•˜์—ฌ ์‚ฌ์ „์— ์—๋Ÿฌ๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ ์šฉ์ดํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

comment-tkdodo

PR(#7150)์„ ์ƒ์„ฑํ•˜๊ณ  TanStack Query์˜ Maintainer์ธ TkDodo ๊ฐœ๋ฐœ์ž๋‹˜์ด ๋‹ค๋ฅธ ์–ด๋Œ‘ํ„ฐ(Angular, Solid, Svelte, Vue)์—๋„ ์ ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๋ฒ„๊ทธ๋ฅผ ๊ฐœ์„ ํ•ด๋‹ฌ๋ผ๋Š” ๋Œ“๊ธ€์ด ๋‹ฌ๋ ค ์ถ”๊ฐ€๋กœ ์ปค๋ฐ‹ํ•˜์—ฌ ํ•ด๋‹น ์ด์Šˆ๋ฅผ ์™„๋ฒฝํžˆ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ๊ทธ ๋‹ค์Œ ๋‚  Merge ๋˜์–ด TanStack Query์˜ Contributor๊ฐ€ ๋์Šต๋‹ˆ๋‹ค. ๐Ÿ‘

changelog

์ œ๊ฐ€ ๊ฐœ์„ ํ•œ ํƒ€์ž… ์ถ”๋ก  ๋ฒ„๊ทธ๋Š” v5.28.8์— ํฌํ•จ๋˜์–ด ๋ฆด๋ฆฌ์ฆˆ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์น˜๋ฉฐ

์–ด๋–ค ๋ฌธ์ œ๋“  ๋Œ์•„๋ณด๋ฉด ์ฐธ ์‰ฌ์šด ๊ฑฐ ๊ฐ™์•„์š”. ๊ทธ ๊ณผ์ •์—์„œ ์ˆ˜๋งŽ์€ ์‹œํ–‰์ฐฉ์˜ค๊ฐ€ ์žˆ์—ˆ๊ณ  ๋ฐฐ์šฐ๋Š” ๊ฒƒ๋„ ๋งŽ์•˜๊ณ  ์—„์ฒญ ์–ด๋ ค์› ๋Š”๋ฐ ๋ง์ด์ฃ . ํ•ด๋‹น ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์—์„œ๋„ ์–ด๋ ค์›€์ด ๋งŽ์•˜์–ด์š”. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํƒ€์ž…, any ํƒ€์ž…์—์„œ ์›ํ•˜๋Š” ํƒ€์ž…์œผ๋กœ ์ถ”๋ก ํ•˜๋Š” ๋ณต์žกํ•œ ๋กœ์ง, ๋‚ด๋ถ€์ ์ธ ์ดํ•ด๋„ ํ•„์š”ํ•˜๊ณ  ๋ณตํ•ฉ์ ์œผ๋กœ ๋งŽ์€ ์ดํ•ด๋„๊ฐ€ ํ•„์š”ํ–ˆ๋˜ ๊ฒฝํ—˜์ด์—์š”.

ํ•ด๋‹น ๋ฒ„๊ทธ๋ฅผ ๊ฐœ์„ ํ•˜๋ฉด์„œ ๊ธฐ์ˆ ์ ์œผ๋กœ ๋ฐฐ์šด ๊ฒƒ์ด ๋งŽ์•„์š”. useQuery์˜ ๋งค์ปค๋‹ˆ์ฆ˜ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์„ฑ, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ, NX CI ๋“ฑ ๋งŽ์€ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์— ์ต์ˆ™ํ•ด์งˆ ์ˆ˜ ์žˆ์—ˆ์–ด์š”. ํšŒ๊ณ  ๊ธ€์—๋Š” ์ด ์ˆ˜๋งŽ์€ ๊ณผ์ •์ด ๋“ค์–ด๊ฐ€ ์žˆ์ง€ ์•Š์ง€๋งŒ ์ด๋Ÿฐ ์‚ฌ์†Œํ•œ ๋ฒ„๊ทธ๋ฅผ ๊ณ ์น˜๋Š” ๊ณผ์ •์—์„œ ์ƒˆ๋กœ์šด ๊ฒฝํ—˜๊ณผ ๊ธฐ์ˆ ์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒŒ ์˜คํ”ˆ์†Œ์Šค ๊ธฐ์—ฌ์— ์žฅ์ ์ธ ๊ฑฐ ๊ฐ™์•„์š”.

์—ฌ๋Ÿฌ๋ถ„๋“ค๋„ ์˜คํ”ˆ์†Œ์Šค ๊ธฐ์—ฌ๋ฅผ ํ†ตํ•ด์„œ ๋งŽ์€ ์„ฑ์žฅ๊ณผ ์‚ฌ๊ณ ๋ฅผ ํ–ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. TanStack Query์— ๊ธฐ์—ฌ์— ๊ด€๋ จํ•˜์—ฌ ๊ถ๊ธˆํ•˜์‹œ๊ฑฐ๋‚˜ ๊ด€์‹ฌ์ด ์žˆ์œผ์‹œ๋‹ค๋ฉด ์—ฐ๋ฝ ์ฃผ์‹œ๋ฉด ์ตœ๋Œ€ํ•œ ๋„์™€๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ