토이 프로젝트(1) 로그인&게시판 페이지 만들기

Hohomi·2023년 3월 10일
0

🥹 첫 토이 프로젝트

항해99 사전 스터디에서 팀으로 만난 분들과 한 달 조금 넘게 같이 공부를 해왔다. 2주 정도는 항해에서 제공되는 강의를 각자 들었고, 그 후 일주일 정도 시간이 남았던 우리는 토이 프로젝트를 해보자는 쪽으로 의견을 모으게 되었다.

시작 때까지만 해도 나는 스파르타코딩클럽의 <웹개발 종합반> 강의에서 했던 실습을 약간 응용하면 되는 일이겠거니 하고 쉽게 생각했다. mongDB를 사용해서 데이터를 입출력하는 것까지 해보았으니 회원가입, 로그인, 수정과 삭제 기능이 있는 게시판 만들기쯤이야 gum이라고 생각했던 거다.

하지만 프로젝트를 나름 성공적으로 마치고 난 후 지금의 나에게 이 프로젝트의 코드는 아직도 반쯤 수수께끼와 같다. 팀원분들의 도움으로 완성까지는 어떻게 왔지만, 오는 도중 나는 길을 잃고 넋도 살짝 잃고 말았다. 메소드와 html 파일이 늘어나고, 데이터가 여기서 저기로 왔다 갔다 하는 경로가 다양해질수록 내 머릿속은 하얘져갔다. 길을 찾으려고 펜과 종이를 들어 그 어지러운 핑-퐁 과정을 그려가면서 코드를 뚫어지게 보기도 했지만, 좀 알겠다~ 싶을 때면 다른 오류가 또 생기고, 그 오류에 당황하다 또 길을 잃고 하기를 반복했다.

이 프로젝트를 하면서 내게 인상 깊게 남은 건 다음과 같다.

  • 내가 입력한 코드 한 줄이 어떤 원리로 작동되는 건지 이해하는 일을 게을리하면 안되겠구나!
부정형으로 표현하는 이유는 코드의 작동 원리를 이해하는 데에 게을러지기가 무척 쉬웠기 때문이다.
그리고 그런 일이 늘어날수록 점점 더 코드 지도에서 길치가 되어가는 느낌이었다...
그 느낌은 정말 쉣이어따..
내가 쓴 건데 이게 어떻게 해서 이렇게 되는 건지를 내가 모르다니;;
  • 문제가 생겼을 때 '당황하는 사람'에서 '어떻게 하면 해결할 수 있을지를 적극적으로 고민하는 사람'이 되어야겠구나.
나는 문제 앞에서 전형적인 당황러다.
당황을 하기 시작하면 '어? 이게 왜 안 되지?!?'라는 생각 외에 다른 생각은 잘 들지 않게 된다.
그리고 이 습관은 프로그램 개발을 공부하는 사람이 필수적으로 바꿔야 할 습관이다.
해결을 필요로 하는 문제를 만나는 게 일상인 개발자에게 이런 습관은 그야말로 쥐약이라는 걸 
몸으로 확! 깨달았다. 
  • 협업이자 함께 하는 공부인 만큼 그때 그때 소통을 명확히 하고, 상대의 코드를 이해하는 능력을 길러야겠다.
글로만 봤을 땐 너무 당연하고 뻔한 말이지만, 이번에 처음으로 함께 프로젝트를 꾸려보면서
의사소통과 상대의 코드에 대한 이해의 중요성을 느끼게 되어 좋았다.
'협업'이 어떤 건지, 어떻게 돌아가는 건지를 아주 살짝 맛본 느낌이다.
특히 같은 회원가입-로그인 팀이었던 성윤님께 영감(!)과 도움을 받았는데,
시도와 실패를 공유하고 같이 해결해나가는 과정에서 배운 것들이 많았다.



👨🏻‍🔬 프로젝트 회고 시작!

함께 하는 프로젝트는 끝났지만, 코드의 동작과 원리를 곱씹어보기 위해 회고를 남겨보려 한다. 원래는 코드 분석만 하고 넘어가려 했는데, 혼자서 코드를 보기만 한다고 해서 뭐가 되는 것 같지 않아서 이렇게 글로 정리해보려 한다.

1. 전체 흐름 설계

1-1. 회원가입 페이지

클라

  • 입력값 받아 서버에 보내기 : formdata.append, fetch
  • 하나라도 비었을 경우 '00를 입력해주세요' 알림
  • id 중복값 걸러내기 : 조건문 사용
  • 입력 후 '회원가입' 누르면 가입 완료/실패 알림 후 로그인 페이지로 넘어가기 : window.location.replace("/login")

서버

  • 입력값 받기 : request.form
  • 패스워드 해싱(암호화) : Hashlib
  • 데이터 받아 db에 저장 : Flask의 request.form['key']

1-2. 로그인 페이지

클라

  • id,pwd값 입력받아서 서버에 보내기
  • 쿠키에서 jwt 토큰 확인되면 알림 띄우고 게시판 페이지로 넘어가기
  • id,해싱pw 또는 토큰 만료시간 유효하지 않으면 로그인 실패 알림

서버

  • 입력값 받아 패스워드 해싱 후 db에 데이터 조회
  • id,해싱pw 조회되면 jwt token(만료시간 포함) 생성해서 클라에 리턴 : jquery $.cookie(토큰명)

1-3. 글 작성 페이지

클라

  • 제목,내용 입력값 받아서 서버에 보내기
  • 작성 완료/실패 알림 후 게시판 페이지로 넘어가기

서버

  • 쿠키에서 토큰을 가져와 디코딩 후 db에서 해당 id값 조회
  • 작성된 글 db에 데이터가 쌓일 때 무조건 db에 생성된 데이터 갯수보다 1이 더해진 seq(시퀀스) 변수 생성
    - 여기가 진짜 진짜 복잡하고 어려웠는데, 글 수정/삭제를 위해 데이터 고유값을 만들어주기 위한 발버둥의 과정이었다..
  • user db에서 조회된 id,namer값 + 입력받은 제목,내용 + 글 작성시 생성되는 seq값을 board db에 저장

여기부터 본격적으로 길을 잃은 구간이다. ^_ㅠ..
게시판 페이지와 글 수정, 삭제 페이지는 코드를 다시 보아도
정확하게 어떤 동작들을 하고 있는지 파악하기 어려운 부분들이 있었다.
'일단 구현'에 목표를 두고 이것저것 추가하며 만들어간 코드이다보니
대~강은 알겠는데 코드에 대한 이해가 정확하지 않은 데다가,
검색을 해보아도 나로서는 분석이 어려운 부분이 꽤 있어서 회고 기록은 이 단계까지만 하려고 한다.


2. 화면 구현

2-1. 회원가입

클라이언트

      function save_member() {
        if ($("#name").val() == "") {
          alert("이름을 입력하세요.");
          $("#name").focus();
          return false;
        }
        if ($("#id").val() == "") {
          alert("아이디를 입력하세요.");
          $("#id").focus();
          return false;
        }
        if ($("#pwd").val() == "") {
          alert("비밀번호를 입력하세요.");
          $("#pwd").focus();
          return false;
        }
        if ($("#email").val() == "") {
          alert("이메일을 입력하세요.");
          $("#email").focus();
          return false;
        }
        if ($("#tel").val() == "") {
          alert("전화번호를 입력하세요.");
          $("#tel").focus();
          return false;
        }

        let name = $("#name").val();
        let id = $("#id").val();
        let pwd = $("#pwd").val();
        let email = $("#email").val();
        let tel = $("#tel").val();

        let formData = new FormData();
        formData.append("name_give", name);
        formData.append("id_give", id);
        formData.append("pwd_give", pwd);
        formData.append("email_give", email);
        formData.append("tel_give", tel);

        fetch("/register", { method: "POST", body: formData })
          .then((res) => res.json())
          .then((data) => {
            alert(data["msg"]);
            window.location.replace("/login");
          });
      }

서버

@ app.route("/register", methods=["POST"])
def register():
    id_receive = request.form['id_give']
    name_receive = request.form['name_give']
    pwd_receive = request.form['pwd_give']
    email_receive = request.form['email_give']
    tel_receive = request.form['tel_give']
    pwd_hash = hashlib.sha256(pwd_receive.encode('utf-8')).hexdigest()

    doc = {
        'id': id_receive,
        'name': name_receive,
        'pwd': pwd_hash,
        'email': email_receive,
        'tel': tel_receive
    }
    chk = db.user.find_one({'id': id_receive})

    if chk is None:
        db.user.insert_one(doc)
        return jsonify({'msg': '회원가입 완료!'})
    else:
        return jsonify({'msg': '회원가입 실패!'})

2-2. 로그인

클라이언트

function ajax_login() {
            if ($("#id").val() == "") {
                alert("아이디를 입력하세요.");
                $("#id").focus();
                return false;
            }
            if ($("#pwd").val() == "") {
                alert("비밀번호를 입력하세요.");
                $("#pwd").focus();
                return false;
            }

            $.ajax({
                type: "POST",
                url: "/api/login",
                data: {
                    id_give: $("#id").val(),
                    pwd_give: $("#pwd").val()
                },
                dataType: "JSON",
                success: function (reponse) {
                    if (reponse['flag'] == 'true') {
                        $.cookie('mytoken', reponse['mytoken']);
                        alert(reponse['msg'])
                        window.location.href = '/'
                    }
                    else {
                        alert(reponse['msg'])
                    }
                }
            })
        }

서버

@ app.route("/api/login", methods=["POST"])
def api_login():
    id_receive = request.form['id_give']
    pwd_receive = request.form['pwd_give']
    pwd_hash = hashlib.sha256(pwd_receive.encode('utf-8')).hexdigest()
    chk = db.user.find_one({'id': id_receive, 'pwd': pwd_hash})
    if chk is not None:
        payload = {
            'id': id_receive,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=60 * 60)
        }
        token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
        return jsonify({'msg': '로그인 성공', 'flag': 'true', 'mytoken': token})
    else:
        return jsonify({'msg': '로그인 실패 : ID/PW를 확인하세요!', 'flag': 'false'})

2-3. 글 작성

클라이언트

function ajax_write() {
            if ($("#title").val() == "") {
                alert("제목을 입력하세요.");
                $("#title").focus();
                return false;
            }
            if ($("#comment").val() == "") {
                alert("내용을 입력하세요.");
                $("#comment").focus();
                return false;
            }

            $.ajax({
                type: "POST",
                url: "/api/write",
                data: {
                    title_give: $("#title").val(),
                    comment_give: $("#comment").val()
                },
                dataType: "JSON",
                success: function (response) {
                    if (response['flag'] == 'true') {
                        alert(response['msg'])
                        window.location.href = '/'
                    }
                    else {
                        alert("작성 실패!!")
                    }
                }
            })
        }

서버

@ app.route("/api/write", methods=["POST"])
def save_board():
    title_receive = request.form['title_give']
    comment_receive = request.form['comment_give']
    token_receive = request.cookies.get('mytoken')
    SECRET_KEY = 'TEST'
    payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
    user_info = db.user.find_one({"id": payload['id']})

    seq = list(db.board.find(
        {}, {"_id": False, "seq": True}).sort('seq', -1).limit(1))

    seq_chk = re.sub(r'[^0-9]', '', ":".join(map(str, seq)))
    if seq_chk == "":
        seq_zero = int(0)
        doc = {
            'id': user_info["id"],
            'name': user_info["name"],
            'title': title_receive,
            'comment': comment_receive,
            'seq': seq_zero
        }
    else:
        seq_num = int(re.sub(r'[^0-9]', '', ":".join(map(str, seq))))
        doc = {
            'id': user_info["id"],
            'name': user_info["name"],
            'title': title_receive,
            'comment': comment_receive,
            'seq': seq_num + 1
        }

    db.board.insert_one(doc)

    return jsonify({'msg': '작성 성공!', 'flag': 'true'})

2-4. 게시판

이런 식으로 만들었다.


어휴, 며칠에 걸쳐서 드디어 다 썼다.
처음에 어떻게 써야 하는 건지 한참을 고민하다가, 일단 쓰다가, 이 과정을 어떻게 담아야 할지 또 고민하다가, 또 조금 쓰다가, 를 반복했다...ㅋㅋㅋ
코드를 분석해보려고 시작한 일인데, 막상 와다다 진행하고 끝난 프로젝트의 코드를 찬찬히 살펴보려하니 어디부터 어떻게 건드려야 할지 막막함이 밀려왔다.🥹

특히 해보고 싶었던 건 전체 흐름을 파악하는 일이었다.
프로젝트 하면서 오류가 생기거나 기능 구현이 막힐 때마다 닥치는대로 찾아보고, 시도하고, 지우고, 고민하고 를 반복하다보니 나중에는 이 기능이 왜 필요한지, 이걸 구현하려면 어떤 데이터를 어디서 어디로 어떻게 왜 보내야하는지 하는 기본적인 것들이 헷갈렸다.
그게 헷갈리기 시작하니 내가 뭘 하고 있는지까지도 헷갈리는... 혼돈의 상태가 찾아왔다.@_@
그래서 전체적인 흐름을 쭉 살펴보고, 기능들이 코드와 어떻게 연결되고 있는지 하는 것들을 다시 한번 찬찬히 보고 싶었다.

함께 했던 사전스터디 팀원분들 모두 고생 많으셨습니다 🙏 
profile
게발로 개발하기

0개의 댓글