일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 백준
- spring-boot
- 솝트
- 솝트 후기
- 절차지향
- 파이썬 #백준 #BFS
- 서류전형
- AWS
- CRUD
- jwt
- nodejs
- 합격후기
- yolov5
- 파이썬
- S3
- 프로그래머스
- 인공지능
- Java
- 카카오
- 멋사
- 페이지네이션
- MongoDB
- 멋쟁이사자처럼
- 피로그래밍
- Python
- 사물인식
- EC2
- 면접전형
- objectdetection
- jQuery
- Today
- Total
찔끔찔끔씩😎
[Sopt] 4차 세미나(2) - Nodejs API 실습 본문
API 실습
🔎 필요한 Collection?
이러한 뷰 형태를 위한 Collection은?
- 영화 정보를 저장할 Movie Collection
- 리뷰 정보를 저장할 Review Collection
그럼 이 뷰에서 만들 수 있는 API는?
- 영화 정보 저장 API
- 영화 정보 조회 API
- 리뷰 작성 API
- 리뷰 조회 API
등등
리뷰 생성
🔎 1. Model 만들기
Movie 모델 (src/models/Movie.ts)
import mongoose from "mongoose";
import { MovieInfo } from "../interfaces/movie/MovieInfo";
const MovieSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
director: {
type: String,
required: true
},
startDate: {
type: Date
},
thumbnail: {
type: String
},
story: {
type: String
}
});
export default mongoose.model<MovieInfo & mongoose.Document>("Movie", MovieSchema);
Review 모델 (src/models/Review.ts)
- Review의 writer, movie는 기존에 만들어둔 넘들의 Object를 참조하도록 한다.
- 형식은 ref: "ModelName" 으로 참조한다.
import mongoose from "mongoose";
import { ReviewInfo } from "../interfaces/review/ReviewInfo";
const ReviewSchema = new mongoose.Schema({
writer: {
type: mongoose.Types.ObjectId,
required: true,
ref: "User"
// id를 직접 참조하는 reference 방식임을 지정
},
movie: {
type: mongoose.Types.ObjectId,
required: true,
ref: "Movie"
// id를 직접 참조하는 reference 방식임을 지정
},
title: {
type: String,
required: true
},
content: {
type: String
}
});
export default mongoose.model<ReviewInfo & mongoose.Document>("Review", ReviewSchema);
Interface 에 Info 만들기
MovieInfo.ts (src/interfaces/movie/MovieInfo.ts)
export interface MovieInfo{
title:string;
director:string;
startDate:Date;
thumbnail:string;
story:string;
}
ReviewInfo.ts (src/interfaces/review/ReviewInfo.ts)
import mongoose from 'mongoose';
export interface ReviewInfo {
writer: mongoose.Types.ObjectId;
movie: mongoose.Types.ObjectId;
title: string;
content: string;
}
🔎 2. Collection 생성
src/loaders/db.ts
서버 실행과 동시에 빈 Collection을 생성하도록 코드를 추가한다.
Movie.createCollection().then(function (collection) {
console.log("Movie Collection is created!");
});
Review.createCollection().then(function (collection) {
console.log("Review Collection is created!");
});
📍 express-validator
express-validator 란 validation 을 위한 npm 라이브러리로 여러가지 validation 함수를 제공한다.
아래 명령어로 내려받기.
yarn add express-validator
🔎 3. Dto 만들기
리뷰 작성 및 조회 API를 위한 DTO
src/interfaces/review/ReviewCreateDto.ts
import mongoose from 'mongoose';
export interface ReviewCreateDto {
writer: mongoose.Types.ObjectId;
title: string;
content: string;
}
src/interfaces/review/ReviewResponseDto.ts
import { MovieInfo } from "../movie/MovieInfo";
export interface ReviewResponseDto {
writer: string;
movie: MovieInfo;
// movie는 movie에 대한 모든 정보 조회
title: string;
content: string;
}
🔎 4. Controller 만들기
src/controllers/ReviewController.ts
- express-validator에서 제공하는 validationResult를 사용할 것이기 때문에, import 해주기
- validationResult(req) : req 에 error 가 있는지 판단한 후에 변수에 넣어준다.
/**
* @route POST /review/movies/:movieId
* @desc Create Review
* @access Public
*/
const createReview = async (req: Request, res: Response) => {
// body 검사에 대한 result가 들어온다.
const error = validationResult(req);
if (!error.isEmpty()) {
// error 가 있다면(비어있지 않다면)
return res
.status(statusCode.BAD_REQUEST)
.send(util.fail(statusCode.BAD_REQUEST, message.NULL_VALUE));
}
const reviewCreateDto: ReviewCreateDto = req.body;
const { movieId } = req.params;
try {
const data = await ReviewService.createReview(movieId, reviewCreateDto);
res.status(statusCode.CREATED).send(
util.success(
statusCode.CREATED,
message.CREATE_REVIEW_SUCCESS,
data,
),
);
} catch (error) {
console.log(error);
res.status(statusCode.INTERNAL_SERVER_ERROR).send(
util.fail(
statusCode.INTERNAL_SERVER_ERROR,
message.INTERNAL_SERVER_ERROR,
),
);
}
};
🔎 5. Router 만들기
src/routes/ReviewRouter.ts
- express-validator에서 제공하는 body 를 사용하여 validation을 적용한다.
- controller로 넘기기 전에 validator가 body를 검사한다.
- 해당 코드에서는 .notEmpty()를 사용하여 해당 필드가 비어있는지 검사한다.
import { body } from 'express-validator';
const router: Router = Router();
// validation 적용
router.post(
'/movies/:movieId',
[
// controller로 넘어가기 전에 validator가 body를 검사함
body('title').notEmpty(),
body('writer').notEmpty(),
body('content').notEmpty(),
],
ReviewController.createReview,
);
src/routes/index.ts
- index에도 잊지 않고 ReviewRouter를 연결해준다.
🔎 6. Service 만들기
src/services/ReviewService.ts
- 리뷰 작성시에 Reference(Movie)는 ObjectId를 그대로 저장한다.
const createReview = async (
movieId: string,
reviewCreateDto: ReviewCreateDto,
): Promise<PostBaseResponseDto> => {
try {
const review = new Review({
title: reviewCreateDto.title,
content: reviewCreateDto.content,
writer: reviewCreateDto.writer,
movie: movieId, // reference는 ObjectId를 그대로 저장
});
await review.save();
const data = {
_id: review._id,
};
return data;
} catch (error) {
console.log(error);
throw error;
}
};
📍 req.body 를 몇개 빠뜨린다면?
express-validator 에 의해 아까 정의한대로 validationResult 에서 error를 반환하기 때문에
400 BAD REQUEST를 반환한다!
리뷰 조회
🔎 1. Controller 만들기
src/controller/ReviewController.ts
/**
* @route GET /review/movies/:movieId
* @desc Get Review
* @access Public
*/
const getReviews = async (req: Request, res: Response) => {
const { movieId } = req.params;
try {
const data = 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.INTERNAL_SERVER_ERROR,
),
);
}
};
🔎 2. Router 만들기
src/routes/ReviewRouter.ts
router.get('/movies/:movieId', ReviewController.getReviews);
🔎 3. Service 만들기
src/services/ReviewService.ts
const getReviews = async (movieId: string): Promise<ReviewResponseDto[]> => {
try {
// 걍 find를 하면 ObjectId가 반환될 것.
// 해당 Id의 특정 정보를 받아오기 위해 ""populate"" 사용
// 1) movie == movieId 인넘 find
// 2) populate(reference되어있는 넘, 그넘중 불러올 넘)
// ex) .populate('writer', 'name').populate('movie'); : writer의 이름, movie 전체 정보 가져오기
const reviews = await Review.find({
movie: movieId,
})
// populate(path: ref되어있는 필드명, select: 특정 필드)
.populate('writer', 'name')
.populate('movie');
console.log(reviews);
// 확인해보면 객체까지 같이 반환하는데, 딱 name과 정보만 보내려한다!
const data = await Promise.all(
// map: 배열을 돌면서 작업함
reviews.map(async (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;
}
};
📍위에서 쓴 Populate !!
document 의 경로를 다른 collection의 실제 document로 자동으로 바꾸는 방법이다.
즉, 다른 document 의 ObjectId를 실제 객체로 반환해준다.
사용방법
const reviews = await Review.find({ movie: movieId,}).populate('writer', 'name').populate('movie');
- 이때 그냥 find를 하면 ObjectId 가 반환될 것이다. 해당 Id의 특정 정보를 받아오기 위해 populate를 사용하는 것이다.
1) movie == movieId 인 넘을 find 한 뒤
2) populate(reference 되어 있는 넘, 그 넘 중 불러올 정보)
3) .populate('writer', 'name').populate('movie'); : writer의 이름, movie 전체 정보 가져오기
📍Populate 를 넘겨줄 때
Pomise.all을 사용하여 새로운 데이터 배열을 생성해준다.
await Promise.all(reviews.map(async (review: any)
DTO에 맞게 데이터를 가공해 준다.
ex) review.writer.name 으로 writer의 name만 가져오기
'Server > Nodejs' 카테고리의 다른 글
[Sopt] 7차 세미나(1) - Nodjs AWS S3 파일 업로드 (0) | 2022.06.16 |
---|---|
[Sopt] 5차 세미나(1) - Middleware, Authentication, API 명세서 (0) | 2022.05.18 |
[Sopt] 4차 세미나(1) - AWS EC2, PM2, S3 (0) | 2022.05.17 |
[Sopt] 3차 세미나(2) - NodeJS CRUD 실습✨ (0) | 2022.05.17 |
[Sopt] 3차 세미나(1) - MongoDB, Mongoose, 견고한 NodeJS Project (0) | 2022.05.17 |