(김유선) 중고거래 사이트를 SPA로 만들기 7일차 - 로그인, 로그아웃

2024. 4. 16. 01:42프로젝트 일지

로그아웃 기능

서버쪽 코드

app.get('/logout',(요청,응답)=>{
  요청.logout(  async(err)=>{ // 로그아웃 코드는 콜백함수를 포함해야 하므로 에러코드를 넣어줌
    if (err) return next(err)
  })
    응답.redirect('/') // 홈페이지로 다시 가줌
})

 

클라이언트쪽 코드

$('.logoutbtn').click(function(){
            $.get('/logout')
            .done(function(){
                alert('로그아웃 되었습니다.')
                location.href = location.href;
            })
        })

 

로그인 후

 

세션 데이터 상태

 

로그아웃 후

 

세션 데이터 상태

 

잘 동작한다.

 

하지만 문제가 있다. 로그인을 이미 한 상태인데도 사이트를 새로고침하면 로그인 창이 다시 뜬다. 로그인을 한 상태이면 새로고침을 몇 번을 하든 계속 로그아웃 버튼이 떠야 하는데 그렇지 않은 것이다. 

물론 세션 데이터는 그대로 있지만, 클라이언트에서 디자인이 로그인 후 변화하도록 만들었기 때문에 새로고침을 하면 다 날아가는 것이다. 이를 해결하기 위해서는 처음 사이트를 열었을 때 요청.user 가 존재하면 로그인/회원가입을 로그아웃으로 바꿔두면 될 것이다.

 

그럼 처음 세팅에 필요한 데이터는 일단 '요청.user', '물건들' 정도가 되겠다.

 

서버 비용을 아끼려면 처음에 '/'으로 get 요청으로 html 파일을 가져오고 추가적인 get 요청으로 한번에 제이슨 데이터를 가져오는 것보다, 처음부터 데이터를 한번에 가져오는 것이 좋겠다고 판단했다. 그래서 index.html 파일을 index.ejs로 수정해서 데이터를 가져와보겠다.

 

기본 페이지 수정 (html -> ejs)

원래의 .'/'으로 get 요청

app.get('/', (요청, 응답)=>{
  응답.sendFile('index.h.tml') // 그저 파일을 보내줬음
})

 

수정된 '/'으로 get 요청

app.get('/', (요청, 응답)=>{
  let firstdata = {}
  firstdata.user = 요청.user // 유저의 정보
  firstdata.products =  [ {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' }, {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' },{img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' }, {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' },{img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' }, {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' }, {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' }, {img : "https://images.unsplash.com/photo-1709082804530-d588656ade88?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTcxMjU5MzM2NA&ixlib=rb-4.0.3&q=80&w=1080", title : '아주 귀여운 오양이', price : '5,000원' } ] // 상품의 정보
  응답.render('index.ejs', {firstdata : firstdata}) // 데이터 렌더링
})

 

그 전의 index.html 에서의 상품 목록 가져오는 코드 (서버 코드)

$.get('/callproducts')
        .done(function(data){
            data.forEach(function(a,i){
                $('.products').append(`
                <div class="product_container">
                        <img class="product_img" src=${data[i].img}>
                        <p class="product_title">${data[i].title}</p>
                        <div class="product_boxing">
                            <p class="product_price">${data[i].price}</p>
                            <img class="heart_empty" src="./img/heart-empty.png" alt="">
                        </div>
                    </div>
                `)
            })
        })

 

index.ejs에서 상품 목록 가져오는 코드 (클라이언트 코드)

<div class="products">
            <% firstdata.products.forEach(function(a,i){ %>
                <div class="product_container">
                        <img class="product_img" src=<%=firstdata.products[i].img%>>
                        <p class="product_title"><%=firstdata.products[i].title%></p>
                        <div class="product_boxing">
                            <p class="product_price"><%=firstdata.products[i].price%></p>
                            <img class="heart_empty" src="./img/heart-empty.png" alt="">
                        </div>
                    </div>
            <% }) %>
        </div>

 

상품이 제대로 뜨니까 이제 로그인을 했을 때 새로고침을 해도 로그인이 뜨지 않도록 바꿔야 한다. 

그래서 서버에서 세팅해뒀던 유저 정보가 제대로 뜨는지 한번 검증해봄.

 

이와 같이 로그인을 하고 나서 로그아웃하지 않은 상태에서는 새로고침을 여러번해도 계속해서 user 정보가 제대로 떴다.

 

로그인하지 않은 경우에는 아무리 새로고침을 해도 undefined가 떴음.

 

그러면 유저 정보가 undefined일 때는 로그인 버튼을 활성화시키고 반대의 경우엔 로그아웃 버튼을 활성화시키면 되지 않겠음?

 

제이쿼리로 원하는 코드를 입력했는데 안됨....

 

그래서 혹시나 해서 자바스크립트로 작성해봤는데도 안된다...

 

<% if(firstdata.user==undefined){ %>
        <h1>로그인 안됨</h1>
    <% } %>
    <% if(firstdata.user!=undefined){ %>
        <h1>로그인 됨</h1>
    <% } %>

그래서 요렇게 작성해봄

로그인 안한 상태로 접속했을 때

 

로그인하고 '새로고침'했을 때 (로그인만 한다고 바로 변하지는 않음, 당연히 처음 로딩됐을 때의 데이터를 한번 가져온 거라 로그인한다고 이미 받아온 데이터가 변하지 않음)

 

내가 건드리려던 건 display 속성이었는데 그건 안될 것 같으니 이렇게 해보자

<div class="loginbtn">로그인 / 회원가입</div>
<div class="logoutbtn">로그아웃</div

원래 요렇게 작성해뒀던 것을(로그아웃은 처음 display='none'이었음, 그래서 그 속성은 삭제함)

<% if(firstdata.user==undefined){ %>
                <div class="loginbtn">로그인 / 회원가입</div>
            <% } %>
            <% if(firstdata.user!=undefined){ %>
                <div class="logoutbtn">로그아웃</div>
            <% } %>

이렇게 작성해봤다.

 

잘된다!!!!ㅜㅜ

로그아웃버튼을 누르면 이 창이 뜨고, 확인버튼을 누르면

rediret 되어서 바로 로그인 버튼이 활성화된다!!! (logout 요청에 응답.rediret 작성했었음)

물론 세션 데이터도 사라짐 ㅎㅎ

 

근데 로그아웃하고 다시 로그인하니 하나 문제가 있다.

로그인한 직후에는 로그아웃 창이 새로 뜨지 않는다.

.done(function(data){
                    alert(data) // 위 코드에서 응답한 json 데이터를 알림창으로 보내줌
                    $('.login_bg').css('display','none')
                    $('.login_container').css('display','none')
                    if(data=='나다장터에 오신 걸 환영합니다.'){ // 로그인되면 바로 변할 것
                        $('.loginbtn').css('display','none')
                        $('.logoutbtn').css('display','block')
                    }
                })

로그인이 되었을 때의 코드를 이렇게 작성했는데, 애초에 위처럼 로그인 상태에 따라 하나를 만들기만 했기에 로그인을 안한 상태로 접속한 후, 로그인을 하게 되면 logoutbtn이라는 게 존재하질 않게 된다.

 

그러면 로그아웃버튼을 두 개 만들어서 하나는 숨겨두었다가 로그인하면 보이게 하는 건 어떨까?

.done(function(data){
                    alert(data) // 위 코드에서 응답한 json 데이터를 알림창으로 보내줌
                    $('.login_bg').css('display','none')
                    $('.login_container').css('display','none')
                    if(data=='나다장터에 오신 걸 환영합니다.'){ // 로그인되면 바로 변할 것
                        $('.loginbtn').css('display','none')
                        $('.logoutbtn').eq(0).css('display','block')
                    }
                })

로그인 get요청 코드를 이렇게 바꾸고

<% if(firstdata.user==undefined){ %>
                <div class="loginbtn">로그인 / 회원가입</div>
            <% } %>
            <div class="logoutbtn hide">로그아웃</div> <=== 숨겨두는 아이
            <% if(firstdata.user!=undefined){ %>
                <div class="logoutbtn">로그아웃</div>
            <% } %>

하나를 새로 만들어서 숨겨뒀다.

.hide{ display: none; }

바로 이렇게 (스타일 태그 안에 있는 속성).

 

자... 과연...

됐다!

 

이젠 새로고침을 아무리 해도 알맞은 버튼이 보인다. 로그인과 로그아웃을 하는 즉시에도 상태에 맞는 버튼이 표시되어있다.

 

근데 사실 ejs는 ssr이다. 서버 비용을 조금이라도 아끼려고 ejs로 구현해봤다. spa를 구현하기 위해선 csr로 보통 구현하지만, 첫 페이지를 ssr로 구현하면 첫페이지 SEO에 유리하고, 첫 페이지 로딩이 빠르며, 화면 전환이 부드럽다. 이렇게 첫 페이지를 ssr로 구현하고, 그 이후 페이지를 csr로 구현하는 것을 universal rendering (유니버셜 렌더링)이라고 한다. 

https://www.youtube.com/watch?v=lc-9Uh9euls

 

처음부터 유니버셜 렌더링을 알고서 한 게 아니었는데 나같은 식으로 하는 게 유니버셜 렌더링이라고 하니까 알아서 효율적으로 잘한 것 같아 뿌듯하다.