Todo 리스트 API 연결 및 CORS

frookie·2023년 4월 12일
0

ExpressJS

목록 보기
3/4

저번 시간 Todo 리스트의 백엔드를 구현 했다.
https://velog.io/@frookie/ExpressJS
이번 시간에는 이와 연결할 프론트엔드를 구현해보자.

Todo 리스트 페이지 만들기

1. 기본 구조 및 CSS 작업

  • App.jsx
import CreateToDo from "./components/CreateToDo";
import ToDoCard from "./components/ToDoCard";

function App() {
  return (
    <div className="min-h-screen flex flex-col justify-start items-center pt-16">
      <h1 className="text-4xl font-bold">MY TO DO LIST 😎</h1>
      <div>
        <div className="mt-8 text-sm font-semibold">Believe in your self</div>
        <div className="text-xs">자신을 믿어라</div>
      </div>
      <CreateToDo />
      <ul className="mt-16 flex flex-col w-1/2">
        <ToDoCard />
      </ul>
    </div>
  );
}

export default App;

=> ToDo를 입력 및 생성ToDo 카드 컴포넌트로 만들어 삽입

  • CreateToDo.jsx
//ToDo 생성창
function CreateToDo() {
  return (
    <form className="flex mt-2">
      <input
        className="grow border-2 border-slate-400 rounded-lg focus:outline-slate-700 px-2 py-1 text-lg"
        type="text"
      />
      <input
        className="ml-4 px-2 py-1 bg-slate-400 hover:bg-slate-700 rounded-lg text-slate-50 cursor-pointer"
        type="submit"
        value="Create"
      />
    </form>
  );
}

export default CreateToDo;

=> Enter키를 사용할 수 있도록 inputsubmit을 이용

  • ToDoCard.jsx
function ToDoCard() {
  return (
    <>
      <div className="flex my-4">
        <div className="border-4 border-cyan-700 w-8 h-8 rounded-xl"></div>
        <div className="text-2xl ml-4">To Do Title</div>
        <button className="ml-10 text-gray-400 text-xl hover:text-black hover:scale-110">
          X
        </button>
      </div>

      <div className="flex my-4">
        <div className="relative">
          <div className="border-4 border-cyan-700 w-8 h-8 rounded-xl"></div>
          <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-cyan-700 w-8 h-8 rounded-xl"></div>
        </div>
        <div className="text-2xl ml-4">To Do Title</div>
        <button className="ml-10 text-gray-400 text-xl hover:text-black hover:scale-110">
          X
        </button>
      </div>
    </>
  );
}

export default ToDoCard;

=> 위 div는 끝내지 않은 일(isDone), 아래 div는 끝낸 일(!isDone)

2. Routing으로 전체 ToDo 목록 불러오기 (READ)

  • app.jsx
const [toDoList, setToDoList] = useState();

  const getToDoList = async () => {
    try {
      const response = await axios.get(
        `${process.env.REACT_APP_BACKEND_URL}/todo`
      );
      //.env 파일 만들면 리액트 서버 재시작

      if (response.status !== 200) {
        alert("데이터를 불러오지 못했습니다.");
        return;
      }
      
	//api를 통해 응답 받은 데이터를 return에 적용하기 위해
      setToDoList(response.data);
      
    } catch (error) {
      console.error(error);
    }
  };

//처음 시작할 때 backend.json 파일에 있는 데이터 불러오기
  useEffect(() => {
    getToDoList();
  }, []);

=> get으로 리스트 불러오기
=> 처음 마운트 되면 toDoList를 불러오기 위해 useEffect 사용해서 불러온다

하지만, 여기서 그대로 진행할 시, CORS(Axios Error)가 뜨게 된다

CORS 정책 위반

http 주소는 각각의 구성요소가 하나로 합쳐진 형태이다

  • https:// - Protocol
  • api.openweathermap.org - Host
  • data/2.5/weather - Path
  • lat={_lat}&lon={_lon}&appid=${process.env.REACT_APP_WEATHER_API}&units=metric - Query String
    (생략되어 있지만 http - 80, https - 443 Port가 포함)

웹에서는 https://api.openweathermap.org:443 프로토콜과 호스트가 포함된 경로를 같은 출처라고 인식한다

SOP(Same-Origin Policy)

같은 출처를 가진 곳에서만 리소스를 요청할 수 있는 정책이다.

  • 프론트 엔드 코드를 완벽하게 복사한 가짜 사이트에서 요청을 보내면 구분하지 못하는 문제를 해결하기 위해 SOP가 탄생

CORS(Cross-Origin Resource Sharing)

교차 출처(다른 출처)간의 리소스를 공유할 수 있는 정책이다.

  • 기본적으로 CORS 정책은 서버 ↔ 브라우저 간에 발생한다.
  • CORS 정책 위반이 발생하면 브라우저 측에서는 해당 요청을 파기 시킨다.

이를 해결하기 위해 CORS 라이브러리를 설치 후 Express 미들웨어로 추가해야 한다. (이때의 CORS는 정책의 CORS가 아니라 NodeJS의 패키지 중 하나인 CORS이다 - 동일이름)

npm i cors
  • app.js (backend)
const cors = require("cors")

app.use(cors());

이는 모든 요청에 대해 CORS를 허용한다. 제한적으로만 허용하려면 다음과 같이 작성해야 한다.

app.use(
  cors({
    origin: "http://localhost:3000",	▶본인의 경로
    credentials: true,
  })
);

다시 App.jsx로 돌아가서

  • App.jsx
<ul className="mt-16 flex flex-col w-1/2">
        {toDoList &&
          toDoList.map((v, i) => {
            return <ToDoCard />;
          })}
      </ul>

toDoList는 반복 되므로 map함수로 ToDoCard가 여러개 나오도록 처리

3. ToDo 생성 (CREATE)

import axios from "axios";
import { useState } from "react";

//ToDo 생성창
function CreateToDo({ setToDoList }) {
  const [title, setTitle] = useState("");

  //submit 해서 backend로 보내기
  const onSubmitAdd = async (e) => {
    try {
      e.preventDefault();

      if (!title) {
        alert("타이틀을 입력해주세요!");
        return;
      }

      const response = await axios.post(
        `${process.env.REACT_APP_BACKEND_URL}/todo`,
        {
          title,
          desc: `${title} 화이팅`,
          // 임시
        }
      );

      if (response.status !== 200) {
        alert("요청을 불러오지 못했습니다");
        return;
      }

      //props로 불러와 검색하고 자동으로 새로고침하기 위해 서버에 있는 데이터 볼러오기
      setToDoList();
      
      setTitle("");
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <form className="flex mt-2" onSubmit={onSubmitAdd}>
      <input
        className="grow border-2 border-slate-400 rounded-lg focus:outline-slate-700 px-2 py-1 text-lg"
        type="text"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <input
        className="ml-4 px-2 py-1 bg-slate-400 hover:bg-slate-700 rounded-lg text-slate-50 cursor-pointer"
        type="submit"
        value="Create"
      />
    </form>
  );
}

export default CreateToDo;

=> backend로 생성해 보내기
=> Input 창에 넣은 값을 인식하도록 "onChange" 사용
=> submit을 사용해서 새로고침 방지해서 title 내용 넣기
=> 이후 다시 getToDoList 불러와서 다시 자동으로 리스트 새로고침 적용
=> 이후 ToDoCard를 설정해줘야 한다

4. ToDoCard 변경

인자들을 ToDoCard.jsx에 넣어줘야 리스트가 보임

  • App.jsx
<ul className="mt-16 flex flex-col w-1/2">
	{toDoList && toDoList.map((v, i) => {
		return <ToDoCard key={i} isDone={v.isDone} title={v.title} />;
	})}
</ul>

=> map함수를 써서 파라미터를 v.~로 내려줘야 함

  • ToDoCard.jsx
function ToDoCard({ isDone, title }) {

  return (
    <div className="flex my-4">
      {isDone ? (
        <>
          <div className="relative">
            <div className="border-4 border-cyan-700 w-8 h-8 rounded-xl"></div>
            <div
              onClick={onClickIsDone}
              className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-cyan-700 w-8 h-8 rounded-xl"
            ></div>
          </div>
          <div className="text-2xl ml-4 line-through">{title}</div>
        </>
      ) : (
        <>
          <div
            className="border-4 border-cyan-700 w-8 h-8 rounded-xl"
            onClick={onClickIsDone}
          ></div>
          <div className="text-2xl ml-4">{title}</div>
        </>
      )}
      <button
        onClick={onClickDelete}
        className="ml-10 text-gray-400 text-xl hover:text-black hover:scale-110"
      >
        X
      </button>
    </div>
  );
}

=>isDonetitle을 구조 분해로 받아 서 사용
=>isDone이 true이면 색이 채워진 것, false면 비워진 것으로 삼항연산자 처리

5. ToDo 체크박스 수정 (UPDATE)

토글 기능을 추가하기

  • App.jsx
return (
<ToDoCard
  key={i}
  isDone={v.isDone}
  title={v.title}
  index={i}
  getToDoList={getToDoList}
  />
);

=> 인자로 map함수의 iindex기 때문에 :id 역할과 같으므로 내려준다
=> getToDoList는 마찬가지로 자동으로 목록을 새로고침 하기 위해 내려준다

  • ToDoCard.jsx
function ToDoCard({ isDone, title, index, getToDoList }) {
  const onClickToggle = async () => {
    try {
      const response = await axios.put(
        `${process.env.REACT_APP_BACKEND_URL}/todo/done/${index}`
      );

      if (response.status !== 200) {
        alert("요청을 불러오지 못했습니다");
        return;
      }

      getToDoList();
    } catch (error) {
      console.error(error);
    }
  };

=> 인자로 내려받은 indexgetToDoList를 이용해 클릭하면 체크박스 채워지도록 설정
=>여기서 경로에 주의. /todo/done/${index}

6. ToDo 체크박스 삭제 (DELETE)

  • ToDoCard.jsx
const onClickDelete = async () => {
    try {
      const response = await axios.delete(
        `${process.env.REACT_APP_BACKEND_URL}/todo/${index}`
      );

      if (response.status !== 200) {
        alert("요청을 불러오지 못했습니다");
        return;
      }

      getToDoList();
    } catch (error) {
      console.error(error);
    }
  };

반복되지만 주소 /todo/${index}에 주의

profile
비전공자의 코딩, 블록체인 공부 일지

0개의 댓글