미니 플젝 회고

soo's·2023년 4월 3일
0

TIL

목록 보기
26/53
post-thumbnail

팀원들과 미니 프로젝트를 진행했다.
해당 프로젝트 S.A는 미니 프로젝트 S.A 참고.

우리는 회원가입을 통한 사용자의 개인 공부 시간 기록과 한 눈에 보이는 투두리스트가 주된 기능으로 명언과 출석 체크 등 부가기능을 만들어 사용자의 더 나은 학습 관리를 위한 프로젝트를 진행하기로 했다.
나는 회원가입 기능을 맡게 되었다.

내가 처음 생각한 회원가입 구현의 흐름은 아래와 같다.

  1. 사용자로부터 닉네임, 아이디를 입력받는다.
  2. 아이디가 DB에 이미 존재하는지 중복여부를 확인한다.
  3. 사용자로부터 입력받은 두 패스워드가 서로 동일한지 확인한다.
  4. 회원가입 버튼을 눌러 데이터를 서버로 보내 DB에 저장한다.

𝟏. 값 입력받기

제이쿼리를 사용해서 사용자가 입력한 값들을 가져와서 formdata에 담아 fetch를 통해 api endpoint인 ('api/join')으로 보낸다.

let formData = new FormData();
  const user_id = $("#id").val();
  const user_nickname = $("#nickname").val();
  const user_pw = $("#pw2").val();

  formData.append("id", user_id);
  formData.append("nickname", user_nickname);
  formData.append("password", user_pw);

𝟐. 아이디 중복 확인

이 부분은 어떻게 할까 고민하다가 사용자가 회원가입 버튼을 눌렀을 때, 데이터를 서버로 보내고 아이디가 이미 DB에 존재하면 서버에서 바로 return 하여 메세지를 보냈다. fetch로 받아온 메세지를 alert로 띄웠다.

join() 일부분

fetch("/api/join", { method: "POST", body: formData })
    .then((res) => res.json())
    .then((data) => {
      if (data["message"] == "이미 존재하는 아이디입니다.") {
        $("#id").focus();
        alert(data["message"]);
      } else if (data["message"] == "가입 완료") {
        alert(data["message"]);
        window.location.href = "/login";
      }
    });

app.py의 ('/api/join') 일부분

@app.route("/api/join", methods=["GET", "POST"])
def join():
    if request.method == "POST":
		...
        #  아이디 중복 검사
        if db.pjs.find_one({"id": id}):
            return jsonify({"message": "이미 존재하는 아이디입니다."})

따라서 서버로부터 받은 메세지가 중복검사로 인해 리턴된 값이면 바로 알러트를 띄우게 하고 가입 완료 메세지일 경우 로그인 페이지로 이동하게 한다. (모든 값이 form 안의 input이므로 각각의 input에 required 속성을 통해 제출 버튼 클릭은 모든 정보가 입력된 상태임)


𝟑. 패스워드 일치 확인

회원가입할 때 패스워드를 두 번 입력받아 입력된 값이 서로 일치하는지를 확인하여 사용자로 하여금 입력한 패스워드를 한 번 더 체크할 수 있게 했다.
먼저 회원가입 버튼에 disabled 속성을 추가한다. signup.js 파일에서 유저가 입력한 패스워드 값을 가져와서 일치하면 속성을 없애고 비밀번호가 일치하다는 문구를 보여준다. 일치하지 않으면 속성을 유지하여 버튼을 누르지 못해 form 제출을 막는다.

const check_pw = () => {
  const user_pw1 = $("#pw1").val();
  const user_pw2 = $("#pw2").val();
  if (user_pw1 == user_pw2) {
    $("#text-pw-check").html("<div class='txt-pw-correct'>비밀번호가 일치합니다.</div>");
    $(".btn-join").attr("disabled", false);
  } else {
    $("#text-pw-check").html("<div class='txt-pw-incorrect'>비밀번호가 일치하지 않습니다.</div>");
  }
};

𝟒. 데이터 전송 및 DB 저장

일단 전송과 관련해서 한가지 기록할 부분은, 원래 회원가입 버튼에 온클릭 이벤트를 걸어서 만들었는데 찾아보니, 버튼 type을 submit으로 바꿔서 form 태그로 보낼 수 있었다.
따라서 아래와 같이 수정하여 버튼을 사용했다.

<form id="form-join">
     ...
	<button type="submit" class="btn-join">JOIN</button>
</form>

submit 이벤트를 감지하면 발생하는 form 태그의 default 행동은 다큐먼트가 렌더될때 바로 막을 수 있게 아래와 같은 부분을 js에 추가했다.
참고로 form 태그에 대한 나의 트라이는 아래 링크로 따로 정리해놨다.
회원가입 - form 태그 이 놈

$(document).ready(() => {
  $("#form-join").on("submit", (e) => {
    e.preventDefault();
    join();
  });
});

그 다음 가장 중요한 데이터 저장 부분은 해싱과 솔트를 이해해야 했다.
이 부분에 대한 내용 또한 위 링크에 정리했다.

@app.route("/api/join", methods=["GET", "POST"])
def join():
    if request.method == "POST":
        ... 중략

        # 랜덤 솔트 값 생성
        salt = os.urandom(16)

        # 솔트랑 비번 합쳐서 해싱/ Encode로 bytes obj 만들어서 해싱하고 다시 16진수 스트링으로 변환(hexdigest())
        hashed_password = hashlib.sha256(password.encode() + salt).hexdigest()

        doc = {
            "id": id,
            "nickname": nickname,
            "salt": salt,
            "password": hashed_password,
        }
        db.디비이름.insert_one(doc)

        return jsonify({"message": "가입 완료"})
    else:
        return render_template("join.html")

사용자의 입력값을 받아서 encode() 시키고 랜덤 salt 값을 합쳐서 hashlib의 sha256 해싱함수를 통해 값을 해싱하고 값을 편하게 사용할 수 있게 hexdigest() 함수로 다시 16진수 스트링으로 변환하여 저장한다.
디비 저장까지 완료 후 가입 완료 메세지를 리턴해주어 클라이언트 쪽에서 로그인 페이지로 이동하게 해준다(2번 목차 참고)

📌 느낀점

일단 짧은 시간안에 미니 프로젝트로 기획부터 기능 구현까지 진행해야 했는데, 생각보다 커뮤니케이션이 정말 중요하다는 것을 깨달았다. 각자 맡은 기능들이 다르기 때문에 내가 어디까지 진행했고 앞으로 어느 부분을 할건지 서로 이해하는 부분들이 중요했다.
무엇보다 api 명세를 정하지 않고 바로 각자의 기능을 구현하고 머지하려다 보니 혼선이 있었다. 이 부분은 다음 프로젝트 진행 시 무조건 첫 시작을 api 명세를 정립해야겠다고 깨달았다. 또, End point가 동일하더라도 if문을 사용해 메소드를 구분지어 사용하는 부분을 적용해봤는데 코드도 깔끔하고 하나의 end point로 작성할 수 있어서 좋았다.
팀원들이 정말 적극적으로 의견을 제시하고 그것을 받아들이고 피드백 하는 과정이 원활해서 4일의 짧은 시간임에도 불구하고 처음 기획했던 부분들을 전부 구현해낼 수 있었음에 참 뿌듯했다.

참고 자료

encode 함수 참고 : encode_mdn
hash 참고 : hash and salt_tistory
form tag 참고 : form _mdn

0개의 댓글