본문 바로가기
혼자 공부하는 것들

node.js & express.js 단단하게 설계하기

by applepick 2021. 9. 9.
반응형

잘못된 부분이 있으면 피드백해주시면 감사합니다.

책도 찾아보고 혼자 공부해보면서 다른 사람들이 어떻게 설계하였는지 참고해보면서 나만의 아키텍처를 구성해보았습니다. 

node.js는 타 프레임워크와 달리 파일의 구성들을 잡아주지 않습니다. 빈 박스에 본인이 필요한 기능들을 추가하는 방식입니다. 처음에 하나의 파일로 구성해보면서 많은 불편함을 느껴 구조 설계에 관심을 가지게 되었습니다. MVC 패턴을 참고하여 제작하였습니다.


폴더 구조

├─ API
│  ├── controllers
│  │   ├── (해당기능이름).controller.js
│  │   └── (해당기능이름).controller.js
│  ├── models
│  │   └── (해당기능이름).model.js
│  ├── test
│  │   └── (해당기능이름).spec.js
│  ├── util
│  │   └── util.js
│  └── validation
│      └── (해당기능이름).js
├── app.js
├── package.json
├── routes
└── views

controllers & models & validation

 컨트롤러는 말 그대로 view와 model을 컨트롤해주는 매개체 같은 역할입니다. 이때 관심사 분리 원칙을 지켜주어야합니다. 관심사 분리 원칙이란 프로그램을 하나의 단일 블록으로 작성하지 말고 작은 조각으로 나누어 각각 간단한 개별 작업을 완료할 수 있도록 만드는 것입니다.

controller의 process

사용자가 예를 들어 "POST : /login"이라는 경로로 들어오면 validation을 통해 사용자가 입력한 값을 검증합니다. 값이 올바르게 들어왔다면 controller를 통해 기능을 실행합니다. 이때 DB에서 필요한 값이 필요할 경우 model을 통해 DB에서 값을 가져와 controller로 넘겨줍니다. controller에서는 관심사 분리 원칙에 따라 DB query 및 res의 검증이 있으면 안 됩니다. 잘못된 예시를 확인해보겠습니다.

//bad code
../controllers/test.controller.js

const mysql = require('../../config/database'),
      connection = mysql.init();
mysql.connect(connection);

 const result = {
    post_result : async (req, res, next) =>{
    	let body = req.body;
        if((body.user != undefined) || ...){
        	//req검증
            return res.send("400 error");
        }
        	
        let q = "DB query...";
        connection.query(q, (err, result) => {
            if (err) {
                throw err;
            }
            return res.render("result.ejs", {
                data : result
       	    });
      }
    }
}
module.exports = result;

나쁜 사례를 보자면 controller가 DB 쿼리와 함께 res를 같이 검증합니다. 이런 식으로 구성하다 보면 나중에 수많은 req와 res가 생기고 스파게티 코드가 될 것이 분명합니다. 가독성도 떨어지고 버그가 생기면 모든 코드를 들여다봐야 합니다. 

좋은 코드를 확인하기전에 routes파일을 확인해볼까요?

const express = require('express');
const router = express.Router();
const test_result = require('../API/controllers/test.controller'),
      test_validate = require('../API/validation/test');

router.post('/result',test_validate.test_validation, test_result.post_result);

module.exports = router;

'POST: /result'로 들어오는 트래픽은 validation에서 값을 검증해주고 controller에 넘겨줍니다. 

//good code
../controllers/test.controller.js
const test_model = require('../models/test.model');

const result = {
    post_result : async (req, res, next) =>{
        let body = req.body;
        const testModel = await test_model.test_model(body);
        console.log("testModel : ", testModel);
        return res.render("result.ejs", {
            data : testModel
        });
    }
}
module.exports = result;

 

이렇게 작성하게 된다면 controller는 res의 값을 검증하지 않고, DB query도 하지 않습니다. 이렇게 작성하게 된다면 res값 검증이 잘못되었다면 validation 파일을, DB에서 문제가 있다면 model 파일을 보면 됩니다. 유지 보수하기 좋으며 테스트 코드를 작성할 때 빛을 보게 됩니다. 테스트 코드 작성하는 방법은 추후에 정리해보겠습니다. 감사합니다.

 

+ 추가

제가 view를 밖으로 꺼낸 이유는 추후 react나 프론트엔드 프레임워크와 연동할 때 손쉽게 때어낼 수 있도록 하기 위해서입니다. 프론트쪽은 잘 알지 못하기에 ejs를 통해 간단하게 작성했습니다.

반응형

댓글