상세 컨텐츠

본문 제목

[Node.js] 노드에서 자주 사용되는 기술들을 이용한 템플릿 프로젝트 만들기

IT/프로그래밍

by James Lee. 2019. 4. 21. 23:13

본문

Github Project link : https://github.com/jhleed/node-default-project

Intro

올해는 작년보다 더 많은 사이드 프로젝트를 하려고 한다. 그런데 프로젝트를 매번 세팅할때마다 아래와 같은 번거로움이 있다.

  1. 중복된 환경설정을 해줘야 한다.
  2. 환경설정에서 삽질하는 경우가 적지 않다. "아 이거 전에 했었는데 뭐더라..?" → 결국 이전 프로젝트 환경설정을 다시 참고하게 됨

그래서 사이드 프로젝트를 만들때 바로 가져다 쓸 수 있도록 뼈대를 갖춰놓은 기본 프로젝트를 만들려고 한다.

이 프로젝트가 완성되면 사이드 프로젝트를 만드는데 걸리는 시간이 훨씬 줄어들 것이다.

기술 스택은 .. 노드를 선택하기로 했다.

나는 왜 노드를 선택하였나?

나는 노드에 대한 경험이 정말 거의 없다.

내 기술 스택 중 자바스크립트의 이해도가 제일 높음에도 불구하고, 지금껏 서버 사이드를 구축할때는 Type-Safety한 언어들만을 고집해왔다.

자바스크립트의 과도한 유연함(장점이기도 하지만)과 IDE 지원이 제대로 되지 않는 점, 그리고 노드는 싱글 스레드 기반이라 자주 서비스가 죽어버린다는 점 등은 서버 사이드에서 노드를 사용하는것을 망설이게 했다.

그러나 이번에는 아래 이유로 노드를 선택했다.

  1. 각종 프론트엔드 프레임워크와의 궁합이 좋다.
  2. 뛰어난 생산성
  3. 그래서 작은 규모의 사이드 프로젝트에 적합할 것 같다. (이게 노드를 선택한 주 이유)
  4. 회사에서도 어쩌면 노드를 쓰게 될 수도 있다.
  5. 그냥 노드 해보고 싶어서, 새로운 거 배워보고 싶어서..

그 외 노드의 장점들을 떠오르는대로 써 보면

  • 러닝커브가 적음 (자바스크립트는 세계에서 제일 많이 쓰는 프로그래밍 언어)
  • 서버 구동이 빠름 (피드백이 빠르다는 것은 꽤나 큰 장점이다. )
  • 노드의 거대한 생태계

물론 개인적으로는 여전히 노드는 대규모 프로젝트에는 적합하지 않다는 생각을 가지고 있다.

그러나 예전 대부분 모노리틱으로 프로젝트를 구축했을때와는 다르게 지금은 대부분의 대규모 프로젝트는 기능 단위의 API 서버들을 엮어서 (반드시 MSA가 아닐지라도) 만들기 때문에 이제는 이러한 것들이 그다지 큰 단점이 되지 않는다고 생각이 들었다.

세팅할 기능들은 대략적으로 아래와 같다.

  • Backend : Express, Q Promise, Sequelize, API Connector (미정)
  • Frontend : Vue, Webpack
  • Common : Testing(Mocha), Logging, MVC Pattern, CI & CD

그 외

  • Github Issue Template & Task 설정하기

Config File

보안 문제로 외부 시스템과 연결되는 프로퍼티 파일은 프로젝트 외부에 분리하고 Import 해서 사용하도록 함

config/config-path.json 파일에 프로퍼티 파일의 경로를 설정

{  
    "dbConfigPath": "/Users/jongholee/dev/workspace/project-config/node-pattern-sample/config/config.json"
}

import 할 때는 아래와 같이 사용한다.

//보안상 프로퍼티 정보는 외부에서 가져오도록 설정
const configPath = require(__dirname + '/../config/config-path.json').dbConfigPath;
const config = require(configPath)[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

Express

참고 문서 : express-generator - Node.js + Express 프로젝트 생성하기

$ express --view=hbs
$ cd <project dir>
$ npm install
$ npm start

Express와 자주 사용하는 라이브러리들 (파서, 로거 등)을 함께 셋업해준다.

기본 포트는 3000, 프로필은 development다.

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', userRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

PM2

노드 프로세스 관리 도구 (nodemon도 사용해봤는데, PM2가 더 좋은 듯 하다.)

현재 주로 사용하는 기능은

  1. 핫 리로드 - 코드 수정시 바로 반영
  2. 서비스 죽으면 자동으로 재기동

기본 세팅은 아래와 같다.

module.exports = {
    apps: [
        {
            // pm2로 실행한 프로세스 목록에서 이 애플리케이션의 이름으로 지정될 문자열
            name: "Your Service Name",
            // pm2로 실행될 파일 경로
            script: "./bin/www",
            // 개발환경시 적용될 설정 지정
            env: {
                "PORT": 3000,
                "NODE_ENV": "development"
            },
            // 배포환경시 적용될 설정 지정
            env_production: {
                "PORT": 8080,
                "NODE_ENV": "production"
            },
            watch : true,
            ignore_watch : ["logs"]
        }
    ]
};

Mocha

  • 동기 / 비동기 각 경우에 대한 단위 테스트
  • Transaction Rollback (적용 예정)
  • Mocking (적용 예정)
const assert = require('assert');
const userRepository = require('../../persistence/userRepository');

describe('userRepository', () => {
    it('should return 2 elements when call userCategories', function (done) {
        userRepository.getUsers().then((val) => {
            assert.strictEqual(val[0].id, 1);
            assert.strictEqual(val[0].name, '이종호');
            done();
        })
    });
});

Sequelize

  • ORM of Javascript

    npm i sequelize mysql2
    npm i -g sequelize-cli
    sequelize init

sequelize-cli 가 생성해주는 index.js를 수정해야 한다.

'use strict';

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';

//보안상 프로퍼티 정보는 외부에서 가져오도록 설정
const configPath = require(__dirname + '/../config/config-path.json').dbConfigPath;
const config = require(configPath)[env];
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

db.sequelize = sequelize;
db.Sequelize = Sequelize;

//모델 정보 읽어옴
db.User = require('./user')(sequelize, Sequelize);

//TODO 모델 관계 매핑

module.exports = db;

CI & CD (적용 예정)

Docker 적용 예정

Github Issue Template & Task 설정하기

비록 혼자 하는 프로젝트지만 실제 프로젝트처럼 관리할 겸, 그리고 Github 사용법에도 더 익숙해질 겸 해서 GitHub로 프로젝트 관리하기 Part1 - 이슈 발급 부터 코드리뷰까지 글을 참고하여 Issue Template 세팅을 하여 Feature 단위로 Task를 발급하였다.

회사에서는 프로젝트를 관리하는 방식이 조금 다르지만 어쨌든 배워두면 추후 사이드 프로젝트를 관리할 때 도움이 될 것 같다. (그리고 다 떠나서 일단 새로운 걸 배운다는 건 재밌다.)

후기

  • 노드 특징상 컴파일 타임에 잡히는 에러보다 런타임시 잡히는 에러가 월등히 많다. 그래서 테스트 코드를 견고하게 짜는게 정말 중요하다는 생각이 든다.
  • 만들면서 아이디어가 계속 떠오른다. 이것도 해보면 어떨까, 저것도 해보면 어떨까?
  • 이것저것 새로 시도해보는게 많아서 재밌었다. (삽질도 많이 했다.)
  • 노드에 익숙해지면 굉장히 자주 쓰게 될 것만 같은 느낌


관련글 더보기

댓글 영역