코딩 노트
React08 - 포켓몬스터 CRUD / 배포용 주소 본문
no가 있으면 수정, 없으면 추가이다.
none: css에서 없다
empty: 배열에서 비어있는 것을 의미
undefinded: 선언만 하고 값을 준 적이 없으면, 값이 아예 없으면 정의 된 적이 없는 것을 의미
null: 항목은 있는데 대상이 없는 것을 의미
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">×</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;
유효성 검사 및 차단 코드 : 회원 할 때 처럼 이펙트나 블러로 미리 검사해야함.
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">×</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 파일도 생성한다.
env.local은 내가 지금 개발할 때 사용할 주소
env.production은 배포용 주소
npmstart를 하면 local이 실행되고 npm run build를 하면 production이 실행된다.
process는 node.js 환경에서 쓰는 객체이다.
url:`${process.env.REACT_APP_REST_API_URL}/pocketmon/`,
이런식으로 바꿔야 한다.
'React' 카테고리의 다른 글
React09 - 무한스크롤 (0) | 2023.11.07 |
---|---|
React07 - Book, pocketmon REST API로 생성 / 예외처리 (0) | 2023.11.06 |
React06 - Rest API2(insert, delete, selectOne, update) (0) | 2023.11.03 |
React05 - Rest API (0) | 2023.11.02 |
React04 - todoList CRUD / 백엔드 연결 (0) | 2023.11.02 |