항해 99 1주차 토이프로젝트 주간 회고

2023. 4. 5. 12:32개발일기

 

What's your weather like?

  • 감정을 날씨에 비유, 본인의 감정을 기록하고 추적할 수 있는 감정트래커 프로젝트.
  • 실시간 현재 날씨 정보를 가져와서 배경으로 나타낸다.
  • 사용자 ux/ui 고려 (슬라이더, div 팝업 모달, 사용자 이용 흐름 고려)

What's your weather like?  깃허브 바로가기

 

토이 프로젝트 되짚어 보기

시간이 정말 정말 짧았다.

3일이라는 시간 동안 처음 만나는 사람들과 기획을 하고 순식간에 프로젝트를 구현해야했다.

각자 기술의 정도가 다르고 한사람만 잘 알고 있는 기술 스택으로 구현을 할 경우 프로젝트의 기여도가 한 사람에게 몰리고 그 한사람은 기술적으로 얻는 것이 있더라도 협업환경에 대해 이해할수 없을 것 같아 모두가 기초지식을 알고 있었던 파이선, 플라스크, 몽고디비, 자바스크립트, html을 사용해서 간단한 게시판 형태의 프로젝트를 구상했다.

다양한 기능을 구현하고 싶지만 팀원도 다른 팀에 비해 적었고 기간도 많이 짧아서 할수있는것들을 최선을 다해서 구현하는 것을 목표로 했다.

 

프로젝트 구성 단계

db설계, api 설계, 코드 작성 규칙, 브랜치 사용 모든게 낯설엇다.

무엇보다 프로젝트 구조를 처음 짤때 어떻게 짜야할지 많이 고민됬다.

(결국은 기본으로 파이선 플라스크가 권장하는 구조로 짜기는 했지만..)

파일들의 명명 규칙등도 고민이 되었다. 빨리 구현을 해야하는 만큼 어려운 명명규칙을 사용하지 않고 직관적으로 사용했다. 

이전에 회사에서 근무했을때 프로젝트 구조나 프로젝트 파일 명명 규칙 등을 자세히 살펴보았으면.. 이라는 생각을 많이 했다.

DB 구성을 하였는데 이전에 사용해본 DB가 모두 RDBMS 여서 기본적으로 테이블을 다 조각조각 설계해서 조인을 해올 생각으로 DB설계를 했다. 코드성 데이터인 감정정보와 날씨정보 등을 모두 별도의 DB에 저장하고 회원정보아 회원이 가지고 있는 감정트래킹 정보도 별도로 설계했다. 여기서 문제가 발생했다.

 

프로젝드 구현 단계

모두 프론트앤드 개발자를 바라고 있는 사람들끼리 한 조가 되었지만 토이프로젝트이다 보니 백앤드와 프론트앤드 작업을 모두 경험해 보는 것이 좋을 것이라고 생각해서 각자 기능별로 화면을 맡아서 기능과 화면 동시에 구현하기 시작했다. 어느정도 화면 레이아웃을 맞추고 서로 커밋을하고 머지를 하는데.. 충돌이 많이 발생했다. 우선적으로 모두 깃 사용이 처음이라 어렵기도 했었지만 작업을 하던 중간중간에도 문제가 많이 생겼다. 특히 갑자기 푸시와 풀이 모두 안되는 현상이 생겼는데 해당 부분을 해결하지 못해서 구현이 한참 지연됬었다.

깃은 구현단계 꾸준히 문제였다. 특히 구현 초반에는 팀원들 중에 나만 깃저장소가 푸시, 풀이 되지 않았는데 처음에는 깃 레포지토리 권한이 설정되어있지 않아서 푸시와 풀을 하지 못하였다. 권한을 다시 잘 부여하고 푸시를 하려는데 또 푸시가 되지 않아서 한참 일정이 밀리고 있었다. 알고보니 그날 모두가 푸시가 되지 않았었는데 깃허브 문제였다. 깃허브가 푸시가 되지도 않는 날이 오다니..

 

날씨 api 

날씨 api를 통해 접속자 지역을 기준으로 실시간 날씨 데이터를 가지고와서 특정 날씨에 따라 배경화면을 다르게 표현하는 부부을 구현하고 있었다. 분명 테스트데이터를 가져올때는 api가 잘 되었는데 키를 발급받고 사용하니 api key가 인가 되지 않은 키라는 오류가 나타났다.

잘못 사용한줄 알고 계속 해서 찾아봤는데 알고 보니 블로그를 통해서 알게된 해당 날씨 api를 제공하는 서비스가 유료로 변경되어 있었다.

검색을 해서 사용하다 보면 최신이 계속 바뀌기 때문에 늘 크로스 체크를 하는것이 중요하다고 느꼈다.

토이프로젝트의 컨셉에서 많은 영역을 차지하는 실시간 날씨를 가져올수 없게 되어서 고민을 많이 했는데 다른 api키를 발급 받으려니 발급까지 시간이 걸려서 다른 방법을 생각해야만 했다. 결국 정말 최악의 상황일때 사용하기로 했었던 웹크롤링을 선택했고 파이선 플라스크의 requests를 사용해서 날씨를 검색해서 그 값을 크롤링 해서 들고오는 방식으로 비교적 간단하게 구현했다.

문제 2 데이터베이스 설계를 RDBMS에서 하 듯 테이블을 잘게 나누고 조인을 하려고 보니 mongoDB는 NoSQL DB였다... 조인이 불가능 한것은 아니었지만 DBMS에 맞는 방식대로 하는게 좋을것 같아서 테이블 구조를 변경하였다. 화면 레이아웃을 잡고 있었던 구현 초반에 발견해서 전체 프로젝트에 영향을 크게 끼치지 않았지만 설계단계의 문제는 구현에서 발견될수록 문제가 영향을 끼치는 범위가 커진다는 것을 실감했다.

 

화면의 이동

간단하게 등록, 조회, 수정, 삭제 기능을 만들고 각자 만든 화면을 연결하려 하는데 서로가 이해하고 있었던 화면의 흐름이 달랐다. 다시 화면의 흐름을 정리해야할 필요가 있을 것같아서 전체적으로 그림을 그리면서 정리했다.

팀원들과 그려봤었던 화면 흐름도

화면흐름을 작성해 두고보니 놓치고 있었던 기능들이 나타났고 부족한 부분을 구현해야 했다. (사용자가 홈 화면에서 등록하기 버튼이나 로그인 버튼을 클릭해서 접속을 하게 되면 등록 하는 화면으로 가게 되고 작성이 완료되면 목록 화면으로 이동하자. 까지 생각했는데 전체적으로 화면을 그려보니 사용자가 접속하고 목록화면으로 바로갈수 있는 방법이 없었다! 그래서 목록으로 바로 갈수있도록 호출하는것을 추가하였다.)

기획단계에서 선행되었다면 좋았을 텐데 꼼꼼하게 한다고 했지만 러프하게 기획한 것이 아쉬웠다. 이렇게 생각하지 못한 부분들이 나올때마다 힘들고 지치기 보다는 아쉬움이 너무 컸다.

모두가 싫어하는 개발문서 작업이지만 분명히 필요한 작업임에는 분명하다는 생각을 했다.

 

세션 생성

로그인 기능, 회원가입 기능을 구현하고 생각해보니까 로그인을 통해서 해당 사용자가 인가된 사용자인지는 확인을 하였지만 그 이후 세션을 유지해주는 기능이 빠져있었다! 너무나 당연한 기능인데 이걸 넣지 않았다니.. 정신머리가 빠진건가 싶어서 부랴부랴 세션에 로그인 정보를 유지해야한다고 말하고 기능을 구현하기 시작했다. 하지만 파이선을 모두 처음 사용해보는데 분명 세션생성을 하는데도 세션이 생성되지 않아서 문제였다. 환경설정 문제인가? 왜 세션이 구현이 안될까 생각을 하다가 쿠키에 사용자 로그인 정보를 저장하는 방법도 생각했다. 하지만 일시적인 로그인기능이라 쿠키에 저장하는 것은 아닌것 같아 방법을 고민하다가 결국 웹스토리지를 사용해서 구현을 하였다.

 

헤더 푸터 적용, 커스텀 모달창 사용

각자 화면을 맡아서 구현하지만 공통의 프로젝트이기 때문에 공통 레이아웃이 필요하다고 생각했다. 파이선에서 레이아웃을 잡아본 경험이 없어서 많은 검색을 했는데 flask의 상속 기능을 사용하면 공통 레이아웃을 잡기가 쉽다고 해서 해당 기능으로 헤더 푸터를 적용했다.

또 화면에 나타나는 모달창을 커스텀 모달창도 구현했다. 상속기능을 사용해서 모달창 div를 모달을 호출하게 되는 화면에 추가하였고 해당 버튼들에 특정 값을 넣을수 있도록 구현했다.

아쉬운점은 모든 alert를 커스텀 모달로 구현하고 싶었지만으로 구현하고 싶었지만 프로젝트 마지막에 추가된 팝업들이 많아서 모두를 커스텀 하지는 못했다.

<!-- 
templates/register.html
{% include "common/popup_test.html" ignore missing %} 를 통해서 공통 html 소스를 가져온다.
-->
<body>
    {% extends "common/layout.html" %}
    {% block content %}
    <video src="../static/video/ocean.mp4" autoplay loop muted class="b_video" id="b_viedo"></video>

    <div class="wrap_content">
        <h2 class="fir_title">당신의 날씨를 기록하세요!</h2>

        <h2 class="sec_title">sign up</h2>

        <span class="guide">* 표시된 항목은 필수 입력입니다.</span>

        <div class="register-info">
            <div class="input-group">
                <div class="input_group_row1">
                    <span class="star1">* </span><input id="user_nm" type="text" class="form-control"
                        pattern="^[ㄱ-ㅎㅏ-ㅣ가-힣]*$" requiredplaceholder placeholder="닉네임" />
                    <button onclick="chk_nm()" type="button" id="chk_nm_btn" disabled="disabled"
                        class="regist_btn">확인하기</button>
                </div>
            </div>
            <div class="input-group-result_text input-group-nm-result"></div>
        </div>
        <div class="input-group">
            <div class="input_group_row2">
                <span class="star2">* </span><input id="user_pw" type="password" class="form-control"
                    oninput="confirm_pw()" required placeholder="비밀번호" />
                <span class="star3">* </span><input id="confirm_pw" type="password" class="form-control"
                    oninput="confirm_pw()" required placeholder="비밀번호 확인" />
            </div>
            <div class="input-group-result_text input-group-confirm"></div>
        </div>

        <button onclick="sign_up()" id="sign_up_btn" type="button" disabled="disabled" class="regist_btn">
            회원 가입하기
        </button>
    </div>
    {% include "common/popup_test.html" ignore missing %}
    </div>
    {% endblock %}
</body>

 

<!-- 
공통 커스텀 팝업 HTML
각 엘리먼트 요소들의 아이디를 통해 제어한다.
-->
<div class="p_alert" style="display:none;">
    <div class="dark p_alert" style="display:none;"></div>
    <div class="login_popup">
        <span id="sad" class="p_emoji" style="display:none; font-size:220px; margin-bottom: 30px;">😥</span>
        <span id="love" class="p_emoji" style="display:none; font-size:220px; margin-bottom: 30px;" >😘</span>
        <span id="smail" class="p_emoji" style="display:none; font-size:220px; margin-bottom: 30px;">😊</span>
        <strong id="msg_p_alert">메세지 자리</strong>
        <div class="btn_login">
            <button id="btn_p_alert_yes" class="btn_yes">
                예
            </button>
            <button id="btn_p_alert_no" class="btn_no">
                아니요
            </button>
        </div>
        <button id="btn_p_alert_close" class="btn_close">
            <i class="fa-solid fa-xmark"></i>
        </button>
    </div>
</div>

 

       <!-- 
       팝업창을 호출하는 화면 스크립트에서 조건에 따라 팝업창을 컨트롤 한다.
       -->
       <script>
        /*
            팝업 표시를 위한 설정 함수
            String s_p_type : 표시할 팝업의 타입, [love|sad|smail]
            String s_p_msg : 팝업에 표시할 메세지
            Boolean b_p_btn : 버튼 사용 여부 [true - 둘다 사용|false-'예'만 사용]
            String s_url : 호출할 화면 url
            Boolean b_p_close : 닫기버튼 활성 여부, [true - 닫기버튼 이벤트|false-'예' 이벤트와 동일]
            Boolean b_p_session : 세션 활성화 여부, [true - 세션을 바로 담아서 넘겨줘야 하는 경우 ex)회원가입 완료후 로그인 처리 |false-세션 이미 들어있거나 상관 없음]
            String s_p_user_name : 사용자명, 세션 유지 및 안내 메시지에서 사용 가능
         */
         function set_popup(s_p_type, s_p_msg, b_p_btn, s_url, b_p_close, b_p_session, s_p_user_name) {
            // 팝업 표시
            for (i = 0; i < document.getElementsByClassName("p_alert").length; i++) {
                document.getElementsByClassName("p_alert")[i].style.display = "block"
            }
            // 아이콘 표시
            document.getElementById(s_p_type).style.display = "block"

            // 버튼 표시
            if (!b_p_btn) {
                // 아니오 버튼 표시 안함
                document.getElementById("btn_p_alert_no").style.display = "none"
                // 예 버튼 크기 늘리기
                document.getElementById("btn_p_alert_yes").style.width = "100%"
            }

            // 이벤트 걸기
            if (!b_p_btn) {
                // 예
                document.getElementById("btn_p_alert_yes").addEventListener("click", function () {
                    window.location.href = s_url
                })
            } else {
                // 예
                document.getElementById("btn_p_alert_yes").addEventListener("click", function () {
                    window.location.href = s_url
                })
                // 아니오
                document.getElementById("btn_p_alert_no").addEventListener("click", function () {
                    for (i = 0; i < document.getElementsByClassName("p_alert").length; i++) {
                        document.getElementsByClassName("p_alert")[i].style.display = "none"
                    }
                })
            }

            // 닫기버튼 활성 여부
            if (b_p_close) {
                document.getElementById("btn_p_alert_close").addEventListener("click", function () {
                    window.location.href = s_url
                })
            } else {
                document.getElementById("btn_p_alert_close").addEventListener("click", function () {
                    for (i = 0; i < document.getElementsByClassName("p_alert").length; i++) {
                        document.getElementsByClassName("p_alert")[i].style.display = "none"
                    }
                })
            }

            // 세션 넣어주기
            if(b_p_session){
                sessionStorage.setItem("username", s_p_user_name);
            }

            // 메세지 내용 넣기
            document.getElementById("msg_p_alert").innerText = s_p_user_name+"님\n"+s_p_msg
        }
        </script>

 

구현했었던 커스텀 모달 화면 팝업 호출에 따라 아이콘이 바뀐다 (경고창, 확인창, 컨펌창)

 

프로젝트 배포 단계

AWS 자체

프로젝트를 배포하고 단계에서도 문제가 많았다. 처음에 조장님 AWS계정을 통해 EC2로 배포를 하려 했는데 배포 과정에서 몇번의 실수를 하고 배포를 분명히 제대로 했는데 자꾸 플랫폼 상태가 좋지 않다고 정상적으로 서비스가 돌지 않았다. 반복해서 내리고 다시 배포하고를 하다보니 조장님 AWS계정이 리젝이 되버렸다!.. 배포를 못하는건가 고민하다가 EB를 통해 배포하는 방법을 추천해주셔서 해당 방법으로 배포를 하려 했다. 하지만 조장님 계정은 리젝이 되어서 다른 계정으로 배포를 해야했고 내 계정에서 배포를 하였다.

AWS 배포 작업이 처음이라 너무 무섭기도 했고 (요금이 많이 나오면 어쩌지?) 하다보니 몇번 실패했었다. 그러다가 성공했는데 바보같이 이미지 리소스를 포함하지 않은채로 배포를 해서 문제가 되었다. 기능들이 다 나되는데 이미지 리소스가 하나도 나오지 않는 것이다. 다시 정신차리고 이미지리소스도 포함하니 정상적으로 배포가 되었다.

다같이 만든 프로젝트를 배포해서 사람들이 접근할수 있다는 경험자체가 너무 즐거웠기 때문에 배포에 대해서 조금더 알아봐야겠다고 생각했다.

 

느낀점

많이 아쉬웠다. 구현시간이 짧아서 코드를 아쉽게 작성하고 발표를 해야했다는 점, 또 내가 알고 있는 것들을 팀원들에게 전하는 능력이 부족해서 스스로 너무 답답했다. 코드 구현 외로도 프로젝트 전체의 방향성등에 대한 고민해야하는 지점일때 마다 더 많이 경험했고 더 많이 알고 있어서 새로운 방향을 제안할수 있었다면 좋았을텐데 생각했다.

여러모로 정진해야겠다는 좋은 자극이 되었다.

또 공통으로 사용하는 함수(날씨에 따라서 배경화면을 바꿔주는 함수) 등을 화면에 동일하게 작성했는데 해당 함수를 공통 js로  분리해서 사용했다면 더 보기 좋은 소스 코드가 되었을꺼라는 아쉬움이 있다.

 

우리팀 모두가 정말 적극적으로 참여했기 때문에 삼일이란 시간동안 모두들 처음 배우는 언어들로 저만큼 구현할 수 있었다고 생각한다.

이자리를 빌어 우리팀 사람들에게 감사의 말을전하고 싶다.

 

온라인 부트캠프이기 때문에 풀어지기 쉬운데 다들 열심히 하셔서 나도 더 열심히 할 수 있었다.

또 지난 개발공부했던 기간들을 전체적으로 돌이켜 보며 이런 부분이 개발을 할때 중요하구나 하는 점도 많이 깨달았다.

구현하지 못한 기능들 생각만하고 추가하지 못한 기능들이 많아서 아쉬웠다.

 

동영상

시연 동영상 바로 가기

 

'개발일기' 카테고리의 다른 글

[TIL] 23.04.11.  (0) 2023.04.11
[TIL] 23.04.10.  (0) 2023.04.11
[TIL] 23.04.08.  (0) 2023.04.08
[TIL] 23.04.07.  (0) 2023.04.07
[WIL] 23.04.03. - 23.04.06.  (0) 2023.04.07