[Node.js][번역] node & Express.js로 개발 준비하기

Jessie H·2022년 9월 19일
0

nodejs

목록 보기
2/2
post-thumbnail

본 글은 How to write a production-ready Node and Express app를 번역한 글입니다.
(생략 및 의역 많을 예정)
오역 지적 언제든지 환영합니다.


오프닝...

Node와 Express를 사용하여 개발할 때 가장 어려운 점은 프로젝트 구조를 구성하는 것이다.
Express는 프로젝트 구조에 대한 엄격한 규칙이나 가이드라인이 없기 때문이다.
구조는 내가 원하는대로 만들 수 있다. 하지만 긴 코드의 route handler 등으로 구성을 하다 보면 잠재적인 버그를 생성할 수 있다. 많은 시간을 리팩토링과 버그 해결에 사용하게 될 수도 있다.
필자는 작고 큰 다양한 팀과의 협업을 통해 어떤 식의 프로젝트 구조가 유지 관리하기 쉬운지 알게 되었다.
이제 그 구조에 대해 알아볼 것이다.

MVC(Model View Controller)

MVC는 빠르고 병렬 개발이 가능하다.

병렬 개발이란(parallel development) 한 명의 개발자가 view 부분 로직을 작업할 때 다른 개발자는 controller의 비즈니스 로직을 작업하는 등 동시에 여러 가지 부분을 개발할 수 있다는 것을 의미한다.

예시로 유저 CRUD앱의 구조에 대해 살펴보자.

*유저 CRUD앱(CREATE, READ, UPDATE, DELETE)
: API를 통해 데이터베이스와 상호작용(DB를 생성, 조회, 갱신, 삭제)하는데 쓰는 사용자 인터페이스
ex) 직원 휴가 및 출근 관리 프로그램, 블로그
출처: https://www.dronahq.com/crud-app/

project/
  controllers/
    users.js
  util/
    plugin.js
  middlewares/
    auth.js
  models/
    user.js
  routes/
    user.js
    router.js
  public/
    js/
    css/
    img/
  views/
    users/
      index.jade
  tests/
    users/
      create-user-test.js 
      update-user-test.js
      get-user-test.js
  .gitignore
  app.js
  package.json
  • controllers: app의 라우트 핸들러와 비즈니스 로직 정의하는 역할

  • util: controller에서 사용하는 utility와 helper함수 정의(상수값, 변하지 않는 함수 등)

  • middlewares: route handler로 넘기기 전에 들어온 요청을 일차적으로 걸러주는 함수

  • models: controller와 DB의 미들웨어 역할.
    ex) mongoose로 Schema 정의 등

  • routes: HTTP 메소드를 활용해 app의 루트를 정의
    ex)

    router.post('/users/create', controller.create);
     router.put('/users/:userId', controller.update);
     router.get('/users', controller.getAll);

    mongoose: ORM의 한 종류
    ORM이란?
    객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말한다.
    객체 지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용한다.
    객체 모델과 관계형 모델 간에 불일치가 존재한다.
    ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
    출처: https://gmlwjd9405.github.io/2019/02/01/orm.html

  • public: img파일, JS, CSS

  • views: 서버에서 렌더되는 템플릿 들어있는 폴더(pug, ejs...)

  • tests: 테스트 파일

  • app.js: 앱 첫 실행 또는 프로젝트의 다른 부분을 처음 실행할 때 프로젝트의 메인 파일 역할을 하는 파일.

  • package.json: npm 명령으로 실행할 스크립트 및 프로젝트 버전 관련 정보가 담겨 있다.



Express에서의 예외와 에러 핸들링 처리

promises 사용

then()에 정의된 동기 코드와 비동기 코드 블록에서 발생할 수 있는 명시적 또는 암시적 에러를 처리할 수 있다.

router.post('/create', (req, res, next) => {

   User.create(req.body)    // function to store user data in db
   .then(result => {
   
     // do something with result
    
     return result 
   })
   .then(user => res.json(user))
   .catch(next)
})

try-catch 사용

비동기 코드에서 예외를 처리하는 전통적인 방법이다

router.get('/search', (req, res) => {

 setImmediate(() => {
   const jsonStr = req.query.params
   try { //성공 시 실행 
     const jsonObj = JSON.parse(jsonStr)
     
     res.send('Success')
   } catch (e) { //실패 시 실행
     res.status(400).send('Invalid JSON string')
   }
 })
})

동기 코드 사용은 지양

  • 동기 코드는 이전 코드 실행이 완료될 때까지 다음 실행을 차단하기 때문에 실행이 오래 걸리는 동기 함수나 메서드를 사용할 경우 응답 시간이 길어질 수 있다
  • node.js에서는 .sync .async 모두 제공되지만 개발 시에는 async를 주로 사용하는 것이 좋다

에러 핸들링 관련 자료
Building Robust Node Applications: Error Handling (StrongLoop blog)


로깅(logging) 제대로 하는 법

디버깅 목적

  • 우리가 자주 사용하는 console.log와 console.error는 동기 함수이다
  • debug 모듈
    사용법

앱 활동


require(“./../../../../../../”) 지옥 벗어나는 법

const path  = require('path');
const HOMEDIR  = path.join(__dirname,'/public',);

__dirname은 file명을 제외한 절대경로를 의미한다
절대경로로 설정할 경우 파일을 옮기거나, 다른 서버에서 실행하거나, 다른 OS에서 설정할 경우에도 작동하기 때문이다


NODE_ENV: production 설정

node.js에서 배포할 때는 process.env.NODE_ENV 를 production 으로 설정하는 것이 좋다.
어떤 라이브러리는 프로덕션 모드에 대한 최적화코드를 따로 작성하기 때문에 퍼포먼스 향상에 도움이 되기 때문이다.

process.env.NODE_ENV=production => 배포모드
process.env.NODE_ENV=development => 개발모드

출처: https://steemit.com/kr/@inspiredjw/node-js-nodeenv

node 환경변수 관련 글
https://velog.io/@public_danuel/process-env-on-node-js

ex) 환경변수를 process.env.NODE_ENV=production로 설정할 경우,
Express는 다음과 같은 기능을 가진다.

  • view template 캐싱(임시 저장)
  • CSS extension에서 생성된 CSS파일 캐싱(임시 저장)
  • 에러 메시지를 장황하지 않게 생성

테스트 시 약 3배 정도 성능 향상


Process Manager 사용하기

StrongLoop Process Manager
PM2
Forever

이 세개가 대표적인 Process Manager이다.
셋을 비교한 설명은 expressjs 공식 홈페이지에 나와있다
https://expressjs.com/en/advanced/pm.html

필자는 PM2를 주로 이용한다.

(라인 엔지니어링에서 nodejs의 클러스터링과 PM2를 활용한 zero downtime restarts 원리를 한국어로 잘 설명한 글이 있다)
https://engineering.linecorp.com/ko/blog/pm2-nodejs

PM2에 대한 다른 자세한 설명 글
https://inpa.tistory.com/entry/node-%F0%9F%93%9A-PM2-%EB%AA%A8%EB%93%88-%EC%82%AC%EC%9A%A9%EB%B2%95-%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0-%EB%AC%B4%EC%A4%91%EB%8B%A8-%EC%84%9C%EB%B9%84%EC%8A%A4


nodejs의 클러스터 모듈

Node.js는 기본적으로 싱글 스레드(thread)
Node.js 애플리케이션은 단일 CPU 코어에서 실행 -> CPU의 멀티코어 시스템은 사용할 수 없습니다.
ex) 서버의 사양이 8코어 & 하이퍼스레딩을 지원 시 최대 16개 코어 사용가능.
오직 한 개의 코어만 사용해야 한다면 주어진 자원을 제대로 활용하지 못함.

클러스터(Cluster) 모듈 사용: 단일 프로세스를 멀티 프로세스(Worker)로 늘릴 수 있는 방법
클러스터 모듈 사용 시: 마스터 프로세스에서 CPU 코어 수만큼 워커 프로세스를 생성, 모든 코어를 사용하게끔 개발
(출처: https://engineering.linecorp.com/ko/blog/pm2-nodejs)

profile
코딩 공부 기록장

0개의 댓글