[ react ] 포스트 추가하기! (Server로 전달) re-rendering 방법 & 포스트 삭제하기 (filter()함수, [...]Spread Operator)

Suji Kang·2023년 10월 11일
0

🐾 POST 추가하기, 버튼이 클릭되면❗️❓생기는일

먼저 로직을 생각해보자 차례차례 🎬

  1. 사용자 입력 값 검사:
    📌 사용자가 입력한 4개의 값이 유효한지 확인한다.
  2. 유효성 검사 후 값 전달:
    📌 값이 유효하다면, 이 4개의 값을 서버로 전송한다.
  3. 서버 작업:
    📌 서버는 전달받은 4개의 값을 받아온다.
    📌 MySQL 데이터베이스에 4개의 값을 추가한다.
  4. 서버 응답 처리:
    📌 서버가 추가에 성공하면, 리액트 앱에 응답을 보낸다.
  5. 리액트에서 응답 처리:
    📌 리액트 앱은 서버로부터의 응답을 받아, 추가가 성공했다면 화면을 다시 렌더링한다.

🐾 input tag 에서 사용자가 입력하는것을 기억하려면 ❓

state 변수를 만들어야한다.

const [company, setCompany] = useState('');
const [position, setPosition] = useState('');
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');

🌟 사용자가 입력한 4개의 값이 유효한지 검사

🌟 유효한다면 4개의 값을 서버로 전달

careerViewTable.js

import DashboardLayout from "../../components/common/layout";
import { useState } from "react";

const CareerPage = () => {
    //🌟 state 변수 네개, 사용자가 input 태그에 입력한 값을 기억 용도
    const [company, setCompany] = useState('');
    const [position, setPosition] = useState('');
    const [startDate, setStartDate] = useState('');
    const [endDate, setEndDate] = useState('');

    const onAddCareer = async () => {
        // console.log(company, position, startDate, endDate);
        if(company === ''){
            alert('회사명을 입력해주세요');
            return; //company가 비어있으면 (return으로 바로 종료시키고 alert창이 뜬다)
        }

        if(position === ''){
            alert('직책을 입력해주세요');
            return; 
        }

        if(startDate === ''){
            alert('시작일을 입력해주세요');
            return;
        }

        if(endDate === ''){
            alert('종료일을 입력해주세요');
            return;
        }
    }

    return (
        <DashboardLayout>
                    <tbody>
                        <tr>
                            <td><input onChange={e => setCompany(e.target.value)} /></td> 
                          //변경될때마다 값이 setCompany안에 저장된다.
                            <td><input onChange={e => setPosition(e.target.value)}/></td>
                            <td><input onChange={e => setStartDate(e.target.value)} type="date" /></td>
                            <td><input onChange={e => setEndDate(e.target.value)} type="date" /></td>
                        </tr>
                    </tbody>
                    <tfoot>
                        <tr>
                            <td colSpan={4}>
                                <button onClick={onAddCareer}>추가하기</button>
                              //추가버튼을 클릭하면 onAddCareer function이 실행됨 
                            </td>
                        </tr>
                    </tfoot>
        </DashboardLayout>
    )
}
export default CareerPage;

📝 비교를 할때는 같은 타입이여야한다.

나쁜예제

 //시작일이 오늘 날짜보다 늦으면 안됨
 const today = new Date('');
        if (startDate > today){ 
          //컴퓨터 입장에서 startDate가 뭔데???함 (type이 다르기때문에)  
            alert('시작일은 오늘 날짜보다 늦을 수 없습니다.');
            return;
        }

좋은예제

//시작일이 오늘 날짜보다 늦으면 안됨
const today = new Date('');
        const targetStartDate = new Date(startDate);
        if (targetStartDate > today) {
          //🌟 이렇게 날짜랑 날짜를 비교해야함
            alert('시작일은 오늘 날짜보다 늦을 수 없습니다.');
            return;
        }

careerViewTable.js

const onAddCareer = async () => {
//시작일이 오늘 날짜보다 늦으면 안됨
        const today = new Date('');
        const targetStartDate = new Date(startDate);
        if (targetStartDate > today) {
            alert('시작일은 오늘 날짜보다 늦을 수 없습니다.');
            return;
        }
        //종료일은 비어있어도 됨
        if (endDate === '') { //🌟 invalid date, 오류 발생을 방지하기 위해서, "종료일이 비어있으면", 이라는 코드도 작성 중요! 
            const targetEndDate = new Date(endDate);
            if (targetEndDate < targetStartDate) {
                alert('종료일은 시작일보다 빠를 수 없습니다.');
                return;
            }
            if (targetEndDate > today) {
                alert('종료일은 오늘 날짜보다 늦을 수 없습니다.');
                return;
            }
        }
}

📝 Career 추가하기 ❗️

🌟 서버는 4개의 값을 받아와서, mysql에다 4개의 값을 추가 하기

app.js

//career 추가
app.post('/api/career', async (req, res) => {
    const { company, position, startDate, endDate } = req.body; 

    let sql = `
    insert into tbl_careers
    (email, company, position, start_date, end_date)
    values
    (
      'abc@naver.comddd', 
      ?, 
      ?, 
      str_to_date(?, '%Y-%m-%d'), 
      ${endDate === '' ? null : `str_to_date(?, '%Y-%m-%d')`}
    );
    `;

    let values = [company, position, startDate];
    if (endDate !== '') {
        values.push(endDate);
      //endDate가 비어있는 문자열이 아닐떄만, values 에 추가해줘 endDate를
    }

    try { //성공했을때
        let [results] = await pool.query(sql, values);

        console.log(results);
        let [rows] = await pool.query('select * from tbl_careers WHERE  id=?', [results.insertId])
            console.log(rows);
      
        res.json('커리어 추가 완료!');

    } catch (err) { //실패했을때
        console.log(err);
        res.status(500).json('서버에서 오류 발생함');
    }
});

🌟 서버는 추가성공이 되면, 리액트한테 응답 (res)

careerViewTable.js

import axios from "axios";

const onAddCareer = async () => {
 // 정상적으로 실행되는 코드
        try {
            let res = await axios.post('/api/career', {
                company, position, startDate, endDate
            });
            //res.data에는 방금 추가한 "객체"가 들어있음
          //4개의 데이터를 담아서 줘야함
            //성공했을때 뭔가 할것
            alert('추가 완료!');
        } catch (err) {
            console.log(err);
        }

🌟 리액트는 응답을 받아서, 추가가 성공이 되었다면 화면을 다시 리렌더링 (새로고침)

careerViewTable.js

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

const CareerViewTable = () => {
   const [careerList, setCareerList] = useState([]);
  //요소가 몇개냐느에 따라 10개 100개 ...된다
  
const onAddCareer = async () => {
 // 정상적으로 실행되는 코드
        try {
            let res = await axios.post('/api/career', {
                company, position, startDate, endDate
            });
            //res.data에는 방금 추가한 "객체"가 들어있음
          //4개의 데이터를 담아서 줘야함
            //성공했을때 뭔가 할것
            alert('추가 완료!');
            //  window.location.reload(); //새로고침

            //새로고침 대신 생각해 볼수있는것
            //방금 추가한 객체를 이미 20개의 객체가 요소로 들어있는
            //배열의 마지막 요소로 추가
            setCareerList([...careerList, res.data]);

        } catch (err) {
            console.log(err);
        }
}

🐾 re-rendering 2가지 방법 (각각 장단점이 있다. 판단해서 사용)

careerListstate변수,
careerList가 변경되면 <CareerViewTable/>이 다시 그려지면서 아래표가 보여짐.
최초로 렌더링 되면 데이터베이스에 작성되어있는 행들을 받아와서 careerList를 그려주기 때문에 화면상에는 커리어 목록들이 보이게 된다.(직접 새로고침을눌러야함)

👉 1. window.location.reload();

📝 새로고침하는방식

  1. 새로운 커리어 추가하여 데이터베이스에 행추가.
    행 추가가 된다고해서 화면에 커리어 목록이 추가되지는 않는다.
  1. 새로고침실행
    새로고침 실행하면,
<CareerViewTable/>

이 다시 최초로 렌더링을 하기 때문에,
데이터베이스에서 행들을 가져오게 되고, 새로고침이전에 데이터베이스에 추가한 행도 보이게 된다.

📌 단점? 깜빡깜빡 현상이 있고, 데이터가 느리다면, 처음 보일때 빈화면이 보일수있다. 사용자 입장에서는 기다렸다가 데이터를 받는다.


👉 2.careerList에 직접추가 (state변수)

📝 careerList에 직접추가하는 방법.

  1. 새로운 커리어 추가하여 데이터베이스에 행추가.
    👉 행 추가가 된다고해서 화면에 커리어 목록이 추가되지는 않는다.
    -->이거는 첫번째 방법과 같음
  2. careerList에 직접추가.
    careerList가 변화했기 떄문에
<CareerViewTable/>

이 다시 그려지고, 우리눈에 보임

app.js

 try {
        let [rows] = await pool.query('select * from tbl_careers WHERE  id=?', [results.insertId])
       //console.log(rows);
        res.json('커리어 추가 완료!');

    } catch (err) {
        console.log(err);
        res.status(500).json('서버에서 오류 발생함');
    }
🌟 console.log(rows) 값은? 배열👇

🌟 console.log(rows[0])? 👇


👉 방금 추가한 객체 나옴


🐾 [...] 전개 연산자 (Spread Operator )

배열을 풀겠다. 배열 안에서 순회가능한 것을 펼치면 아이템 하나하나가 배열로 전달된다.

//ex)
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const arr3 = [7, 8, 9];
const arrAll = [...arr1, ...arr2, ...arr3];

console.log(arrAll); 
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
//arr1, arr2, arr3 모두가 들어감

careerViewTable.js

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

const CareerViewTable = () => {
   const [careerList, setCareerList] = useState([]);
  //요소가 몇개냐느에 따라 10개, 20개, 100개 ...된다
  
const onAddCareer = async () => {
 // 정상적으로 실행되는 코드
        try {
            let res = await axios.post('/api/career', {
                company, position, startDate, endDate
            });
            //res.data에는 방금 추가한 "객체"가 들어있음
          //4개의 데이터를 담아서 줘야함
            //성공했을때 뭔가 할것
            alert('추가 완료!');
            //  window.location.reload(); //새로고침

            //새로고침 대신 생각해 볼수있는것
            //🌟 방금 추가한 객체를 이미 20개의 객체가 요소로 들어있는
            //기존의 careerList에 있는요소들과,  data, 배열의 마지막 요소로 추가
            setCareerList([...careerList, res.data]);

        } catch (err) {
            console.log(err);
        }
}

📝 전개 연산자를 사용하여 기존에 있던 careerList있는요소들과, 새로 추가한 res.datasetCareerList변수에 넣어준다. (state변수가 변경이 있으면 새로고침을한다.)


🐾 Post Delete 하기 ❗️

onDeleteCareer 함수id라는 매개변수를 받아와 해당 id를 사용하여 Express 서버로 삭제 요청을 보내는 역할.
👉 1. id를 인자로 받는다.
👉 2. 이때, id를 요청body 데이터로 담아 서버로 보낸다.
👉 3.서버에서는 해당 id를 기반으로 경력 정보를 삭제하고, 클라이언트에게 응답한다.
👉 4.삭제 요청이 성공하면 res에는 서버에서 온 응답 데이터가 담긴다. 이때, 클라이언트에게 '삭제 완료!' 알림을 띄운다.

careerViewTable.js

import axios from "axios";

const CareerViewTable = () => {
 //td셀 클릭시 삭제하는 함수
    const onDeleteCareer = async (id) => {
        //id를 가지고 express한테 삭제 요청
      	// id(4)에는 몇번 객체가 삭제되는지에 대한 정보가 들어있음
        try {
            let res = await axios.delete('/api/career/', { data: {id} }); //감싸서 body에다가 담아줌
            alert('삭제 완료!');

            //careerList에서 삭제된 id를 가진 요소를 삭제하고 변경
            //re-rendering 되면서 마치 우리눈에는 사라진것 처럼 보임
        } catch (err) {
            alert('삭제 실패!');
        }
    }
    
return (
  <td 
    onClick={() => onDeleteCareer(e.id)} 
    style={{
           display: 'flex', 
           justifyContent: 'center',
           alignItems: 'center',
           cursor: 'pointer'
       }}>
         <DeleteSweepIcon />
   </td>
 )

}

export default CareerViewTable;

📝 서버에 delete 요청하기

app.js

//career 삭제
app.delete('/api/career', async (req, res) => {
    const { id } = req.body; //react에서 받아온 body
   //삭제할 행 id 는 id에 들어있음
    let sql = `DELETE FROM tbl_careers WHERE id=?`;

    try {
         await pool.query(sql, [id]);
        //console.log(results);
        res.json('삭제 완료!'); // 삭제를 성공하면,
    } catch (err) {
        // console.log(err);
        res.status(500).json('서버에서 오류 발생함');
    }
});

매개변수에 해당하는 값으로, id에 해당하는 행삭제한다다.
클라이언트로부터 받은 id 값을 사용하여 데이터베이스에서 특정 경력 정보를 삭제하고, 그 결과를 👉 클라이언트에게 응답으로 보내는 역할을 한다.

🐾 HTTP DELETE 요청에서 데이터를 보내는 방법은 두 가지

📌 URL 파라미터 (req.params 사용): 요청을 보낼 때 URL에 데이터를 포함시켜서 보낼 수 있다. 예를 들어, /api/career/:id와 같이 URL에 id를 포함시켜 요청할 수 있다. 서버에서는 req.params.id를 사용하여 이 데이터에 접근할 수 있다. 하지만 DELETE 요청에서는 이 방법을 사용하기 어렵다.

📌 요청 본문 (req.body 사용): 보통 DELETE 요청에서는 데이터를 요청 본문에 넣어서 보낸다. 이 방법은 HTTP DELETE 요청에서 데이터를 보내는 표준적인 방법이다. 클라이언트에서 요청을 보낼 때, 데이터를 요청 본문에 넣어 서버에게 보내고, 서버에서는 req.body로 이 데이터에 접근할 수 있다.

📝 따라서, HTTP DELETE 요청은 URL에 데이터를 넣기 어렵기 때문에, body에 데이터를 담아서 서버로 보내는 것이 표준적인 방법. 이렇게 하면 서버에서 req.body로 데이터에 접근할 수 있다


🔎 filter() 함수

filter() 함수는 배열 내 요소를 필터링하는 데 사용됨. 이 함수는 주어진 조건을 만족하는(true가되는) 요소들로 이루어진 새로운 배열을 생성한다.

careerViewTable.js

 //td셀 
    const onDeleteCareer = async (id) => {
        //id를 가지고 express한테 삭제 요청
        try {
            let res = await axios.delete('/api/career/', { data: { id } });
            alert('삭제 완료!');

            //삭제한 id를 제외한 나머지 요소들만 뽑아서 새로운 배열을 만들어서
            //careerList state 변수에 넣어준다.
            let newCareerList = careerList.filter((e) => e.id !== id);
            setCareerList(newCareerList);

            //careerList에서 삭제된 id를 가진 요소를 삭제하고 변경
            //re-rendering 되면서 마치 우리눈에는 사라진것 처럼 보임
        } catch (err) {
            alert('삭제 실패!');
        }
    }      

to be continued...🌟

profile
나를위한 노트필기 📒🔎📝

0개의 댓글