프리온보딩 | 1차과제 - 2

2022. 10. 6. 03:00·wecode
반응형

회의

깃허브

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
'wecode' 카테고리의 다른 글
  • 프리온보딩 | 2차과제 - 1: db모델링, 데이터조회기능
  • 프리온보딩 | 1차과제 - 3
  • 프리온보딩 | 1차과제 - 1
  • 구방문방구 | 리팩토링: 상품리스트 모듈화
이라후
이라후
  • 이라후
    화이팅
    이라후
  • 전체
    오늘
    어제
    • 분류 전체보기 (133)
      • TIL (23)
      • 기타 (26)
      • Python (14)
      • Django (10)
      • JavaScript (8)
      • git & GitHub (8)
      • Web (10)
      • Go (3)
      • wecode (31)
  • 반응형
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
이라후
프리온보딩 | 1차과제 - 2
상단으로

티스토리툴바