반응형

1. Ajax를 위한 Axios 설정

npm install axios

axios 설치

Axios : 브라우저와 Node.js에서 사용 가능한 HTTP 클라이언트 라이브러리, 주로 비동기적으로 서버와 통신할 때 사용

-> API 요청을 보내고 그 응답을 받을 때 사용한다고 생각하면 됨

 

import axios from "axios"

export const API_SERVER_HOST = 'http://localhost:8080'

const prefix = `${API_SERVER_HOST}/api/todo`

export const getOne = async (tno) => {
    const res = await axios.get(`${prefix}/${tno}`)

    return res.data
}

export const getList = async (pageParam) => {

    const {page, size} = pageParam

    const res = await axios.get(`${prefix}/list`, {params:{page, size}})

    return res.data
}

mall\src\api\todoApi.js

prefix로 공통 경로를 /api/todo로 지정 -> http://localhost:8080/api/todo가 기본 경로

getOne 함수 : todo 항목을 가져오고, 서버로부터 받은 응답(res)에서 data 부분을 반환함

getList 함수 : todo목록을 페이지 단위로 가져옴

 

 

 

useEffect( ) → 컴포넌트 상태 변경 시 렌더링과 함께 Axios 함수를 재호출하여 무한한 반복 구조 발생!!

useEffect()를 활용하여 컴포넌트 내에서 특정 조건을 충족할 때 동작하는 방법 제공

✓ 컴포넌트 실행 시 단 한 번만 실행되는 비동기 처리

✓ 컴포넌트의 여러 상태 중 특정 상태 변경 시 비동기 처리

 

2. 조회 기능 구현

* React 함수형 컴포넌트의 기본 구조 단축어 : rfce

 

import React, { useEffect, useState } from 'react'
import { getOne } from '../../api/todoApi';

const initState = {
    tno:0,
    title:'',
    writer:'',
    dueDate:'',
    complete: false
}

function ReadComponent({tno}) {

    const [todo, setTodo] = useState(initState)

    useEffect(() => {

        getOne(tno).then(data => {
            console.log(data)
            setTodo(data)
        })

    }, [tno]);

  return (
    <div>

    </div>
  )
}

export default ReadComponent

mall\src\components\todo\ReadComponent.js

먼저 todo 항목의 객체를 정의

useState를 사용하여 todo라는 상태를 생성하고, setTodo라는 상태 업데이트 함수를 제공

useEffect는 컴포넌트가 렌더링될 때 및 tno 값이 변경될 때마다 실행됨

 

mall\src\pages\todo\ReadPage.js

버튼 없애고 위와 같이 수정

 

http://localhost:3000/todo/read/33 에 접속했을 때 다음과 같은 화면이 나오게 된다.

 

! intellij에서 서버 실행하고 확인해야 함 !

관리자모드에서 네트워크를 보면 http://localhost:8080/api/todo/33으로 연결된 것을 확인할 수 있다.

 

import React, { useEffect, useState } from 'react'
import { getOne } from '../../api/todoApi';

const initState = {
    tno:0,
    title:'',
    writer:'',
    dueDate:'',
    complete: false
}

function ReadComponent({tno}) {

    const [todo, setTodo] = useState(initState)

    useEffect(() => {

        getOne(tno).then(data => {
            console.log(data)
            setTodo(data)
        })

    }, [tno]);

  return (
        <div className = "border-2 border-sky-200 mt-10 m-2 p-4">
            {makeDiv('Tno', todo.tno)}
            {makeDiv('Writer', todo.writer)}
            {makeDiv('Title', todo.title)}
            {makeDiv('Due date', todo.dueDate)}
            {makeDiv('Complete', todo.complete ? 'Completed' : 'Not Yet')}
        </div>
    )
}

const makeDiv = (title,value) =>
    <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
            <div className="w-1/5 p-6 text-right font-bold">{title}</div>
            <div className="w-4/5 p-6 rounded-r border border-solid shadow-md">
                {value}
            </div>
        </div>
    </div>

export default ReadComponent

mall\src\components\todo\ReadComponent.js

조회 코드를 추가

 

3. 네비게이션 관련 커스텀 훅

커스텀 훅: React에서 재사용 가능한 로직을 빼내어 독립적으로 관리하기 위해 사용하는 함수

 

import React, { useEffect, useState } from 'react'
import { getOne } from '../../api/todoApi';
import useCustomMove from '../../hooks/useCustomMove';

const initState = {
    tno:0,
    title:'',
    writer:'',
    dueDate:'',
    complete: false
}

function ReadComponent({tno}) {

    const [todo, setTodo] = useState(initState)

    const {moveToList} = useCustomMove() //커스텀 훅

    useEffect(() => {

        getOne(tno).then(data => {
            console.log(data)
            setTodo(data)
        })

    }, [tno]);

  return (
        <div className = "border-2 border-sky-200 mt-10 m-2 p-4">
            {makeDiv('Tno', todo.tno)}
            {makeDiv('Writer', todo.writer)}
            {makeDiv('Title', todo.title)}
            {makeDiv('Due date', todo.dueDate)}
            {makeDiv('Complete', todo.complete ? 'Completed' : 'Not Yet')}

            {/* buttons.........start */}
            <div className="flex justify-end p-4">

                <button type="button"
                        className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
                        onClick={() => moveToList()}
                    >
                List
                </button>
            </div>

        </div>
    )
}

const makeDiv = (title,value) =>
    <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
            <div className="w-1/5 p-6 text-right font-bold">{title}</div>
            <div className="w-4/5 p-6 rounded-r border border-solid shadow-md">
                {value}
            </div>
        </div>
    </div>

export default ReadComponent

mall\src\components\todo\ReadComponent.js

useCustomMove는 커스텀 훅으로, 컴포넌트에서 특정 페이지로 이동하는 동작을 함

 

import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom"

const getNum = (param, defaultValue) => {

    if(!param) {
        return defaultValue
    }
    return parseInt(param)
}

const useCustomMove = () => {

    const navigate = useNavigate()

    const [queryParams] = useSearchParams()

    const page = getNum(queryParams.get('page'), 1)
    const size = getNum(queryParams.get('size'), 10)

    const queryDefault = createSearchParams({page, size}).toString()

    const moveToList = (pageParam) => {

        let queryStr = ""

        if(pageParam){
            const pageNum = getNum(pageParam.page, 1)
            const sizeNum = getNum(pageParam.size, 10)
            queryStr = createSearchParams({page: pageNum, size: sizeNum}).toString()
        } else {
            queryStr = queryDefault
        }
        navigate({pathname: `../list`, search: queryStr})

    }

    return {moveToList}

}

export default useCustomMove

mall\src\hooks\useCustomMove.js 에 useCustomMove가 정의되어 있다.

useCustomMove는 React Router의 useNavigate 및 useSearchParams 훅을 활용하여 쿼리 파라미터를 관리하고 이동 로직을 구현했다.

 

따라서 List 버튼이 생겼고 누르면 해당값도 출력이 된다.

 

 

4. 목록 처리(1)

수정과 관련된 커스텀 훅도 생성

커스텀 훅을 사용하면 간편하게 구현이 가능함

 

import { useEffect, useState } from "react";
import { getList } from "../../api/todoApi";
import useCustomMove from "../../hooks/useCustomMove";

const initState = {
    dtoList:[],
    pageNum: [],
    pageRequestDTO: null,
    prev: false,
    next: false,
    totalCount: 0,
    prevPage: 0,
    nextPage: 0,
    totalPage: 0,
    current: 0
}

function ListComponent(props){

    const {page, size} = useCustomMove()

    const [serverData, setServerData]= useState(initState)

    useEffect(()=> {

        getList({page, size}).then(data => {
            console.log(data)
            setServerData(data)
        })
    }, [page, size])

    return (
        <div>
            Todo List Page Component
        </div>
    );
}

export default ListComponent

mall\src\components\todo\ListComponent.js

 

import React from 'react'
import { useSearchParams } from 'react-router-dom';
import ListComponent from '../../components/todo/ListComponent';

const ListPage = () => {

  return (
    <div className="p-4 w-full bg-white ">
        <div className="text-3xl font-extrabold">
            Todo List Page Component 
        </div>
        <ListComponent/>
        </div>
        );
}

export default ListPage

mall\src\pages\todo\ListPage.js

ListPage에서 ListComponent를 import 하고 page, size 속성을 전달

컴포넌트 중심으로 수정하면 간단한 코드로 구현이 가능해진다.

 

 

return (
        <div className="border-2 border-blue-100 mt-10 mr-2 ml-2">

        <div className="flex flex-wrap mx-auto justify-center p-6">

            {serverData.dtoList.map(todo =>

                <div 
                    key= {todo.tno} 
                    className="w-full min-w-[400px] p-2 m-2 rounded shadow-md"
                >
                    <div className="flex ">
                        <div className="font-extrabold text-2xl p-2 w-1/12">
                            {todo.tno} 
                        </div>
                        <div className="text-1xl m-1 p-2 w-8/12 font-extrabold">
                            {todo.title}
                        </div>
                        <div className="text-1xl m-1 p-2 w-2/10 font-medium"> 
                            {todo.dueDate} 
                        </div>
                        </div>
                    </div>
                )}
        </div>
        </div>
    );

리스트가 출력되도록 수정

 

 

서버에서 전달받은 데이터도 응답에 모두 출력되는 것을 볼 수 있다.

 

5. 목록 처리(2) - 동일 페이지 클릭 처리

import React from 'react';

function PageComponent({ serverData, movePage }) {
    return (
        <div className="m-6 flex justify-center">
            {serverData.prev ? (
                <div 
                    className="m-2 p-2 w-16 text-center font-bold text-blue-400"
                    onClick={() => movePage({ page: serverData.prevPage })}>
                    Prev
                </div>
            ) : null}

            {serverData.pageNumList && serverData.pageNumList.length > 0 ? (
                serverData.pageNumList.map(pageNum => (
                    <div 
                        key={pageNum}
                        className={`m-2 p-2 w-12 text-center rounded shadow-md text-white
                        ${serverData.current === pageNum ? 'bg-gray-500' : 'bg-blue-400'}`}
                        onClick={() => movePage({ page: pageNum })}>
                        {pageNum}
                    </div>
                ))
            ) : (
                <div className="m-2 p-2 text-center text-white">No pages available</div>
            )}

            {serverData.next ? (
                <div 
                    className="m-2 p-2 w-16 text-center font-bold text-blue-400"
                    onClick={() => movePage({ page: serverData.nextPage })}>
                    Next
                </div>
            ) : null}
        </div>
    );
}

export default PageComponent;

mall\src\components\common\PageComponent.js

오류가 있어서 강의와 다르게 수정했고, 서버 코드에 pageNumList를 소문자로 작성된 것이 있어서 이 부분도 수정했다...

 

this.current = pageRequestDTO.getPage();

그리고 오류는 없었는데 current가 추가되어서 pageResponseDTO.java에 코드를 추가했다.

 

mall\src\components\todo\ListComponent.js

ListComponent에서 moveToList를 사용할 수 있도록 추가

 

list 페이지에 위와 같이 페이징 처리가 붙은 것을 볼 수 있다.

페이지와 사이즈가 변하지 않으면 다시 서버를 호출하지 않음

따라서 동일 페이지를 클릭했을 때도 서버를 호출하도록 수정해야 함

 

import { useState } from "react"
import { createSearchParams, useNavigate, useSearchParams } from "react-router-dom"

const getNum = (param, defaultValue) => {

    if(!param) {
        return defaultValue
    }
    return parseInt(param)
}

const useCustomMove = () => {

    const navigate = useNavigate()

    const [queryParams] = useSearchParams()

    const [refresh, setRefresh] = useState(false)

    const page = getNum(queryParams.get('page'), 1)
    const size = getNum(queryParams.get('size'), 10)

    const queryDefault = createSearchParams({page, size}).toString()

    const moveToList = (pageParam) => {

        let queryStr = ""

        if(pageParam){
            const pageNum = getNum(pageParam.page, 1)
            const sizeNum = getNum(pageParam.size, 10)
            queryStr = createSearchParams({page: pageNum, size: sizeNum}).toString()

        } else {
            queryStr = queryDefault

        }

        setRefresh(!refresh)

        navigate({pathname: `../list`, search: queryStr})

    }

    const moveToModify = (num) => {
        navigate({
            pathname:`../modify/${num}`,
            search:queryDefault
        })
    }

    return {moveToList, moveToModify, page, size, refresh}

}

export default useCustomMove

mall\src\hooks\useCustomMove.js

컴포넌트 내부에 매번 변하는 상태(state) 값을 이용

 

refresh값은 ListComponent의 useEffect( )에서 사용하도록 설정

 

이제 동일 페이지를 클릭하면 서버 호출이 이루어짐

 

6. 조회 페이지 이동

mall\src\hooks\useCustomMove.js

useCustomMove( )를 이용해서 moveToRead( ) 함수 추가

 

mall\src\components\todo\ListComponent.js

ListComponent에 moveToRead를 사용하기 위한 코드 작성

 

이제 페이지를 유지한 채로 조회가 가능하다.

=> 여기까지 조회 기능 완료

이제 등록 기능 구현

 

등록 컴포넌트와 모달 처리

 

 

import AddComponent from "../../components/todo/AddComponent";


const AddPage = () => {

  return (
      <div className="p-4 w-full bg-white">
          <div className="text-3xl font-extrabold">
              Todo Add Page
          </div>

          <AddComponent/>
      </div>

      );
      
}

export default AddPage;

mall\src\pages\todo\AddPage.js

AddComponent 임포트

 

 

import React, { useState } from "react";

const initState = {
  title: "",
  writer: "",
  dueDate: "",
};

function AddComponent(props) {
  const [todo, setTodo] = useState({ ...initState });

  const handleChangeTodo = (e) => {
    console.log(e.target.name, e.target.value);

    todo[e.target.name] = e.target.value;

    setTodo({ ...todo });
  };

  const handleClickAdd = () => {
    console.log(todo);
  };

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TITLE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="title"
            type={"text"}
            value={todo.title}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">WRITER</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="writer"
            type={"text"}
            value={todo.writer}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>

      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="dueDate"
            type={"date"}
            value={todo.dueDate}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-end">
        <div className="relative mb-4 flex p-4 flex-wrap items-stretch">
          <button
            type="button"
            onClick={handleClickAdd}
            className="rounded p-4 w-36 bg-blue-500 text-xl text-white"
          >
            {" "}
            ADD{" "}
          </button>
        </div>
      </div>
    </div>
  );
}

export default AddComponent;

AddComponent 기능 구현

useState를 사용하며, 입력값을 처리하고 추가 버튼 클릭 시 콘솔에 현재 상태를 출력한다.

 

export const postAdd = async (todoObj) => {

    const res = await axios.post(`${prefix}/`, todoObj)

    return res.data
}

mall\src\api\todoApi.js

Axios를 사용하여 서버로 POST 요청을 보내는 비동기 함수 추가

 

 

 

7. 모달 컴포넌트 제작

import React from "react";

function ResultModal() {
  return (
    <div
      className={`fixed top-0 left-0 z-[1055] flex h-full w-full justify-center bg-black bg-opacity-20`}
    >
      <div className="absolute bg-white shadow dark:bg-gray-700 opacity-100 w-1/4 rounded mt10 mb-10 px-6 min-w-[600px]">
        <div className="justify-center bg-warning-400 mt-6 mb-6 text-2xl border-b-4 bordergray-500">
          title
        </div>

        <div className="text-4xl border-orange-400 border-b-4 pt-4 pb-4">
          DESC
        </div>

        <div className="justify-end flex ">
          <button className="rounded bg-blue-500 mt-4 mb-4 px-6 pt-4 pb-4 text-lg text-white">
            Close Modal
          </button>
        </div>
      </div>
    </div>
  );
}

export default ResultModal;

mall\src\components\common\ResultModal.js 추가

 

ResultModal 컴포넌트를 사용할 수 있도록 mall\src\components\todo\AddComponent.js에 추가

 

배경이 생기고 모달창을 볼 수 있음

 

import React, { useState } from "react";
import ResultModal from "../common/ResultModal";
import { postAdd } from "../../api/todoApi";

const initState = {
  title: "",
  writer: "",
  dueDate: "",
};

function AddComponent(props) {
  const [todo, setTodo] = useState({ ...initState });

  const [result, setResult] = useState(null);

  const handleChangeTodo = (e) => {
    console.log(e.target.name, e.target.value);

    todo[e.target.name] = e.target.value;

    setTodo({ ...todo });
  };

  const handleClickAdd = () => {
    //console.log(todo);
    postAdd(todo).then((result) => {
      setResult(result.TNO);
      setTodo({ ...initState });
    });
  };

  const closeModal = () => {
    setResult(null);
  };

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TITLE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="title"
            type={"text"}
            value={todo.title}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">WRITER</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="writer"
            type={"text"}
            value={todo.writer}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>

      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-500 shadow-md"
            name="dueDate"
            type={"date"}
            value={todo.dueDate}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-end">
        <div className="relative mb-4 flex p-4 flex-wrap items-stretch">
          <button
            type="button"
            onClick={handleClickAdd}
            className="rounded p-4 w-36 bg-blue-500 text-xl text-white"
          >
            ADD
          </button>
        </div>
      </div>

      {result ? (
        <ResultModal
          title={"Add Result"}
          content={`New ${result} Added`}
          callbackFn={closeModal}
        ></ResultModal>
      ) : (
        <></>
      )}
    </div>
  );
}

export default AddComponent;

mall\src\components\todo\AddComponent.js

add 버튼을 누르면 값을 가져오고 추가되는 것을 확인할 수 있고 close Modal 버튼을 눌렀을 때 입력했던 값이 초기화된다.

 

새 글이 등록되면 list 페이지도 이동하도록 수정한다.

기존에 moveToList를 만들었기 때문에 이걸 사용해서 17, 37번째 줄만 추가했다.

 

Add가 완료되면 list 페이지로 돌아오면 추가가 된 것을 확인할 수 있다.

 

8. 수정/삭제 처리

→ 삭제(Delete 버튼) : 삭제 결과를 모달창으로 보여주고 '/todo/list'로 이동

→ 수정(Modify 버튼) : 수정 결과를 모달창으로 보여주고 '/todo/read/번호'로 이동

→ 결과의 출력은 공통적으로 모달 창을 이용

 

export const deleteOne = async (tno) => {
  const res = await axios.delete(`${prefix}/${tno}`);
  return res.data;
};

export const putOne = async (todo) => {
  const res = await axios.put(`${prefix}/${todo.tno}`, todo);
  return res.data;
};

mall\src\api\todoApi.js

수정과 삭제 함수 추가

 

import React, { useEffect, useState } from "react";
import { getOne } from "../../api/todoApi";

const initState = {
  tno: 0,
  title: "",
  writer: "",
  dueDate: "",
  complete: false,
};

function ModifyComponent({ tno }) {
  const [todo, setTodo] = useState(initState);

  useEffect(() => {
    getOne(tno).then((data) => {
      console.log(data);
      setTodo(data);
    });
  }, [tno]);

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      <div className="flex justify-end p-4">
        <button
          type="button"
          className="inline-block rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
        >
          Delete
        </button>
        <button
          type="button"
          className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
        >
          Modify
        </button>
      </div>
    </div>
  );
}

export default ModifyComponent;

mall\src\components\todo\ModifyComponent.js

 

import { useParams } from "react-router-dom";
import ModifyComponent from "../../components/todo/ModifyComponent";

const ModifyPage = () => {
  const { tno } = useParams();

  return (
    <div className="p-4 w-full bg-white">
      <div className="text-3xl font-extrabold">Todo Modify Page</div>
      <ModifyComponent tno={tno} />
    </div>
  );
};

export default ModifyPage;

mall\src\pages\todo\ModifyPage.js

 

 

 

import React, { useEffect, useState } from "react";
import { getOne } from "../../api/todoApi";

const initState = {
  tno: 0,
  title: "",
  writer: "",
  dueDate: "",
  complete: false,
};

function ModifyComponent({ tno }) {
  const [todo, setTodo] = useState(initState);

  useEffect(() => {
    getOne(tno).then((data) => {
      console.log(data);
      setTodo(data);
    });
  }, [tno]);

  const handleChangeTodo = (e) => {
    console.log(e.target.name, e.target.value);

    todo[e.target.name] = e.target.value;

    setTodo({ ...todo });
  };

  const handleChangeTodoComplete = (e) => {
    const value = e.target.value;
    todo.complete = value === "Y";
    setTodo({ ...todo });
  };

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      <div className="flex justify-center mt-10">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TNO</div>
          <div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
            {" "}
            {todo.tno}{" "}
          </div>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">WRITER</div>
          <div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
            {" "}
            {todo.writer}{" "}
          </div>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TITLE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
            name="title"
            type={"text"}
            value={todo.title}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
            name="dueDate"
            type={"date"}
            value={todo.dueDate}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">COMPLETE</div>
          <select
            name="status"
            className="border-solid border-2 rounded m-1 p-2"
            onChange={handleChangeTodoComplete}
            value={todo.complete ? "Y" : "N"}
          >
            <option value="Y">Completed</option>
            <option value="N">Not Yet</option>
          </select>
        </div>
      </div>
      <div className="flex justify-end p-4">
        <button
          type="button"
          className="inline-block rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
        >
          Delete{" "}
        </button>
        <button
          type="button"
          className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
        >
          {" "}
          Modify{" "}
        </button>
      </div>
    </div>
  );
}

export default ModifyComponent;

mall\src\components\todo\ModifyComponent.js

Modify 페이지 수정

 

tno와 writer는 변경이 안되고 complete는 선택할 수 있게 수정한 것이다.

 

 

ModifyComponent에 handleClickModify( ), handleClickDelete( ) 함수 정의, 버튼에 이벤트 처리

위와 같은 응답이 뜨면 되고 헤더에도 put 방식으로 전달됨

 

그리고 delete도 해봤는데 나는 오류 없이 105번이 사라졌다..

 

 

 

import React, { useEffect, useState } from "react";
import { deleteOne, getOne, putOne } from "../../api/todoApi";
import useCustomMove from "../../hooks/useCustomMove";
import ResultModal from "../common/ResultModal";

const initState = {
  tno: 0,
  title: "",
  writer: "",
  dueDate: "",
  complete: false,
};

function ModifyComponent({ tno }) {
  const [todo, setTodo] = useState(initState);

  const [result, setResult] = useState(null);

  //수정하면 조회 페이지로 이동
  //삭제하면 리스트 페이지로 이동
  const { moveToRead, moveToList } = useCustomMove();

  useEffect(() => {
    getOne(tno).then((data) => {
      console.log(data);
      setTodo(data);
    });
  }, [tno]);

  const handleChangeTodo = (e) => {
    console.log(e.target.name, e.target.value);

    todo[e.target.name] = e.target.value;

    setTodo({ ...todo });
  };

  const handleChangeTodoComplete = (e) => {
    const value = e.target.value;
    todo.complete = value === "Y";
    setTodo({ ...todo });
  };

  const handleClickDelete = () => {
    deleteOne(tno).then((data) => {
      console.log("delete result: " + data);
      setResult("Deleted");
    });
  };

  const handleClickModify = () => {
    putOne(todo).then((data) => {
      console.log("modify result: " + data);
      setResult("Modified");
    });
  };

  const closeModal = () => {
    if (result === "Deleted") {
      moveToList();
    } else {
      moveToRead(tno);
    }
  };

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      <div className="flex justify-center mt-10">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TNO</div>
          <div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
            {" "}
            {todo.tno}{" "}
          </div>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">WRITER</div>
          <div className="w-4/5 p-6 rounded-r border border-solid shadow-md bg-gray-100">
            {" "}
            {todo.writer}{" "}
          </div>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">TITLE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
            name="title"
            type={"text"}
            value={todo.title}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">DUEDATE</div>
          <input
            className="w-4/5 p-6 rounded-r border border-solid border-neutral-300 shadow-md"
            name="dueDate"
            type={"date"}
            value={todo.dueDate}
            onChange={handleChangeTodo}
          ></input>
        </div>
      </div>
      <div className="flex justify-center">
        <div className="relative mb-4 flex w-full flex-wrap items-stretch">
          <div className="w-1/5 p-6 text-right font-bold">COMPLETE</div>
          <select
            name="status"
            className="border-solid border-2 rounded m-1 p-2"
            onChange={handleChangeTodoComplete}
            value={todo.complete ? "Y" : "N"}
          >
            <option value="Y">Completed</option>
            <option value="N">Not Yet</option>
          </select>
        </div>
      </div>
      <div className="flex justify-end p-4">
        <button
          type="button"
          className="inline-block rounded p-4 m-2 text-xl w-32 text-white bg-red-500"
          onClick={handleClickDelete}
        >
          Delete
        </button>
        <button
          type="button"
          className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
          onClick={handleClickModify}
        >
          Modify
        </button>
      </div>
      {result ? (
        <ResultModal
          title={"처리결과"}
          content={result}
          callbackFn={closeModal}
        ></ResultModal>
      ) : (
        <></>
      )}
    </div>
  );
}

export default ModifyComponent;

mall\src\components\todo\ModifyComponent.js 전체 코드

화면 이동에 필요한 기능을 가져오고 모달창이 close될 때 호출하도록 변경

 

수정과 삭제를 했을 때 모달창이 뜬다.

수정을 하면 조회 페이지로, 삭제를 하면 리스트 페이지로 돌아가는 것을 확인하면 성공 !

반응형

'Web' 카테고리의 다른 글

5. 상품 API 서버 구성하기 - 2  (0) 2024.10.14
5. 상품 API 서버 구성하기 - 1  (0) 2024.10.08
3. 스프링부트와 API 서버 - 2  (0) 2024.10.02
3. 스프링부트와 API 서버 - 1  (0) 2024.10.01
2. React-Router  (0) 2024.09.24