
오늘은 리뷰페이지를 제작하는 것에서 시작했다.

먼저, 상단에 있는 리뷰 작성하기를 클릭하면, 하단과 같이 리뷰를 작성할 수 있는 공간이 나오도록 설정했다. 그리고 파란 공간에 있는 리뷰 작성하기를 클릭하면, 새로고침되면서 작성한 리뷰는 저장되고, 파란 박스는 사라지도록 javascript를 구현해서 동작하게 하였다.

Javscript
$(document).ready(function () {
$('#review_write').hide();
show_reviews();});
function review_write() {
$('#review_write').show();};
- 코드로 이야기하면, review_write() 함수가 onclick으로 실행되었을 때, HTML의 $('#review_write')해당 부분이 보여지도록 설정한 것이고,
- $(document).ready 홈페이지가 리로드되면, HTML의 $('#review_write')이 숨겨겨지도록 설정한 것이다.



작성자 닉네임 등이 입력되지 않았을 때, 정보를 서버로 보내기 전에 경고창을 띄움으로 누락된 정보가 없도록 처리하였다. 해당 구분은 조건문을 통해서 공백이면 경고창을 띄우라는 각각의 설정을 해주었다.
function show_reviews() {
$.ajax({
type: "GET",
url: "/review/get",
data: {},
success: function (response) {
let rows = response['reviews']
for(let i =0;i<rows.length;i++) {
let lecturer = rows[i]['lecturer']
let star = rows[i]['star']
let staricon = '★'.repeat(star)
let comment = rows[i]['comment']
let nickname = rows[i]['nickname']
let date = rows[i]['date']
let html = `
<div class="reviewtxt" style="width: 100%; height: 150px; background-color: black; color:white; padding: 10px 10px 10px 10px; margin-bottom: 15px;">
<a>강사명 : ${lecturer} </a><br><a> 작성자 : ${nickname}</a><br><a>별점 : ${staricon} (${star})</a>
<p>${date}</p>
<p>${comment}</p>
</div>`
$('#reviewtxt').append(html)
}
}
})
}
위의 코드에 따라서 웹페이지가 리로드되면, 서버를 통해 DB에서 가져온 정보가 $('#reviewtxt').append() 괄호에 입력된 내용에 따라 기록되도록 코드를 작성하였다.

위의 이미지에서 볼 수 있듯이, 별점 좋은 순/별점 낮은 순으로 정렬을 하고 싶었다. 그래서 위의 함수코드에서 불러온 자료를 반복문으로 설정하기 전에 sort를 하고자 하였다. 그런데 객체 안에 담겨 있는 객체에 접근하지 못해서, 정렬을 시키지 못했다. 배열의 정렬은 쉽게 했는데, {{객체정보:num, 이름:name}, {객체정보:num, 이름:name}} 과 같이 되어 있는 부분의 자료를 가공하지 못한 것이다. 한참을 고민한 끝에 방법을 찾았고 적용했다.

위의 사진은 별점이 낮은 순으로 정렬한 부분이다. 해당 코드의 설정은 DB에서 정보를 가져오는 단계에서 정렬을 실시함으로 해결하였다.
@app.route('/review/star_ascending', methods=["GET"])
def review_star_ascending():
reviews = list(db.reviews.find({},{'_id':False}).sort('star'))
return jsonify({'reviews':reviews})
db.reviews.find({},{'_id':False}).sort('star')부분에서 볼 수 있듯이, DB에 담겨있는 자료를 불러올 때, 'star'에 해당되는 값을 기준으로 오림차순 정렬이 실행되도록 하였다. 그 결과 1~5순서로 해당 값이 정렬된 상태에서 클라이언트로 전달되어 가공할 수 있었다.
반대로, 내림차순 정렬을 어떻게 할까? -1을 추가해 주면 되는데, 아래와 같다.
@app.route('/review/star_desending', methods=["GET"])
def review_star_desending():
reviews = list(db.reviews.find({},{'_id':False}).sort('star',-1))
return jsonify({'reviews':reviews})
sort('star',-1)) 부분에 -1을 설정해 줌으로 내림차순으로 정렬된 자료가 클라이언트로 전달되게 하였다.

DB에 사전에 입력된 ID에 대한 정보가 있으면 가입이 되지 않도록 설정하는 것이다. 해당 기능은 서버에서 DB에 접속하여, 사용자가 입력한 ID의 값이 존재하는지 확인하여 존재하면 위와 같은 경고창을, 그렇지 않으면 가입을 승인해주면 될 것이다.
@app.route('/login', methods=["POST"])
def login():
id = request.form['id']
pw = request.form['pw']
nick = request.form['nick']
checkId = db.test.find_one({'id':id})
if checkId is not None:
return jsonify(({'msg':'이미 존재하는 id입니다.'}))
else :
doc = {
'id' : id,
'pw' : pw,
'nick' : nick,
}
db.test.insert_one(doc)
return jsonify(({'msg':'가입완료'}))
동일한 ID를 확인하는 방법의 핵심은 조건문에 있다. 위의 코드는 Python-Flask패키지를 통해서 서버를 실행한 코드이다. 해당 부분을 보면 checkId변수를 통해서 클라이언트가 입력한 id값을 DB에서 먼저 찾고,만약 DB에 해당 ID가 존재하면(checkId is not None), 경고창이 실행되도록 한 것이다. 이를 통해서 동일한 ID의 가입을 제한할 수 있게 된다.
이제 로그인을 구현해보자. 로그인을 한다고 하면, 사용자로부터 전달받은 복수의 자료가 하나의 DB정보 안에서 일치하는지 알아봐야 할 것이다. 하단의 두개의 코드를 비교해보자.
user = db.test.find_one({'id':id}, {'pw':pw})
먼저 찾을 자료를 {중괄호} 각각에 입력한 경우이다. 이 경우에는 id값만 맞으면 로그인정보가 이뤄지기 때문에 잘못된 코드이다. 필자 역시 잘못된 코드를 인터넷에서 검색하고 실행하는 가운데 발생되어 다시 찾게 되었다. 만약 복수의 정보를 확인하고 싶을 시에는 각각의 {중괄호}가 아니라 하나의 {중괄호}에서 해당 내용을 기록해야 한다.
user = db.test.find_one({'id':id, 'pw':pw})
이렇게 해야 사용자가 입력한 정보를 서버에서 DB에서 찾을 때, 복수의 조건에 해당되는 자료를 찾게 된다. 이때 만약 존재하는 값을 찾으면 "로그인 완료"를, 그렇지 않으면 "아이디 또는 비밀번호가 일치하지 않습니다."를 경고창으로 띄어주면 된다.
@app.route('/login2', methods=["POST"])
def login2():
id = request.form['id']
pw = request.form['pw']
user = db.test.find_one({'id':id, 'pw':pw})
if user == None :
return jsonify(({'msg':'아이디 또는 비밀번호가 일치하지 않습니다.'}))
else :
return jsonify(({'msg':'로그인완료'}))


보안이 강조되는 시대인 만큼, 비밀번호를 안정하게 저장하고 관리하는 것만큼 중요한 것은 없을 것이다. python은 이 부분에 대해서 flask_bcrypt 패키지를 제공하며, 이 문제를 취급하고 있다.
pip install flask_bcrypt
설치했다면, app.py에 import 해주면 된다.
from flask_bcrypt import Bcrypt, generate_password_hash, check_password_hash
bcrypt = Bcrypt(app)
@app.route('/login', methods=["POST"])
def login():
id = request.form['id']
pw = request.form['pw']
nick = request.form['nick']
pw_hash = bcrypt.generate_password_hash(pw, 10)
checkId = db.test.find_one({'id':id})
if checkId != None:
return jsonify(({'msg':'이미 존재하는 id입니다.'}))
else :
doc = {
'id' : id,
'pw' : pw,
'pw2' : pw_hash,
'nick' : nick,
}
db.test.insert_one(doc)
return jsonify(({'msg':'가입완료'}))
이제 사용자로부터 입력받은 정보를 DB에 저장해야 하는 코드를 작성하는 곳에 pw_hash = bcrypt.generate_password_hash(pw) 와 같이 입력하고, DB에 insert_one 하면 된다. 결과를 살펴보자.

아이디와 비밀번호와 닉네임 모두 Edwin119라고 기록했지만, pw의 경우에는 해당 내용이 변경되어 저장된 것을 볼 수 있다. 이제 저장을 암호화하여 저장했으니, 로그인을 시도해 보자.

결과는 실패이다. 이는 pw(Edwin119)로 입력받은 정보 그대로, DB에서 찾게 되는데, 해당 내용의 값은 암호와 되어 일치하지 않기에 찾을 수가 없는 것이다. 그렇다면 어떻게 이를 확인해줄 수 있을까?
@app.route('/login2', methods=["POST"])
def login2():
id = request.form['id']
pw = request.form['pw']
if db.test.count_documents({'id': id}) == 0:
return jsonify(({'msg':'id를 찾을 수 없습니다.'}))
else :
check_pwd = db.test.find_one({"id":id})
if check_password_hash(check_pwd.get('pw'), pw):
return jsonify(({'msg':'로그인 성공'}))
우선 조건문으로 id를 찾아주었다. 이때 DB의 명령어 가운데 새로운 명령어를 실행했는데, count_documents이다. 이는 db에서 해당 값으로 된 정보를 전부 찾아준다. 만약 존재하지 않다면 0이 될 것이고, 아니면, else 구문으로 넘어갈 것이다. 그리고 비밀번호를 비교해주는데, 오류가 발생되었다.

ValueError: Invalid salt
일단 오늘은 여기까지 인 듯 하다.
사실 협업은 뭘 하는지 모르겠다. 각자 하고 싶은 것을 하다가 마무리 되는 셈인 것 같다. 결과물을 도출해야 하는데 할 생각이 없는 사람들 같다. 어제의 빌런은 오늘 CSS 디자인을 회의와 상관없이 본인 하고 싶은데로 만들어왔다. 잘 만들었기에 문제는 없어서 그렇게 하시라고 했다. 그러나 중요한 것은 진행이 되지 않는다는 점이다. 원래 이런 세계인 것인지 모르겠다. 해야 되는 이슈할당을 받고 끝내고 각자 하고 싶은 것을 하던지, 아니면 본인이 하고 있는 작업에 대해서 설명을 해주던지 할 필요가 있어 보이는데, 욕망만 충분한 세계에서 살아남는 것은 정말 어려운 일인 것 같다. 위의 암호화 한 것을 복원화 하는 내용은 추후에 살펴보도록 하자.