백엔드 심화 5-1 ~ 5-6 (유효성 검사 및 미니프로젝츠 연동)

Develop Kim·2024년 9월 24일

programmers

목록 보기
17/40

5 유효성 검사를 만나다

5-1 유효성 검사

1️⃣ 유효성 검사(valid ation)는 무엇인가?

  • if문을 수시로 사용하게 되면 코드가 더러워짐

  • "사용자가 입력한 값(사용자의 요청)"에 대한 유효성(타당성)을 확인하는 것

    • userId: 값이 있어야, 숫자여야 함
    • 채널 name: 값이 있어야, 문자여야 함
    • 사람 name: 숫자가 아니며, 문자열이어야, 2자 이상이어야 함
  • 지금까지는 이것을 if문을 사용했지만, 외부모듈 express-validater을 사용하면 코드가 깔끔해질 수 있다.

  • 설치 후 [사용 가이드]를 참고해서 코드에 적용해보자




5-2 userId

1️⃣ userId에 대한 유효성 검사를 해보자

const {body, validationResult} = require('express-validator') // body 메소드를 불러와 벨리데이터 모듈을 넣어준다, 추가적으로 오류를 받아주는 리절트 변수도 넣어준다

router
  .route('/:id')
  .post(
    body('userId').notEmpty().isInt().withMessage('숫자를 입력하자')  // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
    , (req, res) => {
      const err = validationResult(req) // 만약 에러가 있다면

      if (!err.isEmpty()){ // 에러를 반환하도록
        console.log(err.array())
      }
    
    const {name, userId} = req.body
    if (name){ // 유효성 검사를 위에서 하니 userId는 빼줘도 됨
      let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
      let values = [name, userId]

      conn.query(sql, values,
        function(err, results) {
          res.json(results); // 쿼리 결과를 클라이언트에 응답
        }
      )
    } else {
      res.status(400).json({
        message : '요청 값을 제대로 보내주세요'
      })
    }
  })



5-3 name 추가 그리고, return 등장

1️⃣ name의 유효성 검사 추가

  • 여러가지 유효성 검사를 하려면 검사할 대상들을 [배열]로 묶어주면 된다.
  • 또, if문을 실행하고 뒤 코드를 실행하고 싶지 않다면 return을 이용해 곧바로 원하는 결과로 코드를 종료할 수 있다.
  .post(
    [body('userId').notEmpty().isInt().withMessage('숫자입력 필요'),  // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
    body('name').notEmpty().isString().withMessage('문자입력 필요')] // 배열을 이용하면 여러 유효성 검사 가능
      , (req, res) => {
        const err = validationResult(req) // 만약 에러가 있다면

        if (!err.isEmpty()){ // 에러를 반환하도록 하고
          console.log(err.array()) // 에러의 배열을 꺼내보자
          return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
        }
    
    const {name, userId} = req.body

    let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
      let values = [name, userId]

      conn.query(sql, values,
        function(err, results) {
          res.json(results); // 쿼리 결과를 클라이언트에 응답
        }
      )  
  })
  • 이름에 숫자를 넣고 아이디에 문자열을 넣으니 에러를 잘 반환한다.



5-4 sql 에러 (err)

1️⃣ 없는 유저id로 채널을 생성하면 데이터베이스에 저장이 안된다

  • 없는 유저 id로 채널을 생성하면 에러가 발생하지 않는다.

  • 다시 제대로 채널을 만들고 확인해보니 채널 테이블에는 저장이 안 되어있고 아이디는 붕 떠있는 것을 확인할 수 있다. 에러는 발생하지 않았지만, 테이블 저장은 안되는 것이었다.

  .post(
    [body('userId').notEmpty().isInt().withMessage('숫자입력 필요'),  // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
    body('name').notEmpty().isString().withMessage('문자입력 필요')]
      , (req, res) => {
        const err = validationResult(req) // 만약 에러가 있다면

        if (!err.isEmpty()){ // 에러를 반환하도록
          console.log(err.array())
          return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
        }
    
    const {name, userId} = req.body

    let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
      let values = [name, userId]

      conn.query(sql, values,
        function(err, results) {
          if (err) {
            console.log(err)
            return res.status(400).end(); // 에러가 걸리면 끝내도록 설정
          }
          res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
        }
      )  
  })
  • 이렇게 하니 클라이언트에게 에러를 넘겨줄 수 있게 됨




5-5 channels 리팩토링 + API 우선순위

1️⃣ SQL을 먹이고 나머지 코드도 DB와 연동해보자

const express = require('express')
 // 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌
const conn = require('../mariadb'); // Promise 기반의 mariadb 연결 객체 가져옴
const {body, param, validationResult} = require('express-validator') // body 메소드를 불러와 벨리데이터 모듈을 넣어준다, 추가적으로 오류를 받아주는 리절트 변수도 넣어준다

router.use(express.json()) //http 외 모듈 사용 'json 모듈'


let db = new Map()
let id = 1


router
  .route('/') // route로 URL 묶어주기

// 채널 개별 생성
  .post(
    [body('userId').notEmpty().isInt().withMessage('숫자입력 필요'),  // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
    body('name').notEmpty().isString().withMessage('문자입력 필요')]
      , (req, res) => {
        const err = validationResult(req) // 만약 에러가 있다면

        if (!err.isEmpty()){ // 에러를 반환하도록
          return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
        }
    
    const {name, userId} = req.body

    let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
      let values = [name, userId]

      conn.query(sql, values,
        function(err, results) {
          if (err)
          { console.log(err)
            return res.status(400).end()
          } // 에러가 걸리면 끝내도록 설정
          res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
        }
      )  
  })


// 채널 전체 조회
.get(
  body('userId').notEmpty().isInt().withMessage('숫자 입력 필요') // 유효성 검사는 사용자의 요청에 대해서 구분이 가능
  , (req, res) => { 
    const err = validationResult(req) // 만약 에러가 있다면

    if (!err.isEmpty()){ // 에러를 반환하도록
      return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
    }

  let {userId} = req.body 
  
  let sql = 'SELECT * FROM channels WHERE user_id = ?'

  conn.query(sql, userId,
    function(err, results) {
      if (err) {
        console.log(err)
        res.status(400).end()
      }

      if (results.length) {
        res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
      } else {
        notFoundChannel(res)
      }
    }
  )  
})




router
  .route('/:id')

// 채널 개별 조회
.get(
  param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
  ,(req, res) => {
    const err = validationResult(req) // 만약 에러가 있다면

    if (!err.isEmpty()){ // 에러를 반환하도록
      return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
    }

  let {id} = req.params // req.params 객체에서 URL 경로에 포함된 id 값을 추출하여 id 변수에 저장
  id = parseInt(id) // URL에서 문자열로 추출된 id를 정수로 변환

  let sql = 'SELECT * FROM channels WHERE id = ?'

  conn.query(sql, id, // email 값을 바인딩
    function(err, results) {
      if (err) {
        console.log(err)
        res.status(400).end()
      }

      if (results.length) {
        res.status(200).json(results); // 쿼리 결과를 클라이언트에 응답
      } else {
        notFoundChannel(res)
      }
    }
  )
})



5-6 update, delete

1️⃣ update

  .put(
    [param('id').notEmpty().withMessage('채널id 필요'), // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
    body('name').notEmpty().isString().withMessage('채널명 오류')
    ],(req, res) => {
      const err = validationResult(req) // 만약 에러가 있다면

      if (!err.isEmpty()){ // 에러를 반환하도록
        return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
      }

      let {id} = req.params
      id = parseInt(id)
      let {name} = req.body

      let sql = 'UPDATE channels SET name = ? WHERE id = ?'
      let values = [name, id]

      conn.query(sql, values,
        function(err, results) {
          if (err) {
            console.log(err)
            res.status(400).end()
          }

          if (results.affectedRows == 0) { 
            return res.status(400).end()  // 쿼리가 실행되었지만 변경된 행이 없을 경우 400 에러 응답
          } else {
            res.status(200).json(results)  // 쿼리 실행으로 행이 변경된 경우 성공적으로 응답
          }
        }
      )
})

2️⃣ delete

  .delete(
    param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
    ,(req, res) => {
      const err = validationResult(req) // 만약 에러가 있다면

      if (!err.isEmpty()){ // 에러를 반환하도록
        return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
      }

    let {id} = req.params
    id = parseInt(id)

    let sql = 'DELETE FROM channels WHERE id = ?'
    conn.query(sql, id, // email 값을 바인딩
      function(err, results) {
        if (err) {
          console.log(err)
          res.status(400).end()
        }

        res.json(results); // 쿼리 결과를 클라이언트에 응답
      }
    )
})
  • 채널이 정상적으로 삭제되는 것을 볼 수 있음.

  • 없는 채널을 삭제해도 정상작동을 함. 여기도 affectedRows가 없다면 에러메시지 추가

  .delete(
    param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
    ,(req, res) => {
      const err = validationResult(req) // 만약 에러가 있다면

      if (!err.isEmpty()){ // 에러를 반환하도록
        return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
      }

    let {id} = req.params
    id = parseInt(id)

    let sql = 'DELETE FROM channels WHERE id = ?'
    conn.query(sql, id, // email 값을 바인딩
      function(err, results) {
        if (err) {
          console.log(err)
          res.status(400).end()
        }

        if (results.affectedRows == 0) { 
          return res.status(400).end()  // 쿼리가 실행되었지만 변경된 행이 없을 경우 400 에러 응답
        } else {
          res.status(200).json(results)  // 쿼리 실행으로 행이 변경된 경우 성공적으로 응답
        }
      }
    )
})
  • 없는 채널 삭제 시 400 표출 잘 됨
profile
김개발의 개발여정

0개의 댓글