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

Express 김세환 sprint7 #6

Open
wants to merge 4 commits into
base: express-김세환
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
32 changes: 32 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Node.js 기본
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# 환경 변수 파일
.env
env.js

# 빌드 디렉터리
dist/
build/

# OS 및 편집기 파일
.DS_Store
Thumbs.db

# IDE/Editor 관련
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln

# 로그 파일
*.log

# 테스트 관련
coverage/
requests.http
13 changes: 13 additions & 0 deletions Dockerfile
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. 배포를 docker로 하시는건가요? 더할나위없이 좋습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:18

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
117 changes: 117 additions & 0 deletions article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import * as dotenv from 'dotenv';
dotenv.config();
import express from 'express';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

const app = express();
app.use(express.json());

function asyncHandler(handler) {
return async function (req, res) {
try {
await handler(req, res);

} catch (err) {
if (err.name === 'ValidationError') {
res.status(400).send({ message: err.message });
} else if (err.name === 'CastError') {
res.status(404).send({ message: 'article not found' });
} else {
res.status(500).send({ message: err.message });
}
}
}
}

// app.get('/articles', asyncHandler(async (req, res) => {
// const articles = await prisma.article.findMany();
// res.json(articles);
// }));

app.get('/articles', asyncHandler(async (req, res) => {
const { sort, page = 1, limit = 10, search = "" } = req.query;

const skip = (page - 1) * limit;

const searchQuery = {
OR: [
{ title: { contains: search, mode: 'insensitive' } },
{ content: { contains: search, mode: 'insensitive' } }
]
};

const sortOptions = {
createdAt: sort === 'recent' ? 'desc' : 'asc'
};

const articles = await prisma.article.findMany({
where: searchQuery,
orderBy: sortOptions,
skip: skip,
take: parseInt(limit),
select: {
id: true,
title: true,
content: true,
createdAt: true
}
});

const total = await prisma.article.count({
where: searchQuery
});

res.send({
total,
page: parseInt(page),
limit: parseInt(limit),
articles,
});
}));

app.get('/articles/:id', asyncHandler(async (req, res) => {
const { id } = req.params;
const article = await prisma.article.findUnique({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findUniqueOrThrow 를 해주시면 바로 validation 에러를 내뱉어줘서 에러 핸들링이 더 쉽습니다~ 현재는 없을때 서버에러가 날거라서요!

where: { id },
});
res.json(article);
}));

app.post('/articles', asyncHandler(async (req, res) => {
const { title, content } = req.body;
const article = await prisma.article.create({
data: {
title,
content,
},
});
res.json(article);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create의 경우 201 상태코드를 같이 내려주는게 좋을것 같아요

}));

app.patch('/articles/:id', asyncHandler(async (req, res) => {
const { id } = req.params;
const { title, content } = req.body;
const article = await prisma.article.update({
where: { id },
data: {
title,
content,
},
});
res.json(article);
}));

app.delete('/articles/:id', asyncHandler(async (req, res) => {
const { id } = req.params;
const article = await prisma.article.delete({
where: { id },
});
res.json(article);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete의 경우 204 상태코드와 함께 보통 빈 응답을 내려줍니다 ㅎㅎ

}));

const runningPort = process.env.PORT || 3000;
app.listen(runningPort, () => {
console.log(`Server is running on http://localhost:${runningPort}`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runningPort로 까지는 안빼도 될것 같아요~ ${process.env.PORT || 3000} 요렇게 해주시면 될것 같습니다!

});
123 changes: 123 additions & 0 deletions data/mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const data = {
list: [
{
name: "테스트",
description: "상품 등록 테스트",
price: 16,
tags: ["테스트", "테스트입니다"],
images: [],
ownerId: 496,
favoriteCount: 0,
createdAt: "2024-12-04T12:38:15.916Z",
updatedAt: "2024-12-04T12:38:15.916Z",
},
{
name: "살찐 고양이",
description: "파는건 아니에요",
price: 3333333,
tags: ["고양이", "거북목", "집사"],
images: [
"https://sprint-fe-project.s3.ap-northeast-2.amazonaws.com/Sprint_Mission/user/466/1733310657338/KakaoTalk_Photo_2024-12-03-15-08-50.jpeg",
],
ownerId: 466,
favoriteCount: 1,
createdAt: "2024-12-04T11:10:57.618Z",
updatedAt: "2024-12-04T11:13:04.951Z",
},
{
name: "네모박스",
description: "네모박스팔아요",
price: 0,
tags: ["1", "2"],
images: [
"https://sprint-fe-project.s3.ap-northeast-2.amazonaws.com/Sprint_Mission/user/493/1733242431404/Group%2033744.png",
],
ownerId: 493,
favoriteCount: 0,
createdAt: "2024-12-03T16:13:52.399Z",
updatedAt: "2024-12-03T16:13:52.399Z",
},
{
name: "상품 이름",
description: "string",
price: 0,
tags: ["전자제품"],
images: ["https://example.com/..."],
ownerId: 19,
favoriteCount: 0,
createdAt: "2024-11-30T08:22:16.098Z",
updatedAt: "2024-11-30T08:22:16.098Z",
},
{
name: "상품 이름",
description: "string",
price: 0,
tags: ["전자제품"],
images: ["https://example.com/..."],
ownerId: 19,
favoriteCount: 0,
createdAt: "2024-11-30T08:21:11.338Z",
updatedAt: "2024-11-30T08:21:11.338Z",
},
{
name: "상품 이름",
description: "string",
price: 0,
tags: ["전자제품"],
images: ["https://example.com/..."],
ownerId: 479,
favoriteCount: 0,
createdAt: "2024-11-28T14:39:07.254Z",
updatedAt: "2024-12-04T10:35:40.507Z",
},
{
name: "상품 이름테스트",
description: "string",
price: 0,
tags: ["전자제품"],
images: ["https://example.com/..."],
ownerId: 475,
favoriteCount: 1,
createdAt: "2024-11-26T06:52:13.772Z",
updatedAt: "2024-12-03T06:06:18.058Z",
},
{
name: "jetbrain",
description: "jetbrain mono",
price: 123456,
tags: [],
images: [],
ownerId: 464,
favoriteCount: 0,
createdAt: "2024-11-20T12:47:01.832Z",
updatedAt: "2024-11-20T12:47:01.832Z",
},
{
name: "상품 이름",
description: "string",
price: 1000,
tags: ["전자제품"],
images: [],
ownerId: 462,
favoriteCount: 0,
createdAt: "2024-11-14T11:09:01.391Z",
updatedAt: "2024-11-14T11:09:01.391Z",
},
{
name: "노이미지 상품",
description: "노이미지 상품 테스트",
price: 1000,
tags: ["no", "image"],
images: [
"https://sprint-fe-project.s3.ap-northeast-2.amazonaws.com/Sprint_Mission/user/462/1731555173279/Codeit_FE9.png",
],
ownerId: 462,
favoriteCount: 1,
createdAt: "2024-11-14T03:32:58.831Z",
updatedAt: "2024-11-14T03:33:15.885Z",
},
],
totalCount: 182,
};

export default data;
11 changes: 11 additions & 0 deletions data/seed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mongoose from 'mongoose';
import data from './mock.js';
import Product from '../models/Product.js';
import { DATABASE_URL } from '../env.js';

mongoose.connect(DATABASE_URL);

await Product.deleteMany({});
await Product.insertMany(data.list);

mongoose.connection.close();
33 changes: 33 additions & 0 deletions http/request_article.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
GET http://localhost:4000/articles?sort=recent&limit=10&page=1&search=PS5

###

GET http://https://backend-c2ut.onrender.com/articles?sort=oldest&limit=10&page=1&search=PS5

###

GET http://localhost:4000/articles/cm4skodgm00036u331w9xxd95
###

POST http://localhost:4000/articles
Content-Type: application/json

{
"title": "PS5 30주년 버전 팝니다13",
"content": "PS5 30주년 버전 팝니다13"
}

###

PATCH http://localhost:4000/articles/cm4sl1iwu0001a96yr1ry9yu8
Content-Type: application/json

{
"title": "PS5 30주년 버전 팝니다2 -> 3",
"content": "PS5 30주년 버전 팝니다2 -> 3"
}

###

DELETE http://localhost:4000/articles/cm4sl1iwu0001a96yr1ry9yu8

38 changes: 38 additions & 0 deletions http/request_product.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
GET http://localhost:3001/products?sort=oldest&limit=10&page=1&search=PS5

###

GET http://https://backend-c2ut.onrender.com/products?sort=oldest&limit=10&page=1&search=PS5

###

GET http://localhost:3000/products/6753d92d0ba17d1e827bb
###

POST http://localhost:3000/product
Content-Type: application/json

{
"name": "PS5 30주년 버전 팝니당",
"description": "PS5 30주년 버전 팝니다",
"price": 700000,
"images": ["https://img.youtube.com/vi/NULUo-7gNAI/maxresdefault.jpg"]
}

###

PATCH http://localhost:3000/products/6753d9080ba17d1e82ef97b9
Content-Type: application/json

{
"name": "PS5 30주년 버전 팝니당",
"description": "PS5 30주년 버전 팝니다",
"price": 600000,
"images": ["https://img.youtube.com/vi/NULUo-7gNAI/maxresdefault.jpg"],
"tags": ["30주년", "PS5", "급처"]
}

###

DELETE http://localhost:3000/products/6753d9080ba17d1e82ef97b9

Loading
Loading