next.js와 express.js의 운영배포설정 및 과정을 정리해보자.
package.json에 빌드 스크립트를 작성하고 빌드를 해보자.
"prestart": "NODE_ENV=production next build",
"start": "NODE_ENV=production next start"
빌드를 하면 .next 폴더가 생성되는데 이쪽에 생성된 파일들을 next start시에 읽는다.
.next 폴더는 .gitignore에 포함시킨다.
만약 redux를 사용시에는 운영환경일때 redux상태가 보이지 않도록 하는 부분을 추가해준다.
아래 예는 saga를 같이 사용한 경우의 예이다.
import { createStore, applyMiddleware, compose } from 'redux'
import { createWrapper } from 'next-redux-wrapper'
import { composeWithDevTools } from 'redux-devtools-extension'
import createSagaMiddleware from 'redux-saga'
import rootReducer from '../reducers/rootReducer'
import rootSaga from '../sagas'
const makeStore = () => {
const sagaMiddleWare = createSagaMiddleware()
const middlewares = [sagaMiddleWare]
const enhancer =
process.env.NODE_ENV === 'production'
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(applyMiddleware(...middlewares))
const store = createStore(rootReducer, enhancer)
store.sagaTask = sagaMiddleWare.run(rootSaga)
return store
}
const wrapper = createWrapper(makeStore, {
debug: process.env.NODE_ENV !== 'production'
})
export default wrapper
next.config.js는 내부적으로 webpack설정이 디폴트로 되어있는데 커스텀도 가능하다.
운영모드일때는 webpack devtool 모드를 hidden-source-map을 해줘야지 소스를 숨길수 있다.
module.exports = {
compress: true,
...
webpack(config, { webpack }) {
const prod = process.env.NODE_ENV === 'production'
return {
...config,
mode: prod ? 'production' : 'development',
devtool: prod ? 'hidden-source-map' : 'eval',
plugins: [...config.plugins]
}
}
}
logger middleware for node.js 다른 logger 모듈을 사용해도 무방하다.
const morgan = require('morgan')
app.use(morgan(<format>, <option>))
confined : production환경에 적합한 format
dev : development환경에 적합한 format
express-session사용시 설정을 환경에 따라 달리하자
const session = require('express-session')
app.use(session(<sessionOption>))
const sessionProdOpt = {
resave: false,
saveUninitialized: false,
secret: process.env.SECRET,
cookie: {
httpOnly: true,
secure: true
}
proxy: true,
}
const sessionDevOpt = {
resave: false,
saveUninitialized: false,
secret: process.env.SECRET,
cookie: {
httpOnly: true,
secure: false
}
}
sequelize를 기준으로 DB설정 값을 변경한다고 하자
config.js의 username, password, database, host를 환경별로 .env에 설정해준다.
require('dotenv').config()
module.exports = {
development: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'mysql'
},
test: {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: 'mysql'
},
production: {
username: process.env.DB_PROD_USER,
password: process.env.DB_PROD_PASSWORD,
database: process.env.DB_PROD,
host: process.env.DB_PROD_HOST,
port: process.env.DB_PORT,
dialect: 'mysql',
logging: false // sql문이 로깅되지 않는다.
}
}
app.listen(process.env.PORT, () => {
logger.info(`server on ${process.env.PORT}`)
})
heroku로 테스팅한다고 할 때 참고링크앞서 morgan이라는 logger를 사용했는데 winston을 사용하는 이유는 두 모듈에 사용목적이 다르기 때문이다. morgan은 http요청과 응답을 format화 하여 http 관련 로깅을 확인하는데 좋다. 하지만 이런 로깅을 파일이나 db에 남겨 놓지 않는다면 문제발생시 에러추적을 하기 어렵다. 이럴때 winston을 사용하여 logging을 저장해 둘 수 있다.
AWS, GCP 같은 클라우드를 사용시에는 보통 console.log를 사용해도 로깅이 파일이나 디비에 저장되지만, 클라우드를 사용하지 않는 경우에는 console.log가 파일이나 디비에 남겨지지 않을 수 있다. 따라서 winston을 사용해서 파일이나 db에 logging을 남길 수 있다.
winston사용예 링크
세션store를 사용해야지 서버가 재시작 되더라도 세션이 유지된다. 따라서 운영시에는 메모리스토어를 사용하지 않는데 Redis를 한번 사용해보자.
app.js의 store에 client로 연결해준다.//redisClient.js
const redis = require('redis')
const Logger = require('./logger')
require('dotenv').config()
const redisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASSWORD
})
redisClient.unref()
redisClient.on('ready', () => {
Logger.debug('✅ redis is ready')
})
redisClient.on('connect', () => {
Logger.debug('✅ redis is connected')
})
redisClient.on('reconnecting', () => {
Logger.debug('✅ redis is reconnecting')
})
redisClient.on('end', () => {
Logger.debug('✅ redis is end')
})
redisClient.on('error', (error) => {
Logger.error(error)
})
module.exports = redisClient
//app.js
const redisClient = require('./redisClient')
store: new RedisStore({ client: redisClient })
기타사항으로 조치해줄 것들
npm audit
//if any
npm audit fix
The audit command submits a description of the dependencies configured in your project to your default registry and asks for a report of known vulnerabilities.
Helmet helps you secure your Express apps by setting various HTTP headers. It's not a silver bullet, but it can help!
위에 설명은 helmat 패키지를 소개해주는 말이다. 웹은 보안에 취약하기 때문에 보안에 도움이 될만한 패키지는 운영배포시에 꼭 설치해주자.
Express middleware to protect against HTTP Parameter Pollution attacks
위에 설명은 hpp 패키지를 소개해주는 말이다. 웹은 보안에 취약하기 때문에 보안에 도움이 될만한 패키지는 운영배포시에 꼭 설치해주자.