ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Node.js 프로젝트 리팩토링(1)
    BackEnd/Node.js 2022. 2. 27. 00:02

    ------------------------------------------------------------ 사담 ----------------------------------------------------------------

    최근에 Node.js 스터디를 했고, 인턴십 면접도 보면서 기존에 했었던 프로젝트를 리팩토링 해야겠다고 생각했다. 인턴십 사담을 좀 해보자면, 노드 쓰는곳 2군데 스프링 한군데 넣었고 노드는,, 서류는 다 붙었는데 면접 탈했다 ^_^

    쨌든 기존 프로젝트를 좀 더 다듬어서 다음학기에도 또 지원해보고자 ... 리팩토링 과정을 좀 블로그에 기록해두고자 한다. 

    -----------------------------------------------------------------------------------------------------------------------------------

     

    1. 프로젝트 구조 : MVC 패턴을 적용해보자

    $ npm install express-generator -g

    위 명령어를 vscode 터미널에 치면 express의 기본적인 구조를 자동으로 형성해준다. 그 뒤에 npm install 명령어로 필요한 모듈 설치를 해주면 된다. 

    출처 : https://expressjs.com/ko/starter/generator.html

    그럼 위와 같은 구조가 만들어지는데, 여기서 아래처럼 바꿨다. 

     

     

    내 코드의 기존 문제점이라고 해야하나? 노드의 모듈화를 잘 적용하지 못한 것 같다. 처음 노드를 공부할 때에는 스치듯 지나간 다른 사람들의 코드를 보면서 왜 찾아보기 복잡하게 저렇게 해놨지? 라는 생각을 했었다. 처음엔 조금 이해하기 어려웠지만 모듈화 시키는 쪽이 좀 더 개발하기 편리할 것 같다. 

     

    ✅ MVC 패턴

    - Model : 데이터와 비즈니스 로직 관리
        - 어플리케이션의 정보, 데이터를 나타냄
        - 뷰나 컨트롤러에 대한 정보를 몰라야 함
    - View : 클라이언트와 상호작용이 일어나는 부분, 레이아웃과 화면 처리
        - 변경이 일어날 시 모델에게 변경 내용 전달 
    - Controller : 유저의 요청을 처리해서 응답하는 부분. 모델과 뷰 부분으로 라우팅
        - 모델과 뷰 사이를 이어주는 브릿지 역할
        - 모델이나 뷰의 변경을 모니터링, 변경 사항을 각 구성요소에 통지해야 함


     

    2. Modules

    - db와 연결하는 부분, 상태 코드, 메시지 등을 담아두는 폴더이다.

    - message, status는 기존 프로젝트에서도 있었는데 달라진 부분은 connectionPool, mysql부분이다. 

    - mysql.js (db정보는 각자 다 다를테니 맞춰서 작성) 

    📌 database와의 연결을 생성하는 모듈

    const mysql = require('mysql2/promise');
    const database = {
        host: 'host 정보'
        user: 'username 정보',
        database: 'database정보',
        password: 'database비밀번호'
    }
    module.exports = mysql.createPool(database);

    - connectionPool.js

    const poolPromise = require('./mysql');
    module.exports = {
        queryParam : async (sql) => {
            return new Promise (async (resolve, reject) => {
                try {
                    const pool = await poolPromise; // pool생성
                    const connection = await pool.getConnection();
                    try {
                        const result = await connection.query(sql); //sql 쿼리 수행
                        connection.release();
                        resolve(result); //프로미스 이행
                    } catch (err) {
                        connection.release();
                        reject(err); // 프로미스 실패
                    }
                } catch (err) {
                    reject(err);
                }
            })
        }
    }

    - database에 쿼리 날리는 방식을 async-await으로 구현

    - database에 미리 연결된 connection을 생성해서 pool에 저장, 필요 시 pool의 connection 가져다 쓰고, 사용이 끝나면 반환(connection.release())

    📌 queryParam : sql문을 매개변수로 받아서 데이터베이스로부터 쿼리 결과를 받아오는 비동기 함수

     


     

    3. Models

    - user.js : 사용자 관련 작업들 담아둠. 로그인, 닉네임 중복확인 등의 작업들을 정의해두었다. 

    const pool = require('../modules/connectionPool');
    const bcrypt = require('bcrypt');
    
    module.exports = {
        login : async (email) => {
            const sql = `SELECT * FROM User WHERE email="${email}"`;
            try {
                const result = await pool.queryParam(sql);
                return result;
            } catch (err) {
                throw err;
            }
        },
      	/* ..... */
    }

     


     

    4. Controllers

    - 요청에 맞춰서 models/user.js 의 작업들을 수행시킨다. 

    - 예를 들어 위의 로그인 작업을 수행하는 userController 내부 코드는 아래 코드와 같다.

    login : async (req, res) => {
            const {
                email, 
                pw
            } = req.body;
    
            if (!email || !pw) {
                return res.status(statusCode.BAD_REQUEST).send(messageCode.MISS_DATA)
            } // 필요한 데이터 누락
            
            const user = await User.login(email); //models/user.js의 작업 기다림
            if (user.length === 0) { // email 에러
                return res.status(statusCode.MATCH_ERR).send(messageCode.INVALID_USER);
            } else {
                const match = bcrypt.compare(pw, user[0][0].pw);
                if (!match) {
                    return res.status(statusCode.MATCH_ERR).send(messageCode.INVALID_PW);
                }
            }
    
            return res.status(statusCode.SUCCESS).json({
                code: statusCode.SUCCESS,
                message: messageCode.SIGN_IN_SUCCESS,
                userIdx : user[0][0].id
            });  
        },

    - 로그인이 실패했을 경우에 각각의 에러코드와 메시지를 전달한다. 성공했을 경우에는 우선 userIdx를 성공 응답과 함께 보내주는 것으로 짜둠.

    - 아직 JWT 토큰을 안 달아놨는데 그건 다음편에 쓸 예정...

     

     


    5. Routes

    - 클라이언트로부터 들어오는 api요청을 라우팅. 적절한 controller로 보내준다. 

    - 현재는 index.js, user.js 2개

    - REST API구조에 맞춰 주소를 미리 정의해뒀다. userController 쪽으로 넘길거면 주소 형태가 /user/로 시작한다. 

     

    📌 index.js 

    const express = require('express');
    const router = express.Router();
    
    router.use('/user', require('./user'));
    module.exports = router;

    - /user 로 시작하는 경우 ./user(같은 routes폴더에 있는 user.js)에 넘겨준다. 

    - 추후에 /post, /list 뭐 이런 라우터가 있으면 그에 맞는 router로 넘겨주게끔 위 코드 형태로 작성하면 된다.

     

    📌 user.js

    const express = require('express');
    const router = express.Router();
    const userController = require('../controllers/userController');
    
    router.post('/login', userController.login);
    router.post('/join', userController.join);
    module.exports = router;

    - user/login 주소로 POST 요청이 오면 userController의 login 처리

    - user/join 주소로 POST 요청이 오면 userController의 join처리 

    이러한 방식으로 흘러간다. 

     

     


    일단 간단하게 프로젝트 구조를 좀 개선해봤다. 지금은 아직 로그인/회원가입 정도만 대강 구현해놨다. 다음 포스팅에는 다른 기능들이랑 JWT토큰 발급도 공부해서 써야겠다. 

     

    🔽 참고한 블로그들

     

    'BackEnd > Node.js' 카테고리의 다른 글

    Node.js 스터디 6주차  (0) 2022.02.22
    Node.js 스터디 5주차  (0) 2022.02.15
    Node.js 스터디 4주차  (0) 2022.02.07
    Node.js 스터디 3주차  (0) 2022.02.05
    Node.js 스터디 2주차  (0) 2022.01.25
Designed by Tistory.