이번 주에 한 것
- 풀스택 미니 프로젝트 (1/10 ~ 1/13)
참조 : https://velog.io/@genetle/%ED%95%AD%ED%95%B4-99-A%EB%B0%98-2%EC%A1%B0-%EC%B0%A8%EB%B0%95GO
항해 99의 시작과 함께 저 자신을 포함해 4명이 한 팀이 되어 풀스택 미니 프로젝트를 진행했습니다. 테마는 자유선택이었기 때문에 팀원들과 여러가지를 이야기해보던 중 "차박"으로 정해졌고, 따라서 전국의 차박 명소들을 소개하는 사이트를 만들기로 최종결정이 내려졌습니다.
와이어프레임
본격적인 작업에 착수하기 전, 먼저 팀원들과 와이어프레임을 작성했습니다. 와이어프레임에는 만들고자 하는 대상이 어떤 기능과 요소를 가지고 있으며 어떻게 배치되는지에 대한 개요가 담겨져 있습니다. 이를 통해 이후 작업에 대한 가이드라인을 제공함과 동시에 말로는 수백번을 이야기했어야 할 상황들을 그림 한 장으로 단축시키고 시각적으로 명확히 할 수 있었습니다.
API(Application Programming Interface)
출처 : https://www.globaldots.com/resources/blog/api-protection-best-practices/
API는 어플리케이션(프로그램)/ 클라이언트와 서버/데이터 소스 사이에서 상호작용하는 것을 도와주는 역할을 수행합니다.
처음 와이어프레임을 작성할 때 팀원들과 함께 이런 식으로 api를 설계했습니다.
2-1. 회원가입
회원가입은 총 4개의 API로 구성했습니다.
@app.route('/sign_up/check_dup', methods=['POST'])
def check_dup():
username_receive = request.form['username_give']
exists = bool(db.signup.find_one({"id": username_receive}))
return jsonify({'result': 'success', 'exists': exists})
function idcheck() {
let username = $("#id").val()
if (username == "") {
alert('아이디를 입력해주세요.')
return;
}
$.ajax({
type: "POST",
url: "/sign_up/check_dup",
data: {username_give: username},
success: function (response) {
if (response["exists"]) {
alert('사용할 수 없는 아이디입니다.')
return;
} else {
alert('사용할 수 있는 아이디입니다.')
}
}
});
}
회원가입 시 입력한 비밀번호를 한번 더 확인하는 장치를 설정했습니다.
두 값이 다르면 에러 메세지가 출력됩니다.
function createid() {
let id = document.querySelector('#id');
let pw = document.querySelector('#pw');
let rpw = document.querySelector('#rpw');
let name = document.querySelector('#name');
if (id.value == "") {
alert('아이디를 입력해주세요.')
return;
}
if (pw.value == "") {
alert('비밀번호를 입력해주세요.')
return;
}
if (name.value == "") {
alert('이름을 입력해주세요.')
return;
}
if (pw.value !== rpw.value) {
alert('비밀번호가 맞지 않습니다.')
return;
}
비밀번호를 암호화 하지 않고 데이터베이스에 저장된다면 관리자가 비밀번호를 볼 수 있기 때문에 hashlib을 사용해 비밀번호를 암호화 시킨 후에 서버에 저장되게 만들었습니다.
pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()
2-2. 로그인
로그인 시스템의 인증은 JWT(JSON Web Token)를 사용했습니다. JWT는 토큰 기반의 인증 시스템입니다. 사용자가 로그인 요청을 하면 브라우저는 쿠키 안에 저장되어 있는 암호화 된 토큰을 서버로 전송합니다. 서버는 서버 내에 저장된 고유의 값을 가진 암호화 키를 사용해 토큰을 디코딩해 값을 비교하고 해당하는 사용자 데이터를 전송해 로그인이 이루어지게 됩니다.
장점
JWT는 서버가 아닌 브라우저의 쿠키에 사용자 관련 정보가 담긴 토큰이 저장되기 때문에 서버의 부담이 적으며, 이에 따라 서버의 확장성 및 유연성이 좋습니다.
단점
토큰의 별도 제어가 불가능하기 때문에 만료시간 설정이 필요합니다. 만료시간이 설정되지 않으면 브라우저에서 쿠키를 삭제하지 않는 한 토큰이 계속 남아 있게 됩니다. 또한, 토큰이 탈취되었을 경우에 그 토큰을 사용해 접속도 가능합니다.
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
따라서 프로젝트에서는 이런 식으로 1시간의 만료시간을 설정함으로써 토큰이 계속 남아 있게 되는 것을 방지하고, 탈취 후 위험도 완벽히는 아니지만 대비할 수 있게 설정했습니다.
function logout() {
$.removeCookie('mytoken', {path: '/'});
alert('로그아웃!')
window.location.href = '/'
}
로그아웃은 브라우저에 저장되어 있는 쿠키를 삭제함으로써 그 안에 저장되어 있는 토큰 또한 삭제되고, 추후 로그인 시 재발급 받아 사용하는 방식으로 작동합니다.
2-3. 차박 장소 데이터
차박 장소 데이터의 크롤링은 기존에 배웠던 Beautifulsoup의 사용만으로는 동적인 웹사이트의 elements에 접근이 불가능하다는 걸 깨닫고, Selenium을 사용했습니다. 이후 성공적으로 불러와진 데이터들은 pymongo를 통해 데이터베이스에 저장되어 Ajax를 통해 구현되었습니다.
@app.route('/main_page', methods=['GET'])
def show_card():
category_card = list(db.prac.find({}, {'_id': False}))
return jsonify({'all_card': category_card})
function keyword() {
$.ajax({
type: "GET",
url: "/main_page",
data: {},
success: function (response) {
let cards = response['all_card']
for (let i = 0; i < cards.length; i++) {
let title = cards[i]['title']
let address = cards[i]['address']
let description = cards[i]['description']
let image = cards[i]['image']
let temp_html = `<div class="card-box">
<div class="card-image">
<a href="/detail">
<img src="${image}" alt="card-image"> </a>
<div class="container">
</div>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">차박지: ${title}</li>
<li class="list-group-item">코멘트: ${description}</li>
<li class="list-group-item">${address}</li>
</ul>
</div>`
$('#cards').append(temp_html)
}
}
});
}
정말 정신없는 일주일이었다. 항해99 시작 전 사전 강의와 사전 시험을 치르며 '이정도면 어느정도 익숙해졌겠지' 라고 생각했었던 때가 있었는데, 그건 정말 나 혼자만의 생각에 불과했다. 막상 프로젝트가 시작되자 뭘 해야 할지 몰라 머리속이 하얗게 되는 경험을 겪게 되었다. 확실히 아무리 강의를 따라하는 것만으로는 실전에서 적재적소에 필요한 것을 적용하는 능력을 키울 수 없다는 걸 깨닫게 되었고, 그런 점에서 무작정 부딪혀 볼 수 있는 이 미니 프로젝트가 가져다 주는 의미가 정말 컸다. 또한 프론트와 백엔드를 아우르는 전체적인 구조에 대해 체험해 볼 수도 있었고, 평소 단순하게 생각했던 하나하나의 기능들 뒤에 얼마나 많은 것들이 복잡하게 작동하고 있는지도 다시한번 깨닫게 되었다.
아마 혼자였더라면 절대 하루에 12시간 이상을 몰입해가며 프로젝트에 매달리지도, (엉망이긴 하지만) 끝마치지도 못했을 것이다. 정말 힘들었지만 그 이상으로 정말 정말 좋은 팀원들을 만났고, 덕분에 나름의 결과물을 낼 수 있었다. 팀원들에게 정말 감사하고, 또 감사하다.
항해는 이제 시작이다. 갈 길이 멀고 모르는 것 투성이지만 팀 프로젝트 때처럼 부딪히다보면 해낼 수 있을 거라 생각한다. 화이팅!