Skip to main content

Command Palette

Search for a command to run...

ExpressJs Error

Updated
3 min read

저번 포스트에서는 서버 구동에 대해서 포스팅했다. 이번에는 Error 처리에 대해 포스팅 하겠다.

기본적인 에러처리

app.get('/', (req, res) => {
    throw new Error('에러 발생')
})
app.use((err, req, res, next) => {
    console.log(err.message)
})

위와 같이 처리를 하면 큰 문제가 생긴다. 아래와 같이 callback을 받아 error 처리를 할 시 절대로 에러를 잡지 못한다.

app.get('/', (req, res) => {
    callback(error => {
        throw new Error('에러 발생')
    });
})

app.use((err, req, res, next) => {
    console.log(err.message)
})

그래서 하는 방식이 next를 통한 에러를 전달하는 방식이다.

app.get('/', (req, res, next) => {
    callback(error => {
        if(error) return next(error);

        callback2(error => {
            if(error) return next(error);
        });
    });
})
app.use((err, req, res, next) => {
    console.log(err.message)
})

이렇게 처리시 2가지의 문제점을 가지고 있다.

  • 로직 및 모든 부분에 Error 처리를 해줘야 한다. (이거 의외로 되게 유지보수하기 힘들고, 귀찮은 작업이다. 깜빡 한번하면 그냥 죽어버린다.)
  • 내가 Error 처리를 하지 못하는 부분에서는 Error처리를 할 수 없다.(모듈 안에서 에러가 났던가, 기타 등등)

위와 같은 문제 때문에 필자는 node-domain-middleware 미들웨어를 사용하여 에러처리를 했다.(편하고 좋았다.) 하지만 expressJs의 성능우수 사례를 보면 도메인 사용을 권장하지 않고 더이상 사용되지 않는 모듈이라고 되어 있다.

프로미스를 이용한 에러 처리

es2015에 있는 Promise를 이용하여 Error 처리 하는 방식이다.

app.get('/', (req, res, next) => {
    callback()
        .then(_ => {
            // 로직1
        })
        .then(_ => {
            // 로직2
        })
        .catch(next);
})
app.use((err, req, res, next) => {
    console.log(err.message)
})

위와 같이 하면 catch를 통해 promise로 처리하는 부분의 모든 error를 처리 할 수 있다. 위에 단점으로 적었던 2가지 전부를 해결 할 수 있다. 나의 생각 일 수 있지만 코드도 좀더 간결해 보인다(아닐수도 있다.) 하지만 여기서 코드를 좀더 간결하게 해보도록 하겠다. es7 스팩인 async/await를 이용할 것이다.

async/await 이용한 에러 처리

async/await가 다소 생소 할 수 있다. 비동기 코드를 동기화 간편하게 해주는 것이다. es7에 제안된 스팩이며, 자세한 내용은 이곳을 보자. 구글 검색해도 많이 나온다.

async/await 사용하기 위해서는 Babel 혹은 typescript 등을 사용해야된다. 필자는 typescript를 사용한다.

  1. 처음에는 우선 아래와 같이 (req, res, next) 부분을 감싸도록 하겠다.

    const wrap = fn => (...args) => fn(...args).catch(args[2]);
    

    "...args"는 es2015 문법이다. 이렇게 사용하면 args안에 parameter 값들이 순차적으로 들어가게된다. wrap은 함수를 받아서 그 함수를 실행 하고 catch를 통해 error가 발생시 next(error)을 해주는 역활이다. router에서는 기본적으로 paramter를 req, res, next를 주기 때문에 args[2]는 next 이다.

    이걸 풀어서 아래와 같이 할 수 있다.

    const wrap = fn => (req, res, next) => fn(req, res, next).catch(next);
    
  2. 만든 wrap을 아래와 같이 한다.
    app.get('/', wrap(async (req, res, next) => {
    let data = await callback();
    let data2 = await 로직1();
    /* 리턴 값이 없음 아래와 같이 써도 된다. */
    await 로직2();
    }))
    app.use((err, req, res, next) => {
    console.log(err.message)
    })
    
    코드를 보면 짐작하시겠지만 async/await를 통해 비동기 로직을 동기식으로 간편하게 로직처리 한다.

여기에도 유의점이 있다. 이것을 사용하기 위해서는 return promise 이어야된다. 단순 callback에 대한 처리를 할 수 없다. 하지만 많은 모듈 혹은 미들웨어가 promise를 제공(?)하기 때문에 사용하기에는 불편함이 없다.(기존 로직은 async/await로 처리하면되며, mongoose나 mysql 같은경우 이미 promise를 사용 할 수 있어 큰 불편이 없다.)

결론

직접 이렇게 하고 사용을 하면 생산성이 확실히 빨라진다. 코드도 짧아지고 가독성도 좋아 진다. 이렇게 한번 쓰기 시작하면서 모든 노드 프로젝트는 이 방식으로 개발하고 있다. 필자는 이 방식(방법)을 추천한다.

참고

반말로 블로그를 작성하였는데 이해해주시기 바랍니다. 문의 및 수정 사항은 댓글이나 mayajuni10@gmail.com으로 이메일 보내주시기 바랍니다.

More from this blog

법률 AI 검색 실험기 (3) — 복수 정답 문제와 LLM Selector 모델 비교

검색 결과에서 정답을 "선택"하는 것도 문제다 법률 QA 시스템에서 검색(retrieval) 품질은 기본 전제다. 검색이 어느 정도 궤도에 오르자, 다음 병목이 드러났다. Top-50 검색 결과 안에 정답 근거가 들어 있는데도 최종 답변에서 빠지는 경우가 생긴 것이다. 예를 들어 "택배 배송 중 물건이 파손되었을 때 누구에게 책임을 물을 수 있는가?"라는 질문에 대해, 검색 결과에는 민법 제756조(사용자책임)가 포함되어 있었다. 그런데 LLM...

Apr 7, 20265 min read4

법률 AI 검색 실험기 (2) — 임베딩 모델 5종 벤치마크: 법률 도메인 실전 비교

법률 RAG 시스템에서 가장 먼저 결정해야 하는 것은 "어떤 임베딩 모델을 쓸 것인가"다. MTEB 리더보드 점수가 높다고 해서 우리 도메인에서도 잘 동작하리라는 보장은 없다. 한국 법률 조문이라는 특수한 코퍼스 위에서, 실제 질문셋으로 직접 비교하는 것이 유일한 방법이다. 이 글에서는 임베딩 모델 5종을 동일 조건에서 평가한 과정과 결과를 공유한다. 모델 선택 하나가 retrieval 성능의 천장을 결정한다. 평가 대상: 임베딩 모델 5종 ...

Apr 6, 20266 min read10

법률 AI 검색 실험기 (1) — 벡터 검색이 실패하는 이유

도입: 법률 QA를 만들면서 마주한 첫 번째 벽 법률 질의응답 시스템을 만드는 일은, 처음에는 RAG(Retrieval-Augmented Generation)의 교과서적 응용처럼 보였습니다. 법 조문을 임베딩해서 벡터 DB에 넣고, 사용자 질문과 유사한 조문을 검색한 뒤, LLM이 답변을 생성하면 되니까요. 실제로 단일 정답 질문 -- "주택임대차보호법상 대항력은 언제 취득하나요?" 같은 -- 에는 이 방식이 잘 작동했습니다. 해당 조문과 질문...

Apr 6, 20266 min read13

CTO로 한해를 보내며.. (2019 회고)

2020년 설이 지나서야 2019년 회고의 글을 씁니다. 목차를 만들어서 하나씩 회고를 하면서 써나갈까 합니다. 2019년은 정말 다사다난했습니다. 큰일들을 위주로 회고를 시작할까 합니다. CTO가 되다.. ITAM GAMES는 2018년에 시니어 개발자로 입사하게 되었습니다. 개발자로서 만족하면서 개발 일을 프런트, 백앤드를 가리지 않고 개발을 했습니다. 2018년 말 전 CTO님께서 회사를 퇴사하면서 저희 대표님은 저에게 CTO 직을 제시...

Jan 28, 20209 min read20

Serverless를 선택한 이유(Lambda, Altas)

CTO를 맡으면서 제가 선택하고 실무에 적용하면서 경험한 Serverless에 대해서 글을 남기려고 합니다. Serverless? 여기에 와서 글을 읽으시는 분들은 Serverless가 무엇인지 충분히 알고 있을 거라고 생각합니다. 그래도 간단하게만 얘기한다면 진짜 Server가 없는 것은 아니고 Server를 신경 쓰지 않아도 서비스를 할 수 있게 하는 기술이라고 보면 됩니다. 조금 더 있어 보이게 얘기한다면 애플리케이션 개발자가 서버를 ...

Jan 17, 20206 min read5
D

Dongjun's Blog

15 posts