연습용으로 만든 Dummy데이터는 고정되었기에 사용자의 액션에 따라서 데이터를 읽고 쓰고 업데이트, 삭제해보자!
그러기 위해서는 DB를 구축하고 API를 만들어야하는데, JSON서버를 이용해서 RESTful API를 만들어 보자!
[REST API란?]
uri주소와 메서드로 CRUD요청을 하는 것!
Create : POST
Read : GET
Update : PUT
Delete : DELETE
URI는 식별하고, URL은 위치를 가르킨다.
URI: 웹 기술에서 사용하는 논리적 또는 물리적 리소스를 식별하는 고유한 문자열 시퀀스
URL: 흔히 웹 주소라고도 하며, 네트워크 상에 리소스가 어디에 있는지 알려주기 위한 규약
JSON서버는 빠르고 쉽게 REST API를 구축해준다.
공부 목적, 작은 프로젝트로 사용 가능하다. 
[셋팅하기]
1. npm 설치
npm install -g json-server 
json-server --watch ./src/db/data.json --port 3001
json-server --watch (경로) --띄울 포트넘버
[JSON 데이터 처리]
1. dummyList를 API통신해서 올바른 응답받기
useEffect()
첫번째 매개변수: 함수
두번째 매개변수: 의존성배열
어떤 상태값이 바뀌었을 때 동작하는 함수를 작성할 수 있다.
함수가 호출된 시기는 렌더링 결과가 실제 dom에 반영됐을때이다.
그리고 컴포넌트가 사라지기 직전에 마지막으로 호출된다.
상태값 변경 -> useEffect 함수 호출
렌더링이 끝나고 작업을 하고 싶으면 함수를 전달해주면 되는데,
매번 변경이 될때마다 불필요하게 함수가 호출 될 수 있다.
그럴 때 두번째 매개변수를 넣어준다. 
//DayList.jsx
//리스트 출력
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
// import dummy from '../db/data.json'
function DayList() {
    const [days, setDays] = useState([])
    useEffect(() => {
        console.log('111')
        fetch('http://localhost:3002/days')//response는 http응답 실제 json이아님
            .then(res => {
                return res.json() //json파일로 변환됨
            })
            .then(data => {
                setDays(data)
            })
    }, [])
    return (
        <div>
            <ul className="list_day">
                {
                    days.map(dum => (
                        <li
                            key={dum.id}
                        >
                            <Link to={`/day/${dum.day}`}>Day {dum.day}</Link>
                        </li>
                    ))
                }
            </ul>
        </div >
    )
}
export default DayList
JSON.stringify() → 자바스크립트를 제이슨으로 변환
.json() → 제이슨 바디를 자바스크립트로 변환
//Day.jsx
// 특정 날짜를 클릭했을 때 단어가 나오게 만드는 페이지
import React, { useEffect, useState } from 'react'
import dummy from '../db/data.json'
import { useParams } from "react-router-dom"
import Word from './Word'
function Day() {
    // day가 1인것만 나올 수 있도록 만들기
    const { day } = useParams()
    // json서버에서 데이터 통신
    const [words, setWords] = useState([])
    useEffect(() => {
        fetch(`http://localhost:3002/words?day=${day}`)
            .then(res => { return res.json() })
            .then(data => setWords(data))
    }, [day])
    return (
        <div>
            <h2>Day {day}</h2>
            {/* 단어를 클릭했을 때 실행되는 함수, day.id와 list.id 값을 비교해서 맞다면 출력 */}
            <table>
                <tbody>
                    {
                        words.map(word =>
                            <Word word={word} key={word.id} />
                        )
                    }
                </tbody>
            </table>
        </div>
    )
}
export default Day
.then(data => setWords(data.filter(db => db.day === Number(day))))fetch(http://localhost:3002/words?day=${day}`)`이렇게해도 동작은 된다.
더 간편한 방법은 서버주소를 백틱으로 감싸서 직접 쿼리스트링과 함께 추가한다.
[겹치는 부분]
동일한 로직은 custom Hooks를 사용해서 반복되는 로직을 사용할 수 있다. 
//useFetch.jsx
import React, { useEffect, useState } from 'react'
function useFetch(url) {
    const [data, setData] = useState([])
    useEffect(() => {
        fetch(url)
            .then(res => res.json())
            .then(data => setData(data))
    }, [url])
    return data
}
export default useFetch
[api로 데이터를 받아오는 부분은 한줄로 처리가 가능해졌다.]
//DayList.js
import { Link } from 'react-router-dom'
import useFetch from '../hooks/useFetch'
function DayList() {
    const days = useFetch("http://localhost:3002/days")
    return (
        <div>
            <ul className="list_day">
                {
                    days.map(dum => (
                        <li
                            key={dum.id}
                        >
                            <Link to={`/day/${dum.day}`}>Day {dum.day}</Link>
                        </li>
                    ))
                }
            </ul>
        </div >
    )
}
export default DayList
PUT method를 이용해서 isDone을 수정해보자.
현재는 isDone을 단순히 true / false가 되어있어서 새로고침시에 모습이 유지되지 않는데, put을 이용해서 새로고침시에도 작동한 그대로 남아있을 수 있게 데이터를 보내준다.
//Word.jsx
const toggleDone = () => {
    // setIsDone(!isDone)
    fetch(`http://localhost:3001/words/${word.id}`, {
        method: "PUT", //2. 요청의 옵션을 입력한다.
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            ...word,
            isDone: !isDone,
        }),
    }).then(res => {
        if (res.ok) {
            setIsDone(!isDone)
        }
    })
}
두번째 인자로 객체를 받아온 후 해당 객체안에 요청의 옵션들을 입력한다.
import React, { useState } from 'react'
function Word({ word }) {
    ...
    // delete
    function del() {
        if (window.confirm('삭제 하시겠습니까?')) {
            fetch(`http://localhost:3001/words/${word.id}`, {
                method: 'DELETE',
            })
        }
    }
    return (
        <div>
            <tr className={isDone ? 'off' : ''}>
                <td>
                    <input
                        type="checkbox"
                        // checked={word.isDone}
                        checked={isDone}
                        onChange={toggleDone}
                    />
                </td>
                <td>{word.eng}</td>
                <td>
                    {isShow && word.kor}
                </td>
                <td>
                    <button onClick={toggleShow}>
                        뜻 {isShow ? '숨기기' : '보기'}
                    </button>
                    <button onClick={del} className='btn_del'>삭제</button>
                </td>
            </tr>
        </div>
    )
}
export default Word
동작하는 화면을 확인했더니..
confirm창이 뜨지만 아무 변화가 없는상태였고, 새로고침하니 삭제가 된 화면이 출력되는것을 확인할 수 있다.
실제단어는 지워지지만, 페이지에는 변화가 없는데 삭제된 이후의 단어리스트를 다시 그려주지 않아서 그렇다.
삭제요청 -> 확인 -> 컴포넌트 리렌더링 과정이 필요하다.
이때 null을 리턴해주면 아무것도 표현해주지 않는다.
그러면 data의 모습을 null로 바꾸면 클릭시 브라우저에서 삭제된 상태로 표시가 된다. 
function Word({ word: w }) {
    const [word, setWord] = useState(w) //w라는 새로운 변수명으로 할당 
	...
    ...
    
    
    // delete
    function del() {
        if (window.confirm('삭제 하시겠습니까?')) {
            fetch(`http://localhost:3001/words/${word.id}`, {
                method: 'DELETE',
            }).then(res => {
                if (res.ok) {
                    setWord({ id: 0 })
                }
            })
        }
    }
    if (word.id === 0) {
        return null
    }
word를 state로 만들고 props로 받아오는데, 새로운 변수명으로 받아올 수 있다.
삭제가 되면, word의 id를 0으로 바꿔준다.
word의 id가 0일경우 null을 리턴해준다. ( id가 0인상태이면 해당 데이터는 기존 데이터를 날려버리고 null이 차지하게 됨 )
알고 넘어갈 부분
state를 props로 받아 왔는데, 이름이 겹쳤던 상황이였다. 
function Word(props) {
	const [word, setWord] = useState(props.word)
    ...
또는
function Word({ word: w }) {
    const [word, setWord] = useState(w) 
    ...
새로운 변수명으로 받아옴 => 구조 분해 할당을 이용했음
새로운 페이지에서 단어 추가와 day 추가 기능을 만들어 보자.
CreateWord.jsx를 하나 만들어주고 App.js에서는 route를 해주고
header에서 Link걸어주기.
import React from 'react'
import useFetch from '../hooks/useFetch'
export default function CreateWord() {
    const addWord = useFetch("http://localhost:3003/days")
    const handleSubmi = (e) => {
        e.preventDefault()
    }
    return (
        <div>
            <form action="submit">
                <div>
                    <label for="eng">영어 단어</label>
                    <input type="text" id="eng" />
                </div>
                <div>
                    <label for="kor">단어 뜻</label>
                    <input type="text" id="kor" />
                </div>
                <div>
                    <label for="day">추가</label>
                    <select name="days" id="day">
                        {
                            addWord.map(word => (
                                <option key={word.id} value={word.day}>day {word.day}</option>
                            ))
                        }
                    </select>
                </div>
            </form>
            <button onClick={handleSubmi}>
                저장
            </button>
        </div >
    )
}
useFetch로 만든 useEffect를 가져와서 map을 돌려 days를 select리스트로 만든다.
//createWord.jsx
import React, { useRef } from 'react'
import useFetch from '../hooks/useFetch'
export default function CreateWord() {
    const addWord = useFetch("http://localhost:3003/days")
    const handleSubmi = (e) => {
        e.preventDefault()
        console.log(engRef.current.value)
        console.log(korRef.current.value)
        console.log(dayRef.current.value)
    }
    const engRef = useRef(null)
    const korRef = useRef(null)
    const dayRef = useRef(null)
    return (
        <div>
            <form action="submit">
                <div>
                    <label for="eng">영어 단어</label>
                    <input type="text" id="eng" ref={engRef} />
                </div>
                <div>
                    <label for="kor">단어 뜻</label>
                    <input type="text" id="kor" ref={korRef} />
                </div>
                <div>
                    <label for="day">추가</label>
                    <select name="days" id="day" ref={dayRef}>
                        {
                            addWord.map(word => (
                                <option key={word.id} value={word.day}>day {word.day}</option>
                            ))
                        }
                    </select>
                </div>
            </form>
            <button onClick={handleSubmi}>
                저장
            </button>
        </div >
    )
}
const engRef = useRef(null) const korRef = useRef(null) const dayRef = useRef(null)<input type="text" id="kor" ref={korRef} />이렇게 연결해주면 DOM요소가 생성된 후 접근할 수 있다.
위 코드에서 저장 버튼을 클릭하는 시점은 렌더링 결과가 돔에 반영된 후이다. 
engRef.current.valuefetch(`http://localhost:3001/words/`, {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
        day: dayRef.current.value,
        eng: engRef.current.value,
        kor: korRef.current.value,
        isDone: false
    }),
}).then(res => {
    if (res.ok) {
        // 생성완료 후 alert생성
        alert('생성이 완료됐습니다.')
    }
})
axios.post('http://localhost:3003/words', {
      day: dayRef.current.value,
      kor: korRef.current.value,
      eng: engRef.current.value,
      isDone: false
  }).then((res) => console.log(res))
}
양식이 제출되었거나 특정 event발생시 url을 조작해줄 수 있다.
const history = useNavigate()
...
axios.post('http://localhost:3003/words', {
        day: dayRef.current.value,
        kor: korRef.current.value,
        eng: engRef.current.value,
        isDone: false
    }).then(() =>
        alert('등록이 완료됐습니다.'),
        history(`/day/${dayRef.current.value}`)
    )
[완성된 createWord.jsx]
import { useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import axios from 'axios'
import useFetch from '../hooks/useFetch'
export default function CreateWord() {
    const addWord = useFetch("http://localhost:3003/days")
    const history = useNavigate()
    const engRef = useRef(null)
    const korRef = useRef(null)
    const dayRef = useRef(null)
    const handleSubmi = (e) => {
        e.preventDefault()
        // console.log(engRef.current.value)
        // console.log(korRef.current.value)
        // console.log(dayRef.current.value)
        axios.post('http://localhost:3003/words', {
            day: dayRef.current.value,
            kor: korRef.current.value,
            eng: engRef.current.value,
            isDone: false
        }).then(() =>
            alert('등록이 완료됐습니다.'),
            history(`/day/${dayRef.current.value}`)
        )
    }
    return (
        <div>
            <form action="submit">
                <div>
                    <label for="eng">영어 단어</label>
                    <input type="text" id="eng" ref={engRef} />
                </div>
                <div>
                    <label for="kor">단어 뜻</label>
                    <input type="text" id="kor" ref={korRef} />
                </div>
                <div>
                    <label for="day">추가</label>
                    <select name="days" id="day" ref={dayRef}>
                        {
                            addWord.map(word => (
                                <option key={word.id} value={word.day}>day {word.day}</option>
                            ))
                        }
                    </select>
                </div>
            </form>
            <button onClick={handleSubmi}>
                저장
            </button>
        </div >
    )
}
[createDay도 만들어 보자]
import axios from 'axios'
import React from 'react'
import { useNavigate } from 'react-router-dom'
import useFetch from '../hooks/useFetch'
export default function CreateDay() {
    const newPage = useNavigate('')
    const days = useFetch('http://localhost:3003/days')
    let daysNum = days.length
    const addDay = () => {
        axios.post('http://localhost:3003/days', {
            day: daysNum + 1
        }).then(
            alert('등록되었습니다.'),
            newPage(`/`)
        )
    }
    return (
        <div>
            <h2>현재 일수: {days.length}</h2>
            <div>
                <button onClick={addDay}>day 추가</button>
            </div>
        </div>
    )
}