인프런 커뮤니티 질문&답변

Like me black님의 프로필 이미지
Like me black

작성한 질문수

[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지

express session 설정 질문드립니다

해결된 질문

작성

·

1.4K

0

강사님 안녕하세요

질문드리기 전에, http 쿠키와 세션 강의를 먼저 복습했습니다

 

아래처럼 정리했습니다

서버측에서 클라이언트를 구분하기 위해 보내는 게 쿠키

쿠키의 중요한 정보를 클라이언트의 브라우저로 보내면 보안위험이 있고

그래서 쿠키의 중요한 정보는 서버에서 갖고, 클라이언트에게 안보내서,

브라우저에서는 중요정보는 못알아내도록 서버에서 관리하는 게 세션

예를 들면 4장의 session.js에서는 쿠키의 name 대신, 현재 시간을 보낸다

라고 복습하고 http 세션 코드도 복습했습니다

 

그리고나서 cookie2.js를 익스프레스 코드로 변환시킨 코드를
이번에는 session을 이용한 코드로 변경해보려고 했습니다

 const express = require('express');
 const path = require('path');
 const { nextTick, rawListeners } = require('process');
 const morgan = require('morgan');
 const cookieParser = require('cookie-parser');
 const bodyParser = require('body-parser');
 const session = require('express-session');
const { connect } = require('http2');
 
 const app = express();

 app.set('port', process.env.PORT || 3000); //'port' 라는 속성에 포트번호 3000번을 설정합니다.
 //서버의 포트를 3000번으로 지정합니다.
 
app.use(morgan('dev'));

 //app.use(morgan('dev'));
 //쿠키를 객체화 시킵니다.
 app.use(cookieParser('zerochopassword'));
//cookieparser를 사용하기 위해


//app.use('/',express.static(__dirname, 'kkk'))
app.use(express.static(path.join(__dirname, 'kkk')))

 app.use(express.json());
 app.use(express.urlencoded({extended: true}));
//post요청에 의한 req.body를 사용하기 위해서

console.log(path.join(__dirname, 'kkk'))

 //login 경로의 경우입니다.
 app.get('/login', (req, res, next) => { 
   req.cookies // 쿠키 객체화  
 
  const expires = new Date();
  expires.setMinutes(expires.getMinutes() + 5);
   res.cookie('name', encodeURIComponent(req.query.name),{
     expires: expires,
     httpOnly: true,  
     path: '/',
   })

  //  app.use(session({
  //   name: 'connect.sid',
  //   resave: false,
  //   saveUninitialized: false, 
  //   secret: 'zerochopassword',
  //   cookie:{
  //     expires: expires,
  //     httpOnly: true,
  //     path: '/',
  //   }, 
  // }));

  //  req.session.id = req.query.name;
   res.redirect('/');
});
 
app.get('/', (req,res)=>{
  console.log("req.url "+req.url);
  if(req.cookies.name)//name이라는 쿠키가 있는 경우입니다.
  {
   //res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
   res.send(`${req.cookies.name}님 안녕하세요`);//쿠키에 넣은 이름이 웹페이지에 출력됩니다
  }
  else{ //로그인도 아니고, 쿠키도 없는 경우입니다.
    //next(createError(404));
   try {
    // console.log("진입");
    //res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
    //익스프레스에서는 writeHead를 쓰면 안된다 에러가 생긴다
    //res.send( ) 할 때 이미 자동으로 res.status().send() 이런 식으로 헤더를 설정해준다
   
    //res.sendFile(path.join(__dirname, '/cookie2.html'));//cookie2.html 파일을 클라이언트에게 보내준다
    //console.log(err.status)
    console.log("진입");
    res.setHeader('Content-Type', 'text/html');
    //익스프레스에서는 writeHead를 쓰면 에러가 생긴다 그러므로 setHeader를 써라
    res.sendFile(path.join(__dirname, 'cookie2.html')); //cookie2.html 파일을 클라이언트에게 보내준다
    } catch (err) {
    //  res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    //  res.end(err.message);
    next(err); //콜백함수에 err 라는 인자가 있는 app.use로 이동한다
  }
 }
});

// app.get('/:id',(req,res)=>{
//   console.log("req.url "+req.url);
//   console.log("req.params.id "+req.params.id);
  
//   res.send(`Hello ${req.params.id}`);
// })

//아래는 에러처리입니다.
app.use((req,res,next)=>{ // 찾는 경로가 없으면 get을 다 지나서 use로 온다
  console.log("req.url "+req.url);
  next(createError(404)); //찾는 경로가 없으면 404처리
})

 app.use((err,req,res,next)=>{  //   /favicon.ico도 여기로 간다   에러도 여기로 간다
  console.log("req.url "+req.url);
  console.log(res.locals.message);
  res.locals.message = err.message;
  res.locals.error = req.app.get(`env`) === `development` ? err:{};
  console.log(err.status)
  res.status(err.status ||500).send(err.message);
 });
 
 app.listen(3000, () => {
   console.log(app.get('port'), '번 포트에서 서버 대기 중입니다!');
 });



 

 

저는 아래처럼 변경해봤습니다

 

공식문서도 참고했습니다

프로덕션 환경의 Express를 위한 보안 우수 사례 (expressjs.com)

 

 

 

그런데 로그인을 하면 세션이 안생겨서 혹시 제가 잘못 알고있는 부분을 가르쳐주시면 감사하겠습니다

 

+

그리고 63행과 66행에서 req.cookies.name을 req.session.id로 변경해도 에러가 해결이 안되더라구요

 

 

 

답변 2

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

4번째 질문은 저는 그런 말을 한 적이 없습니다. 전부다 쓰이는 곳이 따로 있는 것이지 뭐가 더 낫다 개념이 아닙니다.

Like me black님의 프로필 이미지
Like me black
질문자

bodyparser를 쓰면 옛날사람이라고 말씀하신 부분을, 제가 req.body와 혼동한 것 같습니다
bodyParser는 deprecated 되었다는 맥락으로 말씀하신 것 같은데
제가 req.body와 동일시해서 생각한 것 같습니다 죄송합니다

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

이것도 완전 포인트를 잘못 잡고 계신데요. express-session은 res.cookie같은 걸 쓰는 게 아닙니다. 그리고 app.get 안에서 app.use를 써서도 당연히 안 되고요.

그냥 req.session에다가 값을 넣으면 알아서 브라우저로 connect.sid 쿠기가 보내집니다.

req.session.id는 수정하실 수 없고요.

req.session.name = req.query.name만 하면 됩니다.

4장에서 한 건 진짜 수동으로 직접 한 것이고, 6장에서 하는 건 익스프레스에서 편의를 제공해서 편하게 하는 겁니다. 4장에서보다 6장 코드가 길다? 그럼 문제가 있는 겁니다.

Like me black님의 프로필 이미지
Like me black
질문자

답변해주셔서 감사합니다

아래처럼 수정하니, 원하는 식으로 잘 해결되었습니다

 

다만, 제가 디버깅을 하면서 이해가 안되는 부분이 있어서 생각을 정리하고 다시 질문드리겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

드리고 싶은 질문이 3가지가 있습니다
제가 너무 많은 질문을 드리는 것 같은데, 어려워서 가르쳐주시면 감사하겠습니다


1번째로 드리고 싶은 질문은

아래 사진 23행을 봐주시면, name:connect.sid가 

조사식에서 req.session.name 이 아닌, req.session.id 를 의미하는 것으로 이해해도 되는지 질문드리고 싶습니다

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

아뇨 connect.sid는 req.session.name도 아니고 req.session.id도 아니죠. connect.sid는 쿠키 이름입니다. 그 값으로 req.session.id를 찾을 수 있는 것이고요.

Like me black님의 프로필 이미지
Like me black
질문자

2번째로 드리고 싶은 질문이 있습니다

이렇게 1대1 대응해서 변환하는 거라고 이해해도 되는지 질문드리고 싶습니다
변환한 코드도 결과는 거의 동일하게 나오는 것 같은데,
제가 제대로 이해하고 쓴 건지 확신이 안서서요

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

네 맞습니다. 익스프레스 세션이 알아서 해주는 작업들이 있어서 완벽하게 1대1은 아닙니다. 그런데 session.js는 구린 코드고 익스프레스쪽은 좋은 코드이므로 굳이 구린 코드와 1대1 대응시킬 필요가 없습니다.

Like me black님의 프로필 이미지
Like me black
질문자

3번째로 드리고 질문입니다
session이 서버에서 관리하는데 res.session 이 아닌 req.session 인 이름이 궁금한데, 이건 그저 더 이해하려고 하면 안되는 익스프레스 약속 정도로 넘어가야겠죠?

4번째로 드리고 싶은 질문도 있는데요
session을 쓰는 게 쿠키를 쓰는 것 보다 좋잖아요

그러면 현업자분들은
GET 요청은 req.query를 주로 쓰고
POST 요청은 req.session을 주로 쓰나요?

저번 수업에서 req.body를 쓰면 옛날 스타일이고, req.cookies를 쓰면 최신 스타일이라고 가르쳐주신 게 기억나서요
session 코드를 사용하면 cookie 코드는 사용 안하니까
req.cookies보다도 req.session을 더 많이 사용하시나요?

Like me black님의 프로필 이미지
Like me black
질문자

질문 적는 동안 1번째, 2번째 질문에 대해 가르쳐주신 걸 못봤네요 고맙습니다

Like me black님의 프로필 이미지
Like me black
질문자

3번째 질문인 session 객체가 왜 res가 아닌, req.session인지는 4강 http 부분을 다시 복습해보겠습니다
추측하기로는 브라우저로 응답할 때, session.name을 세팅을 하는 게 아니라,
브라우저로부터 서버가 요청을 받을 때, session.name을 세팅하게 되어서 그런 것 같기도 하네요

Like me black님의 프로필 이미지
Like me black
질문자

강사님 문득 궁금해진 게 있습니다
http 모듈에서는 session[고유값] 이렇게 객체 배열의 인덱스에 고유값이 들어가고
익스프레스에서는 req.session.id = 고유값 이렇게 id 속성에 고유값이 들어가더라구요

그러다가 한번 생각해보니
서버쪽에서 클라이언트의 name을 알아내고 싶으면
그 클라이언트의 고유값을 알아내서 session[고유값].name 이렇게 접근하면 될 것 같았습니다

그런데, 익스프레스에서는
req.session이 배열이 아니라서
고유값을 알아내도 접근 자체가 불가능할 것 같더라구요

왜냐하면 클라이언트가 로그인할 때 고유값이 10 인 것을 알아내도
http에서는 session[10].name 으로 클라이언트 아이디를 알아낼 수가 있었습니다

이렇게 session이 배열이면 여러사람의 세션쿠키를, 서버에서 한번에 관리한다는 게 느껴졌습니다
어떤 클라이언트는 고유값이 10이고 어떤 클라이언트는 고유값이 20이면
이 사람들의 세션정보는 session[10], session[20] 이렇게 서버에 보관이 되는데요

반면에 익스프레스에서는 

한 사람의 세션쿠키만 관리하는 것 같았습니다

req.session은 배열이 아니라서 그렇게 느껴졌습니다

어떤 클라이언트는 서버에 접속할 때 req.session.id 가 200이고

어떤 클라이언트는 서버에 접속할 때 req.session.id가 300이면

req.session은 배열이 아닌데, 이 여러사람의 정보를 담는다는 게 이해가 어려웠습니다

req.session이 배열이 아닌데, 익스프레스는 어떻게 많은 클라이언트의 로그인 정보를 알 수 있는지 질문드리고 싶습니다

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

req.session인지 res.session인지는 의미가 없습니다. 익스프레스가 그렇개 정한거라서요.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

req.session은 그때그때 메모리에서 세션 객체를 불러오는 겁니다. req.session에 세션을 저장하는게 아니라 세션에서 찾아서 req.session에 담는겁니다.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

그래서 배열일 필요가 없습니다. 메모리가 배열이겠죠.

Like me black님의 프로필 이미지
Like me black
질문자

가르쳐주셔서 감사합니다
그러면 익스프레스에서는
http처럼 Session[고유값 인덱스] 이렇게 임의의 유저 정보로 접근 못하나보네요

익스프레스가 다중 접속자의 세션을 관리하게 될 때의 소스코드가 많이 궁금해지네요 
다음 진도로 우선 넘어가보고, 그러다보면 제가 원하는 해답이 있을 거라 믿고 다음 진도로 넘어가겠습니다

많은 질문을 받아주시고 가르쳐주셔서 감사합니다

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

익스프레스 세션 코드를 까보시면 좋을 것 같습니다.

Like me black님의 프로필 이미지
Like me black
질문자

넵 혹시 까보라는 말씀을 제가 이해를 못해서, 전체 소스코드를 보여달라는 말씀이신가요..?
우선 express-session 미들웨어를 가르쳐주신 강의를 다시 듣겠습니다

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

아뇨 express-session 라이브러리 내부 소스 코드를 분석해보세요.

Like me black님의 프로필 이미지
Like me black
질문자

네 공식문서 참고해보면서 구글링도 해보고, 이해 안되는 부분은 다시 질문드리겠습니다

Like me black님의 프로필 이미지
Like me black
질문자

아 라이브러리 내부 소스 코드면 너무 깊게 들어가는 거군요 다음 진도로 넘어가겠습니다

Like me black님의 프로필 이미지
Like me black

작성한 질문수

질문하기