궤도
[백엔드] Node.js + Sequelize + MySQL 상세보기 페이지를 만들어보자 본문
우리의 앱에선 오답노트를 클릭하면
이렇게 노트에 있는 문제들을 하나하나 볼 수 있다. 보다시피 문제 삭제 기능도 있다.
프론트에서는 사용자의 아이디인 stu_id와 선택한 오답노트의 pk인 note_sn을 넘겨줄 것이다.
그럼 나는 해당 오답노트의 페이지들을 하나하나씩 반환해야 한다. 페이지는 문제, 답, 풀이로 구성되어 있으며 페이지네이션을 하려면 전체 문제 수도 넘겨줘야 하고 현재 문제가 몇 번째 문제인지도 알려줘야 한다.
그리고 각 문제들에 대한 삭제 기능도 구현해야 한다
오답노트 페이지
내가 넘겨줘야 하는 정보들을 다시 정리해보자
1. 노트 이름
2. 전체 문제 수
3. 현재 문제가 몇 번째 문제인지
4. 문제 이미지
5. 답안
6. 풀이 이미지
뭐가 많은 것 같지만 할 수 있다.
왜이렇게 화질이 깨지는지 모르겠지만 이 테이블들을 쓸 것이다.
routes/incor-note-content.js(문제 열람)
//오답노트 문제 가져오기
//localhost:3001/api/incor-note-content/0?note_sn=1&stu_id=samdol
router.get('/:pb', async function (req, res, next) {
const pb = req.params.pb;
let result = await models.student.findOne({
where: {
stu_id: req.query.stu_id
}
});
const user = result.dataValues.stu_sn;
//노트 이름
let title = await models.incor_note.findOne({
where: {
note_sn: req.query.note_sn
}
});
//전체 문제 수
let pbCount = await models.incor_problem.count({
where: {
note_sn: req.query.note_sn,
stu_sn: user
}
});
//문제가 없으면 null 상태 반환
if (pbCount == 0) {
res.send({
message: 'No data',
status: 'null'
});
return;
}
//문제들
let problems = await models.incor_problem.findAll({
where: {
note_sn: req.query.note_sn,
stu_sn: user
}
});
//문제 이미지
let pb_img = await models.problem.findOne({
attributes: ['pb_img'],
where: {
pb_sn: problems[pb].dataValues.pb_sn
}
});
//답 정보(답안, 풀이 이미지)
let sols = await models.solution.findOne({
attributes: ['sol_ans', 'sol_img'],
where: {
sol_sn: problems[pb].dataValues.sol_sn
}
});
//문제 정보 : 노트이름, 전체 문제 수, 지금 문제가 몇번째 문제인지, 문제 이미지, 답안, 답 이미지
const pbInfo = {
title: title.dataValues.note_name,
total_pb: pbCount,
now_pb: pb,
pb_img: pb_img.dataValues.pb_img,
sol_ans: sols.dataValues.sol_ans,
sol_img: sols.dataValues.sol_img
}
try {
res.send({ //교재 정보 넘김
message: "problem information",
status: 'success',
data: {
pbInfo
}
});
} catch (err) { //무언가 문제가 생김
res.send({
message: "ERROR",
status: 'fail'
})
}
});
하나하나 뜯어보자
const pb = req.params.pb;
일단 .../0, .../1 이런식으로 페이지를 넘기는게 일반적이니까 현재 문제를 req.params로 받았다.
let result = await models.student.findOne({
where: {
stu_id: req.query.stu_id
}
});
const user = result.dataValues.stu_sn;
이건 늘상하는 id를 pk인 sn으로 바꾸는 과정이다.
//노트 이름
let title = await models.incor_note.findOne({
where: {
note_sn: req.query.note_sn
}
});
note_sn을 받아왔으니 incor_note 테이블에 가서 노트의 이름을 받아오자.
//전체 문제 수
let pbCount = await models.incor_problem.count({
where: {
note_sn: req.query.note_sn,
stu_sn: user
}
});
//문제가 없으면 null 상태 반환
if (pbCount == 0) {
res.send({
message: 'No data',
status: 'null'
});
return;
}
오답노트의 문제 수를 incor_problem에서 받아와서 pbCount에 저장한다. 만약 pbCount가 0이라면 문제가 없다는 것이니 바로 리턴한다. 애시당초 문제가 없으면 클릭조차 안되지 않나?라고 생각할 수 있겠으나, 문제가 1개 남았을 때 그 문제를 삭제하여 0개가 되는 경우가 있기 때문에 넣어주는 것이다.
라잌댓
//문제들
let problems = await models.incor_problem.findAll({
where: {
note_sn: req.query.note_sn,
stu_sn: user
}
});
문제들도 받아오자. 아까 count로 실행한걸 findAll로 실행했을 뿐이다.
여기까지 했다면 DB 상으로 보았을 때
여기까지 불러온 셈이다.
프론트로부터 현재의 문제 번호를 pb로 받아온 것을 기억하는가?
//문제 이미지
let pb_img = await models.problem.findOne({
attributes: ['pb_img'],
where: {
pb_sn: problems[pb].dataValues.pb_sn
}
});
//답 정보(답안, 풀이 이미지)
let sols = await models.solution.findOne({
attributes: ['sol_ans', 'sol_img'],
where: {
sol_sn: problems[pb].dataValues.sol_sn
}
});
problems 배열의 pb번째 데이터에 대해 문제 이미지, 답안, 풀이 이미지를 가져오자.
//문제 정보 : 노트이름, 전체 문제 수, 지금 문제가 몇번째 문제인지, 문제 이미지, 답안, 답 이미지
const pbInfo = {
title: title.dataValues.note_name,
total_pb: pbCount,
now_pb: pb,
pb_img: pb_img.dataValues.pb_img,
sol_ans: sols.dataValues.sol_ans,
sol_img: sols.dataValues.sol_img
}
그리고 pbInfo에 지금까지 불러온 정보들을 전부 저장한다.
try {
res.send({ //교재 정보 넘김
message: "problem information",
status: 'success',
data: {
pbInfo
}
});
} catch (err) { //무언가 문제가 생김
res.send({
message: "ERROR",
status: 'fail'
})
}
넘겨주자
localhost:3001/api/incor-note-content/0?note_sn=1&stu_id=samdol
localhost:3001/api/incor-note-content/1?note_sn=1&stu_id=samdol
각 페이지들의 정보가 잘 나오는 것을 볼 수 있다.
오답노트 문제 삭제
문제 삭제는 상대적으로 간단하다. 넘겨줄 정보도 딱히 없고 삭제만 하면 된다.
routes/incor-note-content.js(문제 삭제)
//오답노트 문제 삭제하기
//localhost:3001/api/incor-note-content/0?note_sn=1&stu_id=samdol
router.delete('/:pb', async function (req, res, next) {
const pb = req.params.pb;
let result = await models.student.findOne({
where: {
stu_id: req.query.stu_id
}
});
const user = result.dataValues.stu_sn;
//문제들
let problems = await models.incor_problem.findAll({
where: {
note_sn: req.query.note_sn,
stu_sn: user
}
});
//문제 삭제
models.incor_problem.destroy({
where: {
incor_pb_sn: problems[pb].dataValues.incor_pb_sn
}
})
.then(num => {
if (num == 1) { //성공
res.send({
message: "Delete problem",
status: 'success'
});
} else { //데이터 입력을 잘못한듯
res.send({
message: "Data was not found!",
status: 'fail'
});
}
})
.catch(err => { //에러
res.send({
message: "Could not delete problem",
status: 'fail'
});
});
});
윗 부분과 중복되는 부분을 빼면 설명할 것이 거의 없다. 그냥 해당하는 문제를 incor_problem에서 삭제하면 된다.
localhost:3001/api/incor-note-content/1?note_sn=1&stu_id=samdol
해당 주소를 다시 get으로 접근하면
원래 3번째였던 문제가 2번째에 위치하게 됐음을 알 수 있다.