코드스테이츠에서 제공해준 심플한 샘플 블로그에 이론으로 배운 Custom Hook, lazy, Suspense 함수를 코드로 직접 사용하여 동작방식 확인한다.
https://github.com/2-phones/fe-sprint-react-hooks
가짜 REST API 서버를 이용하여 json 데이터를 직접 받아와 실제 블로그처럼 되도록 코드를 작성한다.
먼저 가짜 서버를 설치한다.
// json-server라는 라이브러리로, json 파일을 이용하여 REST API 서버를 구축해주는 라이브러리이다.
$ npm i -g json-server
$ cd data
$ json-server --watch data.json --port 3001
json 파일이 있는 data 폴더에서 서버를 실행시키고 json 파일을 이용하여
get,post 요청시 그에 맞게 응답해주는 형식이다.
먼저 app.js 에서 Route 설정한 컴포넌트를 layz 로 바꾸고 Suspense로 감싼다.
import Home './Home'
import CreateBlog './blogComponent/CreateBlog'
import BlogDetail './blogComponent/BlogDetail'
import NotFound './component/NotFound'
// 기존에 있던 위의 코드 불러오기를 지우고 밑에 코드를 추가한다.
import React, { Suspense, lazy } from 'react';
const Home = React.lazy( () => import('./Home'))
const CreateBlog = React.lazy( () => import('./blogComponent/CreateBlog'))
const BlogDetails = React.lazy( () => import('./blogComponent/BlogDetail'))
const NotFound = React.lazy( () => import('./component/NotFound'))
<Suspense fallback={<div>로딩...</div>}>
<Routes>
// ...생략
</Routes>
</Suspense>
useParams로 작성한 내용의 id값을 받아와 해당 id 내용만 화면에 띄우도록한다.
const BlogDetails = () => {
const {id} = useParams();
const request = {
method : "get" ,
headers : {'Content-Type': 'application/json'},
}
fetch(`http://localhost:3001/blogs${id}`, request)
.then(res => res.data)
.cath( err => console.log(err) )
게시글 작성 버튼을 클릭하여 제목, 내용 , 작성자 값을 서버에 post 요청하여 생성 할수 있도록 한다.
const CreateBlog = () => {
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
const [author, setAuthor] = useState('김코딩');
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const bodys = { title,body,author,likes:0 };
const request = {
method:"POST",
body : JSON.stringify(bodys),
headers: {'Content-Type': 'application/json'}
}
fetch('http://localhost:3001/blogs',request)
.then( () => {
navigate('/')
window.location.reload();
})
.catch( err => console.log(err));
console.log(e.type);
}
가짜 서버를 이용하고 있어서 새로고침을 하지 않으면 생성된게 보이지 않는다. 그래서
window.location.reload()
메서드를 사용하여 정상 응답을 받으면 새로고침 되도록했고, navigate( '/')
처음 화면으로 자동으로 이동하여 화면에 잘보인다.
삭제버튼 클릭시, 해당 정보 삭제되고 하트버튼 클릭시 하트 수가 증가하도록 기능 구현한다.
// 삭제버튼
const handleDeleteClick = () => {
const {id} = useParams();
const [isLike, setIsLike] = useState(true);
fetch(`http://localhost:3001/blogs/${id}`, {
method : "DELETE",
headers: {'Content-Type': 'application/json'},
})
.then( () => {
navigate('/')
window.location.reload()
})
.catch( err => console.log(err))
}
// 하트버튼
const handleLikeClick = () => {
// 하트를 누르면 home에서 새로고침을 했을 때 숫자가 올라가야 합니다.
// isLike false 인경우 true 로 바뀐다.
setIsLike(!isLike);
let likeUpdate = blog.likes
// isLike state가 false 이고 ,0보다 큰경우 하트 수 감소하고 , true면 증가
isLike === false ?
( likeUpdate > 0 ? likeUpdate = blog.likes -1 : likeUpdate = blog.likes )
: likeUpdate = blog.likes +1
const putData = { // 기존 데이터를 덮어씌우기 때문에 데이터 전부다 보내줘야한다.
'id' : blog.id,
"title" : blog.title,
"body" : blog.body,
"author" : blog.author,
"likes" : likeUpdate,
}
console.log(likeUpdate)
fetch(`http://localhost:3001/blogs/${id}`,{
method:"PUT",
body : JSON.stringify( putData ),
headers: {
'Content-Type': 'application/json'
},
})
.then( () => {
navigate(`/blogs/${blog.id}`)
})
.catch( err => console.log(err) )
}
GET 메소드를 통해 데이터를 받아오는 useEffect hook은 컴포넌트 내 여기저기 존재하고 있다. 해당 hook은 반복이 되는 부분이 있으므로 Custom Hook 을 만들어 적용시킨다.
export const useFetch = (url) => {
const [blogs, setBlogs] = useState(null);
const [isPending, setIsPending] = useState(true);
const [error, setError] = useState(null);
/* useState를 이용하여 data, isPending, error를 정의하세요. */
/* useFetch 안의 중심 로직을 작성해주세요. */
useEffect(() => {
setTimeout(() => {
fetch(url)
.then(res => {
if (!res.ok) {
throw Error('could not fetch the data for that resource');
}
return res.json();
})
.then(data => {
setIsPending(false);
setBlogs(data);
setError(null);
})
.catch(err => {
setIsPending(false);
setError(err.message);
})
}, 500);
}, [])
return {blogs , isPending , error,}
}
조회를 하려는 곳에서 간편하게 디스트럭처링 문법으로 할당한다음 활용 할수있다.
const {blogs, isPending, error,} = useFetch('http://localhost:3001/blogs');
<h2>{ blog.title }</h2>
<p>Written by { blog.author }</p>
<div>{ blog.body }</div>
{!isLike? '❤️' : '🤍' }