2021년 5월 26일 개발일지

지난 시간에 나의 10 code 웹사이트를 만들면서, 다음과 같은 기능의 구현이 필요하다고 했었다.

  1. 회원가입 기능
  2. 로그인 기능
  3. 회원 포스팅 기능
  4. 좋아요 기능
  5. 프로필 페이지 수정 기능

이제 회원가입 기능은 지난 시간에 살펴보았고, 로그인 기능을 배워보도록 하겠다.

여기서 잠깐 로그인 기능을 배우기 전에, 토글 (toggle) 기능에 대해 먼저 살펴보자.

※ toggle 기능

이 기능은 웹페이지 메인 화면 구현을 위해 사용되는데, 새로고침 없이 화면의 요소들을 숨겼다, 드러냈다 할 수 있는 기능이다.

내가 웹사이트에 바로 들어가면 보이는 페이지는 다음과 같다.

이렇게 로그인 or 회원가입을 하라는 페이지가 뜨는데, 처음 접속한 사람은 회원가입 먼저 해야 하기 때문에 하단에 Sign up을 누르면, 기존 화면은 사라지고 다음과 같은 화면으로 바뀐다.

사실 이건 모든 요소들을 일단 html에선 작성해 두고, javascript에서 toggle 함수로 경우에 따라 hidden 기능을 통해 숨김 처리하는 것이다.

Bulma에서 "is-hidden" 이라는 클래스를 이용해서 요소를 숨길 수 있다.

예를 들어, sign-up-box div에 이 함수를 적용시켜 본다고 하면,

function toggle_sign_up () {
  if ($("#sign-up-box").hasClass("is-hidden")) {
      $("#sign-up-box").removeClass("is-hidden")
  } else {
      $("#sign-up-box").addClass("is-hidden")
  }
}  

이렇게 4줄 정도로 표현이 된다. "is-hidden" class가 적용되어 있으면 function을 실행시키면 class를 없애주고, 없는 상태면 더해주라는 명령이다.

jQuery에는 이걸 더 간단하게 도와주는 함수가 있다. toggleClass() 함수이다. 이렇게 하면 1줄로 간단하게 표현된다.

function toggle_sign_up () {
   $("#sign-up-box").toggleClass("is-hidden")
}

처음 접속한 화면에서 보이지 않아야 하는 영역들에는 "is-hidden" class를 모두 작성해 준다. 그 후, 회원가입 버튼과 취소 버튼을 누르면 다시 사라져야 하는 부분들의 id도 모두 포함하여 한 번에 토글할 수 있는 함수를 만들고, 회원가입 버튼과 취소 버튼에 연결해 주면 된다.

function toggle_sign_up () {
   $("#field").toggleClass("is-hidden")
   $("#div-sign-in-or-up").toggleClass("is-hidden")
   $("#btn-check-dup").toggleClass("is-hidden")
   $("#help-id").toggleClass("is-hidden")
   $("#help-password").toggleClass("is-hidden")
   $("#help-password2").toggleClass("is-hidden")
   $("#cancel").toggleClass("is-hidden")
   $("#signup").toggleClass("is-hidden")
}   

이 토글함수 기능은 포스팅 좋아요 기능에도 쓰일 것이니 잘 기억해 두도록 하자.

2. 로그인 기능

이제 드디어 로그인 기능에 대해 알아보자.

로그인 기능을 위해 JWT 에 대해 알아보자. (JMT 아님 주의)

(1) JWT란?

  • Json Web Token의 줄임말로, JSON 객체를 사용해 정보를 안정성 있게 전달하는 웹 표준이다.
  • 예를 들어, 로그인 기능을 생각해보면 사용자가 로그인하면 서버에서 회원임을 인증하는 토큰을 넘겨줌으로써 이후 회원만 접근할 수 있는 서비스 영역에서 신분을 확인하는 데 쓰일 수 있다.

더 자세히 알아보려면 다음 링크로! https://tansfil.tistory.com/58?category=255594

위 링크로 들어가보면 인증이 왜 필요한지, HTTP 요청이 무엇인지, 세 가지 인증방식 (계정정보를 요청 헤더에 넣는 방식, Session / Cookie 방식, 토큰 기반 인증방식)에 대한 설명이 이해하기 쉽게 되어있다!

(2) 플라스크 서버에서 로그인 기능 구현하기

JWT에 대해 살펴보았으니 서버에서 이를 이용한 로그인 기능을 구현해 보자.

  • 클라이언트가 로그인 시, 비밀번호를 같은 방법으로 암호화한 후, DB에서 해당 아이디와 비밀번호를 갖는 회원이 있는지 찾는다.
  • 회원 정보가 없는 경우 실패 메시지를 보내고, 찾은 경우 아이디와 토큰 만료시간을 저장하는 토큰을 만들어 넘겨준다.
  • 로그인 성공 메시지를 받으면 건네받은 토큰을 쿠키로 저장하여 만료되기 전까지 갖고 있으면서, API 요청을 보낼 때마다 회원임을 확인받는다.
  • 로그아웃 시 해당 토큰을 삭제한다.
# id, pw를 받아서 맞춰보고, 토큰을 만들어 발급한다.
@app.route('/sign_in', methods=['POST'])
def sign_in():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    
    # 회원가입 때와 같은 방법으로 pw를 암호화한다.
    pw_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    
    # id, 암호화된 pw를 가지고 해당 유저를 찾는다.
    result = db.user.find_one({'username': username_receive, 'password': pw_hash})
    
    # 찾으면 JWT 토큰을 만들어 발급합니다.
    if result is not None:
    	# JWT 토큰에는, payload와 시크릿키가 필요하다. (이게 필요한 이유는 상세하게 위에 소개한 링크에 나와있다.)
        # 시크릿키가 있어야 토큰을 복호화해서 payload 값을 볼 수 있다.
        # id와 exp를 담는다. JWT 토큰을 풀면 유저ID 값을 알 수 있다.
        # exp에는 만료시간을 넣어준다. 만료시간이 지나면, 시크릿키로 토큰을 풀때 만료되었다고 에러가 난다.
        payload = {
            'id' : username_receive,
            'exp' : datetime.utcnow() + timedelta(seconds= 60*60*24)
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256'.decode('utf-8')
        
        # 토큰을 줍니다.
        return jsonify({'result':'success', 'token': token})
     # 찾지 못하면
     else:
     	return jsonify({'result':'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})   

유저 정보 확인 API

# 로그인된 유저만 call 할 수 있는 API이다.
# 유효한 토큰을 줘야 올바른 결과를 얻어갈 수 있다.
@app.route('/')
def home():
	token_receive = request.cookies.get('mytoken')
    
    try:
    	# token을 시크릿키로 복호화
        payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
        print(payload)
        
        return render_template ('index.html', user_info=user_info)
except jwt.ExpiredSignatureError:
	return jsonify({'result':'fail', 'msg':'로그인 시간이 만료되었습니다.'})
except jwt.exceptions.DecodeError:
	return jsonify({'result':'fail', 'msg':'로그인 정보가 존재하지 않습니다.'})
    
@app.route('/login')
def login():
	msg = request.args.get("msg")
    return render_template('login.html', msg=msg)

python을 통해 백엔드에서 명령한 것을 프론트에서 html로 구현해보자

function sign_in() {
    let username = $("#input-username").val()
    let password = $("#input-password").val()
    
    if (username =="") {
       $("#help-id-login").text("아이디를 입력해주세요.")
       $("#input-username").focus()
       return;
    } else {
       $("#help-id-login").text("")
    }
    
    if (password =="") {
       $("#help-password-login").text("비밀번호를 입력해주세요.")
       $("#input-password").focus()
       return;
    } else {
       $("#help-password-login").text("")
    }
    
    $.ajax({
       type: "POST",
       url: "/sign_in",
       data: {
          username_give: username,
          password_give: password
       },
       success: function(response) {
          if (response['result'] == 'success') {
             $.cookie('mytoken', response['token'], {path:'/'});
             window.location.replace("/")
          } else {
              alert(response['msg'])
          }
       }
    });
  }  
      
       

참고 $.cookie jquery 사용법

//쿠키 쓰기
$.cookie('쿠키 키', '값', { expires: 7 /*유효 기간(일 단위 기준)*/, path: '/' /*도메인과 path가 일치해야 저장된다.*/});

//쿠키 읽기
$.cookie('쿠키 키');

// 쿠키 삭제
$.removeCookie('name', { path: '/' /*path가 있을 경우, 일치해야 삭제된다.*/});

출처: https://nowonbun.tistory.com/634 [명월 일지]

여기까지 로그인 기능을 완성해 보았다.

  1. 회원가입 기능 V
  2. 로그인 기능 V
  3. 회원 포스팅 기능
  4. 좋아요 기능
  5. 프로필 페이지 수정 기능

다음 게시글에서는 회원 포스팅 기능을 다뤄보겠다.
그럼 20000

좋은 웹페이지 즐겨찾기