상세 컨텐츠

본문 제목

React(리액트) 티키타카 39 커스텀 훅(Custom Hook)

React

by e7e 2026. 1. 19. 15:06

본문

참으로 끔찍하겡 달가운 이야기당.

커스텀 훅을 잘 써야 되는 게 아니야요?~~

 

그렇당 잘 써야 할 꺼이당. 

컴포넌트 내부 소스를 간단히 하기 위해서도, 더불어 가독성을 올려드리고,

재 사용 가능하게 맹글어서, 이 컴포넌트 저 컴포넌트에서도 널리 재활용

가능하게 하기 위해서도.. ( 현실에서도 재활용은 쓰레기를 줄여준당!)

 

사람도 일회성이 아니고, 재활용이 가능했으면 하는 무서운 바람이 분당.

 

Custom Hook을 만드는 건 너무나 간단하지만

좋은 재사용 유용한 Custom Hook을 만드는 일은 그리 간단하지는 않당.

 

개인적인 추천은 컴포넌트를 만들다 보면, 컴포넌트 내부(특히 JSX 리턴 앞부분)의

JS 소스가 많아지면, 보기가 조금씩 힘들어지기 시작하는데, 이것을 커스텀 훅으로

별도 파일로 빼내보면 은근 슬쩍 반복 되는 게 보일 수 있는데, 바로 이때~~

반복성이 눈과 뇌리를 찔렀을 때!, 재 사용성까지 고민하여 유용한 커스텀 훅으로

리팩토링에 도전하면 꽤나 수확이 결코 적지 않지 하지 아니 할 지어다.

 

SPA(Single Page Application)의 경우, 비동기 AJAX의 사용이 대표적으로

많을 수 밖에 없으닝, 여기에 대표적 커스텀 훅 케이스가 존재 할 지어랑.

 

말은 아무리 떠들어야 뇌리에 이미지를 잘 맹글지 못하닝,

코드 기반으로 눈과 뇌리에  전기 지지미 자극을 흘려보장.

 

vite를 이용하여 React 프로젝트를 만드는 건 그냥 알아서 맡기겠당.

[ 참고로 난 재미를 위해 tailwindcss도 설치하고야 말았당. ]

 

그럼 목적지를 ajax(여기선 fetch)를  이용한 커스텀 훅으로 정하고 달리장

 

Idol.jsx

const imgServer = "https://newsimg.sedaily.com";

function Idol({ name, role, imgURL }) {
  return (
    <div className="w-full text-center border-8 border-pink-500 rounded-2xl">
      <h1 className="text-4xl mt-2 mb-2 font-extrabold">{name}</h1>
      <h2 className="text-2xl mb-2 text-blue-600 font-bold bg-gray-300">
        {role}
      </h2>
      <img
        src={`${imgServer}${imgURL}`}
        className="w-full h-120 rounded-[50%]"
      />
    </div>
  );
}

export default Idol;

 

IdolList.jsx

import { useEffect, useState } from "react";
import Idol from "./Idol";

const e7eURL = "https://my-json-server.typicode.com/jangmk/kpop/agents";

function IdolList() {
  const [idols, setIdols] = useState([]);

  useEffect(() => {
    let totalIdols = [];
    const getIdols = async () => {
      const response = await fetch(e7eURL);
      const data = await response.json();

      // 데이터 축출
      data.forEach((ent) => {
        ent.groups.forEach((group) => {
          totalIdols = [...totalIdols, ...group.members];
        });
      });

      console.log("체에킁: ", totalIdols);
      setIdols(totalIdols);
    };
    getIdols();
  }, []);

  return (
    <div className="w-[80%] pt-40  mx-auto grid grid-cols-4 gap-4 ">
      {idols.map((idol) => (
        <Idol key={idol.name} {...idol} />
      ))}
    </div>
  );
}

export default IdolList;

 

App.jsx

import IdolList from "./IdolList";

function App() {
  return (
    <>
      <h1 className="fixed w-full text-center text-8xl bg-black text-white p-5 mb-3 ">
        지원 더 없나용
      </h1>
      <IdolList />
    </>
  );
}

export default App;

 

아마도 아래 비스무리 화면이 보였을거당. (요즘 웬디 노래가 좋당)

 

하지만 관심사는 결코 결과가 아니었당. 그랬당

IdolList.jsx에서 useEffect Hook에 사용한 fetch당.

 

비동기 AJAX를 가마니 쓰고 앉아서  쪼금 가마니 생각해 보면

결과가 나온 성공 케이스 (이때 결과를 data라 하장)

에러가 발생한 실패 케이스 (이때 결과를 error라 하장)

그리고 아직 성공/실패를 알수없는 진행 케이스(이때를 pending이라 하장)

이 3가지를 기본적으로 담아야 한다.

기본적으로 담아야 한다는 말은 해당 코드가 반복될거예요와 같은 의미당.

바뀌는 건 url 일 꺼시당.  (메소드 부분은 일단 머리에서 제외, 상황 간단히)

[ 사실 Promise는 이 3가지 상태를 기준으로 만들어졌당 ]

 

바로 맹글어서 적용해 보장.~~

useFetcher

import { useEffect, useState } from "react";

// 대표적 커스텀 훅(AJAX)
function useFetcher(url) {
  // 3가지 상태를 위한 상태변수, Re-Redering을 발생시키기 위함
  const [data, setData] = useState(null); // 결과가 있는지 없는지
  const [pending, setPending] = useState(true); // 실행중인지 끝났는지
  const [error, setError] = useState(null); // 에러가 있는지 없는지

  useEffect(() => {
    const fetchData = async () => {
      try {
        setPending(true); // 아작스 들어가기 전에 실행중 표시, Re-Rendering
        const response = await fetch(url);
        if (!response.ok)
          throw new Error(`Http Error! Status: ${response.status}`); //  강제 Error발생 -> catch로 이동
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error.message); // Re-Rendering
      } finally {
        setPending(false); // 그냥 초기화 그래도 Re-Rendering 발생
      }
    };

    fetchData(); // 호출

    // 클리어 함수 (unmount 되었을 때 실행)
    return () => {
      // 혹 필요한 작업이 있다면
    };
  }, [url]); // url이 바뀌면 실행됨

  return { data, pending, error }; // 이 훅을 가져다 쓸 때, 꺼내 쓸 수 있도록
}

export default useFetcher;

 

바뀐 IdolList.jsx

import { useEffect, useState } from "react";
import Idol from "./Idol";
import useFetcher from "./useFetcher";

const e7eURL = "https://my-json-server.typicode.com/jangmk/kpop/agents";

function IdolList() {
  const { data, pending, error } = useFetcher(e7eURL);

  if (pending) return <h1 className="text-4xl">땀시만 기다려주삼</h1>;
  if (error) return <h1>{error}</h1>;

  let totalIdols = [];
  data.forEach((ent) => {
    ent.groups.forEach((group) => {
      totalIdols = [...totalIdols, ...group.members];
    });
  });

  return (
    <div className="w-[80%] pt-40  mx-auto grid grid-cols-4 gap-4 ">
      {totalIdols.map((idol) => (
        <Idol key={idol.name} {...idol} />
      ))}
    </div>
  );
}

export default IdolList;

 

결과는 분명 같지 않지 아니하지 않을 수 없지 않음을 본능이 느낀당..

느끼미 친구가 함께 왔다면 더 좋을거당. ~~

 

이 형태에 매력을 느낀 사람들이 더욱 발전시킨 react-query란 걸 만들었당.

당연히 그러할 거시당. React에도 역사란 게 있으닝..  

아래는 훌륭한 React-Query의 사용법이당. 

[분명 맘에 들꺼이당. useEffect가 눈에 보이지 않으닝 좋을 수 밖에~~ (><)]

2025.04.08 - [React] - React(리액트) 티키타카 34 (@tanstack/react-query)

 

분명 천천히 꾸준히 React를 사용한다면 당신도 이런 걸 만들 생각에 

빠지게 될 것이당.  불편함이 모여서 어깨를 짓 누르면 사람은 생각한당.

벗어날 방법을....  당신도 사람이 되어야한당.

 

 


 

인간은 스스로의 가장 큰 장점으로 떠들던  지능을

인공지능이란 이름으로 더 많은 정보를 바탕으로 

추론하여 더 고급지고 논리적으로 보이는데다

심지어 놀라운 속도로 답을 주는 도구를 만들었당.

 

그 누군가는 직업상 평가 문제를 내어야 하는데,

A.I를 이용하여 문제를 만들었당.

저 누군가는 상황상 평가를 받아야 하는데

AI에게 문제의 답을 물어 제출하였다.

그 누군가는 저 누군가에 대한 채점을

A.I에게 맡겼다.

점수가 맘에 안 들었던 저 누군가는 항의 서신을

A.I에게 작성하게 하였고,

그 누군가는 그 회신을 다시 A.I에게 맡겼다.

 

그 누군가와 저 누군가는

심도 있게 문제를 읽은 적도

고민하며 서신을 읽은 적도 없다.

그저 평균 점수가 높았졌네 감탄과

왜 100점이 아니야 하는 한탄이

섞인 감정의 찌끄레기만 사람답다.

 

현실의 아주 개미 허리 정도의 일부분일지도 모르지만

바라보는 3D 위치에 따라서 매우 긍정적으로도

매우 부정적으로도,  초점 위치에 따라서는

긍정과 부정의 상승 기류까지도

바람으로 느낄 수 있는 이야기다.

 

이분법 기반 확실성을 선호하는 인간에게

사실 확률적인 수치는 크게 의미가 없다.

50% 수준에서 정확합니다.

   아이고 모르겠습니다.~~

95% 수준에서 정확합니다.

   이거다 싶지만 틀릴까봐 5% 변명 남겨둡니다. ~~

99.9999% 수준에서 정확합니다.

  유전자 비교 분석 실제 검사 결과입니다.

 

지금의 내가 나일 확률은 얼마일까?

100% 그건 나의 바람이었나?

 

https://www.youtube.com/watch?v=gpdIcAOYdxY

 

개별 지능의 조합 아마도 그게 나 아닐까?

관련글 더보기