Showing

정글사관학교 0주차, 3박 4일 미니 팀프로젝트 <알고풀자> 본문

정글사관학교 6기

정글사관학교 0주차, 3박 4일 미니 팀프로젝트 <알고풀자>

RabbitCode 2023. 3. 6. 02:40

 

카이스트 정글사관학교 강의동

1. 정글에 입성한지 0~1주차

 

입소하자마자 3박 4일 미니 프로젝트 100퍼센트 실화

 

 안녕하세요! FlyDuck Dev입니다. 현재 정글사관학교 6기에 입소한지 1주차(7일차)입니다. 따끈따끈하게 정글사관학교 0주차와 1주차 커리큘럼에 임하고 일요일이 되어 주간 개발 회고를 남기고자 합니다. 정글에 입소하자마자 임하게된 0주차 팀프로젝트에서 어떤 개발 활동들을 하였는지 일련의 과정들과 결과물을 고스란이 해당 포스팅에 담아보았습니다.

 

 

2.  [0주차] 3박 4일 미니 프로젝트

 정글은 입학시험이 있습니다. 입학시험에서 배운 기술들을 토대로 처음 만난 0주차 저를 포함한 3인의 팀원이 3박 4일간 미니 프로젝트를 완성보는 것이 0주차 커리큘럼이었습니다. 파이썬 플라스크, 진자2, 자바스크립트 ajax, jwtpy, bootstrap을 활용해야 했습니다.

 

(1) 미니 프로젝트 기획

-1- 페이지별 기능, 내용

 ot가 끝난 후 하루의 반 밖에 남지 않은 상황, 처음 만난 팀원분들과 머리를 맞대어 기획부터 짜내야만 했습니다. 실제 개발 가능한 기한은 단 이틀이었기 때문에 다음날 기획 발표 이후 바로 개발에 돌입하기 위해서, 휘날리는 글씨체로 어떻게든 기획 및 와이어 프레임을 짜보려고 브레인스토밍 한 흔적들을 사진으로 담았습니다.

 

 그럼에도 불구하고 짧은 시간 동안 3명이서 함께 요리, 패션, 게임, 경제, 음악, 스포츠 관심영역이 있는지 충분히 검토해보았고 팀원 모두가 관심있는 영역은 개발 공부에 대한 기록이었습니다.

그렇게 기획하고 개발하게 된 알고 풀자

http://algopulja.xyz/

 

Algopulja

Weekly Problems Easy

algopulja.xyz

 


아래는 알고 풀자 웹 프로젝트 중간 발표문에서 가져온 텍스트입니다.


프로젝트 개요

매주 백준의 알고리즘 문제 5문제를 새롭게 추천해주고

페이지에 접속한 회원들은 함께 같은 문제를 일주일 동안 풀게 된다.

다양한 회원들이 문제를 풀어서 풀이를 공유하면 다른 회원들은 좋아요를 누르거나 댓글로 리스펙할 수 있다.

다른 사람들에게 좋아요를 받아 랭킹을 올리는 시스템을 통해

꾸준하게 알고리즘 문제를 푸는 재미를 느낄 수 있도록 한다.

 

프로젝트 기획 의도

개발자, 개발을 준비하는 사람들은 꾸준하게 알고리즘 문제를 풀어서 실력을 상승하는 것이 중요하다는 사실은 알고 있다.

  • 꾸준히 알고리즘 공부의 중요성
  • 그러나, 혼자 알고리즘 공부를 지속적으로 이어나가기 어려움

차별화 요소

  • easy / hard 모드 구분을 통해 원하는 난이도에서 특정 문제를 해결하여 상위 랭커에 도전할 수 있다.
  • 매주 문제가 갱신되어 뉴비도 고인물에게 도전할 수 있고, 매번 풀 문제를 고민하지 않아도 된다.
  • 가입된 유저들에게만 회원들의 코드를 볼 수 있도록 하여 회원가입을 유도한다.

페이지 개요

  • 메인 페이지 (이번 주의 문제, 유저 랭킹, 회원가입 버튼, 로그인 버튼)

하지만 ‘혼자’ 문제를 풀면서 지속적으로 백준을 풀만한 동기를 느끼기 어렵다.

또한 백준과 solved.ac 등에서 공개하고 있는 랭킹 시스템은 기존 고인물 유저들이 점령하고 있어 뉴비들이 재미와 자극을 받기 어렵다는 단점이 있다.

따라서, ‘알고 풀자’에서는 매주 알맞은 레벨의 문제를 5개 랜덤으로 공개하여 주어진 문제만 열심히 푼다면 누구나 상위 랭커에 도전할 수 있는 기회를 제공하고자 한다.

다른 회원들과 매주 동일하게 선정된 알고리즘 문제를 함께 풀고, 문제별 ‘좋아요’를 받아 누구나 창의적이거나 효율적인 풀이법을 발견한다면 높은 랭킹에 이름을 올릴 수 있는 시스템을 구상하였다.

이는 알고리즘 뉴비들에게도 꾸준하게 백준 문제를 풀 동기를 부여하는데 도움이 될 것이다.



-2-  프론트 레이아웃 구성

위와 같이 백준을 푼 코드를 색다르게 공유해보는 웹사이트인 알고 풀자의 세부 기획(페이지별 기능, 내용)을 마치고, 레이아웃 구성에 대해서 서로의 의견을 촘촘하게 맞추어나갔습니다.

 

 

 위와 같이 휘날리는 와이어 프레임이 뿐이었지만 감사하게도 팀원분 중에서 피그마를 다룰 줄 아는 분이 계셨기 때문에 아래와 같이 시각 자료를 제작할 수 있었습니다.

 

 

 

3. 개발 전, 중간 발표 자료

 

4. 최종 발표 자료

이름은 라이언으로 가렸습니다!

 

5.  '알고풀자' 최종 개발 사항

링크: http://algopulja.xyz/

 

Algopulja

Weekly Problems Easy

algopulja.xyz

git 링크는 현재 private 상태이기 때문에 공개 전환되면 추가하겠습니다.

  메인 페이지는 상단 로그인, 회원가입, 로그아웃 로직이 반영되어 있고 가운데 난이도를 조절하는 토글 버튼이 있습니다. 난이도에 따라 5문제를 추천해주게 되고 해당 문제 풀이를 제출한 유저들의 목록이 펼쳐지게 됩니다. 메인페이지와 로그인 회원가입 페이지는 같은 팀원 분이신 신**님과 박**님이 함께 백엔드+프론트엔드 작업을 만들어주셨습니다.

 

 저는 코드 제출 페이지와 소스 코드 페이지를 개발하였습니다. Jinja2 템플릿 엔진을 이용한 서버사이드 렌더링을 활용하였습니다. 아래로는 제가 개발한 페이지들에 대한 코드와 설명을 작성해보았습니다.

(1) 담당1 : 코드 제출 페이지

-1- 프론트

프론트 : 각 페이지는 모두 부트스트랩을 썼기 때문에 가로를 줄여도 포맷이 유지됩니다. 

<!DOCTYPE html>
<html lang="ko">

    <head>
        <!-- Webpage Title -->
        <title>알고 풀자 | Code Page</title>
        <link
        rel="stylesheet"
        type="text/css"
        href="{{ url_for('static', filename='style.css')}}"
      />

        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">
        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>

        
        <script type="text/javascript">
            let userName = "";
            $(document).ready(function () {
                $.ajax({
                    type: 'GET',
                    url: '/getNickname',
                    success : function(response) {
                        userName = response['nickname'];
                        $("#author").html(userName);
                    }
                })
            });

            function submitCode() {
                let problemNum = $('#problemNum').html()
                let solution = $('#source-code').val()
                $.ajax({
                    type: "POST",
                    url: "/form",
                    data: {problem_give:problemNum,nickname_give:userName ,solution_give:solution},
                    success: function (response) {
                        window.location.href = '/';
                    }
                })
            }
         
        </script>

        <style type="text/css">
            * {
                font-family: "Do Hyeon", sans-serif;
            }

            h1,
            h5 {
                display: inline;
            }

            path {
                fill: hotpink;
            }

            .info {
                margin-top: 20px;
                margin-bottom: 20px;
            }

            .review {
                text-align: center;
            }

            .reviews {
                margin-top: 100px;
            }
        </style>
    </head>

    <body class="index">
        <!--SECTION : Navbar-->
        <nav class="navbar navbar-expand-lg bg-body-tertiary">
            <div class="container-fluid">
              <a class="navbar-brand" href="/" style = "color: rgb(255, 136, 0)">
                <img src="{{url_for('static', filename='src/img/algopulza.png')}}" width="70px"/>
            </a>
              <div class="collapse navbar-collapse" id="navbarSupportedContent">
              </div>
            </div>
          </nav>
        <div class="container">
            <div class="info">
                <h1>해결 코드</h1>
                <p>자신만의 코드를 공개하고 하트를 모아보세요!</p>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">문제 번호</span>
                    </div>
                    <div type="text" class="form-control" id="problemNum">{{problem_num}}</div>
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">유저 이름</span>
                    </div>
                    <div type="text" class="form-control" id="author"></div>
                </div>
                <div class="input-group mb-3">
                    <div class="input-group-prepend">
                        <span class="input-group-text">풀이 코드</span>
                    </div>
                    <textarea class="form-control" id="source-code" name="submission_give"
                              cols="30"
                              rows="5" placeholder="풀이를 입력해주세요."></textarea>
                </div>
                <div>
                    <button onclick="submitCode()" type="button" class="btn btn-outline-secondary">
                      <span class="visually-hidden">Submit</span>
                    </button>
                </div>
            </div>
        </div>
    </body>

</html>

-2- 백엔드

1) /form

 기본적으로 클라에서 응답값을 받아오고 디비에 인서트하는 코드를 작성하였습니다. 다만, 디비를 잘 다루시는 팀원분이 계셔서 db.counters와 db.challenge 디비 설계를 도맡아 임해주셨습니다. 디비 분산 및 몽고 디비 인덱스 카운팅에 대한 지식이 거의 없는 상태였기에 설명을 많이 듣고 배우면서 디비에 관련된 코드 쪽으로 도움을 많이 받았습니다. 이 과정을 통해 프로젝트에 임하면서 페이지가 많아지고, 기능 간에 교집합이 많아 질수록 계획적인 디비 설계 능력이 필수적이라는 것을 알게 되었습니다. 앞으로 어떤 부분을 더 채워나가야 할지 확실히 알게된 계기가 되었습니다. 

 

@app.route('/form', methods=['POST'])
def submit():
    nickname_receive = request.form['nickname_give']
    solution_receive = request.form['solution_give']
    problem_num = request.form['problem_give']
    
    solution_id = db.counters.find_one({'key':'solution_id'})['seq'] + 1
    problem = db.challenge.find_one({'problem_num': int(problem_num)})
    
    if problem['solution_list']:
        problem['solution_list'].append(solution_id)
        db.challenge.update_one({'problem_num': int(problem_num)}, {'$set':{'solution_list':problem['solution_list']}})
    else:
        db.challenge.update_one({'problem_num': int(problem_num)}, {'$set':{'solution_list':[solution_id]}})
        
    db.counters.update_one({'key':'solution_id'}, {'$set':{'seq':solution_id}})
    
    doc = {
        'solution_id': solution_id,
        'nickname': nickname_receive,
        'source_code': solution_receive,
        'problem_num': problem_num,
        'like': [],
        'comments': []
    }
    
    db.solutions.insert_one(doc)
    
    return render_template('index.html')

 

2) /getNickname

 

두번째 항목 유저이름을 받아오는 쪽의 서버 api은 로그인한 유저의 닉네임 정보값이 필요하여서 이 부분의 api는 jwt 개발을 담당한 팀원분께서 작성해주셨습니다.

시간 관계상 제가 jwt 개발을 맡지는 못하였지만 개발된 코드를 정리해보는 시간을 가져보았습니다.

@app.route('/getNickname', methods=['GET'])
def getNickname():
    token_receive = request.cookies.get('token')
    if token_receive:
        token_receive = bytes(token_receive, 'utf-8')
        try:
            payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
            if db.user.find_one({'id': payload['id']}):
                nickname = db.user.find_one({'id': payload['id']});
                nickname = nickname['nickname'];
                return jsonify({"nickname":nickname})
        except jwt.ExpiredSignatureError:
            return jsonify({'result' : 'fail', 'login_state':0})
        except jwt.exceptions.DecodeError:
            return jsonify({'result' : 'fail', 'login_state':0})
    else:
        return jsonify({'result': 'fail', 'login_state':0})

https://youtu.be/zC5dLbZMAW0

개발 히스토리 이슈

토큰을 서버에서 발급한 뒤 json 형태로 감싸서 클라이언트로 전송
발급된 토큰이 바이트 타입을 가져 json 으로 감싸지 못하는 에러 발생
-> 바이트 형태의 토큰을 string으로 변환하여 전송함

 

따라서 /getNickname 에서는 클라에서 받은 토큰을 다시 bytes 타입으로 감싼 jwt, 시크릿키, HS256 알고리즘을 통해 decode하여 얻은 페이로드를 활용하는 로직이 필요했다. 결과적으로는 몽고디비에서 유저를 찾아 닉네임을 클라로 다시 보내주는 형식

 

(2)  소스 코드 확인 페이지

-1- 프론트

프론트 : 각 페이지는 모두 부트스트랩을 썼기 때문에 가로를 줄여도 포맷이 유지됩니다. 

<!DOCTYPE html>
<html lang="ko">

    <head>
        <!-- Webpage Title -->
        <title>알고 풀자 | Code Page</title>
        <link
        rel="stylesheet"
        type="text/css"
        href="{{ url_for('static', filename='style.css')}}"
      />
        <!-- Required meta tags -->
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta id="solution-id" data-id="{{solution_id}}" data-code="{{code}}">
        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous">
        <!-- JS -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous"></script>

        
        <script type="text/javascript">
            let userName = "";
            $(document).ready(function () {
                $.ajax({
                    type: 'GET',
                    url: '/getNickname',
                    success : function(response) {
                        userName = response['nickname'];
                        console.log(userName);
                    }
                })
                let code = $('#solution-id').data();
                code = code['code'];
                $('#sourceCode').html(code);
                showReview();
            });

            function makeComment() {
                let solutionId = $('#solution-id').data();
                solutionId = solutionId['id'];
                let comment = $('#comment').val()
                if (comment.length == 0) alert('빈 댓글은 달 수 없습니다!')
                if (typeof comment === 'undefined' || comment === 'null') { return undefined;}
                $.ajax({
                    type: "POST",
                    url: "/review",
                    data: {solution_id:solutionId, nickName_give:userName, comment_give:comment},
                    success: function (response) {
                        window.location.reload();
                    }
                })
            }

            function showReview() {
                let solutionId = $('#solution-id').data();
                console.log(solutionId);
                $.ajax({
                    type: "GET",
                    url: `/getreview/${solutionId['id']}`,
                    data: {},
                    success: function (response) {
                            let reviews = response['all_reviews']
                            console.log(response)
                            console.log(reviews)
                        for (let i =0; i< reviews.length; i++){
                            let nickName = reviews[i][0]
                            let comment = reviews[i][1]
                            let temp_html=`<tr>
                        <td>${nickName}</td>
                        <td>${comment}</td>
                    </tr>`
                            $('#comment-field').append(temp_html)
                        }
                        }
                    }
                )
            }

            function giveLike() {
                let solutionId = $('#solution-id').data();
                solutionId = solutionId['id']

                if (typeof solutionId === 'undefined' || userName === 'null') { return undefined;}
                $.ajax({
                    type: "GET",
                    url: `/solution/${solutionId}`,
                    success: function (response) {
                        let data = response.data;
                        let overlap = false;
                        for (var nickname of data['like']) {
                            if (nickname == userName) {
                                overlap = true;
                            }
                        }
                        if (overlap == true){
                            alert("이미 리스펙을 표현했습니다!");
                        }
                        else {
                            $.ajax({
                                type: "POST",
                                url: "/like",
                                data: {solution_id: solutionId, nickName_give:userName},
                                success: function (response) {
                                    // window.location.reload();
                                    console.log('success');
                                }
                            });
                        }
                    }
                });
            }

    </script>

    <style type="text/css">
        * {
            font-family: "Do Hyeon", sans-serif;
        }

        h1,
        h5 {
            display: inline;
        }

        path {
            fill: hotpink;
        }

        .info {
            margin-top: 20px;
            margin-bottom: 20px;
        }

        .review {
            text-align: center;
        }

        .reviews {
            margin-top: 100px;
        }

        .reviewBtn {
            margin-bottom: 10px;
        }
    </style>
</head>

<body class="index">
    <!--SECTION : Navbar-->
    <nav class="navbar navbar-expand-lg bg-body-tertiary">
        <div class="container-fluid">
          <a class="navbar-brand" href="/" style = "color: rgb(255, 136, 0)">
            <img src="{{url_for('static', filename='src/img/algopulza.png')}}" width="70px"/>
        </a>
          <div class="collapse navbar-collapse" id="navbarSupportedContent">
          </div>
        </div>
      </nav>
    <div class="container">
        <div class="info">
            <h1>소스 코드</h1>
            <p>리스펙한다면 하트를 눌러주세요!</p>
            <div class="input-group mb-3">
                <div class="input-group-prepend">
                    <span class="input-group-text">문제 번호</span>
                </div>
            <div type="text" class="form-control" id="problemNum">{{problem_num}}</div>
            </div>
            <div class="input-group mb-3">
                <div class="input-group-prepend">
                    <span class="input-group-text">유저 네임</span>
                </div>
                <div class="form-control" id="author">{{nickname}}</div>
            </div>
            <div class="input-group mb-3">
                <div class="input-group-prepend">
                    <span class="input-group-text">풀이 코드</span>
                </div>
                <pre class="form-control" id="sourceCode"
                          cols="30"
                          rows="5"></pre>
            </div>
            <div>
                <button onclick="giveLike()" type="button" class="btn btn-outline-secondary reviewBtn">
                    <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="red" class="bi bi-heart" viewBox="0 0 16 16">
                    ::before
                    <path d="m8 2.748-.717-.737C5.6.281 2.514.878 1.4 3.053c-.523 1.023-.641 2.5.314 4.385.92 1.815 2.834 3.989 6.286 6.357 3.452-2.368 5.365-4.542 6.286-6.357.955-1.886.838-3.362.314-4.385C13.486.878 10.4.28 8.717 2.01L8 2.748zM8 15C-7.333 4.868 3.279-3.04 7.824 1.143c.06.055.119.112.176.171a3.12 3.12 0 0 1 .176-.17C12.72-3.042 23.333 4.867 8 15z"></path>
                  </svg>
                  <span class="visually-hidden">Respect</span>
                </button>
            </div>
        </div>
        <div class="reviews">
            <table class="table">
                <tr>
                    <div class="input-group mb-3">
                        <input placeholder="멋진 코드에 respect를 남겨주세요!" type="text" class="form-control" id="comment">
                    </div>
                </tr>
                <div class="comment-btn">
                    <button onclick="makeComment()" type="button" class="btn btn-outline-secondary reviewBtn">
                      <span class="visually-hidden">Comment</Command></span>
                    </button>
                </div>
                <thead>
                <tr>
                    <colgroup>
                        <col style="width:20%">
                        <col style="width:80%">
                    </colgroup>
                    <th scope="col">닉네임</th>
                    <th scope="col" class="th_view">댓글</th>
                </tr>
                </thead>
                <tbody id="comment-field">
                </tbody>

            </table>
        </div>
    </div>
</body>

</html>

-2- 백엔드

@app.route('/solution/<problem_num>/<solution_id>', methods=['GET'])
def solution(problem_num, solution_id):
    solution = db.solutions.find_one({'solution_id': int(solution_id)})
    nickname = solution['nickname']
    source_code = solution['source_code']
    
    return render_template("solutionPage.html", problem_num = problem_num, nickname = nickname, code = source_code, solution_id = solution_id)


@app.route('/getreview/<solution_id>', methods=['GET'])
def getReview(solution_id):
    reviews = db.solutions.find_one({'solution_id':int(solution_id)}, {'_id': False})['comments']
    review_list = []
    for review_id in reviews:
        temp = db.review.find_one({'comment_id': review_id})
        review_list.append([temp['nickName'], temp['comment']])
    return jsonify({'all_reviews': review_list})


@app.route('/review', methods=['POST'])
def review():
    solution_id = request.form['solution_id']
    nickName_receive = request.form['nickName_give']
    comment_receive = request.form['comment_give']
    comment_id = db.counters.find_one({'key':'comment_id'})['seq'] + 1
    doc = {
        'nickName': nickName_receive,
        'comment': comment_receive,
        'comment_id': comment_id
    }
    solution = db.solutions.find_one({'solution_id': int(solution_id)})
    solution['comments'].append(comment_id)
    db.solutions.update_one({'solution_id': int(solution_id)}, {'$set':{'comments':solution['comments']}})
    db.review.insert_one(doc)
    db.counters.update_one({'key':'comment_id'}, {'$set':{'seq': comment_id}})
    return jsonify({'msg': '댓글 추가 완료!'})


@app.route('/like', methods=['POST'])
def like_solution():

    nickname = request.form['nickName_give']
    solution_id = request.form['solution_id']
    solution = db.solutions.find_one({'solution_id':int(solution_id)})
    if solution['like']:
        solution['like'].append(nickname)
        db.solutions.update_one({'solution_id': int(solution_id)}, {'$set':{'like': solution['like']}})
    else:
        db.solutions.update_one({'solution_id': int(solution_id)}, {'$set':{'like': [nickname]}})

    return jsonify({'result': 'success'})

@app.route('/getNickname', methods=['GET'])
def getNickname():
    token_receive = request.cookies.get('token')
    if token_receive:
        print(type(token_receive))
        token_receive = bytes(token_receive, 'utf-8')
        try:
            payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
            if db.user.find_one({'id': payload['id']}):
                nickname = db.user.find_one({'id': payload['id']});
                nickname = nickname['nickname'];
                return jsonify({"nickname":nickname})
        except jwt.ExpiredSignatureError:
            return jsonify({'result' : 'fail', 'login_state':0})
        except jwt.exceptions.DecodeError:
            return jsonify({'result' : 'fail', 'login_state':0})
    else:
        return jsonify({'result': 'fail', 'login_state':0})

 

6.  정글 0주차 미니 프로젝트 후기

 

 정글 입소 후 통성명하자마자 프로젝트에 돌격했어야만 한 정글 덕분에 간만에 짧은 시간(3박 4일) 제대로 몰입하여 개발에 임할 수 있었습니다. 열려있는 자세로 소통해주신 박**님과 신**님 덕분에 막힘없이 즐겁게 프로젝트에 임할 수 있었고, 기한 내에 완성과 배포, 시연까지 해낸 첫번째 성공 경험을 정글에서 가지게 되어 기쁘게 생각합니다.

 

 스스로에게 아쉬웠던 점은 위에서 명시한 대로 기본적으로 클라에서 응답값을 받아오고 디비에 인서트하는 코드를 작성하였지만 디비 지식이 백지에 가까웠던 탓에 디비를 잘 다루시는 팀원분이 디비 설계를 거의 도맡아주셨던 부분이라 부담이 많으셨을 것 같습니다. 코드 제출 페이지나 코드 확인 페이지는 제 담당이었는데 해당 부분 프론트에 비해서 백엔드 쪽에서 역량 발휘를 많이 하지 못하여 아쉬움이 많이 남습니다. 백엔드 개발 직군을 희망하고 있기 때문에 제가 어떤 지식이 부족한지를 명확하게 알 수 있었던 계기가 된 정글의 첫 프로젝트였기에 많은 의미와 다짐을 하게 되었습니다.

 

 

 

7. 마무리

 

지금까지 제가 0주차에서 만난 3박 4일 미니 프로젝트 미션을 어떻게 무사히 완수 했는지 작성해보았습니다. 처음이지만 괜찮게 한 부분도 있었고 처음이라서 많이 부족한 부분도 스스로 많이 느꼈습니다. 이제 어떤 부분들을 채워나가야 할 지 감을 잡아서 다행이기도 하였습니다. 다음 정글 프로젝트에서는 더 성장해서 보다 많은 부분을 커버할 수 있는 팀원, 개발자가 되길 바라는 마음으로 정진하고자 합니다.

 

 

지금까지 저의 정글 첫 미니프로젝트 후기를 읽어주셔서 감사합니다!