[ React ] 백엔드와 연동해서 CRD 구현하기

2024. 5. 12. 17:22· FRONT-END/└ React

환경 : Visual Studio Code, Spring Tool Suite4

백엔드 코드 이해 설명 링크 : https://ggingggang05.tistory.com/228

 

전체 흐름 이해

 

  리액트에서 비동기 처리를 위해서 ajax를 사용할 수 있다. 하지만 이는 jquery를 사용했을 경우만 가능하다. 따라서 axios를 활용하여 비동기 통신을 구현할 수 있다. 

 

1) 원하는 프로젝트에 설치한다. 

npm install axios

 

비동기를 사용할 js에 비동기 통신 전용 라이브러리를 import 해준다 

import axios from "axios"; //비동기 통신 전용 라이브러리

 

    (참고) 이후 비동기 통신 진행시 콘솔에서 확인 가능

 

  2) index.js 파일에 <React.StrictMode> 태그를 제거해준다

 

  <React.StrictMode>가 있으면 리액트가 화면을 두 번씩 실행하기 때문에 제거해주는 것이 좋다. (한 번은 테스트)
또한, 통신 등에서 문제가 될 수 있으므로 사용하지 않는 것을 권장한다

 

    - index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';

//bootstrap
import "bootstrap/dist/css/bootstrap.min.css";
import "bootswatch/dist/cosmo/bootstrap.min.css";
import './index.css';

import "bootstrap"; //js는 경로를 생략해도 기본 경로로 설정됨
import { HashRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
//<React.StrictMode>가 있으면 리액트가 화면을 두 번씩 실행한다.(한 번은 테스트)
//통신 등에서 문제가 될 수 있으므로 사용하지 않는 것을 권장
root.render(
  <>
    {/* 리액트 라우터를 사용하는 영역을 지정 */}
    <HashRouter>
    <App />
    </HashRouter>
  </>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 


3) 코드 작성

 //목록
useEffect(() => {
    loadData();
}, []);
const loadData = useCallback(() => {
    axios.get("http://localhost:8080/emp/").then(resp => {
        setEmps(resp.data);
    });
}, [emps]);

 

   (+) loadData 함수를 만들어 여러 곳에서 재활용하도록 만들었다. 

   (+) effect란?

 //effect
    // : 시작 시점 또는 state가 변경되는 시점에 자동으로 실행되는 코드
    // - 가급적이면 안 쓰거나 적게 쓰는게 좋다(성능에 무리가 가기 때문)
    // - index.js에 있는 <React.StrictMode> 태그의 영향을 받는다 (x2)
    // - useEffect(함수, [연관항목]);

    // : 시작하자마자 서버와 통신해서 emps에 데이터를 넣는다*
    useEffect(() => {
        loadData();
    }, []); //연관항목을 지움으로써 최초 1회만 실행 됨

 

   (+) $.ajax와 axios 비교

            $.ajax({
                url:"http://localhost:8080/emp/",
                method:"get",
                //data:{},
                success:function(resp){ //resp에 json의 List<EmpDto>가 들어옴 이를 State에 집어 넣으면 됨!!!!**
                    setEmps(resp);
                },
                //error:function(){},
                //complete:function(){}
            });

        //jquery는 태그제어가 주요 목적 (세부 기능 중 ajax가 있을 뿐)
        // → 따라서 다른 라이브러리를 사용함(Axios- https://axios-http.com/kr/docs/intro)
       
        //짧게 쓴 코드 : axios.get('http://localhost:8080/emp/').then(resp => setEmps(resp.data));
        axios({
            url: "http://localhost:8080/emp/",
            method: "get",
        })
        .then(resp => { //then이 success의 역할, fail/error는 고려하지 않음
            //console.log(resp); //resp에 뭐가 들어있는 지 확인하고자 할 때 사용
            setEmps(resp.data);
         });

 

 


 

const saveInput = useCallback((e) => {
    axios({
        url: "http://localhost:8080/emp/",
        method: "post",
        data: input
    }).then(resp => {
        loadData();

        setInput({
            empName: "",
            empDept: "",
            empDate: "",
            empSal: ""
        });

        closeModal();
    });
}, [input]);

   (+) 데이터는 input에 있는 내용을 넘겨주면 된다!

 

 

 

//ref
const bsModal = useRef();
const openModal = useCallback(() => {
    const modal = new Modal(bsModal.current)
    modal.show();
}, [bsModal]);
const closeModal = useCallback(() => {
    const modal = Modal.getInstance(bsModal.current);
    modal.hide();
}, [bsModal]);

   (+) ref(참조)

       - 리액트에서 태그를 선택하는 대신 사용하는 도구로 그 외의 용도도 가능하다

       - 변수명.current를 이용하여 현재 참조하고 있는 대상 태그를 호출할 수 있다

 

 


     - emp.js 전체코드

import Jumbotron from "../Jumbotron";
import { useCallback, useEffect, useRef, useState } from "react";
import axios from "axios"; //비동기 통신 전용 라이브러리
import { Modal } from "bootstrap";
import { FaPlus } from "react-icons/fa";
import { HiArchiveBoxXMark } from "react-icons/hi2";

function Emp() {
    //state
    const [emps, setEmps] = useState([]);
    const [input, setInput] = useState({
        empName: "",
        empDept: "",
        empDate: "",
        empSal: ""
    });

	//목록 불러오기
    const loadData = useCallback(() => {
        axios.get("http://localhost:8080/emp/").then(resp => {
            setEmps(resp.data);
        });
    }, [emps]);

    //목록
    useEffect(() => {
        loadData();
    }, []);

    //회원 등록
    const changeInput = useCallback((e) => {
        setInput({
            ...input, //원래 회원은 유지하며
            [e.target.name]: e.target.value //name에 해당하는 값만 value로 바꿔 추가!
        });
    }, [input]);
    const saveInput = useCallback((e) => {
        axios({
            url: "http://localhost:8080/emp/",
            method: "post",
            data: input
        }).then(resp => {
        	//등록이 완료되면 갱신을 진행
            loadData();

            setInput({ //화면 비우기
            	//empNo의 경우 시퀀스로 받기 때문에 사용자에게 입력받지 않아도 됨
                empName: "",
                empDept: "",
                empDate: "",
                empSal: ""
            });

            closeModal();
        });
    }, [input]);
    const cancelInput = useCallback((e) => {
        const choice = window.confirm("정말 취소하시겠습니까?");
        if (choice === false) return;

        setInput({
            empName: "",
            empDept: "",
            empDate: "",
            empSal: ""
        });

        closeModal();
    });

    //회원삭제
    const deleteEmp = useCallback((target) => {
        const choice = window.confirm("정말 삭제하시겠습니까?");
        if (choice === false) return;

		//삭제 비동기 요청 후 목록 갱신
        axios({
            url: "http://localhost:8080/emp/" + target.empNo,
            // url:`http://localhost:8080/emp/${target.empNo}`, //백틱 구문
            method: "delete",
        }).then(resp => {
            loadData();
        });
    }, [emps]);

    //ref
    const bsModal = useRef();
    const openModal = useCallback(() => {
        const modal = new Modal(bsModal.current)
        modal.show();
    }, [bsModal]);
    const closeModal = useCallback(() => {
        const modal = Modal.getInstance(bsModal.current);
        modal.hide();
    }, [bsModal]);

    return (
        <>
            <Jumbotron title="사원관리" />


            {/* 신규등록버튼 */}
            <div className="row mt-4">
                <div className="col text-end">
                    <button className="btn btn-primary" onClick={e=>openModal()}>
                        <FaPlus />신규등록
                    </button>
                </div>
            </div>

            {/*  목록 출력 */}
            <div className="row mt-4">
                <div className="col text-center">
                    <table className="table table-hover">
                        <thead>
                            <tr>
                                <th>사원번호</th>
                                <th>사원명</th>
                                <th>부서명</th>
                                <th>입사일</th>
                                <th>급여액</th>
                                <th>관리</th>
                            </tr>
                        </thead>
                        <tbody>
                            {emps.map(emp => (
                                <tr key={emp.empNo}>
                                    <td>{emp.empNo}</td>
                                    <td>{emp.empName}</td>
                                    <td>{emp.empDept}</td>
                                    <td>{emp.empDate}</td>
                                    <td>{emp.empSal}</td>
                                    <td>
                                        <button className="btn btn-danger"
                                            onClick={e => deleteEmp(emp)}>
                                                <HiArchiveBoxXMark />삭제
                                        </button>
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            </div>

            {/* 등록 */}
            <div ref={bsModal} class="modal fade" id="staticBackdrop" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h1 class="modal-title fs-5" id="staticBackdropLabel">사원 등록</h1>
                            <button type="button" class="btn-close" aria-label="Close" onClick={e=>cancelInput()}></button>
                        </div>
                        <div class="modal-body">
                            <div className="row mt-4">
                                <div className="col">
                                    <label>사원명</label>
                                    <input type="text" name="empName" value={input.empName}
                                        onChange={e => changeInput(e)} className="form-control"></input>
                                </div>
                            </div>
                            <div className="row mt-4">
                                <div className="col">
                                    <label>부서명</label>
                                    <input type="text" name="empDept" value={input.empDept}
                                        onChange={e => changeInput(e)} className="form-control"></input>
                                </div>
                            </div>
                            <div className="row mt-4">
                                <div className="col">
                                    <label>입사일</label>
                                    <input type="text" name="empDate" value={input.empDate}
                                        onChange={e => changeInput(e)} className="form-control"></input>
                                </div>
                            </div>
                            <div className="row mt-4">
                                <div className="col">
                                    <label>급여액</label>
                                    <input type="text" name="empSal" value={input.empSal}
                                        onChange={e => changeInput(e)} className="form-control"></input>
                                </div>
                            </div>
                        </div>
                        <div class="modal-footer">
                            <button className="btn btn-success me-2" onClick={e => saveInput()}>등록</button>
                            <button className="btn btn-danger" onClick={e => cancelInput()}>취소</button>
                        </div>
                    </div>
                </div>
            </div>

        </>
    );
}

export default Emp;

 

 

4) 코드 실행

     - 출력결과

 

 

 

 

 

개인 공부 기록용입니다:)

728x90