[서버 week_4] Express validator, populate

SH·2022년 5월 20일
0

서버 세미나

목록 보기
9/14

기억해!!!!!! loader/db.ts에서 connectDB 안에

import mongoose from "mongoose";
import config from "../config";
import Movie from "../models/Movie";
import Review from "../models/Review";

const connectDB = async () => {
    try {
        await mongoose.connect(config.mongoURI);

        mongoose.set('autoCreate', true);

        console.log("Mongoose Connected ...");
        
        // 여기

        Movie.createCollection().then(function (collection) {
            console.log("Movie Collection is created!");
        });
      
        Review.createCollection().then(function (collection) {
            console.log("Review Collection is created!");
        });
        
        // 안에
        
    } catch (err: any) {
        console.error(err.message);
        process.exit(1);
    }
};

export default connectDB;

컬렉션이름.createCollection()할 때 putty에서 서버 돌리면 mongodb에서 컬렉션이 안만들어짐 먼저 local 환경에서 서버 한번 띄워야지 mongodb compass에 collection이 만들어진걸 확인할 수 있다!!!!!!!!!!


+) 그리고,, git clone해서 putty에서 배포하고 local에 있는 코드를 수정한 뒤에 수정한 버전으로 api test를 하고싶으면 localhost로 서버를 띄워서 봐야함 즉 local에서 코드 수정한 내용은 aws에 반영이 안된다는 소리 아니 이 당연한걸 도대체 왜 이걸로 삽질했지?? 진짜어이없다




Express-validator 사용하기

MovieRouter.ts

const { body } = require("express-validator");

router.post("/movies/:movieId", [

    body("title").notEmpty().withMessage("제목써라"),
    body("writer").notEmpty().withMessage("글쓴이써라"),
    body("content").notEmpty().withMessage("내용써라")

], ReviewController.createReview);

Controller 단에서 express-validator의 body를 가져와서 여러가지 검증을 해준다


MovieController.ts

const { validationResult } = require("express-validator");

const createMovie = async (req: Request, res: Response) => {

    const error = validationResult(req);

    if (!error.isEmpty()){
        console.log(error);
        return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, message.BAD_REQUEST, res.json({ errors: error.array() })));
    } 

validationResult로 req에 넘어온 error를 검증해준다

에러메시지를 그대로 보여주고 싶으면

res.json({ errors: error.array() }

이렇게 가져오면 됨


mongoose populate

populate란?

document의 경로를 다른 collection의 실제 document로 자동으로 바꾸는 방법

몽고db에서 하나의 document가 다른 객체의 ObjectId를 참조할 때 ObjectId를 해당 객체로 바꾸는 작업을 해주는 거라고 알면 됨

ReviewInfo.ts

import mongoose from "mongoose";

export interface ReviewInfo {
    writer: mongoose.Types.ObjectId;
    movie: mongoose.Types.ObjectId;
    title: string;
    content: string;
}

review가 movie와 writer를 참조하는 상황이다

이 때 revier를 조회할 때 movie 객체 전부와 writer 객체의 name 필드만 가져오도록 해보겠음


ReviewService.ts

const getReviews = async (movieId: string): Promise<ReviewResponseDto[]> => {

    try{
        const reviews = await Review.find({
            movie: movieId
        }).populate('writer', 'name').populate('movie'); // writer 객체의 name만, movie 객체 전부 가져옴

        const data = await Promise.all(reviews.map((review: any) => {
            const result = {
                writer: review.writer.name, // writer 이름만
                movie: review.movie, // movie 객체 전부가 통째로 가져옴
                title: review.title, 
                content: review.content
            };
            return result;
        }));

        return data;
        
    } catch (error){
        console.log(error);
        throw error;
    }
}

const reviews = await Review.find({
            movie: movieId
        }).populate('writer', 'name').populate('movie'); 

review를 찾는데 key는 매개변수로 전달받은 movieId로 한다.
review는 writer와 movie를 둘 다 참조하기 때문에 populate를 안하면 그냥 ObjectId로만 값을 가져온다.
따라서 writer 객체는 name 필드만, movie 객체는 전부 가져오도록 populate 해준다

writer의 password와 같은 다른 필드도 가져오고 싶으면 아래처럼 써주면 됨

.populate('writer', 'name password')

이렇게 가져온 reviews라는 배열을 map으로 돌려서 새로운 배열을 만들어준다

const data = await Promise.all(reviews.map((review: any) => {
            const result = {
                writer: review.writer.name, // writer 이름만
                movie: review.movie, // movie 객체 전부가 통째로 가져옴
                title: review.title, 
                content: review.content
            };
            return result;
        }));

        return data; 

result에 각각의 필드에 해당하는 값을 넣어주고 이걸 반환하여 data에 저장한다
ReviewResponseDto.ts는 애초에 writer 필드를 writer name만 받기로 정했기 때문에 그것만 넣어준다


Promise.all()에 대한 자세한 설명은 아래 참고
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

https://merrily-code.tistory.com/214


참고로 ReviewResponseDto.ts는 다음과 같이 생겼음

import { MovieInfo } from "../movie/MovieInfo";

export interface ReviewResponseDto {
    writer: string; // writer 이름만
    movie: MovieInfo; // movie 객체 전체 가져오도록
    title: string;
    content: string;
}

ReviewController.ts

const getReviews = async (req: Request, res: Response) => {
   
    const { movieId } = req.params; 

    try{
        const data: ReviewResponseDto[] = await ReviewService.getReviews(movieId);
        res.status(statusCode.OK).send(util.success(statusCode.OK, message.READ_REVIEW_SUCCESS, data));

    } catch(error){
        console.log(error)
        res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, message.READ_REVIEW_FAIL));

    }
    
}
profile
블로그 정리안하는 J개발자

0개의 댓글