ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ExpressJS - express.js 활용1(next활용법, 자주 쓰는 미들웨어)
    FrameWork/Express.js 2024. 3. 19. 14:57

    express.js 활용

    next 활용법

    res.json과 return의 차이

    // 라우터에 return이 없는 경우
    app.get('/',(req, res) =>{
      res.json({hello:'express'});
      console.log('hello express');
    });
    
    // 라운터에 return이 있는 경우
    app.get('/',(req, res) =>{
      return res.json({hello:'express'});
      console.log('hello express');
    });

     

    res.json(), res.send()메서드를 return과 동일시 하기 쉬운데 엄밀히 말하면 res.json과 return은 다르다. 우선 res.json()은 응답을 보낼 뿐이지 함수를 끝내는 것은 아니다.  그래서 return이 없는 경우는 console.log('hello express')가 실행되는 반면에 return이 없는 경우는 실행되지 않는다.  왜냐하면 javascript에서는  함수에서 return을 호출하는 순간 함수가 종료되기 때문이다.

     

     

    매개변수가 존재하는 next()

    app.use((req, res,next) => {
        console.log('모든 요청에 실행하고 싶어요1');
        next();
    },
    (req, res, next) =>{
        try{
        //일부러 에러를 발생시키기 위해서
        //정의되지 않은 변수를 
        //console.log로 출력시킨다.
         console.log(abc);
        }catch (error){
        	console.error(error)
            next(error);
        }
    });

     

    next()메서드에 파라미터가 없이 호출 될때만 다음 미들웨어를 부르는 역할을 하고 next()함수에 매개변수가 들어가는 경우 해당 next()메서드의 경우 error로 처리되어서 다음 미들웨어로 넘어 가는 것이 아니라 바로 error 처리 미들웨어로 넘어가서 error를 반환한다. 그렇기 때문에 error를 처리할 때는 try ~ catch문의 catch문에 next()메서드에 error를 파라미터로 넣어서 처리한다. 

     

     

    next('route')

    app.get('/',(req, res, next) => {
        res.sendFile(path.join(__dirname,'index.html'));
        
        if(true){
            next('route')
        }
    },(req,res) =>{
        console.log('실행될까? false');
    });
    
    app.get('/',(req, res) => {
       console.log('실행 될까? true')
    });

    next('route')를 사용하면 같은 app.get() 안에 들어있는 즉 같은 라우터 다음 미들웨어가 실행되는 것이 아니라 지정된 URL은 같으면서 소스코드상 해당 라우터 아래쪽에 있는 다른 라우터로 실행 순서가 넘어간다. 

     

     

    next를  호출해야 다음 미들웨어가 호출

    • app.use()에서 next()메서드를 호출하지 않으면 응답이 전송 되지 않는다.
    • 다음 미들웨어(라우터 미들웨어)로 넘어가지 않기 때문이다.
    • next에 파라미터로 값을 넣으면 에러 핸들러로 넘어간다.('route'인 경우 다음 라우터로)

     

     

    에러 처리 미들웨어로 에러 보내기


    자주 사용하는 미들웨어(morgan, bodyParser, cookieParser)

    1. morgan('dev')

    const express = require('express');
    const path = require('path');
    const morgan = require('morgan')
    
    const app = express();
    
    app.set('port',process.env.PORT||3400);
    
    app.use((req, res,next) => {
        console.log('모든 요청에 실행하고 싶어요1');
        next();
    });
    
    app.use(morgan('dev'));
    
    app.get('/',(req, res, next) => {
        res.send("hellp express");    
    });

     

    morgan 모듈의 morgan('dev') 메서드를 사용하면 cosole.log에 1)HTTP 매서드  2)요청된 URL은 무엇이었는지   3)응답 상태 코드  4)응답 속도  5)응답바이 에 대한 정보를 출력해준다.

     

    morgan('combined')

    app.use(morgan('combined'));

     

     

    morgan('conbinde')는 morgan('dev')보다 더 자세한 정보를 출력해준다. 개발할때는 'dev'를 운영환경에서는 'combined'를 사용하는 것이 보편적이다.


     

    2.cookie-parser

    요청 헤더의 쿠키를 해석해주는 미들웨어이다.  cookie-parser는 요청(request)에 동봉된 쿠키가 있을 경 쿠키를 해석해 req.cookies 객체로 만든다. 더불어 express에서 더 편리한게 cookie와 관련된 조작을 할 수 있도록해준다. 

    const express = require('express');
    const path = require('path');
    const morgan = require('morgan')
    const cookieParser = require('cookie-parser');
    
    const app = express();
    
    app.set('port',process.env.PORT||3400);
    
    app.use((req, res,next) => {
        console.log('모든 요청에 실행하고 싶어요1');
        next();
    });
    
    app.use(morgan('dev'));
    app.use(cookieParser());
    
    app.get('/cookie',(req, res, next) => {
        //cookies 알아서 객체로 만들어준다.
        console.log(req.cookies); 
    
        //cookie 생성
        const name = "dote10";
        res.cookie('name',encodeURIComponent(name),{
            expires: new Date('2024-04-22'),
            httpOnly:true,
            path:'/cookie',
        });
    
        res.send("hello cookie");    
    });

     

     

    지정한 쿠키가 생겨서 console.log에 출력되는 모습을 확인할 수 있다.

    app.use(coookieParser(비밀키));

     

    비밀 키로 쿠키 뒤에 서명을 붙여 나의 서버가 만든 쿠키임을 검증할 수 있다.

     

    clearCookie()

    app.get('/cookie',(req, res, next) => {
        //cookies 알아서 객체로 만들어준다.
        console.log(req.cookies); 
    
        const name = "dote10";
    
        //cookie 생성
        // res.cookie('name',encodeURIComponent(name),{
        //     expires: new Date() + (1000 * 60 * 60)
        //     httpOnly:true,
        //     path:'/cookie',
        // });
    
        //쿠키 없애기
        res.clearCookie('name',{
            httpOnly:true,
            path:'/cookie',
        });
    
        res.send("hello cookie");    
    });

     

    cookie를 클리어한 후에 다시 해당 URL을 요청하자 req.cookies 객체에 name이라는 속성이 사라진 모습을 확인할 수 있다. res.clearCookie()를 메서드를 사용할 때 주의 할 점은 쿠키를 설정할때와 동일한 httpOny, path 의 속성값을 넣어주너야 한다.


    3.body-parser

    요청 본문에 있는 데이터를 해석해서 req.body 객체로 만들어주는 미들웨어이다. 보통 form 데이터나 AJAX 요청의 데이터를 처리한다. 단, 멀티파트(이미지,동영상,파일) 데이터는 처리하지 못한다. 그 경우에는 multer 모듈을 사용해야 한다.

    app.use(express.json());
    // from을 이용해서 submit 할때 사용
    // ture면 qs, false면 queryString
    app.use(express.urlencoded({extended: false}));

     

    흔히 책이나 코드에서 body-parser를 설치하는 것을 볼 수 있다. 하지만 express 4.16.0 버전부터 body-parser 미들웨어의 일부 기능이 익스프레스 내장되었으므로따로 설치할 필요가 없다.

     

    단, body-parser를 직접 설치해야 하는 경우도 있다. body-parser는 JSON과 URL-encoded 형식의 데이터 외에도 Raw, Text 형식의 데이터를 추가로 해석할 수 있다.

    Raw는 요청의 본문이 버퍼 데이터일때, Text는 텍스트 데이터일 때 이를 해석 하는 미들웨어로 body-parser가 필요할 수있다. 버퍼나 텍스트 요청을 처리할 필요가 있다면 body-parser를 설치한 후에 다음과 같이 추가한다.

    const bodyParser= require('body-parser');
    
    // 바이너리 데이터
    app.use(bodyParser.raw());
    // 문자열
    app.use(bodyParser.text());

     

    요청 데이터 종류를 간단히 살펴보자 JSON은 JSON 형식의 데이터 전달 방식이고, URL-encoded는 주소 형식으로 데이터를 보내는 방식이다. 폼 전송은 URL-encoded 방식을 주로 사용한다. urlencoded 메서드를 보면 {extended: false}라는 옵션이 들어 있다. 이 옵션이 false면 노드의 querystring 모듈을 사용하여 쿼리스트링을 해석하고, true면 qs 모듈을 사용하여 쿼리스트링을 해석한다. qs 모듈은 내장 모듈이 아니라 npm 패키지 이며 querystring 모듈의 기능을 좀 더 확장한 모듈이다.

     

    http 모듈을 사용할 때 POST와 PUT 요청 본문을 전달 받으려면 req.on('data')와 req.on('end')스트림을 사용해야했다. 하지만 body-parser를 사용하면 그럴 필요가 없다 해당 패키지가 내부적으로 스트림을 처리해 req.body에 추가한다.


     

    4.static 미들웨어

     

    static 미들웨어는 정적인 파일들을 제공하는 라우터 역할을 한다. 기본적으로 제공되기 때문에 따로 설치할 필요 없이 express객체 안에서 꺼내 장착하면 된다.

    app.use('요청 경로',express.static('실제 경로'));
    app.use('/', express.static(path.join(__dirname,'public')))

     

    함수의 파라미터로 정적 파일들이 담겨 있는 폴더를 지정하면 된다. 위 코드에는 public 폴더가 지정되어 있다. 예를 들어 public/stylesheets/style.css는 http://localhost:3000/stylesheets/style.css로 접근할 수 있다. public 폴더를 만들고 css나 js 이미지 파일들을 public 폴더에 넣으면 브라우저에서 접근할 수 있게 된다. 

     

    실제 서버의 폴더 경로에는 public이 들어 있지만, 요청 주소에는 public이 들어있지 않다는 점을 주목하라. 서버의 폴더 경로와 요청 경로가 다르므로 외부인이 서버의 구조를 쉽게 파악할 수 없다. 이는 보안에 큰 도움이된다.

     

    또한 정적 파일들을 알아서 제공해주므로 HTTP 모듈을 사용해서 서버를 구축할 때 처럼 fs.readFile로 파일을 직접 읽어서 전송할 필요가 없다. 만약에 요청 경로에 해당하는 파일이 없으면 알아서 내부적으로 next를 호출한다. 만약 파일을 발견했다면 다음 미들웨어는 실행되지 않는다. 응답으로 파일을 보내고 next를 호출하지 않기 때문이다. 

     

    static 미들웨어의 위치 

    // 보통의 순서 
    app.use(morgan('dev'));
    app.use('/',express.static(path.join(__dirname,'static')));
    app.use(cookieParser('dote10password'));
    app.use(express.json());
    app.use(express.urlencoded({extended:true}));
    
    
    // 로그인을 통한 접근 제어 순서
    app.use(morgan('dev'));
    app.use(cookieParser('dote10password'));
    app.use(session());
    app.use('/',express.static(path.join(__dirname,'static')));
    app.use(express.json());
    app.use(express.urlencoded({extended:true}));

    또 mogan과 body-parser와 같은 미들웨어는 default 상태가 내부적으로 next() 메서드를 실행하고 있는 것과 다른게 static 미들웨어는 default로 실행하는것이 아니라 요청한 url과 매칭되는 static 리소스를 가지고 있지 않을 때 next() 메서드가 호출된다. 따라서 정적파일만을 요청할 경우에는 다른 미들웨어를 거치는 것 보다 우선적으로 static 미들웨어에 우선적으로 접근하는 것이 좋기 때문에 해당 미들웨어는 소스코드에서 보통 morgan 미들웨어 다음에 위치하게 된다. 


     

    6.express-session 미들웨어

    세션 관리용 미들웨어이다. 로그인 등의 이유로 세션을 구현하거나 특정 사용자를 위한 데이터를 임시적으로 저장해 둘 때 매우 유용하다. 세션은 사용자별로 req.session 객체 안에 유지된다.

    app.use(session({
        resave: false,
        saveUninitialized:false,
        secret: process.env.COOKIE_SECRET,
        cookie:{
            httpOnly:true,
            secure:false,
        },
        name:'session-cookie',
    }));

     

    express-session 1.5 버전 이전에는 내부적으로 cookie-parser를 사용하고 있어서 cookie-parser 미들웨어보다 뒤에 위치해야 했지만, 1.5 버전 이후 부터는 사용하지 않게 되어 순서가 상관 없어 졌다. 그래도 현재 어떤 버전을 사용하고 있는지 모근다면 cookie-parser 미들웨어 뒤에 놓는 것이 안전하다.

     

    • 세션 쿠키에 대한 설정(secret:쿠키 암호화, cookie: 세션 쿠키 옵셔)
    • 세션 쿠키는 앞에 s%3A가 붙은 후 암호화 되어 프런트에 전송된다. 

     

    express-session은 파라미터로 세션에 대한 설정을 받는다. resave 요청이 올 때 세션에 수정 사이 생기지 않더라도 세션을 다시 저장할지 설정하는 것이고, saveUninitialized는 세션에 저장할 내역이 없더라고 처음부터 세션을 생성할지 설정하는 것이다. 현재는 둘 다 필요 없으므로 false로 설정한다.

     

    express-session 은 세션 관리 시 클라이언트에 쿠키를 보낸다. 안전하게 쿠키를 전송하려면 쿠기에 서명을 추가해야 하고, 쿠키를 서명하는 데 secret 값이 필요하다. cookie-parser의 secret과 같게 설정하는 것이 좋다. 세션 쿠키의 이름은 name 옵션으로 설정한다. 기본 이름은 connect.sid이다.(서명되어 있기 때문에 읽을 수 없는 문자열로 encode되어있다.) 

     

    cookie 옵션은 세션 쿠키에 대한 설정이다. maxAge, domain, path,expires, sameSite, httpOnly, secure등일반적인 쿠키 옵션이 모두 제공된다. 위 설정은 httpOnly를  true로 설정해 클라이언트에서 쿠키를 확인하지 못하도록 했고, secure는 false로 해서 https가 아닌  환경에서도 사용할 수 있게 했다. 배포 시에는 https를 적용하고 secure도 true로 설정한는 것이 좋다.

     

    예제 코드에는 나와 있지 않지만, store라는 옵션도 있다. 현재는 메모리에 세션을 저장하도록 되어 있다. 문제는 서버를 재시작하면 메모리가 초기화되어 세션이 모두 사라진다는 것이다. 따라서 배포 시에는 store에 데이터베이스를 연결하여 세션을 유지하는 것이 좋다. 보통 레디스가 자주 사용된다. 

    req.session.name = 'dote10'; //세션 등록
    req.sessionID; //세션 아이디 확인
    req.session.destroy(); //세션 모두 제거

     


    7.미들웨어간 데이터 전달하기

    req나 res 객체 안에 값을 넣어 데이터 전달 가능

    • app.set과의 차이점: app.set은 서버 내내 유지, req,res는 한번의 요청 동안만 유지한다.
    • req.body 나 req.cookies 같은 미들웨어의 데이터와 겹치지 않게 조심한다.
    app.use((req, res, next) => {
      req.data = '데이터 넣기';
      next();
    },(req, res, next) => {
      console.log(req.data); //데이터 받기
      next();
    });

    3.미들웨어 확장하기

    미들웨어 안에 미들웨어 넣는 방법

    • 아래 투 코드는 동일한 역할
    app.use(morgan('dev'));
    //또는
    app.use((req,res,next) =>{
      morgan('dev')(req, res, next);
    });

     

    • 아래처럼 다양하게 활용 가능(미들웨어 확장법)
    // 미들웨어 확장법
    app.use((req, res, next) =>{
      if(process.env.NODE_ENV === 'production'){
      morgan('combine')(req, res, next);
      }else{
        morgan('dev')(req,res,next);
      }
    });
    
    // 로그인 한사용자에게만 
    // static 리소스 접근 권한 허용
    app.use((req,res,next) =>{
     if(req.session.id){
       static(path.join(__dirname,'static'))(req, res, next);
     }else{
       next();
     }
    });

     

     

     

    [출처 - [개정3판]Node.js 교과서 - 기본부터 프로젝트 실습까지, 저 조현영]

    https://www.inflearn.com/course/%EB%85%B8%EB%93%9C-js-%EA%B5%90%EA%B3%BC%EC%84%9C

     

    [개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지 강의 - 인프런

    노드가 무엇인지부터, 자바스크립트 최신 문법, 노드의 API, npm, 모듈 시스템, 데이터베이스, 테스팅 등을 배우고 5가지 실전 예제로 프로젝트를 만들어 나갑니다. 클라우드에 서비스를 배포해보

    www.inflearn.com

     

    댓글

Designed by Tistory.