Skip to content
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
27 changes: 0 additions & 27 deletions .github/pull-request-template.md

This file was deleted.

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
248 changes: 248 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import Product from "./models/Product.js";
import express from "express";
import cors from "cors";
import { Sequelize, DataTypes } from "sequelize";
import Article from "./models/Article.js";
import Comment from "./models/Comments.js";
import sequelize from "./database.js";

const allowedOrigins = [
"http://localhost:3000",
"https://db-1-45k6.onrender.com",
];
const PORT = 8000;
const app = express();
app.use(express.json());
app.use(
cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error("에러"));
}
},
methods: ["GET", "POST", "PATCH", "DELETE"],
credentials: true,
})
);

sequelize
.authenticate()
.then(() => console.log("PostgreSQL 연결 성공!!!!!!!!!"))
.catch((err) => console.error("PostgreSQL 연결 실패ㅠㅠㅠ :", err));

sequelize
.sync({ alter: true })
.then(() => console.log("데이터베이스 테이블 동기화 완료"))
.catch((err) => console.error("테이블 동기화 중 오류 발생:", err));

app.get("/", (req, res) => res.send("API 동작"));

//상품등록 API
app.post("/api/products", async (req, res) => {
const { name, description, price, tags } = req.body;
if (!name || !description || !price || !tags) {
return res.status(400).send("name, description, price, tags는 필수입니다.");
}

try {
const newProduct = await Product.create({
name,
description,
price,
tags,
});

res.status(201).send(newProduct);
} catch (error) {
res.status(500).send({ message: "상품 등록 실패", error });
}
});

//상품조회
app.get("/api/products", async (req, res) => {
const { page = 1, limit = 10, search } = req.query;

try {
const whereClause = search
? {
[Sequelize.Op.or]: [
{ name: { [Sequelize.Op.iLike]: `%${search}%` } },
{ description: { [Sequelize.Op.iLike]: `%${search}%` } },
],
}
: {};

const products = await Product.findAndCountAll({
where: whereClause,
order: [["id", "DESC"]],
offset: (page - 1) * limit,
limit: parseInt(limit),
});

res.status(200).send({
products: products.rows,
total: products.count,
page: parseInt(page),
totalPages: Math.ceil(products.count / limit),
});
} catch (error) {
res.status(500).send({ message: "상품을 불러오지 못했습니다.", error });
}
});

//상품 수정
app.patch("/api/products/:id", async (req, res) => {
try {
const updates = req.body;
updates.updatedAt = new Date();
const product = await Product.findByIdAndUpdate(req.params.id, updates, {
new: true,
});
if (!product)
return res.status(404).send({ message: "상품을 찾을 수 없습니다." });
res.status(200).send(product);
} catch (error) {
res.status(500).send({ message: "상품 수정 실패", error });
}
});

//상품삭제
app.delete("/api/products/:id", async (req, res) => {
try {
const product = await Product.findByIdAndDelete(req.params.id);
if (!product)
return res.status(404).send({ message: "상품을 찾을 수 없습니다." });
res.status(200).send({ message: "상품이 삭제되었습니다." });
} catch (error) {
res.status(500).send({ message: "상품 삭제 실패", error });
}
});

//상품 목록 조회
app.get("/api/products", async (req, res) => {
try {
const { search, page = 1, limit = 10, sort = "recent" } = req.query;

const query = search
? {
$or: [
{ name: new RegExp(search, "i") },
{ description: new RegExp(search, "i") },
],
}
: {};

const products = await Product.find(query)
.sort(sort === "recent" ? { createdAt: -1 } : {})
.skip((page - 1) * limit)
.limit(Number(limit));

const total = await Product.countDocuments(query);

res.status(200).send({ total, products });
} catch (error) {
res.status(500).send({ message: "상품 목록 조회 실패", error });
}
});

//게시글 등록
app.post("/api/articles", async (req, res) => {
const { title, content } = req.body;
if (!title || !content) {
return res.status(400).send("title과 content는 필수입니다.");
}

try {
const article = await Article.create({ title, content });
res.status(201).send(article);
} catch (error) {
res.status(500).send({ message: "게시글 등록 실패", error });
}
});

// 게시글 조회
app.get("/api/articles", async (req, res) => {
try {
const articles = await Article.findAll({ order: [["createdAt", "DESC"]] });
res.status(200).send(articles);
} catch (error) {
res.status(500).send({ message: "게시글 조회 실패", error });
}
});

//게시글 수정
app.patch("/api/articles/:id", async (req, res) => {
const { id } = req.params;
const updates = req.body;

try {
const article = await Article.update(updates, { where: { id } });
if (!article)
return res.status(404).send({ message: "게시글을 찾을 수 없습니다." });
res.status(200).send({ message: "게시글 수정 완료" });
} catch (error) {
res.status(500).send({ message: "게시글 수정 실패", error });
}
});

//게시글 삭제
app.delete("/api/articles/:id", async (req, res) => {
const { id } = req.params;

try {
const result = await Article.destroy({ where: { id } });
if (!result)
return res.status(404).send({ message: "게시글을 찾을 수 없습니다." });
res.status(200).send({ message: "게시글 삭제 완료" });
} catch (error) {
res.status(500).send({ message: "게시글 삭제 실패", error });
}
});

//댓글 등록
app.post("/api/articles/:articleId/comments", async (req, res) => {
const articleId = parseInt(req.params.articleId, 10);
const { content } = req.body;

try {
const article = await Article.findByPk(articleId);
if (!article) {
return res.status(404).send({ message: "게시글을 찾을 수 없습니다." });
}

const comment = await Comment.create({
content,
articleId,
});

res.status(201).send(comment);
} catch (error) {
res.status(500).send({ message: "등록 실패", error });
}
});

//댓글 조회
app.get("/api/articles/:articleId/comments", async (req, res) => {
const articleId = parseInt(req.params.articleId, 10);

try {
const comments = await Comment.findAll({
where: { articleId },
order: [["createdAt", "DESC"]],
});

if (comments.length === 0) {
return res.status(404).send({ message: "댓글이 없습니다." });
}

res.status(200).send(comments);
} catch (error) {
res.status(500).send({ message: "조회 실패", error });
}
});

app.listen(PORT, () => {
console.log(`서버가 http://localhost:${PORT} 에서 실행 중`);
});
10 changes: 10 additions & 0 deletions database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Sequelize } from "sequelize";
import dotenv from "dotenv";

dotenv.config();

const sequelize = new Sequelize(process.env.DATABASE_URL, {
dialect: "postgres",
logging: false,
});
export default sequelize;
22 changes: 22 additions & 0 deletions models/Article.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DataTypes } from "sequelize";
import sequelize from "../database.js";
import Comment from "./Comments.js";

const Article = sequelize.define(
"Article",
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
title: { type: DataTypes.STRING, allowNull: false },
content: { type: DataTypes.TEXT, allowNull: false },
createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
},
{
freezeTableName: true,
}
);

Article.hasMany(Comment, { foreignKey: "articleId", onDelete: "CASCADE" });
Comment.belongsTo(Article, { foreignKey: "articleId" });

export default Article;
19 changes: 19 additions & 0 deletions models/Comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DataTypes } from "sequelize";
import sequelize from "../database.js";

const Comment = sequelize.define(
"Comment",
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
content: { type: DataTypes.TEXT, allowNull: false },
articleId: { type: DataTypes.INTEGER, allowNull: false },
createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
},
{
freezeTableName: true,
timestamps: true,
}
);

export default Comment;
19 changes: 19 additions & 0 deletions models/Product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DataTypes } from "sequelize";
import sequelize from "../database.js";

const Product = sequelize.define(
"Product",
{
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false },
description: { type: DataTypes.TEXT, allowNull: false },
price: { type: DataTypes.FLOAT, allowNull: false },
tags: { type: DataTypes.ARRAY(DataTypes.STRING) },
createdAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
updatedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
},
{
freezeTableName: true,
}
);
export default Product;
Loading