코딩 노트

React08 - 포켓몬스터 CRUD / 배포용 주소 본문

React

React08 - 포켓몬스터 CRUD / 배포용 주소

newbyeol 2023. 11. 7. 10:56

no가 있으면 수정, 없으면 추가이다.

 

none: css에서 없다

empty: 배열에서 비어있는 것을 의미

undefinded: 선언만 하고 값을 준 적이 없으면, 값이 아예 없으면 정의 된 적이 없는 것을 의미

null: 항목은 있는데 대상이 없는 것을 의미

axios 기본형

patch는 하나씩 나올 때, 풋은 전체가 다 나올 때

 

pocketmon.js 수정 구문 추가

import {useState, useEffect, useRef} from "react";
import axios from "axios";

import {LiaEdit} from "react-icons/lia";
import {AiFillDelete, AiOutlinePlus} from "react-icons/ai";
import { Modal } from "bootstrap";

const Pocketmon = (props)=>{
    const [pocketmonList, setPocketmonList] = useState([]);

    //서버에서 pocketmon list를 불러와서 state에 설정하는 코드
    const loadPocketmon = ()=>{
        axios({
            url:"http://localhost:8080/pocketmon/",
            method:"get"
        })
        .then(response=>{
            //console.log(response);
            setPocketmonList(response.data);
        })
        .catch(err=>{});
    };

    useEffect(()=>{
        loadPocketmon();
    }, []);

    //포켓몬스터 삭제
    //- 이제는 state에서 삭제하는 것이 아니라 서버에 통신을 보낸 뒤 목록을 갱신하면 된다
    const deletePocketmon = (pocketmon) => {
        const choice = window.confirm("정말 삭제하시겠습니까?");
        if(choice === false) return;

        //axios({옵션}).then(성공시 실행할 함수).catch(실패시 실행할 함수);
        axios({
            url:`http://localhost:8080/pocketmon/${pocketmon.no}`,
            method:"delete"
        })
        .then(response=>{
            loadPocketmon();//목록 갱신
        })
        .catch(err=>{});
    };

    //modal 관련된 처리
    const bsModal = useRef();
    const openModal = ()=>{
        const modal = new Modal(bsModal.current);
        modal.show();
    };
    const closeModal = ()=>{
        const modal = Modal.getInstance(bsModal.current);
        modal.hide();

        clearPocketmon();
    };

    //등록과 관련된 state
    const [pocketmon, setPocketmon] = useState({name:"" , type:""});
    const changePocketmon = (e)=>{
        setPocketmon({
            ...pocketmon,
            [e.target.name] : e.target.value
        });
    };
    const clearPocketmon = ()=>{
        setPocketmon({name:"", type:""});
    };

    //axios로 서버에 등록 요청을 보낸 뒤 등록이 성공하면 목록을 갱신하도록 처리
    const savePocketmon = ()=>{
        //입력값 검사 후 차단 코드 추가

        // axios({옵션}).then(성공 시 콜백).catch(실패 시 콜백);
        axios({
            url:"http://localhost:8080/pocketmon/",
            method:"post",
            data:pocketmon
        })
        .then(response=>{ //성공했다면
            loadPocketmon(); //목록을 갱신하고
            closeModal(); //모달을 닫아라
        })
        .catch(err=>{});
    };

    //포켓몬스터 수정 창 열기
    //- target은 수정 버튼을 누른 행의 포켓몬스터 정보
    //- target의 정보를 pocketmon으로 카피 후 모달 열기
    const editPocketmon = (target)=>{
        setPocketmon({...target}); //깊은 복사로 완전히 카피
        openModal();
       
    };

    //포켓몬스터 수정 처리
    const updatePocketmon = ()=> {
        //검사 후 차단 처리

        const {no, name, type} = pocketmon; //구조에 맞게 내가 선언
        axios({
            url:`http://localhost:8080/pocketmon/${no}`,
            method:"put",
            data:{
                name : name,
                type : type
            }
        })
        .then(response=>{
            loadPocketmon();
            closeModal();
        })
        .catch(err=>{});
    };

    return (
        <>
           
            <div className="row">
                <div className="col">
                    <h1>포켓몬스터 관리</h1>
                    <p>React CRUD 연습 예제</p>
                </div>
            </div>

            {/* 추가 버튼 */}
            <div className="row mt-4">
                <div className="col text-end">
                    <button className="btn btn-success" onClick={openModal}>
                        <AiOutlinePlus/>
                        추가
                    </button>
                </div>
            </div>
           
            {/* 출력 위치 */}
            <div className="row mt-4">
                <div className="col">
                    <table className="table">
                        <thead>
                            <tr>
                                <th>번호</th>
                                <th>이름</th>
                                <th>속성</th>
                                <th></th>
                            </tr>
                        </thead>
                        <tbody>
                            {pocketmonList.map(pocketmon=>(
                                <tr key={pocketmon.no}>
                                    <td>{pocketmon.no}</td>
                                    <td>{pocketmon.name}</td>
                                    <td>{pocketmon.type}</td>
                                    <td>
                                        {/* 아이콘 자리 */}
                                        <LiaEdit className="text-warning"
                                            onClick={e=>editPocketmon(pocketmon)}/>
                                        <AiFillDelete className="text-danger"
                                            onClick={e=>deletePocketmon(pocketmon)}/>                                        
                                    </td>
                                </tr>
                            ))}
                        </tbody>
                    </table>
                </div>
            </div>

            {/* Modal */}
            <div className="modal fade" ref={bsModal}
                        data-bs-backdrop="static" tabIndex="-1" role="dialog" aria-hidden="true">
                <div className="modal-dialog" role="document">
                    <div className="modal-content">
                    <div className="modal-header">
                        <h5 className="modal-title" >
                            {/* {pocketmon.no == undefined ? '추가' : '수정'} */}
                            {pocketmon.no == undefined ? '신규 몬스터 등록' : `${pocketmon.no}번 몬스터 수정`}
                        </h5>
                        <button type="button" className="border-0 bg-transparent"
                        data-dismiss="modal" aria-label="Close" onClick={closeModal}>
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div className="modal-body">

                        <div className="row">
                            <div className="col">
                                <label className="form-label">이름</label>
                                <input type="text" name="name" className="form-control"
                                        value={pocketmon.name} onChange={changePocketmon}/>
                            </div>
                        </div>

                        <div className="row mt-4">
                            <div className="col">
                                <label className="form-label">속성</label>
                                <input type="text" name="type" className="form-control"
                                        value={pocketmon.type} onChange={changePocketmon}/>
                            </div>
                        </div>

                    </div>
                    <div className="modal-footer">
                        <button className="btn btn-secondary" onClick={closeModal}>닫기</button>
                        { pocketmon.no == undefined ?
                        <button className="btn btn-success" onClick={savePocketmon}>저장</button>
                        :
                        <button className="btn btn-success" onClick={updatePocketmon}>수정</button>
                        }
                       
                    </div>
                    </div>
                </div>
            </div>

        </>
    );
};

export default Pocketmon;

 

제이쿼리 프론트엔드에서 parse 해석 명령 / stringify 만들어 내는 명령

유효성 검사 및 차단 코드 : 회원 할 때 처럼 이펙트나 블러로 미리 검사해야함. 

 

book.js 등록 수정 구문 추가

import { useEffect, useRef, useState } from "react";
import axios from "axios";
import {LiaEdit} from "react-icons/lia";
import {AiFillDelete, AiOutlinePlus} from "react-icons/ai";

import "./Book.css";
import { Modal } from "bootstrap";

const Book = (props)=>{
    const [bookList, setBookList] = useState([]);

    // const loadBook = ()=>{
    //     //서버에 있는 도서 정보를 불러와서 state에 반영하는 코드
    //     axios({
    //         url:"http://localhost:8080/book/",
    //         method:"get"
    //     })
    //     .then(response=>{
    //         setBookList(response.data);
    //     })
    //     .catch(err=>{
    //         window.alert("통신 오류 발생");
    //     });
    // };

    const loadBook = async () => {
        const response = await axios({
            url:"http://localhost:8080/book/",
            method:"get"
        });
        setBookList(response.data);
    };

    useEffect(()=>{
        loadBook();
    }, []);

    //도서 삭제
    const deleteBook = (book)=> {
        const choice = window.confirm("정말 삭제하시겠습니까?");
        if(choice === false) return;

        axios({
            //url:"http://localhost:8080/book/"+book.bookId,
            url:`http://localhost:8080/book/${book.bookId}`,
            method:"delete"
        })
        .then(response=>{
            loadBook();
        })
        .catch(err=>{});    
    };

    //모달 관련 기능과 참조
    const bsModal = useRef();
    const openModal = ()=>{
        const modal = new Modal(bsModal.current);
        modal.show();
    };
    const closeModal = ()=>{
        const modal = Modal.getInstance(bsModal.current);
        modal.hide();

        clearBook();
    };

    //등록, 수정과 관련된 state와 기능
    const [book, setBook] = useState({
        bookTitle:"", bookAuthor:"", bookPublicationDate:"", bookPrice:0,
        bookPublisher:"", bookPageCount:0, bookGenre:""
    });
    const changeBook = (e)=>{
        setBook({
            ...book,
            [e.target.name] : e.target.value
        })
    };
    const clearBook = ()=>{
        setBook({
            bookTitle:"", bookAuthor:"", bookPublicationDate:"", bookPrice:0,
            bookPublisher:"", bookPageCount:0, bookGenre:""
        });
    };

    // const saveBook = ()=>{
    //     //book 유효성 검사 및 차단 코드
       
    //     axios({
    //         url:"http://localhost:8080/book/",
    //         method:"post",
    //         data: book,
    //         // data:{...book}
    //     })
    //     .then(response=>{
    //         loadBook();
    //         closeModal();
    //     });
    // };

    //async 함수와 await 키워드를 사용한 간소화 작업이 가능
    //- 비동기 작업을 동기화된 코드로 작성할 수 있다
    const saveBook = async ()=>{
        const response = await axios({
            url:"http://localhost:8080/book/",
            method:"post",
            data:book
        });
        loadBook();
        closeModal();
    };

    const editBook = (target)=>{
        setBook({...target});
        openModal();
    };

    const updateBook = ()=>{
        //검사 후 차단 코드
       
        const copyBook = {...book};
        delete copyBook.bookId;
        axios({
            url:`http://localhost:8080/book/${book.bookId}`,
            method:"put",
            data: copyBook
        })
        .then(response=>{
            loadBook();
            closeModal();
        });
    };

    return (
        <>
            <div className="row">
                <div className="col">
                    <h1>도서 관리 화면</h1>
                    <hr/>
                </div>
            </div>

            <div className="row mt-4">
                <div className="col text-end">
                    <button className="btn btn-success" onClick={openModal}>
                        <AiOutlinePlus/>
                        추가
                    </button>
                </div>
            </div>

            <div className="row mt-4">
                <div className="col">
                    <table className="table">
                        <thead>
                            <tr>
                                <th className="pc-only">코드</th>
                                <th>제목</th>
                                <th>저자</th>
                                <th className="pc-only">출판사</th>
                                <th className="pc-only">출간일</th>
                                <th>판매가</th>
                                <th className="pc-only">페이지</th>
                                <th className="pc-only">장르</th>
                                <th></th>
                            </tr>
                        </thead>
                        <tbody>
                            {bookList.map((book, index)=>(
                                <tr>
                                    <td className="pc-only">{book.bookId}</td>
                                    <td>{book.bookTitle}</td>
                                    <td>{book.bookAuthor}</td>
                                    <td className="pc-only">{book.bookPublisher}</td>
                                    <td className="pc-only">{book.bookPublicationDate}</td>
                                    <td>{book.bookPrice}</td>
                                    <td className="pc-only">{book.bookPageCount}</td>
                                    <td className="pc-only">{book.bookGenre}</td>
                                    <td>
                                        {/* 아이콘 자리 */}
                                        <LiaEdit className="text-warning"
                                                                onClick={e=>editBook(book)}/>
                                        <AiFillDelete className="text-danger"
                                                                onClick={e=>deleteBook(book)}/>
                                    </td>
                                </tr>
                            ))}
                           
                        </tbody>
                    </table>                    
                </div>
            </div>


            {/* Modal */}
            <div className="modal fade" ref={bsModal}
                        data-bs-backdrop="static" tabIndex="-1" role="dialog" aria-hidden="true">
                <div className="modal-dialog" role="document">
                    <div className="modal-content">
                    <div className="modal-header">
                        <h5 className="modal-title" >
                            {book.bookId === undefined ? '신규 도서 등록' : `${book.bookId}번 도서 수정`}
                        </h5>
                        <button type="button" className="border-0 bg-transparent"
                                    onClick={closeModal}>
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div className="modal-body">
                       
                        <div className="row"><div className="col">
                            <label className="form-label">제목</label>
                            <input type="text" name="bookTitle" value={book.bookTitle} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">저자</label>
                            <input type="text" name="bookAuthor" value={book.bookAuthor} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">출판사</label>
                            <input type="text" name="bookPublisher" value={book.bookPublisher} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">출간일</label>
                            <input type="date" name="bookPublicationDate" value={book.bookPublicationDate} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">판매가</label>
                            <input type="number" name="bookPrice" value={book.bookPrice} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">페이지</label>
                            <input type="number" name="bookPageCount" value={book.bookPageCount} onChange={changeBook} className="form-control"/>
                        </div></div>

                        <div className="row mt-4"><div className="col">
                            <label className="form-label">장르</label>
                            <select name="bookGenre" value={book.bookGenre} onChange={changeBook} className="form-select">
                                <option value="">선택하세요</option>
                                <option>다큐멘터리</option>
                                <option>판타지/무협</option>
                                <option>소설</option>
                                <option>자서전</option>
                                <option>로맨스</option>
                                <option>수필</option>
                                <option>추리</option>
                            </select>
                        </div></div>

                    </div>
                    <div className="modal-footer">
                        <button className="btn btn-secondary" onClick={closeModal}>
                            닫기
                        </button>
                        {book.bookId === undefined ?
                        <button className="btn btn-success" onClick={saveBook}>
                            저장
                        </button>
                        :
                        <button className="btn btn-success" onClick={updateBook}>
                            수정
                        </button>
                        }
                    </div>
                    </div>
                </div>
            </div>

        </>
    );
};

export default Book;

비동기가 없으면 필요 없다. (await)

 

주소를 그대로 쓰는 것은 하드코딩이다.

리엑트에 설정이 있는가?

 

프로젝트 우클릭 후 New File을 한 다음 .env 파일을 생성한다.

.env.local 파일도 생성한다.

local은 내 컴퓨터, production은 공용

env.local은 내가 지금 개발할 때 사용할 주소

env.production은 배포용 주소

 

npmstart를 하면 local이 실행되고 npm run build를 하면 production이 실행된다.

process는 node.js 환경에서 쓰는 객체이다.

url:`${process.env.REACT_APP_REST_API_URL}/pocketmon/`,

 

이런식으로 바꿔야 한다.