원페이지 쇼핑몰 보강하기-익스트림 CRUD

진행 상황: 회원가입(완료)/로그인(로직만 완료)


전에도 얘기했듯, 쇼핑몰에서 ‘모든 고객의 주문 내역을 볼 수 있는’건 물건을 파는 사람이다. 다른 고객들은 자기 주문만 볼 수 있다. 내가 옆집 김씨가 뭘 시켰는지 모르고, 뒷집 박씨는 내가 뭘 시켰는지 모르는것처럼. 그래서 나중에 페이지 하단에 있는

얘는 관리자 계정으로 로그인해야만 볼 수 있게끔 할 예정이다. 콩둘기 메일의 상태가?

회원가입

로그인과 회원가입을 모달창에서 받는다는 얘기는 전에 했는데, 그럼 어떤 정보를 받느냐…

아이디, 비밀번호, 비밀번호 확인, 이름, 전화번호, 이메일, 주소 받는다. 타임스탬프도 있긴 한데, 그건 입력받는 건 아니고 가입년월일이다.

function account_make() {
    let id = $('#userID').val()
    let password = $('#password').val()
    let password_check = $('#password-confirm').val()
    let username = $('#username').val()
    let phone = $('#userphone').val()
    let email = $('#useremail').val()
    let zipcode = $('#userzipcode').val()
    let useraddr = $('#useraddr').val()
    let detailaddr = $('#useraddr-detail').val()
    let phone_valid = RegExp(/^\d{2,3}-\d{4}-\d{4}/)
    let name_space = RegExp(/^[가-힣]{2,8}$/)
    let mail_valid = RegExp(/^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/)
    let password_valid = RegExp(/^[A-Za-z0-9]{8,20}/)

    let date = new Date()
    let Weekday = new Array(7)
    Weekday[0] = 'Sun'
    Weekday[1] = 'Mon'
    Weekday[2] = 'Tue'
    Weekday[3] = 'Wed'
    Weekday[4] = 'Thu'
    Weekday[5] = 'Fri'
    Weekday[6] = 'Sat'
    let year = date.getFullYear()
    let month = date.getMonth() + 1
    let day = date.getDate()
    let yoil = Weekday[date.getDay()]
    let timestamp = year + '-' + month + '-' + day + '-' + yoil


    if (id.length == 0) {
        $('#reg_userid_blank').css('display', 'block')
        $('#userID').css('border', '2px solid #cc0000')
    } else if ($('#fail').css('display') == 'block') {
        $('#reg_userid_needcheck').css('display', 'block')
        $('#userID').css('border', '2px solid #cc0000')
    }

    if (password != password_check) {
        $('#reg_passwd_nomatch').css('display', 'block')
        $('#password').css('border', '2px solid #cc0000')
        $('#password_check').css('border', '2px solid #cc0000')
    } else if (password.length == 0) {
        $('#reg_passwd_blank').css('display', 'block')
        $('#password').css('border', '2px solid #cc0000')
    } else if (password_check.length == 0) {
        $('#reg_passwd_blank').css('display', 'block')
        $('#password-confirm').css('border', '2px solid #cc0000')
    } else if (!password_valid.test(password)) {
        $('#reg_passwd_invalid').css('display', 'block')
        $('#password').css('border', '2px solid #cc0000')
    }

    if (!name_space.test(username)) {
        $('#reg_name-invalid').css('display', 'block')
        $('#username').css('border', '2px solid #cc0000')
    } else if (username.length == 0) {
        $('#reg_name-blank').css('display', 'block')
        $('#username').css('border', '2px solid #cc0000')
    }

    if (!phone_valid.test(phone)) {
        $('#reg_phone-invalid').css('display', 'block')
        $('#userphone').css('border', '2px solid #cc0000')
    } else if (phone.length == 0) {
        $('#reg_phone-required').css('display', 'block')
        $('#userphone').css('border', '2px solid #cc0000')
    }

    if (!mail_valid.test(email)) {
        $('#reg_email-invalid').css('display', 'block')
        $('#useremail').css('border', '2px solid #cc0000')
    } else if (email.length == 0) {
        $('#reg_email-required').css('display', 'block')
        $('#useremail').css('border', '2px solid #cc0000')
    }

    if ($('#pass').css('display') == 'block') {
        $.ajax({
            type: "POST",
            url: "/register",
            data: {
                'timestamp_give': timestamp,
                'userid_give': id,
                'userpasswd_give': password,
                'username_give': username,
                'useremail_give': email,
                'userphone_give': phone,
                'userzip_give': zipcode,
                'useraddr_give': useraddr,
                'userdetailaddr_give': detailaddr
            },
            success: function (response) {
                alert(response['msg'])
                window.location.reload()
            }
        })
    }
    else {
        alert('아이디 중복확인을 하셨나요? ')
    }

}

유효성검사 진짜 개노가다 맞음… 타임스탬프는 가입년월일이라서, ‘이 사람이 가입한 날짜’ 정보를 Day()를 이용해 조합한 다음 자바스크립트쪽에서 같이 보내준다. 근데 우리가 가입할 때 유효성검사 하는 것 중에 그것도 있음. 아이디 중복검사. 그죠? 그니까 OK하는 조건이 크게

  1. 아이디 중복이 없고
  2. 모든 폼을 다 채웠을 때

이다. AND임. 그럼 ID 중복을 어떻게 체크하는가? 그것은 간단하다.

  1. 자바스크립트에서 파이썬으로 아이디를 넘겨준다. (이거 확인좀)
  2. 파이썬은 몽고DB를 찾는다. (이거 찾아봐)
  3. 몽고DB에서 찾은 결과를 확인한다. (없네?)
  4. 파이썬에서 거기에 대한 처리를 하고 그 값을 반환한다. (없어 쓰라그래)
  5. 자바스크립트는 그 값을 토대로 처리한다. (써도 된대)

대충 이런 식이다. 

@app.route('/check_id',methods=['POST'])
def idcheck():
    id_check_result = ''
    id_receive = request.form['idcheck_give']
    id_find = list(db.register.find({'login id':id_receive}, {'_id': False,'login id':True}))
    if len(id_find) != 0:
        id_check_result = 'Fail'
    else:
        id_check_result = 'Pass'
    return jsonify({'idcheck_result': id_check_result})

JS에서 아이디를 넘겨주면 그걸 찾은 결과값을 처리해서 Pass of Fail을 보내준다. 몽고DB에서 조회한 결과가 리스트로 오게 되는데, 중복되는 ID가 있다면 리스트의 길이가 0이 아니게 된다. (없으면 리스트 안에 암것도 없음) 그래서 리스트 길이가 0이면 Pass, 아니면 Fail.

function idcheck() {
    let id = $('#userID').val()
    let id_test = RegExp(/^[A-Za-z0-9_-]{6,20}$/)
    if (!id_test.test(id)) {
        $('#userid_invalid').css('display', 'block')
        $('#userID').css('border', '2px solid #cc0000')
    } else {
        $.ajax({
            type: "POST",
            url: "/check_id",
            data: {'idcheck_give': id},
            success: function (response) {
                let check_result = response['idcheck_result']
                if (check_result == 'Fail') {
                    alert('이미 존재하는 아이디입니다. ')
                    $('#fail').css('display', 'block')
                    $('#pass').css('display', 'none')
                } else if (check_result == 'Pass') {
                    alert('아이디를 사용하셔도 됩니다!')
                    $('#fail').css('display', 'none')
                    $('#pass').css('display', 'block')
                }
            }
        })
    }

}

JS에서는 ID를 보냈을 때 돌아온 결과를 토대로 아이콘을 보여준다. 위 사진을 보면 아이디 입력란에 X가 있는데, 중복되지 않는 아이디를 입력하면 초록색 체크로 바뀐다.

아 그러면 끝인가요? ㄴㄴ 한가지가 더 있다. 비밀번호는 중요하기때문에 이게 털리면 X된다고 보면 된다. 그니까 제발 12345 이런거 하지 말자. 아무튼… 비밀번호를 DB에 쌩으로 저장하는 게 아니라 암호화를 해서 저장할건데, 여기서는 hashlib을 써볼거다.

import hashlib

부르자.

m = hashlib.sha256()
m.update(userpasswd_receive.encode('utf-8'))
userpasswd_receive_hash = m.hexdigest()

그리고 입력받은 비밀번호를 해싱하고

'password':userpasswd_receive_hash

해싱한 비밀번호를 저장한다.

짜잔

참고로 이렇게 저장하게 되면 비번 까먹으면 망한다. 저걸 원래대로 되돌릴 수가 없거든… 그리고 로그인 할 때도 비밀번호 일치 여부를 확인하기 위해 사용자가 입력한 비밀번호를 해시화한 다음 대조하게 된다.

로그인

일단 로그인은 사용자가 ‘로그아웃을 하기 전까지’ 그 상태가 유지된다. 로그아웃 버튼을 누르건 창을 닫건 사용자가 자의로 로그아웃 하기 전까지 유지되는데, 이거는 일단 토큰 관련 처리가 필요하고… 여기서는 로그인 로직만 일단 구현해보자.

바에 이렇게 세 개가 있는데, 얘가 토큰 유무에 따라 로그인과 로그아웃만 보이게 된다. 토큰이 있으면 로그아웃, 토큰이 없으면 로그인.

로그인 모달창은 구조도 간단하고, 서버를 거치지 않는 유효성 검사도 공란인가만 보면 된다.

아이디랑 비밀번호 일치 여부는 서버를 거쳐야 하기 때문에 여기서는 일단 패스.

function login_user() {
    let id = $('#loginID').val()
    let password = $('#loginPW').val()
    console.log(id, password)
    if (id.length == 0) {
        $('#login-id-required').css('display', 'block')
        $('#loginID').css('border', '2px solid #cc0000')
    }
    if (password.length == 0) {
        $('#login-pw-required').css('display', 'block')
        $('#loginPW').css('border', '2px solid #cc0000')
    }
}

이제 Ajax를 끼얹어보자.

$.ajax({
    type: "POST",
    url: "/login",
    data: {
        'login_id_give': id,
        'login_pw_give': password
    },
    success: function (response) {
        console.log(response)
    }
})

Ajax를 통해 플라스크로 보내는 건 아이디랑 비밀번호가 끝이다. 그러면 서버에서

@app.route('/login',methods=['POST'])
def login_user():
    login_id_receive = request.form['login_id_give']
    login_pw_receive = request.form['login_pw_give']
    m = hashlib.sha256()
    m.update(login_pw_receive.encode('utf-8'))
    login_pw_hash = m.hexdigest()
    id_find = list(db.register.find({'login id':login_id_receive}, {'_id': False,'login id':True}))
    pw_find = list(db.register.find({'password':login_pw_hash}, {'_id': False,'password':True}))

    if len(id_find) == 0:
        msg = '존재하지 않는 아이디입니다!'
    elif len(pw_find) == 0:
        msg = '비밀번호가 일치하지 않습니다! '
    else:
        name_find = list(db.register.find({'login id': login_id_receive}, {'_id': False, 'name': True}))
        name_find = name_find[0]['name']
        msg = '{}님 어서요세요!'.format(name_find)
    return jsonify({'msg': msg})

비밀번호와 아이디를 DB에서 검색해서 일치 여부에 따른 메시지를 반환하게 되고, JS에서는 그 메시지를 그대로 띄우면 된다.

그니까 이런거지.