[프로그래머스] 백엔드심화(5)

Lina Hongbi Ko·2024년 9월 24일
0

Programmers_BootCamp

목록 보기
24/76
post-thumbnail

2024년 9월 24일

✏️ 유효성 검사

  • validation : 사용자가 입력한 값을 유효성(=타당성)을 확인하는 것
    • userId : 값이 있어야함 + 숫자
    • 사람 name : 숫자X, 문자열, 2자 이상...
  • 외부 모듈 'express-validator'을 사용할 예정
    - npm install express-validator

✏️ 채널 생성

💡 채널 생성 유효성 검사(userId)

  • 채널 생성 -> userId 유효성 검사를 해보자
    : userId가 숫자로 들어가야 함 -> 유효성 검사
// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, validationResult } = require("express-validator");

router.use(express.json());

router
.route("/")
.post(
	body('userId').notEmpty().isInt().withMessage('userId는 숫자이어야 해요')
	,(req, res) => {
			const err = validationResult(req)
			
			if (!err.isEmpty()) {
				console.log(err.array());
			}
            
		    const { name, userId } = req.body;
	
		    if (name) {
		        let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`;
		        let values = [name, userId];
		        conn.query(sql, values, function (err, results) {
		           res.status(201).json(results);
		        });
		    } else {
		        res.status(400).json({
		            message: `요청 값을 다시 보내주세요`,
		        });
		    }
 });

POSTMAN) POST + localhost:7777/channels + {”name” : “jjong”, “userId” : 3}

POSTMAN) POST + localhost:7777/channels + {”name” : “jjong”, “userId” : abc} → userId 숫자 말고 문자열 (아직 response는 설정 하지 않아서 아무결과가 없음)

💡 채널생성 유효성 검사(name)

  • 채널 생성 -> name 유효성 검사를 해보자
    : name - 문자열 이어야함 -> 유효성 검사
// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, validationResult } = require("express-validator");

router.use(express.json());

router
.route("/")
.post(
	[body('userId').notEmpty().isInt().withMessage('userId는 숫자이어야 해요'),
	body('name').notEmpty().isString().withMessage('name은 문자로 입력해야 해요')]
	,(req, res) => {
		const err = validationResult(req)
		
		if (!err.isEmpty()) {
			return res.status(400).json(err.array());
		}
		
		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.status(201).json(results);
		});
 });

POSTMAN) localhost:7777/channels + {”name” : “ggung”, “uerId” : “abc”}

POSTMAN) localhost:7777/channels + {”name” : 123, “uerId” : “abc”}

💡 sql 에러 (err)

  • users에 없는 userId를 넣어서 채널 생성를 시도 -> 에러 발생X, 서버 잘 돌아감 + 오류 메시지도 없음 -> 그러나 채널은 생성되지 않음
  • 이 경우를 이제 예외 처리 해줘야함 -> sql문의 err에 대한 정보를 작성하지 않았기 떄문임!
// channels.js

... 생략 ...
conn.query(sql, values, function (err, results) {
			if(err) {
				console.log(err);
				return res.status(400).end();
			}
		  res.status(201).json(results);
});

... 생략 ...

POSTMAN) POST + localhost:7777/users + {”name” : “jjung”, “userId” : 50}

✏️ 채널 전체, 개별 조회 (feat. API 우선순위)

*사용자의 요청이 아닌 부부은 유효성 검사 보다는 우리가 고려해야할 사항임(results.length)

💡 채널 전체 조회 유효성 검사

// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, validationResult } = require("express-validator");

router.use(express.json());

router
  .route("/")
  // 채널 전체 조회
  .get(
	  body('userId').notEmpty().isInt().withMessage('userId는 숫자이어야 해요'),
	  (req, res) => {
	  
		const err = validationResult(req);

        if (!err.isEmpty()) {
          return res.status(400).json(err.array());
        }
	  
	    const { userId } = req.body;
	
	    let sql = `SELECT * FROM channels WHERE user_id = ?`;
      	conn.query(sql, userId, function (err, results) {
	        if(err) {
		        console.log(err);
		        return res.status(400).end();
	        }
            
            if (results.length) {
                res.status(200).json(results);
            } else {
                notFoundChannel(res);
            }
        });
  })

POSTMAN) GET + localhost:7777/channels + {”userId” : 2}

POSTMAN) GET + localhost:7777/channes + {”userId” : 20}

💡 채널 개별 조회

// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, param, validationResult } = require("express-validator");

router.use(express.json());

router
  .route("/:id")
  // 채널 개별 조회
  .get(
	  param('id').notEmpty().withMessage('채널 id가 필요해요')
	  ,(req, res) => {
	  
		  const err = validationResult(req);
      if (!err.isEmpty()) {
        return res.status(400).json(err.array());
      }
	  
	    let { id } = req.params;
	    id = parseInt(id);
	
	    let sql = `SELECT * FROM channels WHERE id = ?`;
	    conn.query(sql, id, function (err, results) {
		    if(err) {
			    console.log(err);
			    return res.status(400).end();
		    }
	      if (results.length) {
	        res.status(200).json(results);
	      } else {
	        notFoundChannel(res);
	      }
	    });
  })

POSTMAN) GET + localhost:7777/channels/3

POSTMAN) GET + localhost:7777/channels/33

POSTMAN) GET + localhost:7777/channels/

그런데 POSTMAN은 localhost:7777/channels/ 을 id값이 url에 들어있어서 채널 개별 조회로 들어 오는 건지, 아니면 채널 전체 조회인데 body값에 userId를 안보낸건지 알 수 없음

단지, location : body에 userId가 없는지에 대한 내용을 알 수 있으므로, 채널 전체 조회가 우선순위인지 알 수 있다.

그럼 왜 채널 조회가 우선순위가 먼저 일까???

📍 API 우선순위

  • 코드 순서에 따라 다름
  • 채널 개별 조회 보다 채널 전체 조회가 코드상 위에 있으므로 전체 조회가 우선
  • 우리 확인 할 수 있는 방법은 코드를 보고 채널 전체 조회가 먼저 쓰여져 있는 것을 알 수 있음
  • 어쨌든, 클라이언트 입장에서는 400 bad request 코드를 받는게 중요 -> 프론트에서는 어차피 400 status 코드 가지고 작업할 것임(나중에 프론트에서도 유효성을 검사하기 때문에)

✏️ 채널 수정

: 채널 수정 유효성 검사

// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, param, validationResult } = require("express-validator");

router.use(express.json());

router
  .route("/:id")
// 채널 개별 수정
  .put(
	  [param('id').notEmpty().withMessage('채널 id가 필요해요'),
	  body('name').notEmpty().isString().withMessage('채널명은 문자로 입력해주세요']
	  ,(req, res) => {
		  const err = validationResult(req);
	      if (!err.isEmpty()) {
	        return res.status(400).json(err.array());
	      }
	    
          let { id } = req.params;
          id = parseInt(id);
          let { name } = req.body;

          let sql = `UPDATE channels SET name = ? WHERE id = ?`;
          let values = [id, name]
          conn.query(sql, values, function (err, results) {
            if (err) {
              console.log(err);
              return res.status(400).end();
            }

            res.status(200).json(results);
          });
  })

POSTMAN) PUT + lcalhost:7777/channels/99 + {”name” : “룰루”} 채널 아이디가 없는 것 수정요청

에러가 나지 않고, response의 body에 affectedRows: 0 확인 → 없는 아이디 수정 요청해서 수정한 건 없다고 함(에러X) _업데이트된건 없음

POSTMAN) PUT + lcalhost:7777/channels/2 + {”name” : “lullu”}

affectedRows: 1 →성공함

  • 예외처리
// channels.js

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

         if (results.affectedRows == 0) {
            return res.status(400).end();
         } else {
            res.status(200).json(results);
         }
      });
  })

POSTMAN) PUT + lcalhost:7777/channels/99 + {”name” : “lullu”}

✏️ 채널 삭제

: 채널 삭제 유효성 검사

// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, param, validationResult } = require("express-validator");

router.use(express.json());

... 생략 ...
router
  .route("/:id")
	.delete(
    param("id").notEmpty().isInt().withMessage("채널 id가 필요해요"),
    (req, res) => {
      const err = validationResult(req); // 채널 id를 입력하지 않으면
      if (!err.isEmpty()) {
        console.log(err);
        return res.status(400).end();
      }

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

      let sql = `DELETE FROM channels WHERE id = ?`;
      conn.query(sql, id, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).end();
        }

        res.status(200).json(results);
      });
    }
  );

POSTMAN) DELETE + localhost:7777/channels/3

-> affectedRows: 1

  • 삭제한걸 다시 삭제해보자
    POSTMAN) DELETE + localhost:7777/channels/3


-> affectedRows: 0

  • 예외 처리 추가
... 생략 ...
      conn.query(sql, id, function (err, results) {
              if (err) {
                // 없는 채널 id 입력하면
                console.log(err);
                return res.status(400).end();
              }
              if (results.affectedRows == 0) {
                return res.status(400).end();
              } else {
                res.status(200).json(results);
              }
      });
    }
  );

POSTMAN) DELETE + localhost:7777/channels/70

✏️ 검사 미들 웨어 분리

const err = validationResult(req);
      if (!err.isEmpty()) {
        console.log(err);
        return res.status(400).end();
      }

위의 코드가 계속 반복됨

따라서, 함수로 바꿔주자.

  • 함수를 미들웨어처럼 쓸 수 있도록 모듈화해보자
  • 하나의 파일을 통째로 모듈화시키고, require해서 외부에서 불러와 사용했는데, 하나의 파일 안에서도 함수를 모듈화해서 다른 부분에서 모듈처럼 사용할 수 있음
  • http 모듈 외에 express.json()을 쓴 것처럼 사용
  • 변수에 담으면 됨
  • 그리고, get([body~],(req, res)⇒{}) : []안에 내부모듈로 사용한 함수 사용가능
    • 유효성 검사 뿐만 아니라, 핸들러(리퀘스트 요청이 왔을 때 호출되는 메소드)도 작성 가능
      • 파라미터값, 등 아무거나 핸들러에 들어갈 수 있음
// channels.js

const express = require("express");
const router = express.Router();
const conn = require("../mariadb.js");
const { body, param, validationResult } = require("express-validator");

router.use(express.json());


const validate = (req, res) => {
	const err = validationResult(req);

      if (!err.isEmpty()) {
        // userId가 숫자가 아니거나 입력하지 않으면
        return res.status(400).json(err.array());
      }
}

router
  .route("/")
  // 채널 전체 조회
  .get(
    [
	    body("userId").notEmpty().isInt().withMessage("userId는 숫자이어야 해요"),
	    validate
    ],
    (req, res) => {
      const err = validationResult(req);

      if (!err.isEmpty()) {
        // userId가 숫자가 아니거나 입력하지 않으면
        return res.status(400).json(err.array());
      }
      const { userId } = req.body;

      let sql = `SELECT * FROM channels WHERE user_id = ?`;
      conn.query(sql, userId, function (err, results) {
        if (err) {
          // users에 없는 userId 넣으면
          console.log(err);
          return res.status(400).end();
        }
        if (results.length) {
          res.status(200).json(results);
        } else {
          notFoundChannel(res);
        }
      });
    }
  )

POSTMAN) GET + localhost:7777/channels + userId 보내지 않음 → 유효성 검사 실패

유효성 검사 잘 작동되는것 학인

POSTMAN) GET + localhost:7777/channels + {”userId” : 4} → 유효성 검사 성공

request가 계속 요청 됨(sending request)→ 에러가 없어서 콜백함수로 넘어가야하지만 못 넘어가고 있음

why ?

vaildate를 미들웨어 형태로 선언해서 일어나는 일임

🍏🍎 오늘의 느낀 점 : 으아아아아,, 점점 더 갈수록 복잡해진다.. 코드는 전보다 줄었지만 더 알아야할 지식이 있으니 더 머리속을 정리하며 익숙해지도록 해야겠다.
express-validator 덕분에 유효성검사를 쉽게 사용할 수 있어서 정말 편리했다. API 우선순위에 대해서도 알게 되고,, 그리고 마지막에 배운 함수를 내부 모듈처럼 쓸 수 있는 것을 배우면서 다시 한번 함수의 편리함을 깨닫게 되고,, 다음 시간에 미들웨어 형태로 선언해서 일어나는 일이 왜 그런지 자세히 알아봐야겠다.

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글