0주차 목요일, OT주간 이야기 - 토이프로젝트

Edwin·2023년 2월 9일
post-thumbnail

0주차 목요일, OT주간 이야기 - 토이프로젝트


오늘의 잔디를 심어보자.

01 오늘의 TIL 끄적임



오늘 4일에 걸쳐서 토이프로젝트를 완성했다. 우리가 만든 fourteen 페이지를 소개하겠다. 우리 페이지는 4명의 강사를 소개하는 페이지와 (수정 삭제가 가능한) 리뷰 페이지, 로그인 페이지와 회원가입를 구현했다. 그래도 많은 기술들이 응집되었고, 짜임새 있게 제작되었다. 간략하게 복기를 하자면 다음과 같다.

강사페이지를 하나의 HTML로 만들기

<article id="arti1">
    <div class="arti1">
        <div id="arti1_lecturer_img" style="background-color:#FFF; height: 300px; padding: 10px; background-size: cover; background-position: center;
                    box-shadow: 5px 5px;">
        </div>
        <div id="grade">
        </div>
        <div id="box-line">
        </div>
        <div id="arti1_lecturer_intro">
        </div>
    </div>
    <div class="arti2" id="arti1_lecture">
    </div>
</article>

텅텅 비어있다. 이는 2번째 사진에서 볼 때, 강사소개를 클릭하면 dropdown으로 버튼이 내려오는데, 해당 버튼은 클릭하면 그에 해당하는 JS가 구현되도록 설정해 두었기 때문이다. 이때! 처음 해당 페이지에 진입하면 초기값으로 가나다순의 맨위에 있는 강사의 이름이 보여지도록 설정해두었다. 이제 버튼은 클릭했을 때 실행되는 함수를 살펴보자.

function parkyo() {
    lecture_Evaluation('박OO 프로');
    document.querySelector("#arti1_lecturer_img").style.backgroundImage = "url(/static/parkyo.jpeg)"
    document.querySelector("#arti1_lecturer_intro").innerHTML = `
            <li >박영찬 프로</li>
            강의 : <span style="font-weight: 700">WEB과 HTML</span> <br>
            수강생 : 5년누적 <span style="font-weight: 700">1000만 명</span>
`
     document.querySelector("#arti1_lecture").innerHTML = `
        <p><h2>WEB과 HTML</h2></p>
        <p>1960년....</p>
        <p>HyperText Markup Language의 ...</p>
        <br><br>강의내용
        <ol>
            <li>인터넷과 WEB</li>
            <li>WEB과 HTML</li>
            <li>HTML의 역사와 발전</li>
        </ol>
     `
}

위와 같은 코드를 강사의 수만큼 제작하였다. 함수가 실행되면, querySelector를 통해서 각각의 장소에 해당 내용들이 innerHTML에 따라서 기록되게 많들었다.

리뷰페이지 만들기

function write_review() {
    let nickname = document.querySelector('.navi2').textContent.trim().split(" ")[0].split("님")[0]
    let lecturer = $('#lecturerchoice').val();
    let star = $('#starchoice').val();
    let comment = $('#lecturereview').val();
    let today = new Date();
    let year = String(today.getFullYear());
    let mon = (('0' + (today.getMonth() + 1)).slice(-2));
    let date = ('0' + (today.getDate())).slice(-2);
    let fullDate = `${year}-${mon}-${date}`
    if (lecturer == "강사선택") {
        alert("강사가 선택되지 않았습니다.")
    } else if (star == "별점선택") {
        alert("별점이 선택되지 않았습니다.")
    } else if (comment == "") {
        alert("강의에 대한 소감을 입력하세요")
    } else {
        $.ajax({
            type: "POST",
            url: "/review/written",
            data: {
                lecturer_give: lecturer,
                star_give: star,
                comment_give: comment,
                date_give: fullDate,
                nickname_give: nickname,
            },
            success: function (response) {
                alert(response['msg'])
                document.location.reload()
            }
        })

    }
}

여기서 document.querySelector('.navi2').textContent.trim().split(" ")[0].split("님")[0] 부분을 보자. 이는 사용자로부터 받은 것이 아니라, 로그인 시 생기는 토큰으로 기록된 값을 HTML로부터 불러왔다.

document.querySelector('.navi2')와 같은 호출이 Document Object를 사용하는 것이다. 어제 노마드코더에서 실습했는데 오늘 바로 실무에서 사용할 수 있어서 좋았다.

입력된 내용, 그러니까 DB에 저장된 내용을 수정하는 것은 어떻게 할까? 다양한 방법이 있겠지만, 나는 propmp를 사용했다.

function review_update(num) {
    let update = prompt("내용을 입력해주세요.")
    $.ajax({
        type: "POST",
        url: "/review/edited",
        data: {
            num_give: num,
            edited_give: update,
        },
        success: function (response) {
            alert(response['msg'])
            window.location.reload()
        }
    })
}

prompt로 받은 정보를 변수에 담고, 해당 내용을 서버로 보냈다. 이때! DB가 가진 고유의 번호를 함께 전송했다. 서버는 이를 가지고 DB에 접근해 해당 내용을 변경하는 것이다.

@app.route('/review/edited', methods=["POST"])
def post_review_update():
    num_receive = request.form['num_give']
    edited_receive = request.form['edited_give']
    review_db.update_one({'Num': int(num_receive)}, {'$set': {'comment': str(edited_receive)}})
    return jsonify({'msg': '리뷰 수정'})

또한 삭제도 가능하다. 삭제는 복수의 정보를 확인함으로 하나의 중복되지 않는 유일한 대상의 값이 지정되어 삭제되도록 아래와 같이 구현하였다.

@app.route('/review/deleted', methods=["POST"])
def post_review_removed():
    num_receive = request.form['num_give']
    id_receive = request.form['id_give']
    date_receive = request.form['date_give']
    find = review_db.find({'Num': int(num_receive), 'id': id_receive, 'date': date_receive})
    if find is not None :
        review_db.delete_one({'Num':int(num_receive)})
        return jsonify({'msg': '리뷰 삭제'})

02 오늘의 TIL 끄적임(당황과 불만의 사이 그 어딘가)

플라스크(Flask) Jinja2 기본문법

어제까지의 나는 서버와 클라이언트 사이의 정보통신방법에 있어서, method=post, get 두 가지 밖에 알지 못했다. 그런데 토이프로젝트를 하면서 jinja2에 대해서 접근하게 되었다. 물론 jinja2는 이전에 서버를 배포했을 때, html이 정상작동하지 않는 상황에서, 즉 에러의 상황에서 익숙하게 본 단어 가운데 하나였다. 그런데 이 단어에 문법이 있을 것이라고는 상상하지도 못했다.

jinja2sms 서버에서 웹페이지로 값을 보낼 때 사용하는 도구 가운데 하나이며, 파이썬에서 지원하는 동적 templating을 통해 간단하게 사용할 수 있는 기술이다. 우리의 토이프로젝트에서 작성한 코드 가운데 일부이다.

<div class="navi2">
    {% if result=="fail" %}
		<div><a href="{{url_for('render_login')}}" id="login">로그인</a></div>
    	<div><a href="{{url_for('render_join')}}" id="join">회원가입</a></div>
    {% else %}
    	{{user_info.nickname}}님
    	<div><a onclick="logout()" href="{{url_for('render_review')}}" id="info">로그아웃</a></div>
    {% endif %}
</div>

{% if result=="fail" %}와 같이 기록된 구분이 jinja2 문법이다. 위와 같은 식으로 조건문도 이런 식으로 기록하는 것이 가능하다는 말이다. 기록된 result는 서버에서 어떤 정보에 대해서 그 값이 fail이면 즉 이행되지 않았으면, if구문(jinja2)에 있는 내용이 기록될 것이고, 그렇지 않으면 else구문(jinja2)에 있는 {% endif %} 는 jinja2 문법이 끝난다는 의미이다. 또한 {{user_info.nickname}}으로 서버에서 만든 변수나 객체를 가져오는 것도 가능하다.

JWT-Json Web Token이란?

각주, VELOPERT
쉽게 설명해서 Json객체를 이용해서 자가수용적인(self-contained) 방식으로 정보를 안정성 있게 전달하는 방식이다. Json객체 자체는 C, Java, Python, C++, R, C#, PHP, JavaScript, Ruby, Go, Swift 등 대부분의 언어에서 사용이 가능하기에 JWT는 사용성에 있어서 그 확장성이 매우 뛰어난 방식이 되었다. 또한 JWT으로 발급받은 토큰은 그 안에 signature(검증이 증명됨)을 포함하고 있다는 것도 특징이다. 웹서버에서는 HTTP의 헤더를 통해서 전달할 수도 있고, URL의 파라미터를 통해서 전달할 수도 있다.

JWT의 대표적인 사용사례

회원인증이 가장 대표적인 사례이다. 서버는 유저의 정보를 기반으로 토큰을 발급하여 유저에게 전달한다. 그 후, 유저가 서버에 요청할 때마다 JWT를 전달한다. 즉 서버와 클라이언트의 통신의 상황에서 해당 토큰이 유효하고 인증됐는지 검정하고, 유저가 요청한 작업에 권한이 있는지 확인하여 작업을 처리하는 환경을 만들어준다.

JWT의 생김새

aaaaaa.bbbbbb.cccccc
a부분은 헤더, b부분은 내용, c부분은 서명

먼저 헤더는 1) typ 2) alg 으로 나눠진다. typ 토큰의 타입은 JWT이다. alg 토큰의 해싱 알고리즘은 HS(HMAC SHA256) 혹은 RSA가 사용된다.

Node.js 환경

const header = {
  "typ": "JWT",
  "alg": "HS256"};

이를 인코딩함으로 정보를 사용할 수 있는데, .toString('base64')을 통해서 변환해주면 된다.

다음으로 정보(내용)는 클레임(claim, 조각)이 담겨지며 1) name 2) value 의 한 쌍으로 구성되어 있다. 클레임은 종류에 따라서 등록된 (registered) 클레임, 공개 (public) 클레임, 비공개 (private, 클라이언트와 서버 협의하에 사용되는 클레임) 클레임으로 나눠볼 수 있다.

마지막은 서명이다. JWT에서 가장 중요한 부분이다. 서명은 헤더의 인코딩값과 정보의 인코딩값을 합친후 주어진 비밀키로 해쉬를 통해서 base64 형태로 생성된다.

Python Flask에서 JWT를 통해 로그인 페이지 구현하기

코딩하는 어린콩
나중에 참고해보자..

03 정리하며

오늘도 참 많은 것을 느낀다. 이렇게 열심히 한 적이 있을까? 항해99 0주차 4일째인 오늘 16시간 컴퓨터 앞에 앉아 있는다. 이렇게 열심히 한 적이 없을 것 같은데, 99일의 마지막날 정말 기쁠 것 같다. 부족함이 많기에 더 많은 정리와 공부가 필요한 것 같다. 정답은 구글링에 있다지만, 그것이 내것이 되어야 비로서 의미가 있을 것이다. 그러기에 오늘도 잔디를 심고, 글을 기록한다. 다 기억할 수 없기에 나만의 체계와 언어로 이를 정리해 놓는 것이다.

협업이 힘든 것은 내가 부족하기 때문이다. 성격의 문제는 따로두고 말이다. 협업에서 더 좋은 사람이 되기 위해서는 더 좋은 이해력을 가지는 것 뿐일 것이다. 이를 기억하며 내일도 달려보자.

profile
신학전공자의 개발자 도전기!!

0개의 댓글