[KAIST 정글] 0주차 프로젝트

2023. 3. 4. 21:38회고

728x90
반응형

Project : [ 오늘 뭐 먹지? ]

구분  
기획 의도 함께 식사할 친구를 모집하는 웹서비스
팀원 이주희, 김경민, 김한중
기간 3일
기술 스택 python, flask, jinja2, JWT, MongoDB, pymongo, AWS

 

사용 기술

1. jinja2 템플릿

1) 데이터 활용 / 서버사이드 렌더링

  • {{변수명}}을 적으면 html 파일에 서버의 데이터를 포함시킬 수 있고,
    서버에서 렌더링하여 완성된 화면이 로드된다.

app.py

@app.route('/', methods=['GET'])
def home():

        ...

        return render_template('list.html', room=newRoomList)

list.html

<span>{{ i.menu }}</span>
<span>
  <label>카테고리 </label>
  {{ i.category }}</span>
<span>
  <label> 약속 시간 </label>
  {{ i.date }} {{ i.meet_time }}</span>
<span>
  <label> 인원</label> 
  {{i.current_people}} / {{i.maximum_people}}</span>

2) 동적 화면

  • {% %} 안에서 for문과 if문을 활용해 화면을 그릴 수 있다.
{% for i in room %}
    {% if i.category == "배달" %}
        <span>배달일 때 보여줄 내용</span>
    {% elif i.category == "학식" %}
        <span>학식일 때 보여줄 내용</span>
    {% else %}
        <span>외식일 때 보여줄 내용</span>
    {% endif %}
{% endfor %}

3) 레이아웃

  • {% extends %} 블록을 활용해 코드의 중복을 제거할 수 있다.
{% extends "layout.html" %} {% block content %}
    <span>레이아웃 안에 보여줄 내용</span>
{% endblock %}

2. JWT 토큰 인증 로그인

1) 회원가입

  • hashlib으로 비밀번호 암호화 후 DB에 저장
import hashlib

...

pw\_hash = hashlib.sha256(password.encode('utf-8')).hexdigest()  
db.users.insert\_one(  
{'id': id, 'password': pw\_hash, 'name': name, 'phone': phone})

2) 로그인

  • 회원가입 시 사용한 비밀번호 암호화와 동일하게 hashlib으로 입력받은 비밀번호 암호화
  • DB에서 해당 유저가 있는지 조회 (없으면 잘못된 아이디/비밀번호 예외 처리)
  • 해당 유저가 존재할 경우, JWT 토큰 발급하여 cookie에 저장
id = request.form['id']
password = request.form['password']
pw_hash = hashlib.sha256(password.encode('utf-8')).hexdigest()
findUser = db.users.find_one({'id': id, 'password': pw_hash})
if findUser is not None:
    payload = {
        'id': id,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=토큰유효시간)
    }

    token = jwt.encode(payload, SECRET_CODE,
                        algorithm='HS256')
    resp = make_response(jsonify({'status number': 200}))
    resp.set_cookie('token', token)
    return resp
else:
    return jsonify({'status number': 400, 'msg': '아이디/비밀번호가 일치하지 않습니다.'})

3) 로그인 검증

  • cookie에 저장된 토큰을 가져와서 유효한 토큰인지, 만료되지 않았는지 확인
token_receive = request.cookies.get('token')
try:
    payload = jwt.decode(token_receive, SECRET_CODE,
                        algorithms=['HS256'])
    user_info = db.users.find_one({"id": payload['id']})
    return render_template('list.html', room=newRoomList, loginUser=user_info)
except jwt.ExpiredSignatureError:
    return render_template('list.html', room=newRoomList, loginUser={})
except jwt.exceptions.DecodeError:
    return render_template('list.html', room=newRoomList, loginUser={})

4) 로그아웃

  • cookie의 토큰 제거
    @app.route('/logout', methods=['GET'])
    def logout():
      resp = make_response(jsonify({'status number': 200}))
      resp.set_cookie('token', "")
      resp.set_cookie('user_id', "")
      return resp

3. 메일 전송

  • smtplib 활용하여 메일 전송
  • 인코딩 오류 발생하여 message에 .encode('utf8')로 인코딩

import smtplib

...

def send_email(email, name, menu, category, place,
               date, meet_time, host_email, link):
    # 이메일 설정
    subject = "Junglefood: 모집이 마감되었습니다."
    body = f"{name}님, \n [[{menu}]] 모임의 참가 신청이 마감되었습니다. \n\n\n [모임 정보] \n 🔗 모임 자세히 보기 👉🏻 {link} \n 🍚 메뉴명: {menu} \n 🍽 카테고리: {category} \n 👯 모임 장소: {place} \n ⏰ 모임 날짜 및 시간: {date} / {meet_time} \n\n\n📨 방장에게 연락하기 {host_email}"
    message = f"Subject: {subject}\n\n{body}".encode('utf8')

    # 이메일 전송
    try:
        with smtplib.SMTP("smtp.gmail.com", 587) as connection:
            connection.starttls()
            connection.login(user=메일주소,
                             password=비밀번호)
            connection.sendmail(
                from_addr=메일주소, to_addrs=email, msg=message)
    except Exception as e:
        print("Error sending email: ", e)

4. 날짜 계산

  • 배포 후 로컬 시간과 달라져 마감 시간 여부 계산에 오류 발생
    + timedelta(hours=9)를 더해줘서 로컬 시간과 차이 제거
from datetime import timedelta
import datetime

...

current = datetime.datetime.now() + timedelta(hours=9)
closeYear = closeDate[0:4]
closeMonth = closeDate[5:7]
closeDay = closeDate[8:10]
closeHour = int(closeTime[0:2])
closeMinute = closeTime[3:5]
close = datetime.datetime(int(closeYear), int(closeMonth),
                            int(closeDay), int(closeHour), int(closeMinute))
isClosed = current > close

 

기능 & 화면

1. 목록

  • 개설된 방의 모집 여부, 메뉴, 카테고리 등을 조회한다.
  • 카테고리 별, 모집 여부 별 필터를 적용한 목록을 조회한다.

2. 방 생성

  • 메뉴, 카테고리, 약속 시간, 마감 시간, 장소, 최대 인원을 입력해 방을 개설한다.
  • 개설한 방의 방장 권한이 주어지고, 방장은 방 삭제가 가능하다.

3. 방 참여

  • 방에 참여하거나 참여하고 있는 방에서 참여 의사를 번복한다.
  • 인원이 마감되면 참여할 수 없다.

4. 마감

  • 마감 시간이 지난 방은 참여 멤버의 명단을 노출한다.
  • 참여한 방의 모집이 마감되면 참여 멤버 전원에게 메일을 전송한다.
728x90
반응형