Frontend/React

[RN] URL ํŒŒ๋ผ๋ฏธํ„ฐ vs ์Šคํ† ์–ด ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…: React Native ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฆฌํŒฉํ† ๋ง ์™„๋ฒฝ ๊ฐ€์ด๋“œ

2025. 9. 27. 10:00ยทFrontend/React

๐Ÿค” ๋“ค์–ด๊ฐ€๋ฉฐ

React Native ์•ฑ์—์„œ ๋„ค๋น„๊ฒŒ์ด์…˜์„ ๊ตฌํ˜„ํ•  ๋•Œ, ํŽ˜์ด์ง€ ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ์‹์„ ์–ด๋–ป๊ฒŒ ์„ ํƒํ•˜์‹œ๋‚˜์š”?
URL ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€, ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์‚ฌ์šฉํ• ์ง€๋Š” ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์ด ๊ณ ๋ฏผํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

์ตœ๊ทผ ํ”„๋กœ์ ํŠธ์—์„œ ์ฝ˜ํ…์ธ  ์ƒ์„ธ ํŽ˜์ด์ง€์˜ ๋ผ์šฐํŒ… ๋ฐฉ์‹์„ URL ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ ์Šคํ† ์–ด ๊ธฐ๋ฐ˜์œผ๋กœ ์ „๋ฉด ๋ฆฌํŒฉํ† ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด ๊ณผ์ •์—์„œ ๋ฐฐ์šด ๊ฐ ๋ฐฉ์‹์˜ ์žฅ๋‹จ์ ๊ณผ ์–ธ์ œ ์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ฒฝํ—˜์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“‹ ๊ธฐ์กด ๋ฐฉ์‹: URL ํŒŒ๋ผ๋ฏธํ„ฐ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…

๊ตฌํ˜„ ๋ฐฉ์‹

๊ธฐ์กด์—๋Š” ์ „ํ˜•์ ์ธ URL ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค:

// ๊ธฐ์กด ํŒŒ์ผ ๊ตฌ์กฐ
src/app/content/detail/[id].tsx

// ๋„ค๋น„๊ฒŒ์ด์…˜
const handleItemPress = (itemId: string) => {
  router.push(`/content/detail/${itemId}`);
};

// ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
export default function ItemDetailScreen() {
  const params = useLocalSearchParams<{ id?: string }>();
  const itemId = params.id;

  // API๋กœ ์•„์ดํ…œ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
  const { data: item } = useGetItemById(itemId || '');

  return (
    <View>
      {/* ์•„์ดํ…œ ์ƒ์„ธ ์ •๋ณด ๋ Œ๋”๋ง */}
    </View>
  );
}

๐ŸŽฏ URL ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐฉ์‹์˜ ์žฅ์ 

1. ์ง๊ด€์ ์ธ URL ๊ตฌ์กฐ

/content/detail/item-123
/content/detail/item-456

URL๋งŒ ๋ด๋„ ์–ด๋–ค ์•„์ดํ…œ์˜ ์ƒ์„ธ ํŽ˜์ด์ง€์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋ถ๋งˆํฌ ๋ฐ ๊ณต์œ  ๊ฐ€๋Šฅ

// URL์„ ์ง์ ‘ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Œ
const shareUrl = `myapp://content/detail/${itemId}`;

3. ๋ธŒ๋ผ์šฐ์ €์˜ ๋’ค๋กœ๊ฐ€๊ธฐ ์ง€์›

์›น ํ™˜๊ฒฝ์—์„œ ๋ธŒ๋ผ์šฐ์ €์˜ ๋’ค๋กœ๊ฐ€๊ธฐ ๋ฒ„ํŠผ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

4. SEO ์นœํ™”์  (์›น ์•ฑ์˜ ๊ฒฝ์šฐ)

๊ฐ ํŽ˜์ด์ง€๊ฐ€ ๊ณ ์œ ํ•œ URL์„ ๊ฐ€์ ธ ๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™”์— ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.


๐Ÿšจ ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค...

1. ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์˜ ํ•œ๊ณ„

์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ๋Š” ๋‹จ์ˆœํžˆ ์•„์ดํ…œ ID๋งŒ ํ•„์š”ํ•œ ๊ฒŒ ์•„๋‹ˆ์—ˆ์Šต๋‹ˆ๋‹ค:

// ์‹ค์ œ๋กœ ํ•„์š”ํ–ˆ๋˜ ๋ฐ์ดํ„ฐ๋“ค
interface DetailPageData {
  selectedItemId: string;        // ์„ ํƒ๋œ ์•„์ดํ…œ ID
  allItems: Item[];              // ์ „์ฒด ์•„์ดํ…œ ๋ชฉ๋ก (๋„ค๋น„๊ฒŒ์ด์…˜์šฉ)
  category: string;              // ์นดํ…Œ๊ณ ๋ฆฌ ์ •๋ณด
  searchContext: {               // ๊ฒ€์ƒ‰ ๋งฅ๋ฝ ์ •๋ณด
    searchQuery: string;
    filterOptions: FilterState;
  };
}

์ด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ URL ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•˜๋ ค๋ฉด?

// ์ด๋ ‡๊ฒŒ ํ•ด์•ผ ํ•  ๋ป”ํ–ˆ์Šต๋‹ˆ๋‹ค... ๐Ÿ˜ฑ
const queryString = `?id=${itemId}&category=${category}&filter=${JSON.stringify(filters)}&items=${JSON.stringify(allItems)}`;
router.push(`/content/detail${queryString}`);

2. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ๋ฌธ์ œ

// ๋ชฉ๋ก ํŽ˜์ด์ง€์—์„œ ์•„์ดํ…œ ์„ ํƒ ์‹œ
const handleItemPress = (item: Item) => {
  // ์ด ๋ฐ์ดํ„ฐ๋“ค์ด ์ƒ์„ธ ํŽ˜์ด์ง€์— ์–ด๋–ป๊ฒŒ ์ „๋‹ฌ๋ ๊นŒ์š”?
  const allSearchResults = [item1, item2, item3, ...];
  const currentCategory = "electronics";

  router.push(`/content/detail/${item.id}`); // ID๋งŒ ์ „๋‹ฌ ๐Ÿ˜ฐ
};

// ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ๋Š”...
export default function ItemDetailScreen() {
  const { id } = useLocalSearchParams();

  // ๋‹ค์‹œ API๋ฅผ ํ˜ธ์ถœํ•ด์„œ ๊ด€๋ จ ์•„์ดํ…œ๋“ค์„ ๊ฐ€์ ธ์™€์•ผ ํ•จ
  const { data: relatedItems } = useGetRelatedItems(id);
  // ํ•˜์ง€๋งŒ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ! ๐Ÿ›
}

3. ์„ฑ๋Šฅ ์ด์Šˆ

// ๋งค๋ฒˆ ์ƒ์„ธ ํŽ˜์ด์ง€์—์„œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ๋กœ๋”ฉ
const ItemDetailScreen = () => {
  const { id } = useLocalSearchParams();

  // ์ด๋ฏธ ์žˆ๋Š” ๋ฐ์ดํ„ฐ์ž„์—๋„ ๋‹ค์‹œ API ํ˜ธ์ถœ
  const { data: item, isLoading } = useGetItemById(id);
  const { data: reviews, isLoading: reviewsLoading } = useGetItemReviews(id);
  const { data: related, isLoading: relatedLoading } = useGetRelatedItems(id);

  if (isLoading || reviewsLoading || relatedLoading) {
    return <LoadingSpinner />; // ๋ถˆํ•„์š”ํ•œ ๋กœ๋”ฉ ์‹œ๊ฐ„
  }

  return <DetailView />;
};

4. ๋ณต์žกํ•œ ์ƒํ˜ธ์ž‘์šฉ์˜ ํ•œ๊ณ„

// ๋ชฉ๋ก์—์„œ ๋‹ค๋ฅธ ์•„์ดํ…œ์œผ๋กœ ๋น ๋ฅธ ์ „ํ™˜ ์‹œ
const handleQuickSwitch = (newItemId: string) => {
  const selectedItem = allItems.find(item => item.id === newItemId);

  // ์ด ์ •๋ณด๋“ค์„ ์–ด๋–ป๊ฒŒ ์ „๋‹ฌํ• ๊นŒ์š”?
  // - selectedItem ๊ฐ์ฒด
  // - allItems ๋ฐฐ์—ด (๋„ค๋น„๊ฒŒ์ด์…˜์šฉ)
  // - ํ˜„์žฌ ํ•„ํ„ฐ ์ƒํƒœ
  // - ๊ฒ€์ƒ‰ ๋งฅ๋ฝ

  router.push(`/content/detail/${newItemId}`); // ๊ฒฐ๊ตญ ID๋งŒ ์ „๋‹ฌ...
};

โœจ ํ•ด๊ฒฐ์ฑ…: ์Šคํ† ์–ด ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์œผ๋กœ ์ „ํ™˜

์ƒˆ๋กœ์šด ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„

Zustand๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ƒ์„ธ ํŽ˜์ด์ง€๋ฅผ ์œ„ํ•œ ์ „์šฉ ์Šคํ† ์–ด๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค:

// src/store/detailStore.ts
import { create } from 'zustand';
import { Item } from '@/api/types/item';

interface DetailState {
  selectedItemId: string | null;
  allItems: Item[];
  category: string;
  filterState: FilterState;
  setDetailData: (
    itemId: string,
    allItems?: Item[],
    category?: string,
    filterState?: FilterState
  ) => void;
  clearDetailData: () => void;
}

export const useDetailStore = create<DetailState>((set) => ({
  selectedItemId: null,
  allItems: [],
  category: '',
  filterState: {},

  setDetailData: (itemId, allItems = [], category = '', filterState = {}) => {
    set({
      selectedItemId: itemId,
      allItems,
      category,
      filterState,
    });
  },

  clearDetailData: () => {
    set({
      selectedItemId: null,
      allItems: [],
      category: '',
      filterState: {},
    });
  },
}));

ํŒŒ์ผ ๊ตฌ์กฐ ๋‹จ์ˆœํ™”

// Before: ๋™์  ๋ผ์šฐํŠธ
src/app/content/detail/[id].tsx

// After: ์ •์  ๋ผ์šฐํŠธ  
src/app/content/detail.tsx

URL ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•„์š” ์—†์–ด์กŒ์œผ๋‹ˆ ํŒŒ์ผ ๊ตฌ์กฐ๋„ ๋” ๊ฐ„๋‹จํ•ด์กŒ์Šต๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ๋„ค๋น„๊ฒŒ์ด์…˜ ํ”Œ๋กœ์šฐ

// ๋ชฉ๋ก ํŽ˜์ด์ง€์—์„œ ์•„์ดํ…œ ์„ ํƒ
const handleItemPress = (item: Item) => {
  console.log('์•„์ดํ…œ ์„ ํƒ:', item);

  // 1. ์Šคํ† ์–ด์— ๋ชจ๋“  ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ €์žฅ
  setDetailData(item.id, searchResults, category, currentFilters);

  // 2. ๊ฐ„๋‹จํ•œ ๋ผ์šฐํŒ…
  router.push('/content/detail');
};

// ๋น ๋ฅธ ์•„์ดํ…œ ์ „ํ™˜
const handleQuickSwitch = (newItemId: string) => {
  const item = allItems.find((item) => item.id === newItemId);
  if (item) {
    // ์„ ํƒ๋œ ์•„์ดํ…œ ID๋งŒ ์—…๋ฐ์ดํŠธ (๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋Š” ์œ ์ง€)
    setSelectedItemId(item.id);
  }
};

์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌํ˜„

// src/app/content/detail.tsx
export default function ItemDetailScreen() {
  const {
    selectedItemId,
    allItems,
    category,
    filterState
  } = useDetailStore();

  // ์Šคํ† ์–ด์˜ ID๋กœ API ํ˜ธ์ถœ (์บ์‹ฑ ํ™œ์šฉ ๊ฐ€๋Šฅ)
  const { data: selectedItem } = useGetItemById(selectedItemId || '');

  // ๋„ค๋น„๊ฒŒ์ด์…˜ ์ •๋ณด๋Š” ์Šคํ† ์–ด์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉ
  const navigationItems = useMemo(() =>
    allItems.map((item) => ({
      id: item.id,
      title: item.title,
      isSelected: selectedItem?.id === item.id,
    })),
    [allItems, selectedItem?.id]
  );

  return (
    <View>
      {/* ์•„์ดํ…œ ๋„ค๋น„๊ฒŒ์ด์…˜ */}
      <ItemNavigation 
        items={navigationItems}
        onItemSelect={handleQuickSwitch}
      />

      {/* ์„ ํƒ๋œ ์•„์ดํ…œ์˜ ์ƒ์„ธ ์ •๋ณด */}
      <ScrollView>
        <ItemInfo item={selectedItem} />
        <RelatedSection category={category} />
      </ScrollView>
    </View>
  );
}

๐Ÿ“Š ๋น„๊ต: Before vs After

๋ฐ์ดํ„ฐ ํ”Œ๋กœ์šฐ ๋น„๊ต

Before (URL ํŒŒ๋ผ๋ฏธํ„ฐ):

๋ชฉ๋ก ํŽ˜์ด์ง€ → URL ํŒŒ๋ผ๋ฏธํ„ฐ → ์ƒ์„ธ ํŽ˜์ด์ง€ → API ์žฌํ˜ธ์ถœ → ๋ Œ๋”๋ง
     ↓              ↓            ↓         ↓
  [๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ]   [ID๋งŒ ์ „๋‹ฌ]   [๋งฅ๋ฝ ์†์‹ค]  [์ค‘๋ณต ๋กœ๋”ฉ]

After (์Šคํ† ์–ด ๊ธฐ๋ฐ˜):

๋ชฉ๋ก ํŽ˜์ด์ง€ → ์Šคํ† ์–ด ์ €์žฅ → ์ƒ์„ธ ํŽ˜์ด์ง€ → ์Šคํ† ์–ด์—์„œ ์กฐํšŒ → ๋ Œ๋”๋ง
     ↓           ↓          ↓           ↓
  [๋ชจ๋“  ๋ฐ์ดํ„ฐ]   [์ „์ฒด ์ €์žฅ]  [๋งฅ๋ฝ ์œ ์ง€]   [์ฆ‰์‹œ ํ‘œ์‹œ]

์„ฑ๋Šฅ ๋น„๊ต

ํ•ญ๋ชฉ URL ํŒŒ๋ผ๋ฏธํ„ฐ ์Šคํ† ์–ด ๊ธฐ๋ฐ˜

์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„ 3-4๊ฐœ API ํ˜ธ์ถœ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ ํ™œ์šฉ
์•„์ดํ…œ ์ „ํ™˜ ๋ฐ˜์‘ ํŽ˜์ด์ง€ ๋ฆฌ๋กœ๋“œ ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ
๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰ API ์‘๋‹ต ์ค‘๋ณต ์ €์žฅ ์Šคํ† ์–ด์—์„œ ๊ณต์œ 
๋„คํŠธ์›Œํฌ ์š”์ฒญ ๋งค๋ฒˆ ์ƒˆ ์š”์ฒญ ํ•„์š”์‹œ์—๋งŒ ์š”์ฒญ

โš–๏ธ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„: ๋ฌด์—‡์„ ์žƒ๊ณ  ๋ฌด์—‡์„ ์–ป์—ˆ๋‚˜?

๐Ÿ˜” ์žƒ์€ ๊ฒƒ๋“ค

1. URL ๊ธฐ๋ฐ˜ ๋„ค๋น„๊ฒŒ์ด์…˜

// ๋” ์ด์ƒ ๋ถˆ๊ฐ€๋Šฅ
const directUrl = `/content/detail/${itemId}`;

2. ๋”ฅ๋งํฌ ์ง€์›์˜ ๋ณต์žก์„ฑ

// ๋”ฅ๋งํฌ ์ฒ˜๋ฆฌ ์‹œ ์Šคํ† ์–ด ์ดˆ๊ธฐํ™” ํ•„์š”
const handleDeepLink = (url: string) => {
  const itemId = extractItemId(url);
  // ์Šคํ† ์–ด์— ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด API๋กœ ๊ฐ€์ ธ์™€์•ผ ํ•จ
  await initializeStoreForItem(itemId);
};

3. ๋ธŒ๋ผ์šฐ์ € ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ

์›น ํ™˜๊ฒฝ์—์„œ๋Š” ์ถ”๊ฐ€์ ์ธ ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐ŸŽ‰ ์–ป์€ ๊ฒƒ๋“ค

1. ๋น ๋ฅธ ๋„ค๋น„๊ฒŒ์ด์…˜

// ์ฆ‰์‹œ ํŽ˜์ด์ง€ ์ „ํ™˜, ๋กœ๋”ฉ ์—†์Œ
setDetailData(item.id, allItems, category);
router.push('/content/detail'); // 0.1์ดˆ ๋‚ด ํ™”๋ฉด ์ „ํ™˜

2. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ

// ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์™€ ์ƒ์„ธ ํŽ˜์ด์ง€์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•ญ์ƒ ๋™์ผ
const { allItems } = useDetailStore(); // ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ๊ทธ๋Œ€๋กœ

3. ๋ณต์žกํ•œ ์ƒํ˜ธ์ž‘์šฉ ์ง€์›

// ๋น ๋ฅธ ์•„์ดํ…œ ์ „ํ™˜
const handleQuickSwitch = (itemId: string) => {
  setSelectedItemId(itemId); // ์ฆ‰์‹œ ๋ฐ˜์˜
};

4. ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ

// ํ•œ ๋ฒˆ ๋กœ๋“œ๋œ ๋ฐ์ดํ„ฐ๋Š” ์Šคํ† ์–ด์—์„œ ์žฌ์‚ฌ์šฉ
const cachedData = useDetailStore(); // API ์žฌํ˜ธ์ถœ ๋ถˆํ•„์š”

๐ŸŽฏ ์–ธ์ œ ์–ด๋–ค ๋ฐฉ์‹์„ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ?

URL ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์ ํ•ฉํ•œ ๊ฒฝ์šฐ

1. ๋‹จ์ˆœํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

// ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ํŽ˜์ด์ง€
router.push(`/user/${userId}`); // ID๋งŒ ์žˆ์œผ๋ฉด ์ถฉ๋ถ„

2. SEO๊ฐ€ ์ค‘์š”ํ•œ ์›น ์•ฑ

// ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ
router.push(`/posts/${postSlug}`); // ๊ฒ€์ƒ‰ ์—”์ง„ ์ตœ์ ํ™” ํ•„์š”

3. ๋”ฅ๋งํฌ๊ฐ€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ

// ์ƒํ’ˆ ์ƒ์„ธ ํŽ˜์ด์ง€
const shareUrl = `myapp://product/${productId}`; // ๊ณต์œ  ๊ธฐ๋Šฅ ์ค‘์š”

์Šคํ† ์–ด ๊ธฐ๋ฐ˜์ด ์ ํ•ฉํ•œ ๊ฒฝ์šฐ

1. ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ

// ๋‹ค๋‹จ๊ณ„ ํผ, ์‡ผํ•‘ ์นดํŠธ, ๋ณต์žกํ•œ ์„ค์ •
interface ComplexData {
  step1: FormData;
  step2: FormData;
  context: AppContext;
}

2. ๋น ๋ฅธ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ค‘์š”

// ๋Œ€์‹œ๋ณด๋“œ, ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ
setData(complexData);
router.push('/dashboard'); // ์ฆ‰์‹œ ์ „ํ™˜

3. ์ƒํƒœ ๊ณต์œ ๊ฐ€ ํ•„์š”

// ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์—์„œ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ
const sharedState = useGlobalStore();

๐Ÿ› ๏ธ ์‹ค์ œ ๊ตฌํ˜„ ํŒ

1. ์Šคํ† ์–ด ์ดˆ๊ธฐํ™” ์ฒ˜๋ฆฌ

export default function DetailScreen() {
  const { selectedItemId } = useDetailStore();

  // ์Šคํ† ์–ด๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด ํ™ˆ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  useEffect(() => {
    if (!selectedItemId) {
      router.replace('/(tabs)/home');
    }
  }, [selectedItemId]);
}

2. ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ

export default function DetailScreen() {
  const { clearDetailData } = useDetailStore();

  // ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ ์ •๋ฆฌ
  useEffect(() => {
    return () => {
      clearDetailData();
    };
  }, []);
}

3. ํƒ€์ž… ์•ˆ์ „์„ฑ ํ™•๋ณด

interface DetailState {
  selectedItemId: string | null; // null ์ฒดํฌ ํ•„์ˆ˜
}

const { selectedItemId } = useDetailStore();
if (!selectedItemId) {
  return <NotFoundScreen />;
}

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌ

URL ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์Šคํ† ์–ด ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์„ ์ดํ•ดํ•˜๊ณ , ํ”„๋กœ์ ํŠธ์˜ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๋Š” ์„ ํƒ์„ ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ ํ•ต์‹ฌ ํŒ๋‹จ ๊ธฐ์ค€

  • โœ… ๋‹จ์ˆœํ•œ ID ์ „๋‹ฌ: URL ํŒŒ๋ผ๋ฏธํ„ฐ
  • โœ… ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ: ์Šคํ† ์–ด ๊ธฐ๋ฐ˜
  • โœ… SEO/๋”ฅ๋งํฌ ์ค‘์š”: URL ํŒŒ๋ผ๋ฏธํ„ฐ
  • โœ… ๋น ๋ฅธ UX ์ค‘์š”: ์Šคํ† ์–ด ๊ธฐ๋ฐ˜

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

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋ณ€๊ฒฝ๊ธˆ์ง€ (์ƒˆ์ฐฝ์—ด๋ฆผ)

'Frontend > React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[React] React์—์„œ AI ํƒ€์ดํ•‘ ํšจ๊ณผ ๊ตฌํ˜„ํ•˜๊ธฐ | setInterval์˜ ํ•จ์ •๊ณผ ํ•ด๊ฒฐ๋ฒ•  (0) 2025.09.16
[React] React Hook "useEffect" is called in function ์—๋Ÿฌ ํ•ด๊ฒฐ  (0) 2025.03.03
[React] .env ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‚ฌ์šฉ๋ฒ• (Vite / CRA ํ™˜๊ฒฝ)  (0) 2025.02.27
[React] Props, Event, State  (2) 2024.12.19
[React] ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ๊ณ ์ฐฐ  (3) 2024.12.18
'Frontend/React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [React] React์—์„œ AI ํƒ€์ดํ•‘ ํšจ๊ณผ ๊ตฌํ˜„ํ•˜๊ธฐ | setInterval์˜ ํ•จ์ •๊ณผ ํ•ด๊ฒฐ๋ฒ•
  • [React] React Hook "useEffect" is called in function ์—๋Ÿฌ ํ•ด๊ฒฐ
  • [React] .env ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์‚ฌ์šฉ๋ฒ• (Vite / CRA ํ™˜๊ฒฝ)
  • [React] Props, Event, State
sol_git
sol_git
Full-Stack์„ ๊ฟˆ๊พธ๋Š” Junior Developer๐Ÿ’–
  • sol_git
    ์†”๊นƒํ•œ Dev
    sol_git
  • ๊ธ€์“ฐ๊ธฐ ๊ด€๋ฆฌ์ž
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (44)
      • Frontend (15)
        • Javascript (1)
        • React (11)
        • Vue (1)
        • Svelte (1)
      • Style Sheet (0)
        • Sass (0)
      • Backend (1)
        • Java (3)
        • Python (1)
        • Spring Boot (0)
      • AI (1)
        • LLM (0)
        • Gen AI (0)
      • DevOps (16)
        • Git (16)
        • Kubernetes (0)
      • Cloud (0)
        • AWS (0)
      • DBMS (2)
        • MySQL (1)
        • PostgreSQL (1)
      • IDE & Tools (3)
        • IntelliJ (1)
        • VS Code (1)
        • Tool (1)
      • OS (2)
        • Mac (2)
      • Project ์ผ๊ธฐ (0)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

    • Github
  • ์ธ๊ธฐ ๊ธ€

  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
sol_git
[RN] URL ํŒŒ๋ผ๋ฏธํ„ฐ vs ์Šคํ† ์–ด ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…: React Native ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฆฌํŒฉํ† ๋ง ์™„๋ฒฝ ๊ฐ€์ด๋“œ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”