๐Ÿ”ฅ Trouble Shooting - Tanstack Query์™€ useEffect์˜ ๋ฌดํ•œ ๋ฃจํ”„ ํ•จ์ •

์Š˜ยท2025๋…„ 3์›” 3์ผ

๐Ÿ”ฅ Trouble Shooting

๋ชฉ๋ก ๋ณด๊ธฐ
8/23

๐Ÿšจ ๋ฌธ์ œ ์ƒํ™ฉ

์ƒํ’ˆ ๋“ฑ๋ก/์ˆ˜์ • ํŽ˜์ด์ง€์—์„œ ๊ธฐ์กด ์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ, ์•„๋ฌด๋Ÿฐ ๋กœ์ง์„ ํ•˜์ง€์•Š์•„๋„ alert ๊ฒฝ๊ณ  ๋ฌธ๊ตฌ๊ฐ€ ๊ณ„์† ๋œจ๊ณ  ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ํ›„ ํผ์— ์ฑ„์šฐ๋Š” ๊ฐ„๋‹จํ•œ ์ž‘์—…์ธ๋ฐ ๊ณ„์†ํ•ด์„œ API ํ˜ธ์ถœ์ด ๋ฐ˜๋ณต๋˜๋ฉฐ alert๊ฐ€ ๋Š์ž„์—†์ด ์ƒ์„ฑ๋˜์—ˆ๋‹ค...

๐Ÿ’ป ๋ฌธ์ œ ๋ฐœ์ƒ ์ฝ”๋“œ

const navigate = useNavigate();
const [searchParams] = useSearchParams();
const productId = searchParams.get('productId');

const { data: productData, error: productError } = useProductDetail(productId);

useEffect(() => {
  if (productId && productData) {
    handleProductChange(productData);
  }
  if (productError) {
    alert('์ˆ˜์ •ํ•˜์‹ค ์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
  }
}, [productData, productError]);

const {
  product,
  error,
  handleImageChange,
  handleProductChange,
  handleSubmit,
} = useProductRegistration(onSuccess, productId);

๐Ÿค” ์›์ธ ๋ถ„์„

  1. ์˜์กด์„ฑ ๋ฐฐ์—ด ๋ฌธ์ œ: useEffect ๋‚ด์—์„œ handleProductChange(productData)๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด product ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ณ  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค.

โœ… ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

1. ์˜์กด์„ฑ ๋ฐฐ์—ด ์ˆ˜์ • (์‹คํŒจ)

useEffect(() => {
  if (productId && productData) {
    handleProductChange(productData);
  }
  if (productError) {
    alert('์ˆ˜์ •ํ•˜์‹ค ์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
  }
}, [productId, productData, productError]);

2. ์ตœ์ดˆ ๋กœ๋“œ ์‹œ์—๋งŒ ์‹คํ–‰ํ•˜๋„๋ก ์กฐ๊ฑด ์ถ”๊ฐ€ (์‹คํŒจ)

useEffect(() => {
  if (productId && productData && !product.name) {
    handleProductChange(productData);
  }
  if (productError) {
    alert('์ˆ˜์ •ํ•˜์‹ค ์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
  }
}, [productId, productData, productError, product.name]);

3. Tanstack Query์˜ enabled ์˜ต์…˜ ํ™œ์šฉ (์ตœ์ข… ํ•ด๊ฒฐ์ฑ…)

export const useProductDetail = (productId) => {
  return useQuery({
    queryKey: [QUERY_KEYS.PRODUCT.DETAIL, productId],
    queryFn: async () => {
      if (!productId) return null;
      
      const { data, error } = await supabase
        .from('products')
        .select('*')
        .eq('id', productId)
        .single();
        
      if (error) throw error;
      return data;
    },
    // ๊ธฐ๋ณธ์ ์œผ๋กœ productId๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์‹คํ–‰
    enabled: !!productId
  });
};

4. ๋‹จ์ˆœํ™”๋œ useEffect ๊ตฌํ˜„ (๋ฐฑ์—… ์†”๋ฃจ์…˜)

useEffect(() => {
  if (productData) {
    handleProductChange(productData);
  }

  if (productError) {
    alert('์ƒํ’ˆ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.');
    console.error(productError);
  }
}, [productData, productError]);

๐ŸŽฏ ํ•ด๊ฒฐ ํฌ์ธํŠธ

  1. useQuery์˜ enabled ์˜ต์…˜ ํ™œ์šฉ: ์กฐ๊ฑด๋ถ€ ์ฟผ๋ฆฌ ์‹คํ–‰์œผ๋กœ ๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€
  2. ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์กฐ๊ฑด ์„ค์ •: ์ด๋ฏธ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ๋œ ๊ฒฝ์šฐ ์ค‘๋ณต ์—…๋ฐ์ดํŠธ ๋ฐฉ์ง€
  3. ์˜์กด์„ฑ ๋ฐฐ์—ด ์ตœ์ ํ™”: ํ•„์š”ํ•œ ์˜์กด์„ฑ๋งŒ ํฌํ•จํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ์žฌ์‹คํ–‰ ๋ฐฉ์ง€

๐Ÿ“ ๋ฐฐ์šด ์ 

  1. Tanstack Query์˜ enabled ์˜ต์…˜์€ ์กฐ๊ฑด๋ถ€ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์— ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค.
  2. useEffect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์—๋Š” ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๋ฅผ ํฌํ•จํ•ด์•ผ ํ•œ๋‹ค.
  3. ์ƒํƒœ ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ์กฐ๊ฑด์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.
  4. Tanstack Query์™€ useEffect๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์‹ ์ค‘ํ•˜๊ฒŒ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ”„ ๊ฐœ์„ ๋œ ์ 

  1. ๋ถˆํ•„์š”ํ•œ API ํ˜ธ์ถœ ์ตœ์†Œํ™”๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  2. ๋ฌดํ•œ ๋ฃจํ”„ ์ œ๊ฑฐ๋กœ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ 
  3. ์ฝ”๋“œ์˜ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ ํ–ฅ์ƒ

๐Ÿ’ก ์ถ”๊ฐ€ TIP

  1. React Query์˜ enabled ์กฐ๊ฑด์—์„œ ์ฃผ์˜ํ•  ์ :

    // ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์˜ค๋ฅ˜ ๋ฐœ์ƒ!
    enabled: !!productId && !productData.name

    productData๊ฐ€ ์ดˆ๊ธฐ์— undefined์ผ ๋•Œ .name์— ์ ‘๊ทผํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

  2. onSuccess ์ฝœ๋ฐฑ ํ™œ์šฉ:

    const { data } = useQuery('products', fetchProducts, {
      onSuccess: (data) => {
        // ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์„ฑ๊ณต ์‹œ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
        handleDataLoaded(data);
      }
    });
  3. ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ ๋Œ€์‹  ๊ฐ์ฒด ์ž์ฒด๋ฅผ ์‚ฌ์šฉ:

    // ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค
    }, [product.name]);
    
    // ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•  ์ˆ˜ ์žˆ์Œ
    }, [product]);

    ํ•˜์ง€๋งŒ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

+) ์ถ”๊ฐ€๋กœ ํ—ท๊ฐˆ๋ ธ๋˜ ์ !

useEffect(() => {
  if (!sendAddress) return;
  const searchAddress = async (address) => {
    try {
      const coords = await addressToCoords(address);
      // ...๋‚˜๋จธ์ง€ ์ฝ”๋“œ
    } catch (error) {
      // ...์—๋Ÿฌ ์ฒ˜๋ฆฌ
    }
  };
  searchAddress(sendAddress);
}, [sendAddress, addressToCoords, coordsToAddress, onLocationSelect]);

์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ฌธ๋“ ํ—ท๊ฐˆ๋ ธ๋‹ค. ํ•จ์ˆ˜๋ฅผ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์˜ณ์€ ๋ฐฉํ–ฅ์ด์˜€์„๊นŒ?
์นด์นด์˜ค ๋งต์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ• ๋•Œ๋งˆ๋‹ค ๊ฐ’์„ ๋ณ€ํ˜•(๊ฒฝ๋„ ์œ„๋„ <-> ์ฃผ์†Œ๋ช…)ํ•˜์—ฌ ๋ณด์—ฌ์ค˜์•ผํ•˜๋Š” ๋กœ์ง์ด๊ธฐ์— ํ•จ์ˆ˜๋ฅผ ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ ์ถ”๊ฐ€ํ•œ๊ฑด๋ฐ.. ๊ณผ์—ฐ ์˜ณ์•˜์„๊นŒ?

์šฐ์„ .


๐ŸŒŸ useEffect ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค๋Š”๊ฒƒ์€ ์–ด๋–ค์˜๋ฏธ์ผ๊นŒ?

ํ•จ์ˆ˜๋ฅผ useEffect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํฌํ•จ์‹œ์ผœ์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ•จ์ˆ˜๊ฐ€ ์ผ๊ธ‰ ๊ฐ์ฒด(First-class Object)์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š”๋‹ค:

โœจ 1. ํ•จ์ˆ˜๋„ ๊ฐ์ฒด๋‹ค.

ํ•จ์ˆ˜๋Š” ์ˆซ์ž, ๋ฌธ์ž์—ด, ๊ฐ์ฒด, ๋ฐฐ์—ด์ฒ˜๋Ÿผ ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰๋œ๋‹ค.

โœจ 2. ํ•จ์ˆ˜ ์ฐธ์กฐ๋Š” ๋ Œ๋”๋ง๋งˆ๋‹ค ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ํ•จ์ˆ˜๋Š” ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋˜์–ด ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ์ฐธ์กฐ๋ฅผ ๊ฐ–๊ฒŒ ๋œ๋‹ค

โœจ 3. ํด๋กœ์ €์™€ ์ƒํƒœ ์ฐธ์กฐ

ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ฐธ์กฐํ•˜๋Š” ์ƒํƒœ๋‚˜ props๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ํ•จ์ˆ˜์˜ ๋™์ž‘๋„ ๋ณ€๊ฒฝ ๋  ์ˆ˜ ์žˆ๋‹ค.
ํ•จ์ˆ˜ ์„ ์–ธ์€ ๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ ๋น„์Šทํ•˜๊ฒŒ ์ž‘๋™ํ•œ๋‹ค. ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค:

  1. ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ
  2. ์ด ์ƒˆ ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ์— ์ €์žฅ
  3. ๋”ฐ๋ผ์„œ ์ด์ „ ๋ Œ๋”๋ง์˜ ํ•จ์ˆ˜์™€ ํ˜„์žฌ ๋ Œ๋”๋ง์˜ ํ•จ์ˆ˜๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ์ฒด
    (updateData !== previousUpdateData)
function Component() {
  const [count, setCount] = useState(0);
  
  // ์ด ํ•จ์ˆ˜๋Š” ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋จ
  const updateData = () => {
    // count ๊ฐ’์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง
    fetchData(count);
  };
  
  useEffect(() => {
    updateData();
  }, [updateData]); // updateData๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค effect ์‹คํ–‰
}

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด useCallback์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•จ์ˆ˜ ์ฐธ์กฐ์˜ ์•ˆ์ •์„ฑ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

function Component() {
  const [count, setCount] = useState(0);
  
  // count๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ์ƒ์„ฑ
  const updateData = useCallback(() => {
    fetchData(count);
  }, [count]);
  
  useEffect(() => {
    updateData();
  }, [updateData]);
}

๋”ฐ๋ผ์„œ useEffect ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํ•จ์ˆ˜๋ฅผ ํฌํ•จ์‹œํ‚ค๋Š” ๊ฒƒ์€ ํ•จ์ˆ˜์˜ ์ฐธ์กฐ๋‚˜ ๋‚ด๋ถ€ ๋กœ์ง์ด ๋ณ€๊ฒฝ๋ ๋•Œ effect๋ฅผ ์žฌ์‹คํ–‰ํ•˜๋„๋ก ํ•˜๊ฒŒํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ธ๊ฒƒ์ด๋‹ค.


useEffect(() => {
  if (!sendAddress) return;
  const searchAddress = async (address) => {
    try {
      const coords = await addressToCoords(address);
      // ...๋‚˜๋จธ์ง€ ์ฝ”๋“œ
    } catch (error) {
      // ...์—๋Ÿฌ ์ฒ˜๋ฆฌ
    }
  };
  searchAddress(sendAddress);
}, [sendAddress, addressToCoords, coordsToAddress, onLocationSelect]);

๋‹ค์‹œ ๋Œ์•„์™€์„œ ๊ทธ๋Ÿผ ๋จผ์ € ์ฝ”๋“œ์˜ ๋ชฉ์ ์„ ์ •๋ฆฌํ•ด๋ณด๋ฉด

  1. sendAddress ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค
  2. ํ•ด๋‹น ์ฃผ์†Œ๋ฅผ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  (addressToCoords)
  3. ํ•„์š”ํ•˜๋ฉด ์ง€๋„ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  (setLocation, setCenter)
  4. ์ขŒํ‘œ๋ฅผ ๋‹ค์‹œ ์ƒ์„ธ ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ (coordsToAddress)
  5. ๊ฒฐ๊ณผ๋ฅผ ์ฝœ๋ฐฑ์œผ๋กœ ์ „๋‹ฌ (onLocationSelect)

๊ฒฐ๋ก : ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํ•จ์ˆ˜๋“ค์„ ํฌํ•จ์‹œํ‚จ ๊ฒƒ์€ ์˜ฌ๋ฐ”๋ฅธ ์ ‘๊ทผ์ด์˜€๋‹ค. ๊ทธ ์ด์œ ๋Š”?

addressToCoords์™€ coordsToAddress๋Š” ์ง€์˜ค์ฝ”๋”ฉ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ๋‚ด๋ถ€ ๋กœ์ง์ด๋‚˜ ์ฐธ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ
onLocationSelect๋Š” ์„ ํƒํ•œ ์œ„์น˜ ์ •๋ณด๋ฅผ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋กœ, ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ์ƒˆ ํ•จ์ˆ˜ ์ฐธ์กฐ๊ฐ€ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ๊ธฐ์—..

profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€