pymongo로 log를 기록해보자!

옵주비·2022년 4월 4일
0
post-thumbnail

앞서 에세이에서 언급했지만, 정글에 들어오자마자 2박 3일간 미니 프로젝트를 진행했다. 우여곡절 끝에 나름 성공적으로 끝냈던 프로젝트 중 일부분에 대해 기록해보고자 한다.

우리는 프론트엔드 1명, 백엔드 2명으로 나누어 시작했다. (물론 중간중간 백엔드도 html 건드리고, 프론트엔드도 app.py 건드리고 하게 되었지만, 우린 분업이 잘 된 편이었다) 나는 백엔드였는데, 로그인 기능을 제외한 나머지 부분을 주로 담당했다.

이번 개발일지에서 소개할 부분은, 프로젝트 발표를 마친 후 많은 분들이 질문주셨던 log를 위한 pymongo db 설계에 관한 부분이다.

참고로, 우리는 정글 A반끼리 같이 운동할 수 있도록 해주는 매칭 서비스를 준비했다. 기획 의도는 아래와 같으며, 내가 만든 발표자료에서 따왔다.

템플릿은 https://minheeblog.tistory.com/ 에서 받았던 것을 좀 더 깔끔히 만들어 써봤다. 무수한 팀플에서 조금이나마 얻게된 야매 PPT 실력..

우리 조는,시작할 때 프로젝트 범위를 최대한 좁게 해서 최소한의 서비스를 일단 만들고 나서 추후에 발전시키자는 쪽으로 의견을 모았다. 따라서, 처음에는 정말 main / login / signup 이 서버사이드 렌더링으로 연동 되게끔만 짰고, 매칭 결과도 그냥 main에서 보여주고 끝내는 식으로 구성했다.

참고로 이번 프로젝트에서 강조된 점은 2가지였다.

  • 로그인 기능
  • Jinja2 활용 서버사이드 렌더링

그래서, 우리가 프로토타입에서 최소한으로 잡은 기능은 다음과 같다.

1) 로그인, 로그아웃 기능 구현 - JWT 활용
2) 회원가입 구현 [정글A반 소속, 아이디 중복, 이름 중복 검증]
3) 9시부터 24시까지 운동을 신청할 수 있음. 이 때, 프로젝트 당시의 거리두기 정책을 기준으로 8명 정원이 차면 신청 불가 (물론 운동은 8명이 넘어도 상관 없지만, 끝나고 커피 한 잔 하거나 아침 같이 먹을 수도 있으니까 그냥 일반적인 경우의 거리두기 정원으로 설정했습니다)

하지만, 둘째 날인 화요일 오전에 아이디어 발표를 하고 나서, 코치님께 로그인을 하게 만드는 동기가 적은 것 같다는 피드백을 받았다. 곰곰이 생각해보니 로그인이 필요한 당위성이 부족하단 생각이 들었고, 그럼 어떤 것을 추가로 붙일 수 있을까 고민해보았다.

'어느 날, 어떤 운동' 했는지를 기록할 수만 있다면 ?

그래서 덧붙이게 된 아래의 기능 중, 5번과 6번가 log를 활용한 기능이다.

4) 운동 참가 시, 코멘트(운동 다짐) 달기 기능 - 50자 이내로
5) 24시가 넘으면 이름 왼쪽에 '마이페이지' 버튼이 나타남. 마이페이지에서는 매칭 결과(2명 이상 신청하여 운동 성사된 경우엔 코멘트도 서로 공유), 현재까지의 본인 운동 기록, 전체 랭킹이 나타남
6) 모든 운동이 끝난 매일 오전 9시 직전, 자동으로 log를 기록하고 DB의 register 관련 필드를 날려 운동 신청 현황을 리셋해줌.

log가 기록되기까지의 과정

우선, 내가 설계한 DB는 다음과 같다.

우리가 가입 조건에 정글 A반만 가입할 수 있도록 했고 (이름 확인을 통해), 마침 반 내에 동명이인이 없어서 primary_key 로는 주로 username을 활용했다. PK가 무엇인지는, 다음의 링크 글에 잘 설명되어 있다.

Table 작성 시 PK를 무조건 사용해야 하는 이유

한 줄 요약하자면, 각 데이터를 unique 하게 판별해낼 수 있는 필드를 PK로 설정해야 한다. 만약 동명이인이 있으면, userid 를 PK로 설정하고 username은 그때그때 조회해서 불러오게 됐을 것이다.

log 가 어떻게 처리되었는지 설명하기 위해선, 우선 맨 처음에 base로 잡고 시작한 3번 기능인 '운동 신청', 즉 등록에 대한 이해가 필요하다.

#운동등록기능
@app.route('/register', methods=['POST'])
def register() :
    inputData = request.form
    return doRegister(inputData)

@jwt_required(optional=True)
def doRegister(inputData) :
    user = get_jwt_identity()
    
    if user is None :
        return jsonify({"result": "forbidden", "msg": "로그인 해주세요!"})
    else :
        time_receive = inputData['time_give'] # 1. 클라이언트가 전달한 time_give 변수를 time_receive 변수에 넣음
        type_receive = inputData['type_give'] # 2. 클라이언트가 전달한 type_give 변수를 type_receive 변수에 넣음
        db.user.update_one({'userid':user},{'$set':{"time":time_receive,"type":type_receive}})
        # 2. 성공하면 success 메시지와 함께 counts 라는 운동 인원 수를 클라이언트에 전달합니다.
        print(user)
        tempUser.id = user
        print(tempUser.id)
        return jsonify({'result': 'success', 'msg':'참가 완료!'})

위 코드의 내용은 간단하다. 신청하고자 하는 시간x운동 세션의 '참가' 버튼을 누르면, 그 회원의 데이터에 time과 type 필드를 생성해 값을 넣어주는 역할을 한다. db에서 이 time과 type 필드가 있는지를 판별한 후, 세션마다 해당하는 인원 수를 가져오기에 실시간으로 신청 현황을 확인할 수 있었다.

운동이 모두 끝나고 나면, (우리 A반끼리 의리가 있는데 노쇼하지 않았다는 가정 하에) 운동 신청 현황을 리셋해주기 직전에 운동 log를 기록해준다.

DB 설계에서의 고려사항

사실 처음에는, value로 주어지는 dictionary의 key를 운동 타입으로 잡으려고 하였다. "log":{"헬스":[날짜,..,날짜], "산책":[날짜,..,날짜], "러닝":[날짜,..,날짜]} 이런 식으로. 하지만 그렇게 하면, 그 안에서 또 dictionary가 필요해 더 복잡한 db 설계가 될 것 같았다.

어차피 하루에 16시간 가량 강의동에서 보내게 되는 정글의 특성 상, 운동을 하루 2번하긴 힘드니까, 시간까지 필요하진 않다고 생각해 log에선 제꼈다. 만약 하루 운동 2회가 가능하도록 하려면, 원래 계획했던 log에다 HHMMSS를 붙이는게 나았을 듯

어쨌든, 이번에는 log라는 key 안에 value로 dictionary가 주어지도록 하였다. 이 dictionary의 key는 YYYYMMDD ,value는 운동 종류이다. 위의 발표자료에서 나타낸 함수를 통해, 기록되는 log의 모습은 다음과 같다.

사실 이 log를 활용하면 db 하나로도 랭킹까지 다 뽑아낼 수 있었겠지만, jinja2를 활용해 뽑아내려니까 코드가 복잡해져서 그냥 rank를 기록하는 db도 하나 만들었다.

좀 더 시간 여유가 있었으면, total_count 필드는 설정 안하거나 (나머지 3개로 만들 수 있어서 사실 필요없는 필드임) 아예 user_rank DB 자체를 만들지 않고 했을텐데, 서비스 완성까지 제한 시간이 있기에 좀 더 쉬운 방법을 선택했다.

우리가 가입 조건에 정글 A반만 가입할 수 있도록 했고 (이름 확인을 통해), 마침 반 내에 동명이인이 없어서 primary_key로 username을 잡을 수 있으니까 그냥 과감하게 db를 하나 더 만들어버릴 수 있었던 것 같다. 모두가 가입한다 해도 25명이니까, db가 두개가 된다고 해서 많이 복잡하지도 않고 말이다.

이렇게 기록해낸 log를 활용해 구현한 mypage 예시는 다음과 같다.

운동 랭킹은 쭉쭉 더 나오는데 블라인드 처리가 귀찮아서 2등까지만 나오게 캡쳐를 잘랐다. 이렇게 log를 기록하면, 많은 일들이 가능해진다. 추가적으로 log를 잘 활용하면, 며칠동안 운동을 하지 않았을 때 알람(notice)을 보내준다든지, 평균적으로 다른 회원들은 몇 회의 운동을 하고 있고 본인은 상위 몇 퍼센트인지 등까지 구현할 수 있었을 것 같다.

번외편: 프로젝트를 마치며 feat. 깜빡한 기능들

날씨를 갖다 붙이면 좋겠다는 생각을 프론트엔드 맡으신 팀원분께서 해내셨는데, 이미 날씨 api를 따오려고 요청하기엔 프로젝트 마감이 몇 시간 남지 않았었다. 날씨가 좋지 않으면 운동이 취소될 수 있다는 notice 를 해주는 기능을 만들었다면 좋았을듯?

또한, 미처 생각치 못했는데 신청 취소 버튼을 만들지 않았다. 만약 오전 7시, 헬스 세션에서 오전 8시, 산책 세션으로 변경하고자 하면 그냥 옮기려는 세션의 신청 버튼을 누르기만 하면 해결이 됐는데, 아예 운동 자체를 안하려고 신청 취소하는 기능을 깜빡했다.

주 100시간 공부하려면, 체력이 중요하니까 한 번 등록한 운동은 취소하지 않도록 했다고 약을 팔고 싶지만, 나도 새벽 3시 넘어서 잤다가 지난 토요일 운동을 빼먹은 바 있기에 구차한 변명은 하지 않겠다.

프로젝트 끝나고 나니까, 후련하긴 한데 미처 구현하지 못했거나 깜빡한 기능이 몇 가지 떠오른다. 나중에 진짜로 비슷한 서비스를 런칭하게 된다면, 그땐 다 반영해봐야지.

끝!

0개의 댓글