
2025.06.17
오늘한 내용 : 게시글/댓글 작성 및 회원가입·로그인 api 구현
WEEK 14 : 실력 다지기 - 백엔드 & DB
| 모델 | 필드명 | 타입 | 설명 |
|---|---|---|---|
| User | username | String | 고유 아이디 (trim + unique) |
password | String | 비밀번호 (plain, 추후 해싱) | |
| Post | title | String | 게시글 제목 |
content | String | 게시글 본문 | |
author | String | 작성자 이름 (username) | |
comments | Array | 댓글 배열 (중첩 문서) | |
| Comment | author | String | 댓글 작성자 |
content | String | 댓글 내용 | |
createdAt | Date | 댓글 생성 시간 |
// models/Post.js
const mongoose = require("mongoose");
// 댓글 스키마 (Post에 포함됨)
const commentSchema = new mongoose.Schema({
author: { type: String, required: true }, // 댓글 작성자
content: { type: String, required: true }, // 댓글 내용
createdAt: { type: Date, default: Date.now }, // 생성 시간
});
// 게시글 스키마
const postSchema = new mongoose.Schema(
{
title: { type: String, required: true },
content: { type: String, required: true },
author: { type: String, required: true }, // 게시글 작성자
comments: [commentSchema], // 중첩된 댓글 배열
},
{ timestamps: true }
);
module.exports = mongoose.model("Post", postSchema);
// models/User.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
unique: true, // 중복 방지
trim: true, // 공백 자동 제거
},
password: {
type: String,
required: true,
},
},
{ timestamps: true } // createdAt, updatedAt 자동 생성
);
module.exports = mongoose.model("User", userSchema);
// controllers/userController.js
const User = require("../models/User");
// 회원가입
exports.signup = async (req, res) => {
const { username, password } = req.body;
try {
const existing = await User.findOne({ username });
if (existing) {
return res.status(409).json({ message: "이미 존재하는 사용자입니다." });
}
const newUser = new User({ username, password }); // 나중에 해싱 필요
await newUser.save();
res.status(201).json({ message: "회원가입 성공", username });
} catch (err) {
res.status(500).json({ message: "회원가입 실패", error: err.message });
}
};
// 로그인
exports.login = async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user || user.password !== password) {
return res
.status(401)
.json({ message: "아이디 또는 비밀번호가 틀렸습니다." });
}
// 로그인 성공 → 토큰 발급은 나중에
res.json({ message: "로그인 성공", username });
} catch (err) {
res.status(500).json({ message: "로그인 실패", error: err.message });
}
};
const Post = require("../models/Post");
// 전체 게시글 조회
exports.getAllPosts = async (req, res) => {
try {
const posts = await Post.find().sort({ createdAt: -1 }); // 최신순 정렬
res.json(posts);
} catch (err) {
res.status(500).json({ message: "게시글 조회 실패", error: err.message });
}
};
// 게시글 작성
exports.createPost = async (req, res) => {
const { title, content, author } = req.body;
try {
const newPost = new Post({ title, content, author });
const savedPost = await newPost.save();
res.status(201).json(savedPost);
} catch (err) {
res.status(500).json({ message: "게시글 작성 실패", error: err.message });
}
};
// 게시글 상세 조회
exports.getPostById = async (req, res) => {
const { id } = req.params;
try {
const post = await Post.findById(id);
if (!post)
return res.status(404).json({ message: "게시글을 찾을 수 없습니다." });
res.json(post);
} catch (err) {
res.status(500).json({ message: "게시글 조회 실패", error: err.message });
}
};
// 게시글 삭제
exports.deletePost = async (req, res) => {
const { id } = req.params;
try {
await Post.findByIdAndDelete(id);
res.status(204).send(); // No Content
} catch (err) {
res.status(500).json({ error: "게시글 삭제 실패" });
}
};
// 댓글 추가
exports.addComment = async (req, res) => {
const { id } = req.params; // 게시글 ID
const { author, content } = req.body;
try {
const post = await Post.findById(id);
if (!post)
return res.status(404).json({ message: "게시글을 찾을 수 없습니다." });
const newComment = { author, content, createdAt: new Date() };
post.comments.push(newComment);
await post.save();
res.status(201).json(newComment);
} catch (err) {
res.status(500).json({ message: "댓글 등록 실패", error: err.message });
}
};
// 댓글 삭제
exports.deleteComment = async (req, res) => {
const { id: postId, commentId } = req.params;
try {
const post = await Post.findById(postId);
if (!post)
return res.status(404).json({ message: "게시글을 찾을 수 없습니다." });
// 댓글 필터링
const originalCount = post.comments.length;
post.comments = post.comments.filter((c) => c._id.toString() !== commentId);
if (post.comments.length === originalCount) {
return res.status(404).json({ message: "댓글을 찾을 수 없습니다." });
}
await post.save();
res.status(204).send(); // No Content
} catch (err) {
console.error("댓글 삭제 실패:", err);
res.status(500).json({ message: "댓글 삭제 실패" });
}
};
// routes/posts.js
const express = require("express");
const router = express.Router();
const postController = require("../controllers/postController");
// 전체 게시글 조회
router.get("/", postController.getAllPosts);
// 게시글 작성
router.post("/", postController.createPost);
// 게시글 상세 조회
router.get("/:id", postController.getPostById);
// 게시글 삭제S
router.delete("/:id", postController.deletePost);
// 댓글 추가
router.post("/:id/comments", postController.addComment);
// 댓글 삭제
router.delete("/:id/comments/:commentId", postController.deleteComment);
module.exports = router;
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");
// 회원 가입
router.post("/signup", userController.signup);
// 로그인
router.post("/login", userController.login);
module.exports = router;
// backend/index.js
// .env 파일을 불러와서 process.env로 사용할 수 있게 함
require("dotenv").config();
const mongoose = require("mongoose");
const express = require("express");
const cors = require("cors");
const app = express();
// 환경 변수에서 PORT 불러오고, 없으면 5000 사용
const PORT = process.env.PORT || 5000;
// JSON 요청 파싱
app.use(
cors({
origin: "http://localhost:5173", // 프론트 주소
credentials: true,
})
);
app.use(express.json());
// DB 연결
mongoose
.connect(process.env.MONGO_URI)
.then(() => console.log("✅ MongoDB connected"))
.catch((err) => console.error("❌ MongoDB connection error:", err));
// 라우터 불러오기
const userRoutes = require("./routes/users");
const postRouter = require("./routes/posts");
app.use("/api/users", userRoutes);
app.use("/api/posts", postRouter);
// 서버 시작
app.listen(PORT, () => {
console.log(`✅ Backend running at http://localhost:${PORT}`);
});
Bloodmoney Game is a dark-humor / horror clicker game (short indie experience) in which you need to raise $25,000 for an operation by repeatedly clicking to get money — but the upgrades and choices push the game into disturbing, violent, and fourth-wall-breaking territory.