본문 바로가기

Project Log/RoomieSeoul

[back-end] Node.js - express로 구조잡기

백엔드는 Node.js로 구성하기로 하고, 프레임워크는 express를 사용하기로 하였다.

 

express-generator를 이용해서 간단하게 구성할 수도 있지만 공부할겸해서 express만 이용해서 하나씩 채워나가는 방식으로 구성하였다.

DB는 몽고를 쓸까 MySQL을 쓸까 고민했었는데, 이전에 잠깐 궁금해서 몽고를 써봤을때 RDB에 비해 편하긴하지만 결국에는 RDB처럼 쓰기 위해 뭔가 추가적인 작업을 더 해주는 것이 그냥 애초에 RDB를 쓰는 게 낫겠다 라고 생각했었다. 그리고 유저와 유저가 올린 글, 유저가 좋아요를 누른 글 등의 어쨌든간에 데이터를 JOIN해서 사용할 경우도 많을 것 같아 MySQL을 사용하기로 하였다. DB 작업 시 직접 쿼리문을 만들어서 사용해도 되지만,  JS로 편하게 작업하기 위해  Sequelize를 사용하였다. 시퀄라이즈는 Node.js기반의 ORM(Object-Releational-Mapping)이다. 

 

처음에 모듈을 import/export 할 때 리액트에서 사용했던 것 처럼 import/export문으로 모듈을 사용하는 것을 시도했는데 unexpected identifier라고 떴다. 노드 9버전 부터 ES2015버전의 문법을 사용할 수 있다고 하는데 왜 안되지? 하고 찾아봤더니 파일 확장자명도 mjs로 지정해줘야하고 실행할때 node --experimental-modules [파일명]이라는 옵션을 붙여줘야 한다고 한다.

노드 버전을 체크해보니 10.16.3버전이라 위의 내용을 처리하면 쓸 수 있긴 하지만 그냥 require/exports를 쓰기로 했다.

 

Unhandled rejection SequelizeConnectionError: Client does not support authentication protocol requested 

by server; consider upgrading MySQL client 라는 에러 메세지가 떴다. mysql 5.7버전과 sequelize를 연동하면 문제가 없는데 내가 다운받은 mysql은 8버전이라에러가 떴다. mysql workbench로 가서 root(localhost) 커넥션으로 간다음에 아래의 sql문을 써주었더니 해결됐다.

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'

위의 쿼리문에서 'password' 부분을 원하는 비번으로 바꿔주고 그 비번을 다음 접속 시 사용하면 된다.

 

전체적인 app.js의 구조는 다음과 같다.

const express = require('express');
const cookieParser = require('cookie-parser');
const morgan = require('morgan');
const path = require('path');
const session = require('express-session');
const {sequelize} = require('./models');

require('dotenv').config();

const userRouter = require('./routes/user');

const app = express();
sequelize.sync();

app.set('port', process.env.PORT || 8001);

app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
    resave: false,
    saveUninitailized: false,
    secret: process.env.COOKIE_SECRET,
    cookie: {
        httpOnly: true,
        secure: false,
    },
}));

app.use('/user', userRouter);

app.use((req, res, next) => {
    const err = new Error('Not Found');
    err.status = 404;
    next(err);
});

app.use((err, req, res, next) => {
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};
    res.status(err.status || 500);
});

app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 대기 중');
})

 

일단 회원가입과 로그인부터 구현할 예정이라 라우터는 userRouter하나만 만들어서 연결해두었다. db만들고 서버 실행까지는 성공했다. 이 다음부터는 회원가입부터 시작해서 천천히 구현하도록 해야겠다. 

 

마지막으로 프론트에서의 api요청을 백에서 받기 위해 package.json에 proxy에 대한 구문을 써주고 비동기 api요청을 위해 axios와 redux-thunk 라이브러리를 추가해주었다. 그런데 redux-thunk를 이용해 비동기 요청하는 액션을 만들다보니 매번 PENDING, SUCCESS, FAILURE 만들어 주기 정말 귀찮다... 아니 그냥 이메일이랑 닉네임 중복확인만 하는 간단한 api요청인데도 그에 따른 액션을 6개나 만들어줘야한다 ^^ 후...이게 뭐지? 하다가 찾아보니 요청이 시작, 성공, 실패할 때 액션의 뒷부분에 _PENDING, _FULFILLED, _REJECTED를 반환하는 프로미스 기반의 비동기 작업을 좀더 편하게 해주는 redux-promise-middleware를 추가로 사용하게 되었다. 근데 어차피 액션에 따라 상태를 구현 해줘야하는 건 똑같다...다른 좋은 방법은 없을까? 벨로퍼트님이 만드신 redux-pender라는 라이브러리도 써봤는데 확실히 훨씬 편하긴하다. 그런데 이번에는 thunk도 배우고 노가다+귀찮음을 통해서 promise에 좀 익숙해질겸 써보려고했는데 느무 귀찮아서 좀 갈등중이다.