// 내역 불러오기
router.get("/money", async (req, res) => {
try {
const wallet = await Wallet.find().sort({ createdAt: -1 });
wallet.map((item => ({
id: item._id,
date: item.date,
title: item.title,
category: item.category,
amount: item.amount,
tag: item.tag
})))
res.json(wallet);
} catch(err) {
console.error(err);
}
});
원래는 위의 코드를 작성했지만 wallet.map()를 안해도 되는거 아닌가 싶어서 지워봤는데, 역시 안해도 되는 거였다.. 그동안 res.json()에서 데이터를 전달해주는 거였는데 몰랐지만 알게 되는 계기가 된거 같다.
// 내역 불러오기
router.get("/money", async (req, res) => {
try {
const wallet = await Wallet.find().sort({ createdAt: -1 });
res.json(wallet);
} catch(err) {
console.error(err);
}
});
AccountList에 추가된 rows, columns들이 새로운 게시물일 수록 뒷 페이지로 이동하기 때문에 이를 해결하기 위해서 find()뒤에 sort({ createdAt: -1 })을 추가해서 가장 최근에 만들어진 게시물을 1번 row에 나타나도록 만들었다.
그리고 추가한 리스트를 수정하기 위해서 클릭한 게시물의 ObjectId를 알아야 한다. 이를 위해 row를 클릭하면, ObjectId를 나오게 하는 로직을 작성했다.
const mongoose = require('mongoose');
router.post("/money/:id", async(req, res) => {
try {
const { id } = req.params;
const ObjectId = new mongoose.Types.ObjectId(id);
const wallet = await Wallet.findById(ObjectId);
console.log(wallet);
res.json(wallet);
} catch(err) {
console.error(err);
}
});
기존의 MongoDB를 사용할 때는 { id: new ObjectId(id) };이런 방법으로 ObjectId를 추출했다. 하지만 mongoose를 사용하면 더 간편한 방법으로 ObjectId를 추출할 수 있다. mongoose.Types.ObjectId는 스키마를 만들 때 설정해놓은 데이터 타입을 불러오는 방법이랑 동일하다. 백엔드에서 이런 로직을 작성해주고 프론트엔드로 넘어와서 <TableRow onClick={() => handleRowClick(row._id)}> 함수를 작성해준다. 이 함수를 실행하면 콘솔에 ObjectId가 출력된다.
const handleRowClick = async (id) => {
try {
const response = await axios.post(`http://localhost:4000/wallet/money/${id}`, { id },
{ headers: "Content-Type": "application/json" }
);
console.log(response.data);
} catch(err) {
console.error(err);
}
}
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => {
return (
// row._id: ObjectId이기 때문에 이걸 클릭하면 ObjectId가 눌려지도록 하기
<TableRow hover role="checkbox" tabIndex={-1} key={row._id} onClick={() => handleRowClick(row._id)} >
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
위의 .slice()는 페이지네이션이므로 짧게 설명하자면 page * rowsPerPage는 시작 인덱스, page * rowsPerPage + rowsPerPage는 끝 인덱스로서, 시작 인덱스부터 끝 인덱스까지의 row를 보여주도록 하는 것이다.
그리고 map()메소드를 사용할 때 map()의 키 값으로 row._id를 사용하기 때문에 클릭을 하면 row._id를 추출하는 방법이 가능할 것이라고 생각했다.
이제 행을 만들었으니 행을 띄워줄 열도 만들면 된다. const value = row[column.id] 에서 열의 id를 이용해 인덱스 번호(id)에 해당하는 값을 만든다.
예를 들어 row = ['커피', '우유']가 있다면 커피: 0, 우유: 1의 id를 가진다.
이렇게 해서 클릭할 시 그에 맞는 ObjectId를 추출하고 클릭할 시 window()가 나와서 삭제할 지 정하는 handleDelteWindow(id)도 handleDeleteWindow()에 작성해준다.
const handleRowClick = async (id) => {
try {
const response = await axios.post(`http://localhost:4000/wallet/money/${id}`, { id },
{ headers: "Content-Type": "application/json" }
);
console.log(response.data);
handleDeleteWindow(id);
} catch(err) {
console.error(err);
}
}
// 삭제창 띄우기
const handleDeleteWindow = (id) => {
if (window.confirm("삭제하시겠습니까?")) {
handleDeleteClick(id);
}
}
// 데이터 삭제
const handleDeleteClick = async (id) => {
try {
const response = await axios.post(`/http://localhost/wallet/money/delete/${id}`);
console.log(response.data);
window.location.reload();
} catch(err) {
console.error(err);
}
};
현재 삭제하고 나면 window.location.reload();가 떠야 하는데, 아마도 리액트에서 상태 관리로 useState()를 사용하지 않아서 데이터가 삭제되도 새로고침 되지 않는데 이걸 해결해야 할 것 같다. 이를 해결하기 위해 백엔드에서 응답으로 res.redirect('/') 기본 페이지 로직으로 재연결 해주는 것도 작성했지만 동작하지 않는 버그가 생겼다.
여기서 작성한 이 코드를 useEffect()에 넣어서 한번에 동작하도록 하고 싶은데 useEffect()에 넣으면 제일 처음 진행되야 할 handleRowClick()가 찾지 못한다고 한다.
이 역시 해결하기 위해 밑에 함수들을 호출하는 방법을 통해 문제를 해결해야겠다..(아직 해결 못함)
router.post("/money/delete/:id", async (req, res) => {
try {
const { id } = req.params;
const ObjectId = new mongoose.Types.ObjectId(id);
cont wallet = await Wallet.findById(ObjectId);
console.log("지운 내역: \n", wallet);
// 찾은 :id삭제
wallet.deleteOne();
res.redirect('/');
} catch(err) {
console.error(err);
}
})
{ id } = req.params를 만들고 wallet 상수에 ObjectId를 할당해서 wallet.deleteOne()를 해준 뒤 res.redirect('/')해주는 간단한 로직이다. 그런데 위에서 설명했듯, 삭제 후 페이지 새로고침이 자동으로 되지 않는 버그가 생겼다.
현재까지의 진행도는 자동으로 새로고침 하도록 리스트 상태 관리를 해준 뒤, 수직 막대 그래프를 리스트와 동기화 한 뒤, 원형 그래프도 데이터와 연동해주는 게 1순위
그리고 모달창 만들어서 수정&삭제 기능을 여기서 동작하도록 구현해야 할듯하다. 이게 안된다면 현재처럼 window()띄워서 수정 || 삭제 기능을 해야한다는 선택이 있다.
그 뒤로는 프론트엔드와 서로 파일을 합쳐서 플랜에 맞는 자유로운 소비 로직을 구현하면 된다.
시연할 때는 LiveShare를 켜서 현재 IP를 입력한 뒤, 학교 컴퓨터로 들어가거나, 배포를 하면 이번 프로젝트는 완성된다.