Back-end/Node.js

[Node.js] 익스프레스로 SNS 서비스 만들기 (3)

poppy 2021. 7. 27. 18:17
반응형

https://soohyun6879.tistory.com/174

 

[Node.js] 익스프레스로 SNS 서비스 만들기 (2)

https://soohyun6879.tistory.com/170 [Node.js] 익스프레스로 SNS 서비스 만들기 (1) 익스프레스로 간단한 SNS 서비스를 만들어보겠습니다! 로그인, 이미지 업로드, 게시글 작성, 해시태그 검색, 팔로잉 기능이.

soohyun6879.tistory.com

이전 포스팅에서는 로그인을 구현해보았는데 이번 포스팅에서는 이미지 업로드, 게시물 업로드, 팔로잉, 해시태그 검색 기능을 만들어보겠습니다!

 

1. 이미지 업로드, 게시글 업로드

게시물을 작성할 때 이미지도 같이 업로드할 수 있습니다. 이미지를 업로드 하기 위해 multer 모듈을 사용하겠습니다. 먼저 multer 를 설치합니다.

npm install multer

 

posts 라우터를 작성하겠습니다. routes 폴더 안에 post.js 파일을 만듭니다. POST /post/img 는 이미지 업로드 라우터입니다. 이미지를 어디에 어떤 이름으로 저장할지 설정한 multer 객체를 만든 후 이미지를 업로드합니다. POST /post 는 게시글 업로드를 처리하는 라우터입니다. 이미지를 업로드하면 req.body.url 로 이미지 주소가 전송됩니다. 게시글을 저장한 후에 해시태그를 정규표현식으로 추출합니다. 추출한 해시태그를 저장하는데 해시태그가 이미 존재하면 가져오고 해시태그가 존재하지 않으면 해시태그를 생성한 후 가져옵니다. 해시태그를 가져오면 결과값이 [모델, 생성여부] 로 반환됩니다. 

// routes/post.js
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();

// uploads 폴더 없으면 생성
try {
  fs.readdirSync('uploads');
} catch (error) {
  console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
  fs.mkdirSync('uploads');
}

const upload = multer({
  storage: multer.diskStorage({ // 어디에 저장할지
    destination(req, file, cb) {
      cb(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 {
    console.log(req.user);
    const post = await Post.create({ // 게시글 저장
      content: req.body.content,
      img: req.body.url, // 업로드한 이미지 주소
      UserId: req.user.id,
    });
    const hashtags = req.body.content.match(/#[^\s#]*/g); // 해시태그 추출
    if (hashtags) { // 해시태그가 있다면
      const result = await Promise.all( // [모델, 생성여부] 로 반환
        hashtags.map(tag => {
          return Hashtag.findOrCreate({ // 해시태그가 존재하면 가져오고 존재하지 않으면 생성한 후 가져옴
            where: { title: tag.slice(1).toLowerCase() }, // 앞에 #을 떼고 소문자로 바꿈
          })
        }),
      );
      await post.addHashtags(result.map(r => r[0])); // 해시태그 모델들을 게시글과 연결
    }
    res.redirect('/');
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

 

routes 폴더의 page.js 파일을 수정합니다. 게시글 작성 기능이 추가되었으므로 메인 페이지 로딩 시 게시글을 함께 로딩하도록 하겠습니다.

const express = require('express');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const { Post, User, Hashtag } = require('../models');

const router = express.Router();

// .. 생략
// 게시글 조회
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;

 

2. 팔로잉

팔로잉 기능을 만들겠습니다. routes 폴더 안에 user.js 파일을 만듭니다. 주소의 :id 부분이 req.params.id 가 됩니다. 팔로우할 사용자를 조회한 후 사용자가 있다면 현재 로그인한 사용자와 관계를 지정합니다.

// 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(404).send('no user');
    }
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

 

3. 해시태그 검색

해시태그 검색 기능을 만들어보겠습니다. 메인페이지에서 검색할 수 있으므로 메인페이지인 page.js 를 수정하겠습니다. 입력된 해시태그 값이 있다면 그 해시태그가 DB에 저장되어 있는지 확인합니다. 저장된 해시태그가 있다면 해시태그와 연결된 모든 게시물을 조회한 후 게시글을 페이지에 렌더링합니다. 

const express = require('express');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const { Post, User, Hashtag } = require('../models');

const router = express.Router();

// .. 생략
// 해시태그 검색
router.get('/hashtag', async (req, res, next) => {
  const query = req.query.hashtag; // 해시태그 이름
  if (!query) { // 입력된 해시태그 값이 없으면
    return res.redirect('/');
  }
  try {
    const hashtag = await Hashtag.findOne({ where: { title: query } }); // 해시태그 조회
    let posts = [];
    if (hashtag) { // 저장된 해시태그가 있다면
      posts = await hashtag.getPosts({ include: [{ model: User }] }); // 해시태그와 연결된 모든 게시물 조회
    }

    return res.render('main', {
      title: `${query} | NodeBird`,
      twits: posts, // 게시글 렌더링
    });
  } catch (error) {
    console.error(error);
    return next(error);
  }
});

module.exports = router;

 

콘솔에 npm start 를 입력한 후 웹페이지에서 로그인하면 다음과 같은 화면을 볼 수 있습니다!

 

반응형