multer 패키지로 이미지 업로드 구현하기
SNS 서비스인 만큼 이미지 업로드도 중요합니다.
패키지를 먼저 설치합니다.
$ npm i multer
이미지를 어떻게 저장할 것인지는 서비스의 특성에 따라 달라집니다. Nodebird 서비스는 input 태그를 통해 이미지를 선택할 떄 바로 업로드를 진행하고, 업로드된 사진 주소를 다시 클라이언트에 알릴 것입니다. 게시글을 저장할 때는 데이터베이스에 직접 이미지 데이터를 넣는 대신 이미지 경로만 저장합니다. 이미지는 서브 디스크에 저장됩니다.
그럼 post 라우터를 작성해보겠습니다.
const express = require("express");
const multer = require("multer");
const path = require("path");
const fs = require("fs");
const { Post, Hashtag } = require("../models");
const { isLoggedIn } = require("./middlewares");
const router = express.Router;
try {
fs.readdirSync("uploads");
} catch (error) {
console.error("uploads 폴더가 없어 uploads 폴더를 생성합니다.");
fs.mkdirSync("uploads");
}
const upload = multer({
storage: multer.diskStorage({
destination(req, file, cb) {
co(null, "uploads/");
},
filename(req, file, cb) {
const ext = path.extname(file.originalname);
cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
router.post("/img", isLoggedIn, upload.single("img"), (req, res) => {
console.log(req.file);
res.json({ url: `/img/${req.file.filename}` });
});
const upload2 = multer();
router.post("/", isLoggedIn, upload2.none(), async (req, res, next) => {
try {
const post = await Post.create({
content: req.body.content,
img: req.body.url,
UserId: req.user.id,
});
const hastags = req.body.content.match(/#^\s#]+/g);
if (hastags) {
const result = await Promise.all(
hastags.map((tag) => {
return Hashtag.findOrCreate({
where: { title: tag.slice(1).toLowerCase() },
});
})
);
await post.addHastags(result.map((r) => r[0]));
}
res.redirect("/");
} catch (error) {
console.error(error);
next(error);
}
});
POST /post/img 라우터에는 이미지 하나를 업로드하는 즉시 이미지의 저장 경로를 클라이언트에 응답합니다. static 미들웨어가 /img 경로의 정적 파일을 제공하므로 클라이언트에서 업로드한 이미지에 접근할 수 있습니다.
POST /post 라우터는 게시글 업로드를 처리하는 라우터입니다. 이전 라우터에서 이미지를 업로드했다면 이미지 주소도 req.body.url로 전송됩니다. 비록 데이터 형식이 multipart이지만, 이미지 데이터가 들어 있지 않으므로 none 메서드를 사용했습니다. 이미지 주소가 온 것일 뿐, 이미지 데이터 자체가 오지는 않았습니다. 이미지는 이미 POST /post/img 라우터에서 저장되었습니다.
게시글을 데이터베이스에 저장한 후에는 게시글 내용에서 해시태그를 정규표현식(/#[^\s#]+/g)으로 추출해냅니다. 추출한 해시태그는 데이터베이스에 저장하는데, 먼저 slice(1).toLowerCase()를 사용해 해시태그에서 #을 떼고 소문자로 바꿉니다. 저장할 때는 findOrCreate 메서드를 사용했습니다. 이 시퀄라이즈 메서드는 해시태그가 존재하면 존재하는 걸 가져오고, 존재하지 않으면 생성한 후 가져옵니다. 결과값으로 배열을 반환하는데 result.map(r => r[0])을 사용해 해시태그 모델만 추출해냈습니다. 마지막으로 해시태그 모델들을 post.addHashtags 메서드로 게시글과 연결합니다.
게시글 작성 기능이 추가되었으므로 이제부터 메인 페이지 로딩 시 메인 페이지와 게시글을 함께 로딩하도록 하겠습니다.
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 = 0;
res.locals.followingCount = 0;
res.locals.followerCount = [];
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;
먼저 데이터베이스에서 게시글을 조회한 뒤 결과를 twits에 넣어 렌더링합니다. 조회할 때 게시글 작성자의 아이디의 닉네임을 JOIN해서 제공하고, 게시글의 순서는 최신순으로 정렬했습니다.
'프로그래밍 언어 > NODE JS' 카테고리의 다른 글
| API 서버 이해하기 (0) | 2025.11.10 |
|---|---|
| 프로젝트 마무리하기 (0) | 2025.11.07 |
| 카카오 로그인 구현하기 (2) (0) | 2025.11.01 |
| 카카오 로그인 구현하기 (1) (0) | 2025.10.29 |
| 로컬 로그인 구현하기 (2) (0) | 2025.10.26 |