Exceptions and Validation

0_CyberLover_0·2022년 4월 12일
1

Node.JS # 03

목록 보기
11/19

현재 오류가 있고 console에서 해당 에러가 뭔지 정확히 알수 있다.

또 다른 오류는 뭐가 있는지 찾아 본다.

예를 들어

ValidationError: Video validation failed: createdAt: Cast to date failed for value "lalalallalalal" (type string) at path "createdAt"

이 에러는 validation 에러이다.

createdAt에 잘못된 타입의 데이터를 전송했기에 그렇다.

Date타입이 아니라 에러가 발생 하였다. string을 전송해서 에러가 발생 한거다.

다른 오류는 뭐가 있을지 더 본다.

예를 들어 데이터를 아예 안 보낼수도 있다.

videoController.js에서

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  await Video.create({
    title: title,
    description: description,
    hashtags: hashtags.split(",").map((word) => `#${word}`),
    meta: {
      views: 0,
      rating: 0,
    },
  });
  return res.redirect("/");
};

createdAt을 지워서 date를 아예 안 보내면 어떻게 될지 한번 보도록 한다.

새로고침 한다음 upload해 보면 아무 에러 없이 video가 형성 되었다.

왜 에러 없이 만들어 질수 있을까? createdAtdate여야 한다고 설정했었다.

이유는 required를 적지 않아서 그렇다. 그래서 에러가 나지 않았다.

video를 생성하는데 date를 전송 안 했지만 database에게 괜찮다고

date는 필수가 아니라고 전달해서 그렇다.

required라고 설정하지 않았기에 이런 결과가 나온거다.

현재 mongo console을 보면 db.videos.find()쳐보면

video중에 한videocreatedAt가 없다.

그래서 reuired를 추가해 주도록 한다.

video.js에서

  createdAt: { type: Date, required: true },

이렇게 해주면 이제 에러가 생길거다. 왜냐하면 createdAt은 이제 required이기 때문이다.

사실 다른 모든 부분도 다 required여야 한다.

새로고침 해주고 upload해주면 어떻게 되냐면 에러가 발생 한 걸 확인 할수 있다.

browser자체에는 문제가 없지만 response가 없다.

  return res.redirect("/");

그 이유는 javascript가 에러로 인해 이곳에서 멈춘거다.

에러가 있으면 catch를 해야한다. trycatch를 사용 하면 되지만 좀 있다 해주도록 한다.

현재 이 에러에 집중한다. validation에러라고 적혀 있다.

ValidationError: Video validation failed: createdAt: Path `createdAt` is required.

createdAtrequired라 적혀 있다.

보다시피 mongoose에게 데이터 타입을 구체적으로 작성 할수록 편하다.

예를 들어

createdAt: { type: Date, required: true },
hashtags: [{ type: String }],

이렇게 저 부분은 required이다. 여긴 string이어야 한다.

이렇게 해주면 mongoosemongoDB가 우리를 위해서 validation 해준다.

지금 mongoDBdate가 없기 때문에 video생성을 거부 하고 있는 거다.

실수 할수 있는 부분들을 체크해주고 있는거다.

계속 다른 오류들을 만들어(?) 보도록 한다.

그 전에 먼저 에러만 손 보도록 한다.

페이지는 계속 무한 로딩 중인데 아래 코드가 실행 되지 않아서 그러는 거다.

  return res.redirect("/");

await에서 에러가 생기면 그냥 다 날아가 버리는 거다.

  await Video.create({

바로 이부분 !! 아무것도 실행 되지 않는다. 넘어가기 위해서는 에러를 catch해줘야 한다.

좀 전에 말했던 trycatch를 사용해 준다.

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  try{
    await Video.create({
      title: title,
      description: description,
      hashtags: hashtags.split(",").map((word) => `#${word}`),
      meta: {
        views: 0,
        rating: 0,
      },
    });
    return res.redirect("/");
  }  catch (error) {
    console.log(error);
    return res.render("upload", { pageTitle: "Upload Video" });
  }
};

이렇게 try를 해줄거고 에러가 있으면 catch해줄거다.

그리고 에러도 위치 해 줄거고 에러를 console.log해준다.

에러를 잡아내도 무언가를 return해야한다. 그래서 upload를 다시 render할거다.

그래서 이렇게 결과가 나왔다.

video 생성에 문제가 없다면 home으로 보내질거다.

하지만 에러가 있다면 javascript는 실행 시키지 않고 upload를 다시 render할거다.

다시 한번 새로고침을 해주고 video를 만들어 보면 보다시피 에러가 있고

upload가 다시 render되었다. 그리고 에러는 바로 터미널에서 볼수 있다.

Error: Video validation failed: createdAt: Path `createdAt` is required.

createdAtrequired라고 알려 주고 있다.

이제 에러가 catch되고 있고 사용자는 response를 받게 되었다.

하지만 아직 사용자는 에러 메세지를 직접 볼수 없는 상태이다.

사용자에게 오류 메세지를 바로 띄울수 있는게 훨씬 좋을 거다.

한 가지 방법은 에러 메세지를 보내주는 건데 그 메세지를 upload template으로 보내는 거다.

그럼 바로 보내주도록 한다.

videoController.js에서

export const postUpload = async (req, res) => {
  const { title, description, hashtags } = req.body;
  try {
    await Video.create({
      title: title,
      description: description,
      hashtags: hashtags.split(",").map((word) => `#${word}`),
      meta: {
        views: 0,
        rating: 0,
      },
    });
    return res.redirect("/");
  } catch (error) {
    console.log(error);
    return res.render("upload", {
      pageTitle: "Upload Video",
      errorMessage: error._message,
    });
  }
};

이렇게 바꿔 준다. 터미널을 통해 보면 오류 코드는 정말 길다. 하지만 메세지는 짧다.

그 메세지를 활용해 보도록 한다.

errorMessage: error._message,

그래서 이렇게 작성 하게 되었다. 이제 uploadrender할때 에러 메세지도 함께 render된다.

그리고 이제 upload.pug로 가서

extends base.pug

block content 
    if errorMessage 
        span=errorMessage
    form(method="POST")
        input(placeholder="Title", requried, type="text", name="title")
        input(placeholder="Description", requried, type="text", name="description")
        input(placeholder="Hashtags, separated by comma.", requried, type="text", name="hashtags")
        input(type="submit", value="Upload Video")

form 맨위에 이렇게 작성해준다. 보다시피 templaterender하고 있다.

에러 메세지는 template variable의 행태로 보내고 있다.

그리고 다시 video를 만들어 본다. 메세지가 표시된다~!!

에러 메세지가 포함된 templaterender하고 있다.

이 프로젝트에서는 이 정도에서 만족 할수도 있다.

다시 한번, 지금 에러를 다루는 걸 하고 있다.

await Video.create({

여기에 오류가 생기면 javascript

console.log(error);
    return res.render("upload", {
      pageTitle: "Upload Video",
      errorMessage: error._message,
    });

여기로 이동해서 에러 메세지를 출력해 주는 거다. 이제 console.log는 필요 없으니 지워준다.

에러가 생기면 upload template는 다시 render되서 사용자는 form을 다시 보게 된다.

하지만 전과는 달리 에러 메세지와 함계 표시 될거다.

에러 메세지는 mongoose가 만들어준 에러 메세지와 동일하다.

그리고 그 메세지는 실수를 했을 때만 표시가 된다.

하지만 그 상태에서 upload를 다시 해보면 에러가 없는 templaterender하고 있다.

왜냐하면 templaterender하는 controllerpostUpload가 아닌 getUpload이기 때문이다.

일단 거기까지는 에러가 없다. 그럼 여기를 좀 정리하고 새로운 에러를 또(?) 만들어 본다.

video.js에서 보면

  createdAt: { type: Date, required: true },

createdAtrequired여야 하지만 매번 이렇게 하고 싶지 않다고 해보자.

수 많은 model에 각각 이렇게 해줘야 한다면 너무 고통 스러울 거다.

그래서 createdAtdefault값을 설정해 주는 거다.

  createdAt: { type: Date, required: true, default: Date.now },

이렇게 작성 하지 않는것이 중요한 포인트이다.

  createdAt: { type: Date, required: true, default: Date.now() },

이렇게 적으면 function을 즉각 실행 시킬건데 그걸 원하지 않는다.

그래서 위에 처럼 function을 빼 준다.

그리고 MongoosemongoDB가 똑똑해서 video를 만들면 mongoose가 알아서 처리해 준다.

createdAt: Date.now(),

이 코드와 동일 한 것이다. 그러나 매번 이렇게 작성 해서 실행 시키고 싶지 않다.

그래서 default값을 정해 주는 거다. 그리고 다시 말하지만 ()를 빼는 이유는

바로 실행 시키고 싶지 않기 때문이다.

mongoose는 새로운 video를 생성했을 때만 실행 시킬거다.

이제 에러를 정확히 찾고 처리하는지 또는 video가 제대로 생성 되는지 확인해 보도록 한다.

아무 문제 없이 생성 된걸 확인 할수 있다.

mongo로 가서 db.videos.find()를 쳐보면 많은 종류의 video를 확인 할수 있다.

그리고 videoController.js에서 보면

meta: {
        views: 0,
        rating: 0,
      },

이렇게 되어 있는 부분이 보일 텐데 이 부분도 마음에 안 든다.

지워 준 다음 video.js로 가서

meta: {
    views: { type: Number, default: 0, required: true  },
    rating: { type: Number, default: 0, required: true  },
  },

requireddefault 값이 있으면 약간 쓸모가 없지만 그래도 써준다.

이걸로 인해 이 부분도 중복해서 쓰지 않아도 된다.

이제 또(?) 에러를 만들어 보자.

보다시피 새로운 걸 적용 할수록 코드가 짧아지고 있어서 좋은 코드가 되어 간다.

titledescription에도 required를 부여 할수 있다.

title: { type: String, required: true },
  description: { type: String, required: true },

이 두 항목은 string이라 다른 코드를 추가 해줄수 있는데

예를 들어 title의 최대 길이를 100자로 설정 할수 있다.

또는 최소 열 단어 또는 스무자 이상이어야 한다고 설정 할수 있다.

내 마음에 드는 대로 다 바꿔 줄수도 있다.

이번 파트에서 한걸 되짚어 보면 먼저 에러를 일부러 만들었다.

원래 createdAt코드가 마음에 안 들었다. 하지만 해당 코드가 required이고 default가 없어서

catch에 대해 배울 수 있었다. 그 덕에 에러 메세지를 templaterender해서 보낼 수 있게 되었다.

template는 정상일때 getUploadrender하고 에러가 있는 경우에는 에러 메세지와 함께 render 된다.

trycatch도 배웠다.

기억할게 있다. await되는 코드에 오류가 있다면 javascript는 더 이상 코드를 실행 시키지 않는다.

그렇기에 try를 넣었고 그 덕에 javascripttry 한 뒤 catch 할 거다.

catch가 없다면 서버는 그냥 아무것도 안하게 될거다.

그러니 trycatch는 꼭 같이 붙여줘야 한다.

profile
꿈꾸는 개발자

0개의 댓글