Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7주차] 기본 과제 제출 #6

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,452 changes: 5,452 additions & 0 deletions week4/package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions week4/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
"@types/express": "^4.17.14",
"@types/express-validator": "^3.0.0",
"@types/jsonwebtoken": "^8.5.9",
"@types/multer": "^1.4.7",
"@types/multer-s3": "^3.0.0",
"@types/node": "^18.11.9",
"nodemon": "^2.0.20"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.216.0",
"@prisma/client": "^4.6.0",
"bcryptjs": "^2.4.3",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"express-validator": "^6.14.2",
"jsonwebtoken": "^8.5.1",
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"prisma": "^4.6.0"
}
}
8 changes: 8 additions & 0 deletions week4/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ datasource db {
url = env("DATABASE_URL")
}

model Image {
id Int @id @default(autoincrement())
image String? @db.VarChar(800)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model User {
id Int @id @default(autoincrement())
userName String @db.VarChar(100)
age Int?
email String @db.VarChar(400)
password String @db.VarChar(400)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
26 changes: 26 additions & 0 deletions week4/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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) {
throw new Error("⚠️ Couldn't find .env file ⚠️");
}

export default {
port: parseInt(process.env.PORT as string, 10) as number,

//? 데이터베이스
database: process.env.DATABASE_URL as string,

//? JWT
jwtSecret: process.env.JWT_SECRET as string,
jwtAlgo: process.env.JWT_ALGO as string,

//? AWS
s3AccessKey: process.env.S3_ACCESS_KEY as string,
s3SecretKey: process.env.S3_SECRET_KEY as string,
bucketName: process.env.S3_BUCKET as string,
};
12 changes: 12 additions & 0 deletions week4/src/config/s3Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { S3Client } from "@aws-sdk/client-s3";
import config from ".";

const s3: S3Client = new S3Client({
region: "ap-northeast-2",
credentials: {
accessKeyId: config.s3AccessKey,
secretAccessKey: config.s3SecretKey,
},
});

export default s3;
7 changes: 7 additions & 0 deletions week4/src/constants/responseMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default {
DELETE_USER_FAIL: "유저 탈퇴 실패",
NO_USER: "탈퇴했거나 가입하지 않은 유저입니다.",
INVALID_PASSWORD: "잘못된 비밀번호입니다.",
SEARCH_USER_SUCCESS : "유저 검색 성공",
SEARCH_USER_FAIL : "유저 검색 실패",

// 토큰
CREATE_TOKEN_SUCCESS: "토큰 재발급 성공",
Expand All @@ -30,4 +32,9 @@ export default {

// 서버 내 오류
INTERNAL_SERVER_ERROR: "서버 내 오류",

//이미지
NO_IMAGE:"이미지가 없습니다.",
CREATE_IMAGE_SUCCESS:"이미지 저장 성공",
CREATE_IMAGE_FAIL:"이미지 저장 실패",
};
26 changes: 26 additions & 0 deletions week4/src/controller/imageController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { fail, success } from './../constants/response';
import { Request, Response } from "express";
import { imageService } from "../service";
import { rm, sc } from '../constants';
// 이미지 업로드 api
const uploadImage= async(req: Request, res:Response) => {
const image : Express.MulterS3.File = req.file as Express.MulterS3.File;
const {location}=image;
if(!location){
res.status(sc.BAD_REQUEST).send(fail(sc.BAD_REQUEST, rm.NO_IMAGE));
}

const data=await imageService.uploadImage(location);

if(!data){
res.status(sc.BAD_REQUEST).send(fail(sc.BAD_REQUEST, rm.CREATE_IMAGE_FAIL));
}
res.status(sc.OK).send(success(sc.OK, rm.CREATE_IMAGE_SUCCESS, data));

}

const imageController = {
uploadImage
}

export default imageController;
1 change: 1 addition & 0 deletions week4/src/controller/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as userController } from "./userController";
export { default as imageController } from "./imageController";
18 changes: 16 additions & 2 deletions week4/src/controller/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const getUserById = async (req: Request, res: Response) => {

//* 전체 유저 조회
const getAllUser = async(req:Request, res:Response) => {
const data=await userService.getAllUser();
const {page, limit}= req.query;
const data=await userService.getAllUser(Number(page), Number(limit));

return res.status(sc.OK).json({status:sc.OK,message:rm.READ_ALL_USERS_SUCCESS,data});
}
Expand Down Expand Up @@ -102,8 +103,21 @@ const signInUser = async (req: Request, res: Response) => {
}
};

//* Get ! api/user/?keyword=이름
const searchUserByName = async (req:Request, res:Response) => {
const {keyword, option} = req.query;

const data= await userService.searchUserByName(keyword as string, option as string);

if(!data){
return res.status(sc.BAD_REQUEST).send(fail(sc.BAD_REQUEST, rm.SEARCH_USER_FAIL));
}
return res.status(sc.OK).send(success(sc.OK, rm.SEARCH_USER_SUCCESS, data));
}


const userController = {
getUserById, getAllUser, createUser, updateUser, deleteUser, signInUser
getUserById, getAllUser, createUser, updateUser, deleteUser, signInUser, searchUserByName
};

export default userController;
4 changes: 4 additions & 0 deletions week4/src/interface/ImageCreateResponseDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ImageCreateResponseDTO{
id:number;
image:string;
};
3 changes: 2 additions & 1 deletion week4/src/middlewares/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export {default as auth} from "./auth";
export {default as auth} from "./auth";
export {default as upload} from "./upload";
22 changes: 22 additions & 0 deletions week4/src/middlewares/upload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import multer from "multer";
import multerS3 from "multer-s3";
import config from "../config";
import s3 from "../config/s3Config";

//? 미들웨어로 사용할 multer 모듈
const upload = multer({
//? 실질적인 storage 는 multerS3 이용해 aws s3 로 설정
storage: multerS3({
s3: s3,
bucket: config.bucketName, //? s3 bucket name 지정
contentType: multerS3.AUTO_CONTENT_TYPE, //? mimetype 은 자동으로 설정
acl: "public-read", // Access control for the file

//? key는 파일 이름을 지정. 버킷 내 같은 이름의 파일은 같은 파일로 인식하기 때문에 Unique하도록!
key: function (req: Express.Request, file: Express.MulterS3.File, cb) {
cb(null, `${Date.now()}_${file.originalname}`);
},
}),
});

export default upload;
9 changes: 9 additions & 0 deletions week4/src/router/imageRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Router } from "express";
import { imageController } from "../controller";
import {upload, auth} from "../middlewares";
const router: Router = Router();

router.post("/", upload.single("file") ,imageController.uploadImage); //단일 파일 -> single, 여러 파일 -> arrays


export default router;
3 changes: 2 additions & 1 deletion week4/src/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Router } from "express";
import userRouter from "./userRouter";

import imageRouter from "./imageRouter";
const router: Router = Router();

router.use("/user", userRouter);
router.use("/image",imageRouter);

export default router;
12 changes: 6 additions & 6 deletions week4/src/router/userRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,14 @@ const router: Router = Router();

router.get("/:userId", auth, userController.getUserById);

//* 유저 생성
router.post("/",userController.createUser);

//* 전체 유저 조회
router.get("/",userController.getAllUser);
router.get("/",auth, userController.getAllUser);

//* 유저 수정
router.patch("/:userId",userController.updateUser);
router.patch("/:userId",auth, userController.updateUser);

//* 유저 삭제
router.delete("/:userId",userController.deleteUser);
router.delete("/:userId",auth, userController.deleteUser);

//* 유저 생성 - POST api/user
router.post(
Expand All @@ -25,6 +22,9 @@ router.post(
userController.createUser
);

//* 이름으로 유저 찾기 /search/?keyword={}
router.get("/search", userController.searchUserByName);

router.post(
"/signin",
[
Expand Down
24 changes: 24 additions & 0 deletions week4/src/service/imageService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ImageCreateResponseDTO } from './../interface/ImageCreateResponseDTO';
import { PrismaClient } from "@prisma/client";

const prisma=new PrismaClient();

// 이미지 업로드
const uploadImage = async(location: string): Promise<ImageCreateResponseDTO> => {
const data= await prisma.image.create({
data: {
image: location
}
});
const result: ImageCreateResponseDTO = {
id: data.id,
image: data.image as string
};
return result;
}

const imageService = {
uploadImage
};

export default imageService;
1 change: 1 addition & 0 deletions week4/src/service/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as userService } from "./userService";
export { default as imageService } from "./imageService";
41 changes: 39 additions & 2 deletions week4/src/service/userService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ const signIn = async (userSignInDto: UserSignInDTO) => {
};

//* 유저 전체 조회
const getAllUser = async () => {
const data = await prisma.user.findMany();
const getAllUser = async (page:number, limit:number) => {
const data = await prisma.user.findMany({
skip: (page-1)*limit,
take: limit,
});
return data;
};

Expand Down Expand Up @@ -83,13 +86,47 @@ const getUserById = async (userId: number) => {
return user;
};

const searchUserByName = async(keyword: string, option:string) => {

//? 유저 최신순
if(option=='desc'){
const data= await prisma.user.findMany({
where:{
userName: {
contains: keyword
}
},
orderBy: {
createdAt: 'desc'
}
});
return data;
}

//? 유저 오래된순
if(option=='asc'){
const data= await prisma.user.findMany({
where:{
userName: {
contains: keyword
}
},
orderBy : {
createdAt: 'asc'
}
});
return data;
}
}

const userService = {
createUser,
signIn,
getAllUser,
updateUser,
deleteUser,
getUserById,
searchUserByName
};

export default userService;
Loading