foward & cookie

이지우·2024년 6월 24일
0

멋사

목록 보기
11/16

foward

서버에는 data가 넘어오지만 DB에는 반영되지 않게
edit으로 post요청
보통 화면 보기는 get방식
post로 하는 이유
: content에서 edit으로 넘길 데이터가 많아서

  • 제목을 클릭하면 나오는 상세페이지를 모달로 띄움
  • 모달을 이용하여 바로 수정이 가능하도록 변경
  • 수정된 내용은 바로 목록에 반영하여 보여지도록 함
  • _id에 따라 상세페이지 띄우는 /content와 수정을 위한 /edit, edit.ejs가 필요없어짐
    (modal에 를 추가하여 id를 받아 사용)
  • input 태그에 readonly 속성추가하면 편집이 안됨

updateOne(조건, 변경항목)

app.post('/update', (req, res) =>{
  console.log(req.body);
  mydb.collection('post')
    .updateOne({_id:new ObjId(req.body._id)},
    {$set: {title:req.body.title, content:req.body.content, date:req.body.someDate} })
    .then(result => {
      //res.redirect('/list');
      list(req,res);
    })
    .catch(err => {
      console.log(err);
      res.status(500).send();
    });
});

list.ejs

<!-- The Modal -->
<form action="/update" method="post">
  <span id="hiddenSpan"></span>
  <!-- db update를 위한 _id 값을 담기 위함(숨김처리) -->  
  <div class="modal" id="myModal">
    <div class="modal-dialog">
      <div class="modal-content">
  
        <!-- Modal Header -->
        <div class="modal-header">
          제목 : <div><h4 class="modal-title" id="postTitle"></h4></div>        
          <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
        </div>
  
        <!-- Modal body -->
        <div class="modal-body" >
          <div>
            내용 : <span id="postContent"></span>
          </div>
          <p></p>
          <div>
            작성일 : <span id="postDate"></span>
          </div>
          
        </div>
  
        <!-- Modal footer -->
        <div class="modal-footer">  
          <span id="updateSpan">
            <button type="button" class="btn btn-info" onclick="editDisplay()">수정하러가기</button>
          </span>      
          
          <button type="button" class="btn btn-danger" data-bs-dismiss="modal">Close</button>
        </div>
  
      </div>
    </div>
  </div>
</form>

...

<tr>    
  <!-- 타이틀 부분을 클릭하면 모달 띄워지게
	   이때 모달창에 data가 전달됨 -->
  <td data-bs-toggle="modal" data-bs-target="#myModal">
    <a href="#" onclick="modal_content(`<%= data[i]._id %>`,`<%= data[i].title %>`,`<%= data[i].content %>`, `<%= data[i].date %>`)"><%= data[i].title %></a></td>
  <td><%= data[i].date %></td>
  <td><button class = 'delete btn btn-outline-danger' data-id = '<%= data[i]._id %>'>삭제</button></td>
</tr>

...

<script>
  let modalData;

  // 모달창에 보이게할 내용 설정 함수
  function modal_content(_id, title, content, date){                
    modalData={_id, title, content, date};
    $('#postTitle').text(`${modalData.title}`);                
    $('#postContent').text(`${modalData.content}`);
    $('#postDate').text(`${modalData.date}`);
    $('#updateSpan').html(`<button type="button" class="btn btn-info" onclick='editDisplay()'>수정하러가기</button>`);
    // 마지막 줄이 추가되어 있지 않으면 '수정하러가기'가 아닌 '수정' 버튼이 뜰 수 있음 (동적이라서 생기는 문제)
  }
  
  // 수정하기 버튼 클릭 시 수정 가능하게 바꿔주는 함수
  function editDisplay() {
    //console.log(modalData._id, modalData.title, modalData.content, modalData.date);
    $('#hiddenSpan').html(`<input  type='hidden' value='${modalData._id}' name='_id'>`);  
    $('#postTitle').html(`<input value='${modalData.title}' name='title'>`);                
    $('#postContent').html(`<textarea name='content'>${modalData.content}</textarea>`);
    $('#postDate').html(`<input type='date' value='${modalData.date}' name='someDate'>`);
    $('#updateSpan').html(`<button type="submit" class="btn btn-warning" >수정</button>`);
  }
  // 모달 안에서 사용되어 submit 타입의 버튼 클릭 시 모달 맨 처음 부분에 설정되어 있는 action="/update"로 인해 /update로 데이터가 넘어감
</script>

목표: 어디에서 렌더링할것이냐
CCR/SCR가 아니라 성능이 목표
-> DB까지 가서 가져오는것 보다 Server에 있는 것을 가져오는 것이 성능이 좋음
-> update를 DB에 적용하고 그 결과를 redirect로 가져오면 db에서 다시 데이터를 가져옴
결과를 foward
server에서 update 후 list에 적용하는게 forward

보안 상에도 redirect는 안좋음
중간에 해커가 요청을 변조할 수 있음

https://velog.io/@bongf/learned-redirect-forward
https://nesoy.github.io/articles/2018-04/Redirect-Forward

URL의 변화여부가 필요하다면 Redirect를 사용하는 것이 좋습니다.
객체를 재사용하거나 공유해야한다면 Forward를 사용하는 것이 좋습니다.

쿠키

app.get('/cookie', (req,res)=>{
  res.cookie('milk', `1000원`);
  res.send('쿠키 설정 완료');
});

app.get('/cookie', (req,res)=>{
  let milk = parseInt(req.cookies.milk) + 1000;
  // 맨 처음에는 milk라는 쿠키가 존재하지 않아서 NaN이 나옴
  if(isNaN(milk)){
    milk = 0;
  }
  res.cookie('milk', milk);
  res.cookie('name', '이지우');
  //res.send('쿠키 설정 완료');
  res.send('product : ' + req.cookies.milk + " / name : " + req.cookies.name);
  // send는 두번 실행 안됨
  // send를 작성하지 않아도 브라우저 헤더를 통해 쿠키가 넘어감
});

수명 설정

res.cookie('milk', milk, {maxAge : 1000});

1초 전에 새로고침하면 1000씩 증가되지만
1초 후에 새로고침하면 다시 1000이 됨 (화면에는 0)

값부분에 쓰여있는 것은 암호화가 아니라 인코딩된 것


쿠키 암호화

const cookieParser = require('cookie-parser');
app.use(cookieParser('암호화키'));  // 암호화키 부분에는 아무거나 들어가도 됨
app.get('/cookie', (req,res)=>{
  let milk = parseInt(req.signedCookies.milk) + 1000; // 암호화된 쿠키값
  if(isNaN(milk)){
    milk = 0;
  }
  res.cookie('milk', milk, {signed:true});  // 암호화하여 보이게
  res.cookie('name', '이지우');
  //res.send('쿠키 설정 완료');
  res.send('product : ' + req.signedCookies.milk + " / name : " + req.cookies.name);
  // send는 두번 실행 안됨
});


세션

저장할 때는 주로 텍스트 형태로 저장
객체 형태 그대로 저장도 가능

  • 보안 문제 때문에 서버쪽에 저장

로그인 처리 과정

  1. 웹 서버에서 사용자의 아이디, 패스워드를 입력하고 로그인 버튼을 클릭함
  2. 입력된 정보가 존재하는 사용자의 정보를 DB에서 확인하고, 그 정보에 세션을 할당함
  3. ❗ 세션은 미들웨어를 통해 공간을 할당받아서, 이 공간에 접속할 수 있는 번호(id)를 생성함
  4. 생성된 세션 아이디를 웹 서버 엔진에 의해 리스폰스 헤더에 쿠키로 세팅함
  5. 클라이언트 측에서 생성된 쿠키 정보를 저장하여 다음 요청시 사용함

session 생성

let session = require("express-session");
app.use(
  session({
  secret : 'asdfasdfasf',
  resave : false,   // 접속할 때마다 새로운 세션id 발급 여부
  saveUninitialized: true,  // 세션 사용하기 전까지 세션id 발급 안함
  })
);

app.get("/session", function (req, res) {
  console.log(req.session.milk);
  if(isNaN(req.session.milk)){
    req.session.milk = 0;
  }
  req.session.milk = req.session.milk + 1000;
  res.send("session : " + req.session.milk + "원");
});

connect.sid라는 이름의 키값이 저장됨


cookie와 session 비교


실습

로그인 페이지 생성

login.ejs (enter.ejs 수정하여 사용)

<div class = "container mt-4">
        <form action = "/login" method="post">	<!-- post 방식으로 넘김 -->
            <div class = "form-group">
            <label>아이디</label>
            <input type="text" name = "userid" class = "form-control">
            </div><p></p>
            <div class="form-group">
            <label>비밀번호</label>
            <input type="text" name ="userpw" class="form-control">
            </div><p></p>                
            <button type = "submit" class="btn btn-warning" style="float:right">로그인</button>
        </form>
    </div>

server.js (get, post 라우터 모두 설정)

app.get("/login", (req, res) =>{
  console.log('로그인 페이지');
  res.render('login.ejs');
});

app.post('/login', (req, res) =>{
  console.log('아이디 : '+ req.body.userid);
  console.log('비밀번호 : '+ req.body.userpw);
  res.send('로그인 되었습니다.');
});

정보 일치 여부 확인

server.js

app.post('/login', (req, res) =>{
  console.log('아이디 : '+ req.body.userid);
  console.log('비밀번호 : '+ req.body.userpw);

  mydb
    .collection('account')
    .findOne({userid : req.body.userid})
    .then(result => {
      if(result.userpw == req.body.userpw){
        res.send('로그인 되었습니다.');
      }else{
        res.send('비밀번호가 틀렸습니다.');
      }
    });
});

findOne으로 db에 저장된 userid와 입력된 userid가 같은 값의 데이터 가져오기(result)
이 데이터의 userpw가 입력된 userpw와 같으면 로그인 되도록 로직 추가

❗ 여기에서 입력된 userid 값이 db에 없으면 userpw를 가져올 수 없어 에러 발생함


세션 적용

app.get("/login", (req, res) =>{
  console.log(req.session);
  if(req.session.user){
    // 로그인 시 req.session.user 세션 데이터가 존재하면 로그인 상태인 것으로 간주함
    console.log('세션 유지');
    res.send('로그인 되었습니다.');
  }else{
    res.render('login.ejs');
  }
});

app.post('/login', (req, res) =>{
  console.log('아이디 : '+ req.body.userid);
  console.log('비밀번호 : '+ req.body.userpw);

  mydb
    .collection('account')
    .findOne({userid : req.body.userid})
    .then(result => {
      if(result.userpw == req.body.userpw){
        req.session.user = req.body;
        console.log('새로운 로그인');
        res.send('로그인 되었습니다.');
      }else{
        res.send('비밀번호가 틀렸습니다.');
      }
    });
});

db의 데이터와 입력한 id, pw가 일치하면 req.body에 있는 데이터(입력한 id, pw)가 req.session.user 세션에 추가됨

로그인 성공한 뒤 브라우저를 닫고 다시 /login에 접속하면 바로 로그인 성공 페이지가 띄워짐


로그아웃 (세션 삭제)

app.get('/logout', (req, res) => {
  console.log('로그아웃');
  req.session.destroy();  // 세션 삭제
  res.redirect('/');
});

로그인 후 세션이 있는 상태에서 /logout에 접속하면 홈화면으로 이동하면서 세션이 삭제됨


회원가입 기능 링크

app.post('/login', (req, res) =>{
  console.log('아이디 : '+ req.body.userid);
  console.log('비밀번호 : '+ req.body.userpw);

  mydb
    .collection('account')
    .findOne({userid : req.body.userid})
    .then(result => {
      if(result.userpw == req.body.userpw){
        req.session.user = req.body;
        console.log('새로운 로그인');
        res.render('index.ejs', {user:req.session.user});
      }else{
        res.render('login.ejs');
      }
    });
});

app.get('/logout', (req, res) => {
  console.log('로그아웃');
  req.session.destroy();  // 세션 삭제
  res.render('index.ejs', {user:null});
});
  • /login 요청 시 이미 사용자 세션이 있으면 user 데이터와 함께 index.ejs로 이동
  • 처음 로그인 시에 성공하면 user 데이터와 함께 index.ejs로 이동
  • 로그인 실패 시 login.ejs로 이동
  • 로그아웃 성공 시 user 데이터는 null 값으로 넘겨주면서 index.ejs로 이동

로그인 성공 화면 구성

index.ejs

<h1>홈입니다.</h1>

<% if(user){ %>
<h3>반갑습니다. <%= user.userid %>님.</h3>
<a href="/logout">로그아웃</a>
<% }else{%>
<h3>로그인 해주세요.</h3>
<p><p>
<button class="w-100 btn btn-warning login"><b>로그인</b></button>
<% }%>
  
<script>
  $('.login').click(function(e){
    location.href='/login';
  })
</script>

에러 발생 부분

  • 로그아웃 후 나오는 페이지에서 로그인 버튼 클릭해도 작동 안함
  • localhost:8080/에 접속하면
ReferenceError: D:\study\JavaScript\01_WEB\06_24_login\views\index.ejs:14
    12|     <h1>홈입니다.</h1>
    13|
 >> 14|     <% if(user){ %>
    15|     <h3>반갑습니다. <%= user.userid %>님.</h3>
    16|     <a href="/logout">로그아웃</a>
    17|     <% }else{%>

user is not defined
profile
노력형 인간

0개의 댓글