반응형

DB와 연결하고 포스트맨으로 API 테스트를 하던 중, 회원 등록 요청에서 아래와 같은 오류가 오류가 발생했다.

ValidationError: User validation failed: password: Path `password` is required., email: Path `email` is required.

↑ 오류 메시지

같이 확인해볼 코드는 register API, userSchema 코드와 포스트맨에서 보낸 request body(req.body), response body 부분이다.

// 회원 등록
app.post('/api/user/register', (req, res) => {
    try {
        const user = new User(req.body);
        console.log(user);
        const userData = user.save();
        return res.status(200).json({ success: true, user:userData});
    } catch {
        return res.status(400).json({ success: false, err});
    }
})

↑ register API (오류 발생)

const userSchema = mongoose.Schema({ // schema 필드 정의
    name: {
        type: String,
        maxLength: 50
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true,
        trim: true // 앞뒤 공백 제거
    },
    password: {
        type: String,
        required: true,
        trim: true,
        minLength: 5
    },
    role: {
        type: Number,
        default: 0
    }
})

↑ userSchema

{
    "name": "amanda",
    "email": "amanda@gmail.com",
    "password": "1234567"
}

↑ Postman - request body(req.body)

{
    "success": true,
    "user": {}
}

↑ Postman - response body

🚧 원인 분석

userSchema에서 email, password를 required: true를 설정하고 Postman에서 email, password의 값을 포함해 요청을 보냈음에도, Mongoose는 이 필드가 없다고 판단하며 validation 오류를 발생시킨 것이다.

Express는 기본적으로 요청 본문을 자동으로 파싱하지 않기 때문에, JSON 형식의 요청을 받을 경우 body-parser과 같은 미들웨어를 추가해줘야 req.body가 제대로 채워진다. 그래서 내가 작성한 코드로는 req.body가 undefined 였고, new User(req.body)에 들어가는 값도 비어 있으며, email과 password가 누락된 채 validation에 걸리게 된 것이다.

🛠️ 해결 과정

1. body-parser 미들웨어 추가

body-parser 모듈을 설치한다.

npm install body-parser

이후 코드 상단에 아래와 같이 등록한다.

const bodyParser = require('body-parser'); // HTTP 요청 본문(body) 파싱하는 미들웨어 불러오기

app.use(bodyParser.urlencoded( { extended: true })); // application/x-www-form-urlencoded
app.use(bodyParser.json()); // application/json

↑ register API

여기서 사용한 extended 옵션은 false가 기본이고, false를 설정할 경우 Node.js 기본 모듈인 querystring을 사용해 단순 구조만 파싱한다. true로 설정하면 qs 라이브러리를 사용해서 중첩된 객체나 배열 등 복잡한 구조도 파싱할 수 있다.

여기까지 설정하면 JSON 데이터를 정상적으로 받을 수 있게 된다.

2. async/await 적용

body-parser를 추가해도 여전히 Postman 응답에서 "user": {}처럼 빈 객체가 보였다. Mongoose의 user.save()가 비동기 함수인데 await를 붙이지 않아 저장이 완료되기 전에 응답을 보냈기 때문이다.

// 회원 등록
const bodyParser = require('body-parser'); // HTTP 요청 본문(body) 파싱하는 미들웨어 불러오기

app.use(bodyParser.urlencoded( { extended: true })); // application/x-www-form-urlencoded
app.use(bodyParser.json()); // application/json

app.post('/api/user/register', async (req, res) => {
    try {
        const user = new User(req.body);
        console.log(user);
        const userData = await user.save();
        return res.status(200).json({ success: true, user:userData});
    } catch {
        return res.status(400).json({ success: false, err});
    }
})

↑ register API

Mongoose의 save()는 Promise를 반환하며, 내부적으로 저장 전에 validation을 자동으로 실행한다.

await을 사용하지 않으면 저장이 완료되기도 전에 응답을 보내는 문제가 생길 수 있다.
이 경우 user.save()는 아직 실행 중인 Promise이므로, 응답에서 user: {}처럼 보일 수 있다.
따라서 await을 사용해 저장이 완료된 후 응답을 보내야 한다.

🧠 What I Learned

Express는 요청 본문을 파싱해주는 미들웨어가 필요하다. body-parser.json()을 등록하지 않으면 JSON 형식으로 보낸 데이터도 undefined 처리되어 validation 오류가 발생할 수 있다. user.save()는 비동기 함수이므로, 저장 완료를 보장하려면 await를 사용한다.

반응형

+ Recent posts