[JavaScript] JWT로 회원가입, 로그인 기능 구현하기

JoonQpa·2022년 5월 5일
2

팀 프로젝트

목록 보기
4/10
post-thumbnail

그동안 정신없어서 TIL을 놓쳤다. 윽

웹 개발 팀프로젝트가 시작했다.

그동안은 UI만 클론했다고 하면 이번에는 실제 서비스를 제공할 수 있는 웹 페이지를 개발하는 것이 목표.

내가 맡은 부분은 회원가입, 로그인, 로그아웃 부분 백엔드 개발이다.

크 우리 갓팀원들이 만든 프론트엔드 너무너무 이쁘다.

JWT기반 로그인 인증 구조

JWT가 무엇인지에 관한 자세하고 좋은 글들이 너무 많기 때문에

나는 내가 회원가입 기능을 구현하기위해 생각해야했던 간단한 로직을 정리해봤다.

로직

  1. 유저가 ID, PW 입력
  2. 받은 PW는 서버에서 해쉬화 (HS256 알고리즘 사용)
  3. 해쉬화된 PW와 DB에 저장된 해쉬화된PW가 일치하는지 확인
  4. 일치할 시, 유저의 sessionStorage에 토큰부여

따라서 이 4가지의 과정을 중점적으로 기억하면서 기능 구현을 위한 코드를 작성했다.

JWT기반 회원가입 기능 구현하기

내가 사용한 라이브러리


폴더구성



app.py 코드


유저가 입력한 값을 해쉬화하고 값을 DB값과 비교후 토큰부여, 혹은 DB에 저장(회원가입)

# 회원가입
@app.route('/sign_up/save', methods=['POST'])
def sign_up():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    nickname_receive = request.form['nickname_give']
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest() # password 해쉬화 함수
    doc = {
        "username": username_receive,  # 아이디
        "password": password_hash,  # 비밀번호
        "nickname": nickname_receive,  # 닉네임
        "profile_pic": "",  # 프로필 사진 파일 이름
        "profile_pic_real": "profile_pics/profile_placeholder.png",  # 프로필 사진 기본 이미지
        "profile_info": ""  # 프로필 한 마디
    }
    db.users.insert_one(doc) # 유저가 입력한 아이디 pw 닉네임을 DB에 저장
    return jsonify({'result': 'success'})


# 로그인서버
@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()  # 유저가 입력한 pw를 해쉬화
    result = db.users.find_one({'username': username_receive, 'password': pw_hash}) 
    # 아이디와 유저가 입력한 해쉬화된 pw가 DB에 저장되어 있는 해쉬화된 pw와 일치하는지 확인 

    if result is not None:  # 일치한다면
        payload = {
            'id': username_receive,
            'exp': datetime.utcnow() + timedelta(seconds=60 * 60 * 24)  # 로그인 24시간 유지
        } 
        # 유저 아이디와 유효기간을 담고 있는 payload 생성
        
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256') 
        # jwt기반 토큰 생성

        return jsonify({'result': 'success', 'token': token}) 
        # 토큰을 유저에게 부여
        
    # 찾지 못하면
    else:
        return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})

javascript 코드


유저가 입력한 ID, PW를 로그인, 회원가입 함수에 연결하는 부분

function sign_up() {
    let username = $("#input-username").val()
    let password = $("#input-password").val()
    let nickname = $("#input-nickname").val()
    let password2 = $("#input-password2").val()
    console.log(username, nickname, password, password2)
	
  	

    if ($("#help-id").hasClass("is-danger")) {
        alert("아이디를 다시 확인해주세요.")
        return;
    } else if (!$("#help-id").hasClass("is-success")) {
        alert("아이디 중복확인을 해주세요.")
        return;
    }

    if (password == "") {
        $("#help-password").text("비밀번호를 입력해주세요.").removeClass("is-safe").addClass("is-danger")
        $("#input-password").focus()
        return;
    } else if (!is_password(password)) {
        $("#help-password").text("비밀번호의 형식을 확인해주세요. 영문과 숫자 필수 포함, 특수문자(!@#$%^&*) 사용가능 8-20자").removeClass("is-safe").addClass("is-danger")
        $("#input-password").focus()
        return
    } else {
        $("#help-password").text("사용할 수 있는 비밀번호입니다.").removeClass("is-danger").addClass("is-success")
    }
    if (password2 == "") {
        $("#help-password2").text("비밀번호를 입력해주세요.").removeClass("is-safe").addClass("is-danger")
        $("#input-password2").focus()
        return;
    } else if (password2 != password) {
        $("#help-password2").text("비밀번호가 일치하지 않습니다.").removeClass("is-safe").addClass("is-danger")
        $("#input-password2").focus()
        return;
    } else {
        $("#help-password2").text("비밀번호가 일치합니다.").removeClass("is-danger").addClass("is-success")
    }
    $.ajax({
        type: "POST",
        url: "/sign_up/save",
        data: {
            username_give: username,
            password_give: password,
            nickname_give: nickname
        },
        success: function (response) {
            alert("회원가입을 축하드립니다!")
            window.location.replace("/login")
        }
    });

}

<!--================로그인==================-->

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'])
            }
        }
    });
}



html 코드


유저가 ID,PW를 입력하는 부분

<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="static/script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>

<!-- 회원가입 버튼 -->
<label for="input-username" style="margin-top: 10px"></label>
<input type="text" class="form-control" id="input-username" placeholder="ID" style="margin-bottom: 10px">
<input type="password" class="form-control mx-sm-3" id="input-password" placeholder="PASSWORD" aria-describedby="passwordHelpInline">
<button type="button" class="btn btn-primary" onclick="sign_in()">로그인</button>
<button onclick="sign_up()">회원가입</button>

백엔드를 기능을 구현하는 과정이 가장 어렵고 힘들었지만,

구현된 기능을 팀원이 만들어놓은 프론트엔드 페이지에 연동시키는 과정에서

아직 자잘하게 연동되지 않은 기능들이 하나 둘 생겨 그 과정도 쉽지 않다.

확실히 로직을 이해하고 논리적으로 사고하는 힘이 필요하겠지만 아직은 좀 벅찬 것이 사실 :)

현재는 이것저것 값을 바꿔보고, 프린트해보면서 주먹구구 식으로 끼워맞추기 식으로 구현하고 있다 😂


뭐 하다보면 늘겠지 😏

profile
Intuition factory: from noob to pro

0개의 댓글