Express로 SNS 서비스 만들기 - 프로젝트 구조 갖추기
기능 : 로그인, 이미지 업로드, 게시글 작성, 해시태그 검색, 팔로잉
express-generator 대신 직접 구조를 갖춤
프런트엔드 코드(nunjucks로 만듬)보다 노드 라우터 중심으로 볼 것
관계형 데이터베이스 MySQL 선택
SNS 서비스에는 데이터베이스에 사용자 / 게시글 / 이미지 / 팔로잉 팔로워 관계 / 해시태그 이런 것들을 각각의 테이블로 만든다.
테이블들간에 관계가 있나? 한사람이 여러개의 해시태그 / 한 게시글은 여러개의 해시태그 / 하나의 해시태그도 그에 여러개 속한 여러개 개시글들이 있을 수 있다.
일대다 / 다대다 => 관계형 데이터베이스를 쓴다. MySQL 쓰자.
프로젝트 구조에 필요한 패키지 설치
npm i bcrypt cookie-parser dotenv express express-session morgan mysql2 nunjucks passport passport-local
package.json은 아래와 같다.
{
"name": "nodebird",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "Jay",
"license": "MIT",
"dependencies": {
"bcrypt": "^5.0.1",
"cookie-parser": "^1.4.6",
"dotenv": "^16.0.0",
"express": "^4.17.3",
"express-session": "^1.17.2",
"morgan": "^1.10.0",
"mysql2": "^2.3.3",
"nunjucks": "^3.2.3",
"passport": "^0.5.2",
"passport-local": "^1.0.0"
}
}
명령어 입력
npm i sequelize sequelize-cli
// 후에
npx sequelize init
npm i multer
npm i -D nodemon
아래는 프로젝트 진행중인 error middleware를 뜻한다.
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);
});
passportConfig();
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: true }));
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('/auth', authRouter);
app.use('/post', postRouter);
app.use('/user', userRouter);
app.use('/', pageRouter);
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'), '번 포트에서 대기중');
});
app.js 에서 (next)error 부분은 에러 middleware를 뜻한다.
error를 처리하는 middleware 에서 next는 생략하면 안된다.(일반 middlewares는 next를 생략해도됨)
app.use('/', pageRouter);
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
// error를 처리하는 middleware
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');
});
아래는 개발할 때만 error stack을 보여주고 배포모드 일때 error의 stack을 안보여주는 것을 뜻한다.
app.use((err, req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
routes폴더내에 page.js 파일을 만들자. app.js를 보게되면 아래부분이 있는데
pageRouter부분앞에 그냥 / 이기 때문에
app.use('/', pageRouter);
page.js의 router 부분 앞에는 붙는게 없다.
router.get('/profile', isLoggedIn, (req, res) => {
res.render('profile', { title: '내 정보 - NodeBird' });
});
router.get('/join', isNotLoggedIn, (req, res) => {
res.render('join', { title: '회원가입 - NodeBird' });
});
아래에 twit 부분은 sns에 당장 게시할 부분이 없으므로 빈 배열을 넣어주었다.
// page.js
router.get('/', async (req, res, next) => {
try {
const posts = await Post.findAll({
include: {
model: User,
attributes: ['id', 'nick'],
},
order: [['createdAt', 'DESC']],
});
const twits = [];
res.render('main', {
title: 'NodeBird',
twits: posts,
user: req.user,
});
} catch (err) {
console.error(err);
next(err);
}
});
nunjucks로 html파일들을 만들자.
npm start를 하게 되면 main.css 파일이 없으므로 404에러가 나타나게 된다.
(npm start시 에러가 떠서 아래 블로그를 참고)
https://tristan91.tistory.com/529
TypeError: Router.use() requires a middleware function but got a Object
routes폴더의 파일 마지막에 const express = require('express'); const router = express.Router(); router.post('/', async (req, res, next) => { try { console.log(req); res.json(req); } catch (err) { co..
tristan91.tistory.com
<출처 조현영: Node.js 교과서 - 기본부터 프로젝트 실습까지 >
https://www.inflearn.com/course/%EB%85%B8%EB%93%9C-%EA%B5%90%EA%B3%BC%EC%84%9C/dashboard
[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지 - 인프런 | 강의
노드가 무엇인지부터, 자바스크립트 최신 문법, 노드의 API, npm, 모듈 시스템, 데이터베이스, 테스팅 등을 배우고 5가지 실전 예제로 프로젝트를 만들어 나갑니다. 최종적으로 클라우드에 서비스
www.inflearn.com