2021년 5월 23일 개발일지

안준·2021년 5월 23일
1

코딩

목록 보기
2/2

스파르타코딩클럽 웹개발 플러스 4주차 커리큘럼이 끝났다.
종합반 복습하고 4주 간의 커리큘럼을 2주 만에 끝내야 해서 개발일지는 따로 쓰지 못했다.
방금 마무리한 4주차 커리큘럼에 대한 개발일지를 시작으로 역으로 배운 내용 정리해 보겠다.

4주차에 중점적으로 배운 것은 보안, 로그인, 암호화에 대한 것이다.
지금까지 만든 웹서비스들은 누구나 접속하면 바로 서비스를 이용할 수 있었지만, 회원가입 / 로그인 기능을 활용해 회원의 경우에만 서비스를 활용할 수 있게 하는 것이다.

커리큘럼이 끝나면 만들게 될 웹사이트는 다음과 같다.
http://spartacodingclub.shop/wp/sweeter/login

내가 만든 페이지 메인 모습은 이렇게 생겼다.

약간 트위터 느낌이 나는 자신의 스토리를 올리고 실시간으로 회원들의 이야기를 공유할 수 있는 공간이다.

이를 위해 개발해야 하는 주요 기능은 다음과 같았다.
1. 회원가입 기능
2. 로그인 기능
3. 회원 포스팅 기능
4. 좋아요 기능
5. 프로필 페이지 수정 기능

웹사이트를 만들기 위한 작업과 배운 것들을 차례대로 정리해 보겠다.

Ⅰ. Bulma로 웹사이트 꾸미기

지금까지는 Bootstrap로만 CSS를 구성하였는데, Bulma를 처음 써보았다. Bootstrap과 비슷하게 미리 정해진 모습의 클래스를 가져다 쓸 수 있는 무료 CSS 프레임워크이다.

제공하는 공식문서에서 각 컴포넌트의 묘사와 예시가 잘 정리되어 있다.
공식문서 링크 : https://bulma.io/documentation/

bulma를 쓰기 위해서는 연결하려는 클라이언트 html 파일의 head안에 다음 링크를 적어주고 시작하면 된다.

<!-- Bulma CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">

Bootstrap과 간단히 비교해보면, Bootstrap은 jQuery를 써서 웹사이트에서의 상호작용을 쉽게 구현할 수 있는 반면, Bulma는 순수한 CSS프레임워크이기 때문에 기능을 직접 구현해야만 더 자유롭게 커스터마이징이 가능하다.
Bootstrap은 커뮤니티가 커서 테마나 플러그인 등이 개발이 많이 되어 있고, 질문에 대한 답이나 예시 등을 찾기 쉬우며, Bulma는 문법이 직관적이고 Flexbox등 최신 기술을 많이 쓴다.

<비교 예시>

  • Bootstrap
<button class="btn btn-outline-primary btn-lg">Primary</button>
  • Bulma
<button class="button is-primary is-outlined is-large">Primary</button>

자 그럼 새로 쓰는 CSS 프레임워크에 대해서는 간단히 소개를 끝냈고,
이제 회원가입, 로그인 등의 주요 기능들의 대해 살펴본다.

2. 회원가입 기능

회원가입을 위해 클라이언드에서 id, pw를 받아서 DB에 저장해야 한다.
다만, pw를 저장할 때 값 그대로 가져오면 해커가 DB에 접근한 경우 바로 pw를 알 수 있게 되어 보안의 문제가 생긴다.

이에 pw를 저장하기 전에, pw를 sha256 방법(=단방향 암호화. 풀어볼 수 없음)으로 암호화해서 저장한다.

여기서 새로 배운 개념이 "해시함수"이다.

해시함수란, 알고리즘의 한 종류로서 임의의 데이터를 입력 받아 항상 고정된 길이의 임의의 값으로 변환해 주는 함수를 말한다.

내가 사용한 해시함수 SHA256은 어떤 길이의 입력값을 넣어도 항상 256바이트의 결과값이 나온다.

예를 들어, 내가 8글자의 영문+숫자 조합의 비밀번호를 클라이언트에서 보냈는데, DB에 들어가보면 다음과 같이 알 수 없는 형식으로 pw가 저장되어 있었다.

해시함수는 암호화폐와도 연관이 있는 것으로 알고 있는데, 구체적으로 어떤 관계가 있는 건지 한 번 공부해 봐야 겠다.

자, 그럼 Flask 서버에서 회원가입 기능 구현하기 위해 활용한 python 파일과 html 파일 코드를 살펴보자.

(1) 먼저 python파일 시작 코드
여기서 JWT 패키지는 회원가입 기능에서 바로 활용되진 않고 로그인 기능에서 활용된다.

> from flask import Flask, render_template, jsonify, request, session, redirect, url_for
> app = Flask(__name__)

# Mongo DB 연결
from pymongo import MongoClient
client = MongoClient('mongodb://내 IP주소', 27017, username = "내 ID", password="내 PW"
db = client.DB이름

# JWT 패키지 사용 (PyJWT 패키지 설치해야 함)
import jwt

# 토큰에 만료시간을 주기 위해 datetime 모듈 사용
import datetime

# 해시함수 사용
import hashlib

# 토큰 암호화, 복호화를 위한 키
SECRET_KEY = '내 암호'

(2) 회원가입 기능 백엔드 python 구현

@app.route('/sign_up/save', methods=['POST']
def sign_up():
    username_receive = request.form['username_give']
    password_receive = request.form['password_give']
    password_hash = hashlib.sha256(password_receive.encode('utf-8')).hexdigest()
    doc = {
    	"username" : username_receive, # 아이디
        "password" : password_hash, # 비밀번호
        "profile_name" : username_receive #프로필 이름 기본값은 아이디
        "profile_pic" : "", #프로필 사진 파일 이름
        "profile_pic_real": "profile_pics/profile_placeholder.png", #프로필 사진 기본 이미지
        "profile_info" : "", #프로필 한 마디
    }
    db.users.insert_one(doc)
    return jsonify({'result':'success})

(3) 회원가입 기능 프론트엔드 javascript 구현

자 이제 python으로 작성한 서버에서 보내준 정보를 바탕으로 클라이언트와의 상호작용을 위한 javascript를 짜야 하는데..이게 뭔가 복잡하다..

복잡하다고 느낀 이유는, 구현해야 할 깨알 같은 기능들이 몇 개 있어서다.

  1. ID 중복확인 기능 (이미 존재하는 ID인지 확인 필요)
  2. ID가 2-10자의 영문 및 숫자로 이루어져야 하며, 일부 특수문자 (', _, -) 가능함
  3. PW가 8-20자의 영문 및 숫자로 이루어져야 하며, 일부 특수문자 (!@#$%^&*) 가능함
  4. 비밀번호 입력하고, 비밀번호 재확인도 입력해서 두 개가 다른 경우 가입할 수 없음

기능을 구현하는 큰 축은 가입할 수 있는 ID, PW와 그렇지 않은 ID, PW의 Class를 구분해 주는 것이다.
클라이언트에서 입력한 ID, PW가 가입 가능한 경우 "is-success" 라는 Class를 부여해주고,
아닌 경우 "is-danger"라는 Class를 부여해 준 후, Class에 따라 동작 명령을 내려 준다.

자 그럼 ID, PW 형식의 복잡한 조건을 확인하기 위해 '정규표현식 (Regular Expressions)'을 이용하도록 한다.
이 식의 결과를 참/거짓으로 반환하는 함수를 정의하면 편리하다.
하기 함수에 따라 결과값이 True, False가 나올 것이다. True인 경우에만 후속 동작을 할 수 있도록 명령할 것이다.

// ID 확인 (2-10글자, 영문, 숫자, 일부특수문자 (',_,-))
function is_nickname(asValue) {
	var regExp =/^(?=.*[a-zA-Z])[-a-zA-Z0-9_.]{2,10}$/;
    return regExp.test(asValue);
}

// PW 확인 (8-20글자, 영문, 숫자, 일부특수문자 (!@#$%^&*))
function is_password(asValue){
	var regExp =/^(?=.*\d)(?=.*[a-zA-Z])[0-9a-zA-Z!@#$%^&*]{8,20}$/;
    return regExp.test(asValue);
}

이제 ID 중복확인을 하자. 서버로 POST 요청을 보내 아이디가 존재하는지 확인한다.
다만 서버에 요청을 보내기 전에 클라이언트가 ID를 입력하지 않았거나, 잘못된 형식의 ID를 입력한 경우 효율성을 위해 서버에 보내기 전에 먼저 확인을 해준 후, 서버에 보내 중복확인을 한다.

function check_dup() {
    let usernme = ${"#input-username"}.val()
    console.log(username)
    
    // 1. ID가 빈칸인경우 "ID를 입력해주세요" alert text 띄우고, is-danger class 더해주기
    
    if (username == "") {
    	$("#help-id").text("ID를 입력해 주세요").removeClass("is-safe").addClass("is-danger")
    	$("#input-username").focus()
    	return;
    }
    
    // 2. ID 형식이 올바르지 않은 경우 확인 alert text 띄우고, is-danger class 더해주기
    
    if (!is_nickname(username)){
    	$("#help-id").text("아이디 형식을 확인해 주세요").removeClass("is-safe").addClass("is-danger")
        $("#input-username").focus()
        return;
    }
      
    // 3. ID 중복확인 진행, 중복확인 진행 중인 ID는 is-loading class 더해주기
    
    $("#help-id").addClass("is-loading")
    $.ajax({
    	type: "POST",
        url: "/sign_up/check_dup",
        data: {
        	username_give: username
        },
        success: function(response) {
            if (response["exists"]) {
            	$("#help-id").text("이미 존재하는 ID입니다.").removeClass("is-safe").addClass("is-danger")
                $("#input-username").focus()
            } else {
            	$("#help-id").text("사용가능한 ID입니다.").removeClass("is-danger").addClass("is-success")
            }
            $("#help-id").removeClass("is-loading")
         }
      });   
  }

중복확인을 위한 ID를 받아 확인하는 서버 python 코드는 다음과 같다.
위에서 if (response["exists"] ~) 라고 되어 있는 함수에서 exists가 서버에서 받아온 결과값이다.

@app.route('/sign_up/check_dup', methods=['POST'])
def check_dup():
    username_receive = request.form['username_give']
    exists = bool(db.users.find_one({"username":username_receive}))
    return jsonify({'result':'success', 'exists':exists})

즉 서버에서 동일한 ID가 있는지 찾아서 있으면 exists 결과값을 보내주고, 이를 프론트엔드에서 받아 "이미 존재하는 ID 입니다"라고 클라이언트에게 alert text를 띄운다.

이제 ID 중복확인, 형식 확인까지 마쳤다면, PW 형식, 재입력 확인까지 포함한 회원가입 javascript 함수 작성해 보겠다.

function sign_up(){
    let username = $("#input-username").val()
    let password = $("#input-password").val()
    let password2 = $("#input-password2".val()
    console.log(username, password, password2)
    
    // 1. 먼저 ID에 아직 "is-danger" class가 붙어 있다면 ID 다시확인하라는 메시지 
    + 확인했는데 "is-success"가 안붙어있다면 중복확인 안한 것이므로 중복확인 하고 오라는 메시지 띄우기 
    if ($("#help-id").hasClass("is-danger")){
    	alert("ID를 다시 확인해 주세요.")
        return,
    } else if (!$("#help-id").hasClass("is-success")) {
    	alert ("ID 중복확인 해주세요.")
        return;
    }
    
   // 2. pw 빈칸입력, 형식 확인하기
    if (password == "") {
   	$("#help-password").text("비밀번호 입력해 주세요").removeClass("is-safe").addClass("is-danger")
        $("#input-password").focus()
        return;
   } else if (!is_password(password)){
   	$("#help-password").text("비밀번호 형식을 확인해 주세요.").removeClass("is-safe").addClass("is-danger")
        $("#input-password").focus()
        return
   } else {
   	$("#help-password").text("사용가능한 비밀번호 입니다.").removeClass("is-danger").addClass("is-success")
   }
 
 // 3. 비밀번호 재확인 값이랑 비교하여 동일할 경우 "is-success" Class 더해주기
     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")
  } 
  
 // 4. ID, PW 모두 "is-success" 인 경우에만 서버에 보내서 저장해주기
$.ajax({
    type: "POST",
    url: "/sign_up/save",
    data: {
    	username_give: username,
        password_give: password
    },
    success: function(response) {
    	alert("회원가입을 축하드립니다!")
        window.location.replace("/login")
    }
});    

이렇게 해서..내가 username_give, password_give를 서버에 보냈고,
위 (2) 회원가입 기능 백엔드 python 구현에서 작성한 코드를 통해 서버에서 데이터를 받아 DB에 저장하게 된다.

robo 3T를 통해 본 user db는 다음과 같다.

오늘 회원가입 기능까지 알아보았다. 하루만에 로그인이랑 포스팅 기능까지 다 하려고 했는데 여기까지 작성하는 데 시간이 너무 많이 걸린다.ㅎㅎ

기능들을 매일 나누어 작성해 나가도록 하겠다. 매주 작성하는 것보다 매일 작성하는 게 나은듯!

그럼 이만

profile
개발자 궁금해서 왔습니다.

0개의 댓글

관련 채용 정보