1. Router
우선 Router 기능을 구현하기 위해서는 npm install react-router-dom 명령어를 통해 해당 라이브러리를 추가해주어야 합니다.
App.js
import Day from './component/Day';
import DayList from './component/DayList';
import EmptyPage from './component/EmptyPage';
import Header from './component/Header';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<div className="App">
<Header />
<Routes>
<Route exact path="/" element={<DayList />}></Route>
<Route path="/day/:day" element={<Day />}></Route>
<Route path="*" element={<EmptyPage/>} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Java
복사
위 코드와 같이 구성하면 <Routes> 내부에 있는 태그들은 URL 경로에 따라 변경되는 컴포넌트 들이며, 외부에 있는 컴포넌트들은 그대로 유지될 것입니다.
따라서 Header 컴포넌트들은 유지되고, 정확하게 / 경로로 들어온다면 DayList 컴포넌트가, DayList에서 선택한 day는 /day/:day의 :day에 들어가 요청에 맞는 값들을 보여줄 것입니다.
그 외에 경로는 모두 EmptyPage 컴포넌트가 필터링 해줄 것입니다.
+ exact path를 사용한 이유는 “/” 경로를 제외한 그 어떤 경로에도 <DayList> 이외의 컴포넌트가 나타나지 않게 하기 위해서입니다. 만약 exact 속성이 아니라면 /가 포함된 모든 경로에 DayList 컴포넌트가 포함됩니다.
DayList.js
import { Link } from 'react-router-dom';
import dummy from '../db/data.json';
export default function DayList() {
return (
<ul className="list_day">
{dummy.days.map((day) => (
<li key={day.id}>
<Link to = {`/day/${day.day}`}> Day {day.day} </Link>
</li>
))}
</ul>
);
}
Java
복사
<a> 대신 <Link> 를 사용했습니다.
Day.js
import dummy from "../db/data.json"
import { useParams } from 'react-router-dom';
export default function Day() {
const {day} = useParams();
const wordList = dummy.words.filter(word => word.day === Number(day))
return <>
<h2> Day {day} </h2>
<table>
<tbody>
{wordList.map(word => (
<tr key = {word.id}>
<td>{word.eng}</td>
<td>{word.kor}</td>
</tr>
))}
</tbody>
</table>
</>
}
Java
복사
:day로 넘겨지는 값은 useParams를 통해 낚아채줍니다. 해당 day는 필터링 함수를 통해 각 일자에 맞는 단어 리스트를 추출하는 역할을 합니다.
2. 리팩토링
import dummy from "../db/data.json"
import { useParams } from 'react-router-dom';
export default function Day() {
const {day} = useParams();
const wordList = dummy.words.filter(word => word.day === Number(day))
return <>
<h2> Day {day} </h2>
<table>
<tbody>
{wordList.map(word => (
<tr key = {word.id}>
<td>
<input type="checkbox"/>
</td>
<td>{word.eng}</td>
<td>{word.kor}</td>
<td>
<button>뜻 보기</button>
<button class="btn_del">삭제</button>
</td>
</tr>
))}
</tbody>
</table>
</>
}
Java
복사
체크 박스와 뜻 보기, 삭제 버튼을 추가했다고 가정해봅시다.
뜻 보기를 누르면 해당 단어에 대한 뜻만 안보여야 합니다. 리액트 컴포넌트의 state 값은 각 컴포넌트 마다 별도로 구성됩니다. 따라서 각 행을 컴포넌트로 분리하는 작업이 필요합니다.
Word.js
import { useState } from 'react';
export default function Word({ word }) {
// 초기값
const [isShow, setIsShow] = useState(false);
// 초기값
const [isDone, setIsDone] = useState(word.isDone);
function toggleDone() {
setIsDone(!isDone);
}
function toggleShow() {
setIsShow(!isShow);
}
return (
<tr className={isDone ? 'off' : ""}>
<td>
<input type="checkbox" checked = {isDone} onChange={toggleDone}/>
</td>
<td>{word.eng}</td>
<td>{isShow && word.kor}</td>
<td>
<button onClick={toggleShow}>
뜻 {isShow ? '숨기기' : '보기'}</button>
<button className="btn_del">삭제</button>
</td>
</tr>
);
}
Java
복사
이 쯤에서 한번 state 값과 function에 대해 다시 한번 짚고 넘어가야 할 것 같습니다.
const [isShow, setIsShow] = useState(false);
•
isShow: 상태 변수로, 초기 값은 false입니다. 이 변수는 어떤 요소의 표시 여부를 나타내는 데 사용될 수 있습니다.
•
setIsShow: 이 함수는 isShow 상태를 업데이트하는 데 사용됩니다. 새로운 값이 필요할 때 setIsShow를 호출하여 isShow의 값을 변경할 수 있습니다.
•
useState(false): useState 훅을 호출하며, isShow의 초기 값으로 false를 설정합니다.
function toggleShow() { setIsShow(!isShow); }
•
toggleShow 함수는 isShow 상태의 값을 반전시키는 함수입니다. isShow가 true이면 false로, false이면 true로 변경합니다. 이 함수를 호출함으로써 특정 요소의 표시 상태를 토글할 수 있습니다.
•
useState 훅은 컴포넌트가 렌더링될 때마다 현재 상태 값을 제공하므로, toggleShow 함수 내에서 isShow를 사용할 때, 그 시점에서의 isShow의 현재 값(즉, 가장 최근에 설정된 상태 값)을 참조하게 됩니다.
3. 간단하게 JSON Server 띄우기
npm install -g json-server
설치가 된 후 다음 명령어를 쳐봅시다.
json-server --watch ./src/db/data.json --port 3001
그러면 DB에 있는 두 days, words를 기반으로 한 EndPoint가 만들어집니다.
POST, GET, PUT, DELETE를 통해 CRUD 작업이 가능합니다.
4. REST API 적용하기
import { useParams } from 'react-router-dom';
import Word from './Word';
import { useEffect, useState } from 'react';
export default function Day() {
const {day} = useParams();
const [words, setWords] = useState([]);
useEffect(() => {
fetch("http://localhost:3001/words")
.then(res => {
return res.json();
})
.then(words => {
setWords(words)
})
})
const wordList = words.filter(word => word.day === Number(day))
return <>
<h2> Day {day} </h2>
<table>
<tbody>
{wordList.map(word => (
<Word word={word} key={word.id}/>
))}
</tbody>
</table>
</>
}
Java
복사
이 부분은 다음과 같이 변경가능하다
// 변경 전
useEffect(() => {
fetch("http://localhost:3001/words")
.then(res => {
return res.json();
})
.then(words => {
setWords(words)
})
})
const wordList = words.filter(word => word.day === Number(day))
// 변경 후
const {day} = useParams();
const [words, setWords] = useState([]);
useEffect(() => {
fetch(`http://localhost:3001/words?day=${day}`)
.then(res => {
return res.json();
})
.then(data => {
setWords(data)
})
}, [day]); // 여기 의존성 부분을 더 공부해야된다
Java
복사