๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
  • What would life be If we had no courage to attemp anything?
๐“๐จ๐๐š๐ฒ ๐ˆ ๐‹๐ž๐š๐ซ๐ง

์‚ฌ๋‚ด ๋ณด์•ˆ ๋ฆฌ์†Œ์Šค ํ†ตํ•ฉ ํ”Œ๋žซํผ ๊ตฌ์ถ•๊ธฐ : 3. URL์„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๊ณ„ํ•œ ํ•„ํ„ฐ, ๊ฒ€์ƒ‰, ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ƒํƒœ ๊ด€๋ฆฌ ์ „๋žต

by DevIseo 2025. 6. 13.

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ์ฒด์ปค ๋ชฉ๋ก ํ™”๋ฉด์—์„œ ํ•„ํ„ฐ, ๊ฒ€์ƒ‰, ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ƒํƒœ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ–ˆ๋Š”์ง€ ์ •๋ฆฌํ•˜๊ณ ์ž ํ•œ๋‹ค.
ํŠนํžˆ Next.js ๊ธฐ๋ฐ˜ ํ™˜๊ฒฝ์—์„œ URL ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ์ƒํƒœ๋ฅผ ํ†ตํ•ฉ ๊ด€๋ฆฌํ•˜๊ณ ,
์ด๋ฅผ React Query์™€ ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR)๊นŒ์ง€ ์—ฐ๊ฒฐํ•ด ์ผ๊ด€๋œ UX์™€ ์ดˆ๊ธฐ ์„ฑ๋Šฅ์„ ํ™•๋ณดํ•˜๋Š” ์ „๋žต์„ ์„ค๊ณ„ํ–ˆ๋‹ค.
 

๐Ÿ” ๋ฌธ์ œ ์ •์˜

์ฒ˜์Œ์—๋Š” ํ•„ํ„ฐ์™€ ๊ฒ€์ƒ‰ ์ƒํƒœ๋ฅผ ์ „์—ญ ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ localStorage์— ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ–ˆ์—ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์€ ์‹ค์งˆ์ ์ธ ํ•œ๊ณ„์ ์ด ์กด์žฌํ–ˆ๋‹ค.

  • ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์ƒํƒœ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์–ด ์‚ฌ์šฉ์„ฑ์ด ๋–จ์–ด์ง
  • ํ•„ํ„ฐ๋ง๋œ ์ƒํƒœ๋ฅผ URL๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์—†์–ด ํ˜‘์—… ์‹œ ๋ถˆํŽธํ•จ
  • ๋ธŒ๋ผ์šฐ์ € ๋’ค๋กœ๊ฐ€๊ธฐ/์•ž์œผ๋กœ๊ฐ€๊ธฐ ๋™์ž‘์ด ๊ธฐ๋Œ€์™€ ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•จ
  • SSR์„ ํ™œ์šฉํ•œ ์ดˆ๊ธฐ ๋ Œ๋”๋ง๊ณผ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•„ ๋กœ๋”ฉ ์‹œ UX๊ฐ€ ์ข‹์ง€ ์•Š์Œ

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, URL ์ฟผ๋ฆฌ ๊ธฐ๋ฐ˜์˜ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐฉ์‹์œผ๋กœ ์ „ํ™˜ํ–ˆ๋‹ค.
 

โœ… ํ•ด๊ฒฐ ๋ฐฉํ–ฅ: useSearchParams ๊ธฐ๋ฐ˜์˜ URL ์ƒํƒœ ๋™๊ธฐํ™”

Next.js์˜ useSearchParams๋ฅผ ํ™œ์šฉํ•ด ํ•„ํ„ฐ ์ƒํƒœ, ๊ฒ€์ƒ‰์–ด, ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ •๋ณด๋ฅผ URL ์ฟผ๋ฆฌ๋กœ ์ง์ ‘ ๊ด€๋ฆฌํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.
์ด ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์„ ์ œ๊ณตํ–ˆ๋‹ค.

  • ์ƒˆ๋กœ๊ณ ์นจ ์‹œ์—๋„ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋จ
  • ํ•„ํ„ฐ๋ง๋œ ํ™”๋ฉด์„ URL๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Œ
  • ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ์™€ ์ž๋™์œผ๋กœ ์—ฐ๋™๋˜์–ด UX๊ฐ€ ์ž์—ฐ์Šค๋Ÿฌ์›€
  • SSR์—์„œ๋„ ๋™์ผํ•œ ์ฟผ๋ฆฌ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์ „ ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ์–ด ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋จ

 

โš™๏ธ ์ฃผ์š” ๊ตฌํ˜„ ๋‚ด์šฉ

ํ•ด๋‹น ์ฝ”๋“œ๋“ค์€ ์˜ˆ์‹œ ์ฝ”๋“œ๋“ค๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

1. URL ํŒŒ๋ผ๋ฏธํ„ฐ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜

์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” URLSearchParams ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ–ˆ์œผ๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ set, delete ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋กœ ๊ฐ’์„ ๊ฐฑ์‹ ํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.
 

const updateQueryParams = (newParams: Record<string, any>) => {
  const params = new URLSearchParams(window.location.search);

  Object.entries(newParams).forEach(([key, value]) => {
    if (Array.isArray(value)) {
      value.length > 0 ? params.set(key, value.join(',')) : params.delete(key);
    } else {
      value ? params.set(key, String(value)) : params.delete(key);
    }
  });

  router.push(`/checker?${params.toString()}`, { scroll: false });
};

 

2. URL์—์„œ ํ•„ํ„ฐ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋กœ์ง

ํŽ˜์ด์ง€ ์ง„์ž… ์‹œ searchParams์—์„œ ๊ฐ ํ•„ํ„ฐ ๊ฐ’์„ ์ฝ์–ด๋“ค์—ฌ ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ๋ฅผ ๊ตฌ์„ฑํ–ˆ๋‹ค.

const filterState = useMemo(() => ({
  status: searchParams.get('status')?.split(',') || [],
  search: searchParams.get('search') || '',
  reference_ids: searchParams.get('reference_ids')?.split(',').map(Number) || [],
}), [searchParams]);

 

3. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง๊ณผ์˜ ํ†ตํ•ฉ

์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์‹œ getServerSideProps๋ฅผ ํ™œ์šฉํ•˜์—ฌ URL ์ฟผ๋ฆฌ๋กœ๋ถ€ํ„ฐ ํ•„ํ„ฐ์™€ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๊ณ  ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์™”๋‹ค.
 

export const getServerSideProps = async (context) => {
  const query = context.query;

  const filter = {
    status: query.status?.split(',') || [],
    search: query.search || '',
  };

  const pagination = {
    page: Number(query.page) || 1,
    page_count: Number(query.page_count) || 50,
  };

  const data = await getCheckers(pagination, filter);

  return { props: { data } };
};

 

4. React Query์™€์˜ ํ†ตํ•ฉ

React Query์—์„œ๋Š” queryKey์— ํ•„ํ„ฐ์™€ ํŽ˜์ด์ง€ ์ •๋ณด๋ฅผ ํ•จ๊ป˜ ํฌํ•จ์‹œ์ผœ, URL์ด ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ refetchํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.

const { data } = useQuery({
  queryKey: ['checkers', filterState, pagination],
  queryFn: () => getCheckers(pagination, filterState),
  initialData: props.data,
});

 
 

๐ŸŽฏ ๊ฒฐ๊ณผ

์ด์ฒ˜๋Ÿผ ๊ฐ ์ƒํƒœ(ํ•„ํ„ฐ, ๊ฒ€์ƒ‰, ํŽ˜์ด์ง€๋„ค์ด์…˜)๋ฅผ URL๊ณผ ์ง์ ‘ ์—ฐ๊ฒฐํ•จ์œผ๋กœ์จ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

  • ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ์‹œ์—๋„ ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ํ•„ํ„ฐ ์ƒํƒœ๊ฐ€ ์œ ์ง€๋˜์—ˆ๋‹ค.
  • ํ•„ํ„ฐ๋ง๋œ ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ URL๋กœ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ € ๋’ค๋กœ๊ฐ€๊ธฐ/์•ž์œผ๋กœ๊ฐ€๊ธฐ ๋™์ž‘์ด ๊ธฐ๋Œ€ํ•œ ๋Œ€๋กœ ์ž‘๋™ํ•ด UX๊ฐ€ ์ž์—ฐ์Šค๋Ÿฌ์›Œ์กŒ๋‹ค.
  • SSR๊ณผ React Query ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์„ธํŒ…์ด ํ†ตํ•ฉ๋˜์–ด, ์ฒซ ํŽ˜์ด์ง€ ์ง„์ž… ์†๋„๋„ ๋นจ๋ผ์กŒ๋‹ค.

๐Ÿ” ํšŒ๊ณ  ๋ฐ ๊ฐœ์„  ์—ฌ์ง€

์ด๋ฒˆ ๊ตฌ์กฐ ์„ค๊ณ„๋ฅผ ํ†ตํ•ด ํด๋ผ์ด์–ธํŠธ-์„œ๋ฒ„ ๊ฐ„ ์ƒํƒœ ์ผ๊ด€์„ฑ๊ณผ ๊ณต์œ  ๊ฐ€๋Šฅ์„ฑ์ด๋ผ๋Š” ๋‘ ๋งˆ๋ฆฌ ํ† ๋ผ๋ฅผ ๋ชจ๋‘ ์žก์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๋‹ค๋งŒ ํ–ฅํ›„ ํ•„ํ„ฐ ํ•ญ๋ชฉ์ด ๋งŽ์•„์งˆ ๊ฒฝ์šฐ, ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์ด ๊ธธ์–ด์ง€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด ํŒŒ๋ผ๋ฏธํ„ฐ ์••์ถ• ๋“ฑ์„ ๊ณ ๋ คํ•  ๊ณ„ํš์ด๋‹ค.

๋Œ“๊ธ€