TIL 8주차 Express 미들웨어 개념 / 에러처리

lim1313·2021년 9월 10일
0

부트캠프 TIL

목록 보기
30/49

Express

공식 문서 Express

기본 세팅

  1. npm init : package.json 설치

  2. npm i express

  3. package.json => scrips에 start : nodemon app 으로 설정하면 npm start로 nodemon 실행 가능

  4. npm i nodemon --save-dev
    : 개발자 모드에서는 nodemon이 필요함을 명시해 주어야한다.
    "devDependencies": {"nodemon":"^2.0.12"}

  5. "type":"module"로 설정하여 import export 사용할 수 있도록 한다.

package.json 기본 설정
{
  "name": "preexpress",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}

request 요청

예시) http://localhost:8080/sky/hello/hello2?this=4

app.get('/sky/:id/:id2', (req, res, next) => {
  console.log(req.path); // /sky/hello/hello2
  
  console.log(req.headers); // header의 정보
  
  console.log(req.params); // { id: 'hello', id2: 'hello2' }
  
  console.log(req.query); // { this: '4' }
  
  console.log(req.query.this); // 4
  
  res.status(201).send('this is get');
  
});

response 응답

res.send() vs res.json()

  • res.send('hello')
    : 데이터 보내기

  • res.json({ name: nano })
    : json 형태로 보내기

  • res.sendStatus(404)
    : status만 보내기

  • res.status(201).send('created')
    : status와 데이터 같이 보내기

  • setHeader('key','value')
    : 헤더 설정, key, value 인자를 작성


미들웨어

유용한 미들웨어 정리

미들웨어는 익스프레스 내에서 웹 요청(req)과 응답(res)에 대한 정보를 사용해서 필요한 처리를 진행할 수 있도록 분리된 독립적인 함수를 미들웨어라고 한다.

예를 들어서 요청-응답 사이에 에러처리, 인증확인, 라우팅 등을 수행해야 할 때, 미들웨어로 처리하는 것이다.

각각의 미들웨어는 next() 메소드를 호출해서 그 다음 미들웨어가 작업을 처리할 수 있도록 순서를 넘길 수 있다. next를 통해 미들웨어는 순차적으로 처리된다.

따라서, 미들웨어는 작성된 순서가 굉장히 중요하다.
때문에, callback 안에서 next()를 사용 혹은 res.send()와 같이 response처리를 해주어야한다.

app.get(
  '/',
  (req, res, next) => {
    console.log('helle');
    next(new Error('error')); 
    // => error를 받기 위해서는 error처리를 해주어야한다. ex) app.use((error...) => ...)
  },
  (req, res, next) => {
    console.log('hello2');
    next('route'); // => hello3은 건너뛰고 다음 app.get으로 간다.
  },
  (req, res, next) => {
    console.log('hello3');
    next();
  }
);

app.get('/', (req, res, next) => {
  console.log('end');
  res.send('end');
});

가장 마지막까지 어떠한 처리를 안해준 경로라면, app.use에서 처리를 해준다.
가장 아래에 작성한다.

app.use((req, res, next) => {
  console.log('end');
  res.status(404).send('Not Found, Not available');
});

미들웨어를 사용하는 상황

미들웨어를 자주 사용하는 상황

엔드포인트 확인, 바디 얻어오기, CORS처리, 인증확인, 라우팅, 에러처리

  • 모든 요청에 대해 url이나 메소드를 확인할 때

  • POST 요청 등에 포함된 body(payload)를 구조화할 때(쉽게 얻어내고자 할 때)

  • 모든 요청/응답에 CORS 헤더를 붙여야 할 때

  • 요청 헤더에 사용자 인증 정보가 담겨있는지 확인할 때

app.all vs app.use

all, use 모두 get, post, put, delete 모든 메소드에 대해서 응답한다.

하지만, all의 경우
첫번째 인자의 경로 이외의 경로에 대해서는 실행하지 않는다.

use의 경우
첫번째 인자의 경로를 포함한 다른 경로, 예를 들면 /use/use2와 같은 경로도 실행한다.

// ex) /all/all2 => 실행 되지 않는다. // /all/*라고 한다면 실행됨
app.all('/all', (req, res, next) => {
  console.log('allallall');
  next();
});

// ex) /use/use2 => 실행된다.
app.use('/use', (req, res, next) => {
  console.log('useuseuse');
  next();
});

POST 처리

request의 body를 parse하기 위해서는 express.json()을 사용한다.

import express from 'express';

const app = express();

app.use(express.json());

app.post('/',(req,rse,next) =>{
	console.log(req.body)
}

strict : false : 배열과 객체가 아니더라도 스트링 등 다양한 데이터 형태에 대해 JSON.parse를 해준다.

app.use(express.json({ strict: false }));

error 처리

import express from 'express';
import fs from 'fs';
import fsAsnyc from 'fs/promises';

const app = express();

app.use(express.json());

// 위의 어떤 미들웨어도 처리하지 않는 경로일 경우를 처리 
app.use((req, res, next) => {
  console.log('end');
  res.status(404).send('Not Found, Not available');
});

app.use((error, req, res, next) => {
  console.log(error);
  res.status(500).send('sorry, try later!');
});

app.listen(8080);

1. 동기적 실행 함수

ex) readFileSync

❌
app.get('/file2', (req, res, next) => {
  const data = fs.readFileSync('./file.txt');
});

app.use((error, req, res, next) => {
  console.log(error);
  res.status(500).send('sorry, try later!');
});

file.txt라는 파일이 없다면 에러메시지를 보내줘야 하는데, 이때 fs.readFileSync는 동기적으로 실행되고 에러가 발생하면 app.use에서 에러 메시지를 보낸다.

하지만, 아래와 같이 try, catch를 통해 상황에 맞는 적절한 에러 메시지를 보내는 것이 좋다.

⭕
app.get('/file2', (req, res, next) => {
  try {
    const data = fs.readFileSync('./file.txt');
  } catch (e) {
    res.status(404).send('file not found');
  }

2. 비동기적 실행 함수

ex) fs.readFile

콜백함수 안에 에러가 전달되기 때문에, 코드 아래 app.use((err..) => ...)로 에러를 잡아줄 수 없다.

❌
app.get('/file2', (req, res, next) => {
 const data = fs.readFile('./file2.txt');
})

에러가 발생하더라도 콜백으로 에러가 전달되기 때문에, 에러가 발생해도 알 수 없다.
때문에, callback함수에서 적절한 에러처리가 필요하다.

⭕
app.get('/file2', (req, res, next) => {
 fs.readFile('./file2.txt', (err,data) =>{
   if(err){
     res.status(404).send('File is not found')
   }
 })
});

3. promises 비동기 실행 함수

promsie 내부에서 에러가 전달되기 때문에 app.use((error..) => ...)로 에러를 잡을 수 없다.
때문에, promise의 catch를 통해 에러를 처리해 주어야 한다.

⭕
app.get('/file2', (req, res, next) => {
  fsAsnyc
    .readFile('./file.txt')
    .then((data) => {})
    .catch((err) => {
      console.log('catch');
      res.status(404).send('not found');
    });
});

혹은 catch안에 next를 통해 그 다음 미들웨어로 전달하여 app.use 에러 처리로 넘겨 줄 수 있다.

⭕
app.get('/file2', (req, res, next) => {
  fsAsnyc
    .readFile('./file.txt')
    .then((data) => {})
    .catch(next);
});

4. async await 비동기 함수

await 부분은 동기적으로 실행되지만, 미들웨어 자체는 promise를 반환한다.
promise 안에서 발생하는 에러이므로 catch를 이용해서 잡아야 한다.
즉, app.use에서 에러를 감지할 수 없다.

⭕
app.get('/file2', async (req, res, next) => {
  try {
    const data = await fsAsnyc.readFile('/file.txt'); //-> promise 내부에서 동기적으로 실행
  } catch (err) {
    res.status(404).send('not found');
  }
}); 

간단한 비동기 error 처리

npm i express-async-errors

import {} from 'express-async-errors
// require('express-async-errors')

promise도 마지막 app.use의 에러처리가 가능하도록 해준다.
미들웨어에서 promise를 반환하는 경우에만 감지를 하여 에러처리를 해준다.

⭕
app.get('/file2', (req, res, next) => {
    return fsAsnyc.readFile('./file.txt')
});

app.get('/file2', async (req, res, next) => {
   let data = await fsAsnyc.readFile('./file.txt')
});
profile
start coding

0개의 댓글