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 파일을 생성합니다.
url
과reqConfig
를 인자로 받아 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;