express-validator로 입력값 검증하기

Younha Lee·2026년 2월 3일

TIL

목록 보기
25/61

express-validator

지금까지는 일일히 입력값을 검증했지만, express-validator 를 외부 라이브러리를 npm에서 받아 사용하기로 했어요.

간단 사용법

.post(body('userId').notEmpty().isInt().withMessage('userId must be a number'),
      body('name').notEmpty().isString().withMessage('name must be a string')
        ,(req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) res.status(400).json({"message": errors.array()[0].msg});
        const { name, userId } = req.body;
        let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`;
        let values = [name, userId];
        conn.query(sql, values, (err, results) => {
            if (err) return res.status(500).json(err);
            res.status(201).json(results);
        })
    })

body 에 올 속성을 메소드 체이닝으로 직접 접근해서 검증할 수 있어요.

isint() 를 걸어놨더니 errors 객체의 msg 를 가져왔어요.

여기서 if (!errors.isEmpty()) 대신에 If(errors) 를 쓰고싶어도 JS는 {}를 true로 변환하기 때문에 사용할 수 없어요.

error 객체

validation 과정에서 유효성 검사에 실패하면 error 객체가 반환돼요.

 conn.query(sql, userId,
            (err, results) => {
                if (err) {
                    console.error("Database Error:", err);
                    return res.status(500).json({
                        "message": "서버 내부 오류가 발생했습니다. 잠시 후 다시 시도해주세요."
                    });
                }
            if (results.length) res.json(results);
            else res.status(404).json({"message": "No Channel Found"});
            })
    })

이 error 객체는 서비스 정보가 유출되기 때문에 디버깅용 로그만 찍고, 유저에게는 공개하지 않아야해요.

함수 내부에서 return을 사용하면 종료와 함께 원하는 결과를 반환시킬 수 있어요.
그래서 if문 내부에서 return을 사용하게 되면 else를 사용하지 않아도 자동으로 함수가 종료되기 때문에 함수가 조금이라도 깔끔하게 보일 수 있다.

코드 작성순위에 따른 핸들러 구분

/channels 전체 조회 요청과 /channels + body에 userId를 넣는 개별 조회 요청이 요청을 받는 서버 입장에서 헷갈려요.
사실 GET 요청은 body를 통해 받는 게 안티패턴이기 때문에 이럴 일은 많지는 않겠지만, 코드에 따라 우선순위가 갈린다고 해요.

PUT에서 요청 성공 유무 구분하기

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

        conn.query(sql, values,
            (err, results) => {
                if (err) {
                    console.error("Database Error:", err);
                    return res.status(500).json({
                        "message": "서버 내부 오류가 발생했습니다. 잠시 후 다시 시도해주세요."
                    });
                }
                if (results.affectedRows === 0) {
                    return res.status(404).json({"message": `${id} Channel Not Found`});
                }
                return res.json({
                    "message": "Channel Updated Successfully",
                });
            })

put은 affectedRows 의 개수로 구분하는 게 중요해요.
요청 결과 row의 갯수가 0이면 404를 반환하기로 했어요.
다만 유효성 검사하는 부분이 너무 dry 원칙을 어기는 것 같아서 공통으로 빼야할 필요성이 있어요.

validate 미들웨어

const validate = (req, res, next) => {
    const errors = validationResult(req);
    if (errors.isEmpty()) {
        next();
    }
    return res.status(400).json({"message": errors.array()[0].msg});
}

그래서 다음과 같이 미들웨어를 분리했고,

router
    .route('/')
    .get([
        body('userId').notEmpty().isInt().withMessage('userId must be a number'),
        validate
        ],
        (req, res) => {
        let { userId } = req.body;

다음과 같이 미들웨어를 핸들러에 등록했어요.
미들웨어에서 next()를 명시해야 다음 핸들러로 넘어가져요.

profile
할 땐 하고 놀 땐 노는 일일놀놀입니다.

0개의 댓글