3 minute read

들어가기전에

앱을 구성할 때는 위에서부터 아래로 순서대로 실행되기 때문에

1) 공통 미들웨어: 모든 라우트에서 사용하는 2) 범위가 좁은 라우트: path variable을 사용하는 3) 범위가 넓은 라우트: query String을 사용하는 4) 에러 처리 미들웨어

순으로 작성해야한다.

Express에서의 에러처리는 코드의 마지막에 에러처리 미들웨어를 작성해주면 된다. 요청 > 라우터검색 > 못찾음 > 마지막에 에러처리 순서를 가지기 때문이다.

개요

노드는 싱글 스레드이기 때문에, 에러 처리에 민감해야 한다. 하나뿐인 스레드를 멈추지 않도록 잘 관리해야하기 때문이다.

에러처리 목적

  1. 에러를 처리한다.
  2. 에러페이지 내용을 숨긴다.
# 아래와 같은 에러 페이지 내용을 숨기자
Error: 
    at /fswas/wasadm/nodeStudy/app_temp.js:19:10
        at Layer.handle [as handle_request] 
        (/fswas/wasadm/nodeStudy
        ......(중략)
          

Express 제공 에러처리 미들웨어 구현

.
├── app.js
├── server.js
├── controllers
│   ├── main
│   │   └── main.controller.js
│   └── member
│        └── member.controller.js
├── middlewares
│   ├── customErr.js  # Express 에러처리 미들웨어 모듈화 생성
│   └── logger.js
└── routes
     ├── index.js         
     ├── main
     │    └── index.js   
     └── member
           ├── basic
           │   └── index.js  # [테스트용]강제 익셉션 URL매핑 추가
           └── index.js

에러 핸들링을 위한 미들웨어어 알아본다. Express에서 제공하는 next 함수의 인자로 에러 객체를 전달하면, 에러 핸들러 미들웨어에서 처리할 수 있다.

/app.js

const express = require('express');
const PORT = 3000;
const logger = require('./middlewares/logger');
const customErr = require('./middlewares/customErr');

class App {
    constructor() {
        this.app = express();
        this.setMiddlewares();
        this.setRouting();
	
        //특수한 Express 에러처리 미들웨어 생명주기로 인한 별도로 맨 마지막 등록
        this.app.use(customErr);
    }
    
    setRouting() {
        const routes = require('./routes'); 
        this.app.use('/', routes);
    }
    
    setMiddlewares() {
        this.app.use(logger);
    }
    
}

module.exports = new App().app;

/middlewares/customErr.js

const customErr = function() {
    return function(err, req, res, next) {
        console.log('customErr 미들웨어가 실행되었습니다.');
        res.send('사용자님.. 아쉽지만 이 에러는 관리자만 볼수있습니다.');
        next();
    }
}

module.exports = customErr();

/routes/member/basic/index.js

const express = require('express');
const router = express.Router({mergeParams: true}); // (기본값false)부모의 매개변수를 상속받도록 함

router.get('/detaile/:name/:age', (req, res) => {
    console.log('basic/index.js detaile start  >> ');
    console.log('name: ' + req.params.name);
    console.log('age: ' + req.params.age);
});

// [테스트용]강제 익셉션 URL매핑 추가
router.get('/errTest', (req, res) => {
    console.log('basic/index.js errTest start  >> ');
    throw new Error();
});

module.exports = router;

결과

# 서버측
GET /member/errTest                # 로거 미들웨어
basic/index.js errTest start  >>   # 컨트롤러 로직(강제 익셉션 로직 수행)
customErr 미들웨어가 실행되었습니다.        # 에러처리 미들웨어

# 에러페이지 내용이 커스텀하게 오버라이딩되어 사라졌다.
// 클라이언트(브라우저)
사용자님.. 아쉽지만  에러는 관리자만 볼수있습니다.

자세히보면 Express에서 에러 처리 미들웨어는 4개의 인자로 오버로딩 되어 구현되어있다.
이 미들웨어의 특이한 점은 next() 함수를 호출하지 않아도 된다. 이는 다른 미들웨어와 달리, 요청 처리 결과에 영향을 미치지 않는 <span style=’color:red’red’544 4>특수한 미들웨어</span>이기 때문이다.

  1. app.use를 사용하여 등록 한다.
    • 첫 번째 인자를 err(에러) 객체로 하여 총 4개의 인자를 부여 해야한다.
  2. 특수한 미들웨어라서 일반적으로, 모든 미들웨어 다음줄 code line 에 등록하며, 요청 처리 중 발생하는 에러를 처리한다. 아래는 호출되는 예시다.
    • next(err) 함수를 호출한 경우
        // 실제 미들웨어 내용을 까보면 아래 내용이 구현되어 있을것이다.
        try {
       	   // .. 에러 발생 코드
        } catch(err) {
        	      error(err); // next()에 인수가 있다면, 에러 처리 미들웨어로 점프한다.
        }
      
      
    • 라우터 함수나 미들웨어 함수 내부에서 에러가 발생한 경우
  3. 에러가 발생한 경우 이후의 미들웨어는 실행되지 않고, 바로 에러 처리 미들웨어로 전달 된다.
    • /errRun 을 요청해보면 서버 콘솔 결과는 다음과 같다. 하지만 res.send(‘에러’) 으로 처리해주면 사용자는 에러 내용을 볼 수 없을것이다.
        Error: 
            at /fswas/wasadm/nodeStudy/app_temp.js:19:10
                at Layer.handle [as handle_request] 
                (/fswas/wasadm/nodeStudy
                ......(중략)
      
      

404 강제 에러처리 구현

.
├── app.js
├── server.js
├── controllers
│   ├── main
│   │   └── main.controller.js
│   └── member
│        └── member.controller.js
├── middlewares
│   ├── basicErr.js   # Express 에러처리 미들웨어 모듈화 생성
│   ├── customErr.js  
│   └── logger.js
└── routes
     ├── index.js         
     ├── main
     │    └── index.js   
     └── member
           ├── basic
           │   └── index.js
           └── index.js

요약하자면 서버에서 해당 주소를 찾다가 마지막까지 없다면 해당 미들웨어를(에러처리) 실행하게 되는 것이다.

  • ex) 404 에러페이지

/app.js

const express = require('express');
const PORT = 3000;
const logger = require('./middlewares/logger');
const customErr = require('./middlewares/customErr');
const basicErr = require('./middlewares/basicErr');

class App {
    constructor() {
	    this.app = express();
	    this.setMiddlewares();
	    this.setRouting();
	
        //특수한 Express 에러처리 미들웨어 생명주기로 인한 별도로 맨 마지막 등록
        this.app.use(basicErr);   // (추가) next() 로직이 없기때문에 여기서 끝난다.
	    this.app.use(customErr);
	
    }
    
    setRouting() {
        const routes = require('./routes'); 
        this.app.use('/', routes);
    }
    
    setMiddlewares() {
        this.app.use(logger);
    }
    
}

module.exports = new App().app;

여기서 중요한점은 생명주기이다. customErr 모듈은 Express의 특수한 에러처리 모듈이므로 모든 미들웨어어보다 마지막에 위치해야 했다.

근데 404등의 에러를 처리하는 basicErr 기본 에러 모듈이 마지막에 위치하면 customErr 이후에 무조건 실행되게 된다. 현재 customErr 로직안에 next() 가 있기 때문이다.

/middlewares/basicErr.js

const basicErr = function() {
    return function(req, res, next) {
        console.log('basicErr 미들웨어가 실행되었습니다.');
        res.status(404).send('[404 에러] 페이지가 없습니다.');  // 강제 발생
    }
}

module.exports = basicErr();

결과

# 서버측
GET /member/asdsadGAS             # 틀린 주소 입력
basicErr 미들웨어가 실행되었습니다.    # 에러처리 미들웨어

# 라우터에 해당주소가 없으므로 마지막에 basicErr 에러처리를 실행하게 되는 원리다.
// 클라이언트(브라우저)
[404 에러] 페이지가 없습니다.'

Leave a comment