From 0f6287a2853587b0d56677393c793f930cdf86c9 Mon Sep 17 00:00:00 2001 From: jinsu4755 Date: Fri, 10 Jun 2022 19:20:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=204=EC=B0=A8=EC=84=B8=EB=AF=B8=EB=82=98?= =?UTF-8?q?=20=EC=8B=A4=EC=B2=9C=EA=B3=BC=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Seminar4/.gitignore | 146 ++++++++++++++++++ Seminar4/nodemon.json | 11 ++ Seminar4/package.json | 26 ++++ Seminar4/src/config/index.ts | 23 +++ Seminar4/src/controllers/ReviewController.ts | 53 +++++++ Seminar4/src/controllers/UserController.ts | 92 +++++++++++ Seminar4/src/controllers/index.ts | 5 + Seminar4/src/index.ts | 43 ++++++ .../interfaces/common/PostBaseResponseDto.ts | 5 + Seminar4/src/interfaces/movie/MovieInfo.ts | 7 + .../src/interfaces/review/ReviewCreateDto.ts | 7 + Seminar4/src/interfaces/review/ReviewInfo.ts | 8 + .../interfaces/review/ReviewResponseDto.ts | 8 + Seminar4/src/interfaces/school/SchoolInfo.ts | 4 + Seminar4/src/interfaces/user/UserCreateDto.ts | 9 ++ Seminar4/src/interfaces/user/UserInfo.ts | 9 ++ .../src/interfaces/user/UserResponseDto.ts | 6 + Seminar4/src/interfaces/user/UserUpdateDto.ts | 10 ++ Seminar4/src/loaders/db.ts | 27 ++++ Seminar4/src/models/Movie.ts | 24 +++ Seminar4/src/models/Review.ts | 24 +++ Seminar4/src/models/User.ts | 27 ++++ Seminar4/src/modules/responseMessage.ts | 18 +++ Seminar4/src/modules/statusCode.ts | 15 ++ Seminar4/src/modules/util.ts | 20 +++ Seminar4/src/routes/ReviewRouter.ts | 15 ++ Seminar4/src/routes/UserRouter.ts | 11 ++ Seminar4/src/routes/index.ts | 11 ++ Seminar4/src/services/ReviewService.ts | 55 +++++++ Seminar4/src/services/UserService.ts | 72 +++++++++ Seminar4/src/services/index.ts | 6 + Seminar4/tsconfig.json | 28 ++++ 32 files changed, 825 insertions(+) create mode 100644 Seminar4/.gitignore create mode 100644 Seminar4/nodemon.json create mode 100644 Seminar4/package.json create mode 100644 Seminar4/src/config/index.ts create mode 100644 Seminar4/src/controllers/ReviewController.ts create mode 100644 Seminar4/src/controllers/UserController.ts create mode 100644 Seminar4/src/controllers/index.ts create mode 100644 Seminar4/src/index.ts create mode 100644 Seminar4/src/interfaces/common/PostBaseResponseDto.ts create mode 100644 Seminar4/src/interfaces/movie/MovieInfo.ts create mode 100644 Seminar4/src/interfaces/review/ReviewCreateDto.ts create mode 100644 Seminar4/src/interfaces/review/ReviewInfo.ts create mode 100644 Seminar4/src/interfaces/review/ReviewResponseDto.ts create mode 100644 Seminar4/src/interfaces/school/SchoolInfo.ts create mode 100644 Seminar4/src/interfaces/user/UserCreateDto.ts create mode 100644 Seminar4/src/interfaces/user/UserInfo.ts create mode 100644 Seminar4/src/interfaces/user/UserResponseDto.ts create mode 100644 Seminar4/src/interfaces/user/UserUpdateDto.ts create mode 100644 Seminar4/src/loaders/db.ts create mode 100644 Seminar4/src/models/Movie.ts create mode 100644 Seminar4/src/models/Review.ts create mode 100644 Seminar4/src/models/User.ts create mode 100644 Seminar4/src/modules/responseMessage.ts create mode 100644 Seminar4/src/modules/statusCode.ts create mode 100644 Seminar4/src/modules/util.ts create mode 100644 Seminar4/src/routes/ReviewRouter.ts create mode 100644 Seminar4/src/routes/UserRouter.ts create mode 100644 Seminar4/src/routes/index.ts create mode 100644 Seminar4/src/services/ReviewService.ts create mode 100644 Seminar4/src/services/UserService.ts create mode 100644 Seminar4/src/services/index.ts create mode 100644 Seminar4/tsconfig.json diff --git a/Seminar4/.gitignore b/Seminar4/.gitignore new file mode 100644 index 0000000..a9ba020 --- /dev/null +++ b/Seminar4/.gitignore @@ -0,0 +1,146 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* +yarn.lock + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node \ No newline at end of file diff --git a/Seminar4/nodemon.json b/Seminar4/nodemon.json new file mode 100644 index 0000000..7e70d98 --- /dev/null +++ b/Seminar4/nodemon.json @@ -0,0 +1,11 @@ +{ + "watch": [ + "src", + ".env" + ], + "ext": "js,ts,json", + "ignore": [ + "src/**/*.spec.ts" + ], + "exec": "ts-node --transpile-only ./src/index.ts" +} \ No newline at end of file diff --git a/Seminar4/package.json b/Seminar4/package.json new file mode 100644 index 0000000..d132071 --- /dev/null +++ b/Seminar4/package.json @@ -0,0 +1,26 @@ +{ + "name": "node-typescript-init", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "nodemon", + "build": "tsc && node dist" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/express": "^4.17.13", + "@types/mongoose": "^5.11.97", + "@types/node": "^17.0.25", + "nodemon": "^2.0.15", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" + }, + "dependencies": { + "dotenv": "^16.0.0", + "express": "^4.17.3", + "express-validator": "^6.14.0", + "mongoose": "^6.3.1" + } +} diff --git a/Seminar4/src/config/index.ts b/Seminar4/src/config/index.ts new file mode 100644 index 0000000..4449be4 --- /dev/null +++ b/Seminar4/src/config/index.ts @@ -0,0 +1,23 @@ +import dotenv from "dotenv"; + +// Set the NODE_ENV to 'development' by default +process.env.NODE_ENV = process.env.NODE_ENV || "development"; + +const envFound = dotenv.config(); +if (envFound.error) { + // This error should crash whole process + + throw new Error("⚠️ Couldn't find .env file ⚠️"); +} + +export default { + /** + * Your favorite port + */ + port: parseInt(process.env.PORT as string, 10) as number, + + /** + * MongoDB URI + */ + mongoURI: process.env.MONGODB_URI as string, +}; \ No newline at end of file diff --git a/Seminar4/src/controllers/ReviewController.ts b/Seminar4/src/controllers/ReviewController.ts new file mode 100644 index 0000000..cc9c9f2 --- /dev/null +++ b/Seminar4/src/controllers/ReviewController.ts @@ -0,0 +1,53 @@ +import express, { Request, Response } from "express"; +import statusCode from "../modules/statusCode"; +import message from "../modules/responseMessage"; +import util from "../modules/util"; +import { ReviewCreateDto } from "../interfaces/review/ReviewCreateDto"; +import ReviewService from "../services/ReviewService"; +const { validationResult } = require('express-validator'); + +/** + * @route POST /review/movies/:movieId + * @desc Create Review + * @access Public + */ +const createReview = async (req: Request, res: Response) => { + const error = validationResult(req); + if (!error.isEmpty()) { + return res.status(statusCode.BAD_REQUEST).send(util.fail(statusCode.BAD_REQUEST, message.BAD_REQUEST)); + } + 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)); + } +} + +/** + * @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)); + } +} + +export default { + createReview, + getReviews +} \ No newline at end of file diff --git a/Seminar4/src/controllers/UserController.ts b/Seminar4/src/controllers/UserController.ts new file mode 100644 index 0000000..7768f9c --- /dev/null +++ b/Seminar4/src/controllers/UserController.ts @@ -0,0 +1,92 @@ +import express, { Request, Response } from "express"; +import { UserCreateDto } from "../interfaces/user/UserCreateDto"; +import statusCode from "../modules/statusCode"; +import message from "../modules/responseMessage"; +import util from "../modules/util"; +import { UserService } from "../services"; +import { UserUpdateDto } from "../interfaces/user/UserUpdateDto"; + +/** + * @route POST /user + * @desc Create User + * @access Public + */ +const createUser = async (req: Request, res: Response) => { + const userCreateDto: UserCreateDto = req.body; // User Create Dto 로 req.body 받아옴 + + try { + const data = await UserService.createUser(userCreateDto); + + res.status(statusCode.CREATED).send(util.success(statusCode.CREATED, message.CREATE_USER_SUCCESS, data)); + } catch (error) { + console.log(error); + // 서버 내부에서 오류 발생 + res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, message.INTERNAL_SERVER_ERROR)); + } +} + +/** + * @route PUT /user/:userId + * @desc Update User + * @access Public + */ +const updateUser = async (req: Request, res: Response) => { + const userUpdateDto: UserUpdateDto = req.body; + const { userId } = req.params; + + try { + await UserService.updateUser(userId, userUpdateDto); + + res.status(statusCode.NO_CONTENT).send(); + } catch (error) { + console.log(error); + res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, message.INTERNAL_SERVER_ERROR)); + } +} + +/** + * @route GET /user/:userId + * @desc READ User + * @access Public + */ +const findUserById = async (req: Request, res: Response) => { + const { userId } = req.params; + + try { + const data = await UserService.findUserById(userId); + + if (!data) { + return res.status(statusCode.NOT_FOUND).send(util.fail(statusCode.NOT_FOUND, message.NOT_FOUND)); + } + + res.status(statusCode.OK).send(util.success(statusCode.OK, message.CREATE_USER_SUCCESS, data)); + + } catch (error) { + console.log(error); + res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, message.INTERNAL_SERVER_ERROR)); + } +} + +/** + * @route DELETE /user/:userId + * @desc Delete User + * @access Public + */ +const deleteUser = async (req: Request, res: Response) => { + const { userId } = req.params; + try { + await UserService.delteUser(userId); + + res.status(statusCode.NO_CONTENT).send(); + } catch (e) { + console.log(e); + res.status(statusCode.INTERNAL_SERVER_ERROR).send(util.fail(statusCode.INTERNAL_SERVER_ERROR, message.INTERNAL_SERVER_ERROR)); + } +} + +export default { + createUser, + updateUser, + findUserById, + deleteUser +} \ No newline at end of file diff --git a/Seminar4/src/controllers/index.ts b/Seminar4/src/controllers/index.ts new file mode 100644 index 0000000..0c60fdb --- /dev/null +++ b/Seminar4/src/controllers/index.ts @@ -0,0 +1,5 @@ +import UserController from "./UserController" +// controller index file +export { + UserController +} \ No newline at end of file diff --git a/Seminar4/src/index.ts b/Seminar4/src/index.ts new file mode 100644 index 0000000..dfbec6b --- /dev/null +++ b/Seminar4/src/index.ts @@ -0,0 +1,43 @@ +import express, { Request, Response, NextFunction } from "express"; +import config from "./config"; +const app = express(); +import connectDB from "./loaders/db"; +import routes from './routes'; +require('dotenv').config(); + +connectDB(); + +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); + +app.use(routes); //라우터 +// error handler + +interface ErrorType { + message: string; + status: number; +} + +// 모든 에러 +app.use(function (err: ErrorType, req: Request, res: Response, next: NextFunction) { + + res.locals.message = err.message; + res.locals.error = req.app.get("env") === "production" ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render("error"); +}); + +app + .listen(config.port, () => { + console.log(` + ################################################ + 🛡️ Server listening on port 🛡️ + ################################################ + `); + }) + .on("error", (err) => { + console.error(err); + process.exit(1); + }); \ No newline at end of file diff --git a/Seminar4/src/interfaces/common/PostBaseResponseDto.ts b/Seminar4/src/interfaces/common/PostBaseResponseDto.ts new file mode 100644 index 0000000..6af2f99 --- /dev/null +++ b/Seminar4/src/interfaces/common/PostBaseResponseDto.ts @@ -0,0 +1,5 @@ +import mongoose from "mongoose"; + +export interface PostBaseResponseDto { + _id: mongoose.Schema.Types.ObjectId; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/movie/MovieInfo.ts b/Seminar4/src/interfaces/movie/MovieInfo.ts new file mode 100644 index 0000000..07734e7 --- /dev/null +++ b/Seminar4/src/interfaces/movie/MovieInfo.ts @@ -0,0 +1,7 @@ +export interface MovieInfo { + title: string; + director: string; + startDate: Date; + thumbnail: string; + story: string; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/review/ReviewCreateDto.ts b/Seminar4/src/interfaces/review/ReviewCreateDto.ts new file mode 100644 index 0000000..4c01a93 --- /dev/null +++ b/Seminar4/src/interfaces/review/ReviewCreateDto.ts @@ -0,0 +1,7 @@ +import mongoose from "mongoose"; + +export interface ReviewCreateDto { + writer: mongoose.Types.ObjectId; + title: string; + content: string; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/review/ReviewInfo.ts b/Seminar4/src/interfaces/review/ReviewInfo.ts new file mode 100644 index 0000000..a13df88 --- /dev/null +++ b/Seminar4/src/interfaces/review/ReviewInfo.ts @@ -0,0 +1,8 @@ +import mongoose from "mongoose"; + +export interface ReviewInfo { + writer: mongoose.Types.ObjectId; + movie: mongoose.Types.ObjectId; + title: string; + content: string; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/review/ReviewResponseDto.ts b/Seminar4/src/interfaces/review/ReviewResponseDto.ts new file mode 100644 index 0000000..eee1348 --- /dev/null +++ b/Seminar4/src/interfaces/review/ReviewResponseDto.ts @@ -0,0 +1,8 @@ +import { MovieInfo } from "../movie/MovieInfo"; + +export interface ReviewResponseDto { + writer: string; + movie: MovieInfo; + title: string; + content: string; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/school/SchoolInfo.ts b/Seminar4/src/interfaces/school/SchoolInfo.ts new file mode 100644 index 0000000..dff9d13 --- /dev/null +++ b/Seminar4/src/interfaces/school/SchoolInfo.ts @@ -0,0 +1,4 @@ +export interface SchoolInfo { + name: string; + major: string; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/user/UserCreateDto.ts b/Seminar4/src/interfaces/user/UserCreateDto.ts new file mode 100644 index 0000000..4ac313b --- /dev/null +++ b/Seminar4/src/interfaces/user/UserCreateDto.ts @@ -0,0 +1,9 @@ +import { SchoolInfo } from "../school/SchoolInfo"; + +export interface UserCreateDto { + name: string; + phone: string; + email: string; + age?: number; + school?: SchoolInfo; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/user/UserInfo.ts b/Seminar4/src/interfaces/user/UserInfo.ts new file mode 100644 index 0000000..be9057b --- /dev/null +++ b/Seminar4/src/interfaces/user/UserInfo.ts @@ -0,0 +1,9 @@ +import { SchoolInfo } from "../school/SchoolInfo"; + +export interface UserInfo { + name: string; + phone: string; + email: string; + age: number; + school: SchoolInfo; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/user/UserResponseDto.ts b/Seminar4/src/interfaces/user/UserResponseDto.ts new file mode 100644 index 0000000..587ba01 --- /dev/null +++ b/Seminar4/src/interfaces/user/UserResponseDto.ts @@ -0,0 +1,6 @@ +import mongoose from "mongoose"; +import { UserCreateDto } from "./UserCreateDto"; + +export interface UserResponseDto extends UserCreateDto { + _id: mongoose.Schema.Types.ObjectId; +} \ No newline at end of file diff --git a/Seminar4/src/interfaces/user/UserUpdateDto.ts b/Seminar4/src/interfaces/user/UserUpdateDto.ts new file mode 100644 index 0000000..8b9efec --- /dev/null +++ b/Seminar4/src/interfaces/user/UserUpdateDto.ts @@ -0,0 +1,10 @@ +import { SchoolInfo } from "../school/SchoolInfo"; + +export interface UserUpdateDto { + // update 들어올 수도 있고 아닐 수도 있음 -> optional + name?: string; + phone?: string; + email?: string; + age?: number; + school?: SchoolInfo; +} \ No newline at end of file diff --git a/Seminar4/src/loaders/db.ts b/Seminar4/src/loaders/db.ts new file mode 100644 index 0000000..d54dd46 --- /dev/null +++ b/Seminar4/src/loaders/db.ts @@ -0,0 +1,27 @@ +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("Review 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; diff --git a/Seminar4/src/models/Movie.ts b/Seminar4/src/models/Movie.ts new file mode 100644 index 0000000..c49765b --- /dev/null +++ b/Seminar4/src/models/Movie.ts @@ -0,0 +1,24 @@ +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("Movie", MovieSchema); \ No newline at end of file diff --git a/Seminar4/src/models/Review.ts b/Seminar4/src/models/Review.ts new file mode 100644 index 0000000..667d1f7 --- /dev/null +++ b/Seminar4/src/models/Review.ts @@ -0,0 +1,24 @@ +import mongoose from "mongoose"; +import { ReviewInfo } from "../interfaces/review/ReviewInfo"; + +const ReviewSchema = new mongoose.Schema({ + writer: { + type: mongoose.Types.ObjectId, + required: true, + ref: "User" + }, + movie: { + type: mongoose.Types.ObjectId, + required: true, + ref: "Movie" + }, + title: { + type: String, + required: true + }, + content: { + type: String + } +}); + +export default mongoose.model("Review", ReviewSchema); \ No newline at end of file diff --git a/Seminar4/src/models/User.ts b/Seminar4/src/models/User.ts new file mode 100644 index 0000000..cbee70f --- /dev/null +++ b/Seminar4/src/models/User.ts @@ -0,0 +1,27 @@ +import mongoose from "mongoose"; +import { UserInfo } from "../interfaces/user/UserInfo"; + +const UserSchema = new mongoose.Schema({ + name: { + type: String, + required: true + }, + phone: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + age: { + type: Number + }, + school: { + name: { type: String }, + major: { type: String } + } +}); + +export default mongoose.model("User", UserSchema); \ No newline at end of file diff --git a/Seminar4/src/modules/responseMessage.ts b/Seminar4/src/modules/responseMessage.ts new file mode 100644 index 0000000..f958491 --- /dev/null +++ b/Seminar4/src/modules/responseMessage.ts @@ -0,0 +1,18 @@ +const message = { + NULL_VALUE: '필요한 값이 없습니다.', + NOT_FOUND: '존재하지 않는 자원', + BAD_REQUEST: '잘못된 요청', + INTERNAL_SERVER_ERROR: '서버 내부 오류', + + // 유저 조회 + READ_USER_SUCCESS: '유저 조회 성공', + CREATE_USER_SUCCESS: '유저 생성 성공', + DELETE_USER_SUCCESS: '유저 삭제 성공', + UPDATE_USER_SUCCEss: '유저 삭제 성공', + + // 리뷰 + CREATE_REVIEW_SUCCESS: '리뷰 작성 성공', + READ_REVIEW_SUCCESS: '리뷰 조회 성공' +} + +export default message; \ No newline at end of file diff --git a/Seminar4/src/modules/statusCode.ts b/Seminar4/src/modules/statusCode.ts new file mode 100644 index 0000000..a8a29eb --- /dev/null +++ b/Seminar4/src/modules/statusCode.ts @@ -0,0 +1,15 @@ +const statusCode = { + OK: 200, + CREATED: 201, + NO_CONTENT: 204, + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + FORBIDDEN: 403, + NOT_FOUND: 404, + CONFLICT: 409, + INTERNAL_SERVER_ERROR: 500, + SERVICE_UNAVAILABLE: 503, + DB_ERROR: 600, +}; + +export default statusCode; \ No newline at end of file diff --git a/Seminar4/src/modules/util.ts b/Seminar4/src/modules/util.ts new file mode 100644 index 0000000..e3bc16e --- /dev/null +++ b/Seminar4/src/modules/util.ts @@ -0,0 +1,20 @@ +const util = { + success: (status: number, message: string, data?: any) => { + return { + status, + success: true, + message, + data, + }; + }, + fail: (status: number, message: string, data?: any) => { + return { + status, + success: false, + message, + data + }; + }, +}; + +export default util; \ No newline at end of file diff --git a/Seminar4/src/routes/ReviewRouter.ts b/Seminar4/src/routes/ReviewRouter.ts new file mode 100644 index 0000000..b8e36a1 --- /dev/null +++ b/Seminar4/src/routes/ReviewRouter.ts @@ -0,0 +1,15 @@ +import { Router } from "express"; +import ReviewController from "../controllers/ReviewController"; +import { body } from "express-validator/check"; + +const router: Router = Router(); + +router.post('/movies/:movieId', [ + body('title').notEmpty(), + body('writer').notEmpty(), + body('content').notEmpty() +], ReviewController.createReview); + +router.get('/movies/:movieId', ReviewController.getReviews); + +export default router; \ No newline at end of file diff --git a/Seminar4/src/routes/UserRouter.ts b/Seminar4/src/routes/UserRouter.ts new file mode 100644 index 0000000..19868d7 --- /dev/null +++ b/Seminar4/src/routes/UserRouter.ts @@ -0,0 +1,11 @@ +import { Router } from "express"; +import { UserController } from "../controllers"; + +const router: Router = Router(); + +router.post('/', UserController.createUser); +router.put('/:userId', UserController.updateUser); +router.get('/:userId', UserController.findUserById); +router.delete('/:userId', UserController.deleteUser); + +export default router; \ No newline at end of file diff --git a/Seminar4/src/routes/index.ts b/Seminar4/src/routes/index.ts new file mode 100644 index 0000000..aedd102 --- /dev/null +++ b/Seminar4/src/routes/index.ts @@ -0,0 +1,11 @@ +//router index file +import { Router } from 'express'; +import ReviewRouter from './ReviewRouter'; +import UserRouter from "./UserRouter"; + +const router: Router = Router(); + +router.use('/user', UserRouter); +router.use('/review', ReviewRouter); + +export default router; \ No newline at end of file diff --git a/Seminar4/src/services/ReviewService.ts b/Seminar4/src/services/ReviewService.ts new file mode 100644 index 0000000..f2331a2 --- /dev/null +++ b/Seminar4/src/services/ReviewService.ts @@ -0,0 +1,55 @@ +import { PostBaseResponseDto } from "../interfaces/common/PostBaseResponseDto"; +import { ReviewCreateDto } from "../interfaces/review/ReviewCreateDto"; +import { ReviewResponseDto } from "../interfaces/review/ReviewResponseDto"; +import Review from "../models/Review"; + +const createReview = async (movieId: string, reviewCreateDto: ReviewCreateDto): Promise => { + try { + const review = new Review({ + title: reviewCreateDto.title, + content: reviewCreateDto.content, + writer: reviewCreateDto.writer, + movie: movieId + }); + + await review.save(); + + const data = { + _id: review._id + }; + + return data; + } catch (error) { + console.log(error); + throw error; + } +} + +const getReviews = async (movieId: string): Promise => { + try { + const reviews = await Review.find({ + movie: movieId + }).populate('writer', 'name').populate('movie'); + + const data = await Promise.all(reviews.map(async (review: any) => { + const result = { + writer: review.writer.name, + movie: review.movie, + title: review.title, + content: review.content + }; + + return result; + })); + + return data; + } catch (error) { + console.log(error); + throw error; + } +} + +export default { + createReview, + getReviews +} \ No newline at end of file diff --git a/Seminar4/src/services/UserService.ts b/Seminar4/src/services/UserService.ts new file mode 100644 index 0000000..7371a8b --- /dev/null +++ b/Seminar4/src/services/UserService.ts @@ -0,0 +1,72 @@ +import { PostBaseResponseDto } from "../interfaces/common/PostBaseResponseDto"; +import { UserCreateDto } from "../interfaces/user/UserCreateDto"; +import { UserResponseDto } from "../interfaces/user/UserResponseDto"; +import { UserUpdateDto } from "../interfaces/user/UserUpdateDto"; +import User from "../models/User"; + +const createUser = async (userCreateDto: UserCreateDto): Promise => { + try { + const user = new User({ + name: userCreateDto.name, + phone: userCreateDto.phone, + email: userCreateDto.email, + age: userCreateDto.age, + school: { + name: userCreateDto.school?.name, + major: userCreateDto.school?.major + } + }); + + await user.save(); + + const data = { + _id: user.id + }; + + return data; + } catch (error) { + console.log(error); + throw error; + } +} + +const updateUser = async (userId: string, userUpdateDto: UserUpdateDto) => { + try { + // findByIdAndUpdate + await User.findByIdAndUpdate(userId, userUpdateDto); + } catch (error) { + console.log(error); + throw error; + } +} + +const findUserById = async (userId: string): Promise => { + try { + const user = await User.findById(userId); + + if (!user) { + return null; + } + + return user; + } catch (error) { + console.log(error); + throw error; + } +} + +const delteUser = async (userId: string): Promise => { + try { + await User.findByIdAndDelete(userId); + } catch (error) { + console.log(error); + throw error; + } +} + +export default { + createUser, + updateUser, + findUserById, + delteUser +} \ No newline at end of file diff --git a/Seminar4/src/services/index.ts b/Seminar4/src/services/index.ts new file mode 100644 index 0000000..0fa9cc2 --- /dev/null +++ b/Seminar4/src/services/index.ts @@ -0,0 +1,6 @@ +import UserService from "./UserService" + +//service index file +export { + UserService +} \ No newline at end of file diff --git a/Seminar4/tsconfig.json b/Seminar4/tsconfig.json new file mode 100644 index 0000000..0f8d5f5 --- /dev/null +++ b/Seminar4/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "es6", // 어떤 버전으로 컴파일 + "allowSyntheticDefaultImports": true, // default export가 없는 모듈에서 default imports를 허용 + "experimentalDecorators": true, // decorator 실험적 허용 + "emitDecoratorMetadata": true, // 데코레이터가 있는 선언에 대해 특정 타입의 메타 데이터를 내보내는 실험적인 지원 + "skipLibCheck": true, // 정의 파일 타입 체크 여부 + "moduleResolution": "node", // commonJS -> node 에서 동작 + "module": "commonjs", // import 문법 + "strict": true, // 타입 검사 엄격하게 + "pretty": true, // error 메시지 예쁘게 + "sourceMap": true, // 소스맵 파일 생성 -> .ts가 .js 파일로 트랜스 시 .js.map 생성 + "outDir": "./dist", // 트랜스 파일 (.js) 저장 경로 + "allowJs": true, // js 파일 ts에서 import 허용 + "esModuleInterop": true, // ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져올 수 있게 허용 + "typeRoots": [ + "./src/types/express.d.ts", // 타입(*.d.ts)파일을 가져올 디렉토리 설정 + "./node_modules/@types" // 설정 안할시 기본적으로 ./node_modules/@types + ] + }, + "include": [ + "./src/**/*" // build 시 포함 + ], + "exclude": [ + "node_modules", // build 시 제외 + "tests" + ] +} \ No newline at end of file