Home 미니 블로그 만들기 - 06
Post
X

미니 블로그 만들기 - 06

Custom Hook

  • 여러 컴포넌트에서 공통적으로 사용되는 상태 로직을 추출하여 함수로 만들어서 사용
  • use라는 접두사를 사용하여 함수의 이름을 정의
  • React의 기본 훅(useState, useEffect 등)을 이용하여 구현

즉, React에서 반복되는 로직을 컴포넌트로 만들어 사용했던 것처럼 반복되는 메서드를 Custom Hook으로 만들어 사용하는 것입니다.

  • 장점

    • 코드가 간결해지고 가독성이 좋아집니다.
    • 반복이 줄어 재사용성이 높아집니다.
    • 유지보수 용이

axios

axios에서는 axios(config) 형태로도 HTTP 요청이 가능합니다.

참고 글 : Axios

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// axios(config) 형태
axios({
  method: "post",
  url: "/user/12345",
  data: {
    name: "psmin",
  },
});

// axios(url[, config]) 형태
axios("/user/12345", {
  method: "post",
  data: {
    name: "psmin",
  },
});
  • Config의 파라미터 옵션

    • url : 요청을 보낼 서버 URL
    • method : HTTP 요청 방식 지정
    • data : 요청 body로 함께 전송될 데이터 (주로 POST방식에서 사용합니다.)
    • params : 함께 전송되는 URL 파라미터 (주로 GET방식에서 사용합니다.)
    • responseType: 응답 데이터 타입 지정 (arraybuffer, documetn, json, text, stream, blob)
    • headers : 사용자 지정 헤더
    • timeout : 요청 취소되는 제한시간 지정 (timeout보다 오래걸리면 중단됩니다.)
    • baseURL : url의 앞에 붙는 주소
    • withCredentials : cross-site access-control 요청 허용 유무
    • auth, proxy, validateStatus 등

반복되는 코드 추출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// BoardPage.jsx
import React, { useEffect, useState } from "react";
import { styled } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import BasicButton from "../ui/BasicButton";
import PostList from "../list/PostList";
import axios from "axios";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const BoardPage = () => {
  const navigate = useNavigate();
  // *******************************************
  // 추출할 반복되는 부분
  let [data, setData] = useState([]);

  useEffect(() => {
    let getPosts = async () => {
      try {
        let res = await axios.get("/board/post");

        return res.data;
      } catch (err) {
        console.log(err);
      }
    };

    getPosts().then((res) => setData(res));
  }, []);

  // *******************************************

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"글 작성하기"}
          onClickItem={() => navigate("/board/write")}
        ></BasicButton>
      </BtnWrapper>

      <PostList
        posts={data}
        onClickItem={(item) => {
          navigate(`/board/${item.post_id}`);
        }}
      ></PostList>
    </>
  );
};

export default BoardPage;

Custom Hook으로 만들기 쉽게 axios(url[, config]) 형태로 변경해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// BoardPage.jsx

...

const BoardPage = () => {
  const navigate = useNavigate();
  // *******************************************
  let [data, setData] = useState([]);

  useEffect(() => {
    let getPosts = async () => {
      try {
        // axios(url[, config]) 형태로 변경
        let res = await axios("/board/post");

        return res.data;
      } catch (err) {
        console.log(err);
      }
    };

    getPosts().then((res) => setData(res));
  }, []);

  // *******************************************

  ...

};

export default BoardPage;

axios를 이용해 서버에 데이터를 요청하는 코드는 현재 모든 Page 컴포넌트에서 사용합니다.

또한, 기능이 추가되면 axios의 사용은 더 늘어날 것입니다.

따라서, Custom Hook으로 만들어서 사용해보겠습니다.


useAxios

src 폴더에 hooks 폴더를 만들고 useAxios.js 파일을 생성합니다.

  • urlreqConfig를 인자로 받아 axios(url[, config]) 형태로 데이터를 요청합니다.

  • data, error, loading 값을 state로 관리

    • data : axios로 요청한 데이터 관리
    • error : 데이터 가져오는 과정에서 발생한 에러 관리
    • loading : 데이터 요청 처리 중이면 true 완료 시 false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// useAxios.js
import axios from "axios";
import { useState, useEffect } from "react";

const useAxios = (url, reqConfig = {}) => {
  // 가져올 데이터 담을 변수
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);
  // true를 기본값으로 주어 데이터 요청이 완료되면 false로 전환

  useEffect(() => {
    const fetchData = async () => {
      // try / catch 문으로 에러 처리
      try {
        const response = await axios(url, reqConfig); // axios(url[, config]) 형태

        setData(response.data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false); // 데이터 요청 완료 후 loading 상태 변경
      }
    };

    if (loading) fetchData();
  }, [url, reqConfig, loading]); // url 또는 reqConfig이 변경될 때마다 useEffect 다시 실행

  return { data, error, loading };
};

export default useAxios;

BoardPage 수정

작성한 useAxios를 import 해서 사용합니다.

간단하게 url과 reqConfig를 넘겨주는 것으로 원하는 HTTP 요청을 통해 data, error, loading 값을 얻을 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import React from "react";
import { styled } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import BasicButton from "../ui/BasicButton";
import PostList from "../list/PostList";
import useAxios from "../../hooks/useAxios";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const BoardPage = () => {
  const navigate = useNavigate();

  const { data, error, loading } = useAxios("/board/post");
  // get 방식은 reqConfig 생략 가능

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"글 작성하기"}
          onClickItem={() => navigate("/board/write")}
        ></BasicButton>
      </BtnWrapper>

      {loading ? (
        <p>Data is currently loading...</p>
      ) : error ? (
        <p>There was an error loading</p>
      ) : (
        data && (
          <PostList
            posts={data}
            onClickItem={(item) => {
              navigate(`/board/${item.post_id}`);
            }}
          ></PostList>
        )
      )}
    </>
  );
};

export default BoardPage;
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.