난이도
(실습 난이도 : 하, 소요시간 : 30min±)
실습 설명
오늘은 React-redux를 사용해서 게시판을 만들어볼 것이다.
사용할 컴포넌트는 기본으로 제공되는 App.jsx와
Post, Add, Find, Store를 새로 생성해서 사용할 것이다.
실습
먼저 컴포넌트를 생성하고 App.jsx에 import해주자.
하는김에 React-redux를 사용 할 것이기 때문에 store생성, Provider도 같이 감싸주겠다.
[App.jsx]
import { Provider } from "react-redux";
import { createStore } from "redux";
import "./App.css";
import Add from "./components/Add";
import Post from "./components/Post";
import Find from "./components/Find";
import reducer from "./Store";
const store = createStore(reducer);
export default function App() {
return (
<div className="App">
<Provider store={store}>
<Find />
<Post />
<Add />
</Provider>
</div>
);
}
가장먼저 Post컴포넌트에서 글목록이 보일 수 있게 해보자.
[Post.jsx]
export default function Post() {
const posts = [
{
title: "리액트",
writer: "홍길동",
},
{
title: "리덕스",
writer: "홍길동",
},
{
title: "자바스크립트",
writer: "홍길동",
},
]
return (
<div>
<ol>
{posts.map((post, idx) => (
<li key={idx}>
<span>
{post.title}-{post.writer}
</span>
<button>
글삭제
</button>
</li>
))}
</ol>
</div>
);
}
자 이제 게시글을 검색할 수 있는 Find UI와 Post UI를 만들어보자.
[Find.jsx]
import { useState } from "react";
export default function Find() {
const [postName, setPostName] = useState("");
const formEvent = (e) => {
e.preventDefault();
};
return (
<form onSubmit={formEvent}>
<input
type="text"
placeholder="검색"
value={postName}
onChange={(e) => setPostName(e.target.value)}
/>
<input type="submit" value="검색" />
</form>
);
}
[Add.jsx]
import { useState } from "react";
export default function Add() {
const [postAddData, setPostAddData] = useState({
title: "",
writer: "",
});
const changeValue = (e) => {
const { name, value } = e.target;
const newValue = { ...postAddData, [name]: value };
setPostAddData(newValue);
};
return (
<div>
<div>
<input
type="text"
placeholder="제목"
name="title"
value={postAddData.title}
onChange={changeValue}
/>
<input
type="text"
placeholder="글쓴이"
name="writer"
value={postAddData.writer}
onChange={changeValue}
/>
</div>
</div>
);
}
그럼 이제 Store를 만들어서 각 컴포넌트에서 요청하는 것을 처리하고,
원하는 자료를 건네줄 수 있는 코드를 작성해보자.
[store.jsx]
export default function reducer(currentState, action) {
if (currentState === undefined) {
return {
posts: [
{
title: "리액트",
writer: "홍길동",
},
{
title: "리덕스",
writer: "홍길동",
},
{
title: "자바스크립트",
writer: "홍길동",
},
],
search: {},
};
}
const newState = { ...currentState };
const postMap = {
POSTADD() {
newState.posts.push(action.data);
newState.search = {};
},
POSTREMOVE() {
newState.posts.splice(action.idx, 1);
},
SEARCH() {
if (
newState.posts.filter((posts) => posts.title === action.data)[0] !==
undefined
) {
newState.search = newState.posts.filter(
(posts) => posts.title === action.data
)[0];
} else {
alert("검색결과가 없습니다.\n글제목을 정확히 입력해주세요.");
}
},
};
postMap[action.type]();
return newState;
}
우리가 배웠던 if/else문법에서 객체/함수형으로 바꿔보았다.
물론 if/else가 한번 들어가긴 했지만 그 전 if/else보다는 효율적이고
유지보수 측면에서 postMap에서만 수정,추가,삭제하면 된다는 점에서
훨씬 나은 코드라고 생각한다.
그럼 글목록도 Store에서 받아오고, Find, Add, Remove를 Store를 통해서
할수 있도록 코딩해보자.
추가로 게시글과 공개여부를 더 넣어주었다.
[Post.jsx]
import { useDispatch, useSelector } from "react-redux";
export default function Post() {
const state = useSelector((state) => state);
const dispatch = useDispatch();
return (
<>
<div>
<ol>
{state.posts.map((post, idx) => (
<li key={idx}>
<span>
{post.title}-{post.writer}
</span>
<button
onClick={() => {
dispatch({ type: "POSTREMOVE", idx: idx });
}}
>
글삭제
</button>
</li>
))}
</ol>
</div>
{state.search.title !== undefined && (
<div>
<div>
제목 - {state.search.title} / 글쓴이 - {state.search.writer}
</div>
<div>
{state.search.secret
? "로그인 후 확인 가능합니다."
: state.search.desc}
</div>
</div>
)}
</>
);
}
앞서 작성했던 것에 사용했던 객체선언을 없애고 Store에서 가져올 수 있도록 State를 선언.
글삭제 버튼을 통해 Store에서 선택한 글 번호를 기준으로 삭제할 수 있도록 했다.
다음은 글 작성이다.
[Add.jsx]
import { useState } from "react";
import { useDispatch } from "react-redux";
export default function Add() {
const [postAddData, setPostAddData] = useState({
title: "",
writer: "",
secret: true,
desc: "",
});
const dispatch = useDispatch();
const changeValue = (e) => {
const { name, value } = e.target;
const newValue = { ...postAddData, [name]: value };
setPostAddData(newValue);
};
return (
<div>
<div>
<input
type="text"
placeholder="제목"
name="title"
value={postAddData.title}
onChange={changeValue}
/>
<input
type="text"
placeholder="글쓴이"
name="writer"
value={postAddData.writer}
onChange={changeValue}
/>
<span>공개여부</span>
<input
type="checkbox"
name="secret"
value={postAddData.secret}
onChange={() => {
setPostAddData({
...postAddData,
secret: !postAddData.secret,
});
}}
/>
</div>
<textarea
name="desc"
placeholder="내용을 입력해주세요"
cols="50"
rows="10"
value={postAddData.desc}
onChange={changeValue}
/>
<button
onClick={() => {
dispatch({ type: "POSTADD", data: postAddData });
setPostAddData({ title: "", writer: "", secret: false, desc: "" });
}}
>
등록
</button>
</div>
);
}
작성또한 버튼을 누르면 작성했던 제목,글쓴이,공개여부,글내용을 Store에 게시할 수 있도록 했다.
여기서 느껴지는게 코드 몇줄 수정안했다.
React-redux는 진짜 편한 것 같다.
이어서 Find도 보자.
[Find.jsx]
import { useState } from "react";
import { useDispatch } from "react-redux";
export default function Find() {
const dispatch = useDispatch();
const [postName, setPostName] = useState("");
const formEvent = (e) => {
e.preventDefault();
dispatch({ type: "SEARCH", data: postName });
};
return (
<form onSubmit={formEvent}>
<input
type="text"
placeholder="검색"
value={postName}
onChange={(e) => setPostName(e.target.value)}
/>
<input type="submit" value="검색" />
</form>
);
}
SEARCH라는 타입으로 전송하고, 글제목을 전송해 같은 게시물을 찾아주는 동작이다.
그럼 마지막으로 Store와, Post를 조금 가다듬고 결과를 보자.
최종코드
[store.jsx]
export default function reducer(currentState, action) {
if (currentState === undefined) {
return {
posts: [
{
title: "리액트",
writer: "홍길동",
secret: true,
desc: "리액트란? ...",
},
{
title: "리덕스",
writer: "홍길동",
secret: true,
desc: "리덕스란? ...",
},
{
title: "자바스크립트",
writer: "홍길동",
secret: false,
desc: "자바스크립트란? ...",
},
],
search: {},
};
}
const newState = { ...currentState };
const postMap = {
POSTADD() {
newState.posts.push(action.data);
newState.search = {};
},
POSTREMOVE() {
newState.posts.splice(action.idx, 1);
},
SEARCH() {
if (
newState.posts.filter((posts) => posts.title === action.data)[0] !==
undefined
) {
newState.search = newState.posts.filter(
(posts) => posts.title === action.data
)[0];
} else {
alert("검색결과가 없습니다.\n글제목을 정확히 입력해주세요.");
}
},
};
postMap[action.type]();
return newState;
}
[Post.jsx]
import { useDispatch, useSelector } from "react-redux";
export default function Post() {
const state = useSelector((state) => state);
const dispatch = useDispatch();
return (
<>
<div>
<ol>
{state.posts.map((post, idx) => (
<li key={idx}>
<span>
{post.title}-{post.writer}
</span>
<button
onClick={() => {
dispatch({ type: "POSTREMOVE", idx: idx });
}}
>
글삭제
</button>
</li>
))}
</ol>
</div>
{state.search.title !== undefined && (
<div>
<div>
제목 - {state.search.title} / 글쓴이 - {state.search.writer}
</div>
<div>
{state.search.secret
? "로그인 후 확인 가능합니다."
: state.search.desc}
</div>
</div>
)}
</>
);
}
결과
손쉽게 가짜게시판을 만들 수 있었다.
물론 진짜 게시판은 서버로 전달하고 서버에서 받아오겠지만
React-redux를 연습하기에는 너무 좋은 것 같다.
'React > Practice' 카테고리의 다른 글
UseEffect, Props를 사용한 시계만들기 with Example code (0) | 2023.01.27 |
---|---|
React와 useState를 사용한 숫자입력기 Sample (0) | 2023.01.25 |