프로젝트 마무리하기
이제 팔로잉 기능 과 해시태그 검색 기능만 추가하면 됩니다. 다른 사용자를 팔로우하는 기능을 만들기 위해 routes/user.js를 작성합니다.
const express = require("express");
const { isLoggedIn } = require("./middlewares");
const User = require("../models/user");
const router = express.Router();
router.post("/:id/follow", isLoggedIn, async (req, res, next) => {
try {
const user = await User.findOne({ where: { id: req.user.id } });
if (user) {
await user.addFollowing(parseInt(req.params.id, 10));
res.send("success");
} else {
res.status(400).send("no user");
}
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;
POST /user/:id/follow 라우터입니다. :id 부분이 req.params.id가 됩니다. 먼저 팔로우할 사용자를 데이터베이스에서 조회한 후, 시퀄라이즈에서 추가한 addFollowing 메서드로 현재 로그인한 사용자와의 관계를 지정합니다.
팔로잉 관계가 생겼으므로 req.user에도 팔로워와 팔로잉 목록을 저장합니다. 앞으로 사용자 정보를 불러올 때는 팔로워와 팔로잉 목록도 같이 불러오게 됩니다. req.user를 바꾸러면 deserializeUser롤 조작해야 합니다.
const passport = require("passport");
const local = require("localStrategy");
const kakao = require("./kakaoStrategy");
const User = require("./models/user");
module.exports = () => {
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findOne({
where: { id },
include: [
{
model: User,
attributes: ["id", "nick"],
as: "Followers",
},
{
model: User,
attributes: ["id", "nick"],
as: "Followings",
},
],
})
.then((user) => done(null, user))
.catch((err) => done(err));
});
local();
kakao();
};
세션에 저장된 아이디로 사용자 정보를 조회할 때 팔로잉 목록과 팔로워 목록도 같이 조회합니다. include에서 계속 attributes를 지정하고 있는데, 이는 실수로 비밀번호를 조회하는 것을 방지하기 위해서입니다.
팔로윙/팔로워 숫자와 팔로우 버튼을 표시하기 위해 routes/page.js를 수정합니다.
const express = require("express");
const { isLoggedIn, isNotLoggedIn } = require("./middlewares");
const { Post, User } = require("../models");
const router = express.Router();
router.use((req, res, next) => {
res.locals.user = req.user;
res.locals.followerCount = req.user ? req.user.Followers.length : 0;
res.locals.followingCount = req.user ? req.user.Followings.length : 0;
res.locals.followerIdList = req.user
? req.user.Followings.map((f) => f.id)
: [];
next();
});
router.get("/profile", isLoggedIn, (req, res) => {
res.render("profile", { title: "내 정보 - NodeBird" });
});
router.get("/join", isNotLoggedIn, (req, res) => {
res.render("join", { title: "회원가입 - NodeBird" });
});
router.get("/", async (req, res, next) => {
try {
const posts = await Post.findAll({
include: {
model: User,
attributes: ["id", "nick"],
},
order: [["createdAt", "DESC"]],
});
res.render("main", {
title: "NodeBird",
twits: posts,
});
} catch (err) {
console.error(err);
next(err);
}
});
module.exports = router;
로그인한 경우에는 req.user가 존재하므로 팔로잉/팔로워 수와 팔로워 아이디 리스트를 넣습니다. 팔로워 아이디 리스트를 넣는 이유는 팔로워 아이디 리스트에 게시글 작성자의 아이디가 존재하지 않으면 팔로우 버튼을 보여주기 위해서입니다.
const express = require("express");
const { isLoggedIn, isNotLoggedIn } = require("./middlewares");
const { Post, User, Hashtag } = require("../models");
const router = express.Router();
router.use((req, res, next) => {
res.locals.user = req.user;
res.locals.followerCount = req.user ? req.user.Followers.length : 0;
res.locals.followingCount = req.user ? req.user.Followings.length : 0;
res.locals.followerIdList = req.user
? req.user.Followings.map((f) => f.id)
: [];
next();
});
router.get("/profile", isLoggedIn, (req, res) => {
res.render("profile", { title: "내 정보 - NodeBird" });
});
router.get("/join", isNotLoggedIn, (req, res) => {
res.render("join", { title: "회원가입 - NodeBird" });
});
router.get("/", async (req, res, next) => {
try {
const posts = await Post.findAll({
include: {
model: User,
attributes: ["id", "nick"],
},
order: [["createdAt", "DESC"]],
});
res.render("main", {
title: "NodeBird",
twits: posts,
});
} catch (err) {
console.error(err);
next(err);
}
});
router.get("/hastag", async (req, res, next) => {
const query = req.query.hastag;
if (!query) {
return res.redirect("/");
}
try {
const hastag = await Hashtag.findOne({ where: { title: query } });
let posts = [];
if (hastag) {
posts = await hastag.getPosts({ include: [{ model: User }] });
}
return res.render("main", {
title: `${query} | NodeBird`,
twits: posts,
});
} catch (error) {
console.error(error);
return next(error);
}
});
module.exports = router;
해시태그로 조회하는 GET /hastag 라우터입니다. 쿼리스트링으로 해시태그 이름을 받고 해시태그 이름을 받고 해시태그 값이 없는 경우 메인페이지로 돌려보냅니다. 데이터베이스에서 해당 해시태그를 검색한 후, 해시태그가 있다면 시퀄라이즈에서 제공하는 getPosts 메서드로 모든 게시글을 가져옵니다. 가져올 때는 작성자 정보를 합칩니다. 조회 후 메인 페이지를 렌더링하면서 전체 게시글 대신 조회된 게시글만 twits에 넣어 렌더링합니다.
마지막으로 routes/post.js와 routes/user.js를 app.js에 연결합니다. 업로드한 이미지를 제공할 라우터는 (/img)도 express.static 미들웨어로 uploads 폴더와 연결합니다. express.static을 여러번 쓸 수 있다는 사실을 기억해두세요. 이제 uploads 폴더 내 사진들이 /img 주소로 제공됩니다.
const express = require("express");
const cookieParser = require("cookie-parser");
const morgan = require("morgan");
const path = require("path");
const session = require("express-session");
const nunjucks = require("nunjucks");
const dotenv = require("dotenv");
const passport = require("passport");
dotenv.config();
const pageRouter = require("./routes/page");
const authRouter = require("./routes/auth");
const postRouter = require("./routes/post");
const userRouter = require("./routes/user");
const { sequelize } = require("./models");
const passportConfig = require("./passport");
const app = express();
passportConfig(); // 패스포트 설정
app.set("port", process.env.PORT || 8001);
app.set("view engine", "html");
nunjucks.configure("views", {
express: app,
watch: true,
});
sequelize
.sync({ force: false })
.then(() => {
console.log("데이터베이스 연결 성공");
})
.catch((err) => {
console.error(err);
});
app.use(morgan("dev"));
app.use(express.static(path.join(__dirname, "public")));
app.use("/img", express.static(path.join(__dirname, "uploads")));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(
session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
})
);
app.use(passport.initialize());
app.use(passport.session());
app.use("/", pageRouter);
app.use("/auth", authRouter);
app.use("/post", postRouter);
app.use("/user", userRouter);
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
app.use((err, req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== "production" ? err : {};
res.status(err.status || 500);
res.render("error");
});
app.listen(app.get("port"), () => {
console.log(app.get("port"), "번 포트에서 대기중");
});'프로그래밍 언어 > NODE JS' 카테고리의 다른 글
| Nodebird 서비스와 데이터베이스를 공유하는 프로젝트 구조 갖추기(1) (0) | 2025.11.13 |
|---|---|
| API 서버 이해하기 (0) | 2025.11.10 |
| multer 패키지로 이미지 업로드 구현하기 (0) | 2025.11.04 |
| 카카오 로그인 구현하기 (2) (0) | 2025.11.01 |
| 카카오 로그인 구현하기 (1) (0) | 2025.10.29 |