회의
깃허브
pr템플릿 추가 -> 팀으로 프로젝트 하는 부분에 있어서 그래도 서로 리뷰를 하고 머지를 해야될것같아서 추가했다.
db
키(height) 데이터타입 float으로 수정 -> decimal, double, float중에서 정확하지 않아도 상관없는 데이터니까 빠르고 적은 메모리를 차지하는 타입으로 선택
record_data에 타임스탬프 안찍기로 결정 -> 계속 고민했는데 각 기록은 어차피 한나의 기록에 종속된거라서 타임스탬프 추가 안하기로
schema.sql .gitignore에 추가 -> schema.sql에 어디까지 마이그레이션 진행했는지 기록돼있어서 다른사람이 같은 마이그레이션 파일을 가지고 마이그레이트 하면 제대로 작동안하니까
typeorm 안쓰는걸로 변경 -> 조건에 orm사용하지 말라는게 있었는데 typeorm을 원래도 db랑 연결 목적으로만 사용하고 쿼리를 직접 쓰고있어서 그냥 사용하는걸로 했다가, 사용하지 않는걸로 변경
// models/common.js
const mysql = require("mysql2/promise");
const pool = mysql.createPool({
host: process.env.MYSQL_HOST,
port: process.env.MYSQL_PORT,
user: process.env.MYSQL_USERNAME,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
connectionLimit: 10,
});
module.exports = pool;
// server.js
const pool = require("./models/common");
...
app.get("/test", async (req, res) => {
const [result] = await pool.query(`select * from users;`);
res.json({message: result});
});
기능
특정 측정기록+데이터 가져오기
측정기록을 아이디로 조회하면 데이터도 가져오는데 유저삭제를 하면 비활성화되고 데이터는 살아있다.
이 살아있는 데이터를 조회하려고 했을때 그냥 조회가 되게 할것인지, 삭제한 유저의 정보니까 보여주지 않을것인지 회의를 했고
비활성화된 유저의 기록을 조회하려고 하면 보여주지 않는것으로 결정했다.
기능구현
특정 측정기록+데이터 가져오기

records의 id를 가지고 기록을 조회했을때 네 테이블을 전부 조인해서
유저정보, 기록일시, 당시몸무게, 각 타입별 타입명과 데이터를 모두 보여줘야 한다.
하나의 기록에 타입별로 여러 데이터가 들어있을 수 있어서 응답 데이터 형태는
{
유저정보: ...,
기록일시: ...,
기록데이터: [
{
측정타입:어쩌고,
수치: 몇몇,
...
},
{
...
},
...
]
}
이런식으로 나오게 하고싶었다. -> JSON_ARRAYAGG(JSON_OBJECT())사용
const [[result]] = await pool.query(`
SELECT
users.id as userId,
users.name as userName,
users.birthday as userBirthday,
users.height as userHeight,
users.mobile_number as userMobileNumber,
records.id as recordId,
records.weight as userWeightOnRecord,
records.created_at as recordDateTime,
JSON_ARRAYAGG(
JSON_OBJECT(
'recordDataId', record_data.id,
'recordTypeId', record_data.record_type_id,
'recordType', record_types.type,
'recordDataOnType', record_data.data
)
) as recordData
FROM record_data
JOIN record_types ON record_types.id = record_data.record_type_id
JOIN records ON records.id = record_data.record_id
JOIN users ON users.id = records.user_id
WHERE records.id = ?
GROUP BY records.id
;`, [recordId]);
res.status(200).json({result: result});
});
내가 원하는 결과가 배열로 두번 감싸져서 나와서 두번 벗겼다[[result]]
예외처리
1. 삭제(비활성화된) 유저의 기록이면 데이터를 보내주지 않는다. -> 해당 기록의 주인의 is_active값 확인
const [[isActive]] = await pool.query(`
SELECT users.is_active
FROM users
JOIN records ON users.id = records.user_id
WHERE records.id=?;`, [recordId]);
이렇게 하면 isActive에는 {is_active: 1 또는 0}이 들어있다.
if(!isActive.is_active){
return res.status(400).json({message: "INVALID_RECORD"})
}
그래서 isActive.is_active로 조건을 줬다.
2. 없는데이터 요청했을경우
기록의 id값으로 요청이 오는데 기록 자체가 없을 경우
const [[isExists]] = await pool.query(`
SELECT EXISTS (SELECT 1 FROM records WHERE id = "${recordId}");`);
if (!Object.values(isExists)[0]) {
return res.status(404).json({message: "RECORD_DOES_NOT_EXIST"})
}
isExists의 값은 { 'EXISTS (SELECT id FROM records WHERE id = "4")': 1 } 이렇게 들어가있다.
그래서 1을 뽑으려고 Object.values(isExists)[0] 를 사용했다ㅠ
다른사람 코드를 보니까 이렇게까지 안했던데 어떻게한거지.......
자원이 존재하지 않는다는 의미인 404를 에러코드로 넣었다.
아직 레이어드 나눠진채로 짜는게 익숙하지 않아서 일단 이렇게 server.js에 전부 하나로 만들었다.
레이어드패턴
이제 나누기
// server.js
const recordController = require("./controllers/record_controller")
app.get("/records/:recordId", recordController.getRecordWithData);
// controllers/record_controller.js
const recordService = require("../services/record_service")
const getRecordWithData = async (req, res) => {
const {recordId} = req.params;
const result = await recordService.getRecordWithData(recordId);
res.status(200).json({result: result});
};
module.exports = {getRecordWithData}
// services/record_services.js
const recordDao = require("../models/record_dao")
const getRecordWithData = async (recordId) => {
console.log("service1");
return await recordDao.getRecordWithData(recordId);
};
module.exports = {getRecordWithData}
// models/record_dao.js
const pool = require("./common");
const getRecordWithData = async(recordId) => {
console.log("model1");
const [[result]] = await pool.query(`
SELECT
users.id as userId,
users.name as userName,
users.birthday as userBirthday,
users.height as userHeight,
users.mobile_number as userMobileNumber,
records.id as recordId,
records.weight as userWeightOnRecord,
records.created_at as recordDateTime,
JSON_ARRAYAGG(
JSON_OBJECT(
'recordDataId', record_data.id,
'recordTypeId', record_data.record_type_id,
'recordType', record_types.type,
'recordDataOnType', record_data.data
)
) as recordData
FROM record_data
JOIN record_types ON record_types.id = record_data.record_type_id
JOIN records ON records.id = record_data.record_id
JOIN users ON users.id = records.user_id
WHERE records.id = ?
GROUP BY records.id
;`, [recordId]);
console.log("model2");
return result
};
module.exports = {getRecordWithData}
정상요청일때만 옮겨놨다
이제 에러처리하고 라우터 적용하면 된다...
라우터
// server.js
// 위에 코드블럭에 있던 두줄 없어짐
// routes/index.js
const express = require("express");
const router = express.Router();
const recordRouter = require("./record_router");
router.use("/records", recordRouter)
module.exports = router;
// routes/record_router.js
const express = require("express");
const recordController = require("../controllers/record_controller")
const recordRouter = express.Router();
recordRouter.get("/:recordId", recordController.getRecordWithData)
module.exports = recordRouter;
다시 에러처리(예외처리)

에러 열심히 던져보다가 트라이캐치...매번써야돼..?싶어서 위코드 레포 구경하다가 편리한 도구를 복붙해왔다 복붙도 실력이야~
error_creator.js는 메세지랑 상태코드 던져주면 에러 만들어주는거,
error_handler.js는 에러 잡으면 상태코드랑 메세지 리턴해주는거
// routes/record_router.js
const errorHandler = require("../middlewares/error_handler");
const recordRouter = express.Router();
recordRouter.get("/:recordId", errorHandler(recordController.getRecordWithData));
그래서 이렇게 라우터에 던질때부터 errorHandler에 감싸서 던지면 안에서 에러를 throw만 해도 메세지랑 코드가 리턴된다.
예외처리한거 어디다가 끼워넣어야할지 기웃대면서 생각하다가
원래 목적대로 sql은 dao에, sql결과가지고 에러내는건 로직처리하는 service에 넣는게 맞는거같아서 각 예외처리의 sql과 if문을 갈랐다
// models/record_dao.js
// 없는기록 불렀을때
const checkExistRecord = async (recordId) => {
const [[isExists]] = await pool.query(`
SELECT EXISTS (SELECT 1 FROM records WHERE id = "${recordId}");`);
return Object.values(isExists)[0]
}
// 비활성화유저의 기록 불렀을때
const checkActiveRecord = async (recordId) => {
const [[isActive]] = await pool.query(`
SELECT users.is_active
FROM users
JOIN records ON users.id = records.user_id
WHERE records.id=?;`, [recordId]);
return isActive.is_active
};
record_dao.js에 이렇게 예외처리에 필요한 sql문 추가하고 조건으로 판단할 데이터 리턴해주고
// services/record_service.js
const getRecordWithData = async (recordId) => {
// 존재안할때
const isExists = await recordDao.checkExistRecord(recordId);
if (!isExists) {
throw new ErrorCreator("RECORD_NOT_EXISTS", 404)
}
// 비활성화유저꺼일때
const isActive = await recordDao.checkActiveRecord(recordId);
if (!isActive) {
throw new ErrorCreator("INVALID_RECORD", 404)
}
return await recordDao.getRecordWithData(recordId);
};
이렇게 추가해서 완성...자야지
'wecode' 카테고리의 다른 글
| 프리온보딩 | 2차과제 - 1: db모델링, 데이터조회기능 (0) | 2022.10.09 |
|---|---|
| 프리온보딩 | 1차과제 - 3 (1) | 2022.10.06 |
| 프리온보딩 | 1차과제 - 1 (0) | 2022.10.05 |
| 구방문방구 | 리팩토링: 상품리스트 모듈화 (0) | 2022.10.01 |
| Code Kata | Week3 - Day2 (0) | 2022.09.14 |