[Flask + MySQL] 서강신청 백엔드 회고록

nowkim·2021년 8월 20일
14

회고록

목록 보기
3/7
post-thumbnail

⚠ 참고

  • 이미 Notion에 정리한 내용이지만, 이 내용을 참고하여 시행착오를 겪지 않았으면 좋겠습니다.
  • 혹은 좋았던 부분은 참고하여 더 쉽게 진행하길 바랍니다.
  • 최대한 기술적인 부분은 제외하고 프로젝트가 진행되었던 과정과 느낀 점 위주로 작성하였습니다.
  • Github Link

목차

파트1

  1. 개발 기간
  2. 사용한 tool
  3. 서비스 기획 + 파트 배분
  4. 구현해야 할 서비스

파트2

  1. 개발환경 및 언어 정하기
  2. 개발환경 구축, 파일 디렉토리 구조 설정
  3. db 모델링
  4. 크롤러 구축
  5. REST API 구축
  6. 서비스 배포 & Hotfix

개발 기간

실제 개발 기간은 객관적으로 길지 않았다. 하지만 초기 개발환경 세팅과 개발 계획을 세우는 부분에서 생각보다 고려할 부분도 많았고, 프로젝트가 끝난 후 돌아봤을 때 전체 기간의 절반이나 차지하고 있다는 것을 알게 되고 이 부분 또한 정리해 보려고 한다.

사용한 Tool

이번 프로젝트를 하면서 특히 마음에 들었던 Tool은 바로 Docker app이었다.
많이 쓰이는 기본적인 도커 명령어들을 클릭으로 대체할 수 있다는 점과 직관적으로 눈에 보이는 점이 초보자의 입장에서 너무 좋았다.

세부적인 내용은 아래를 참고.

Vscode, docker app , postman, mysqlworkbench
MacBook Pro (Retina, 13-inch, Early 2015), 작업 환경은 container 내에서 remote로 작업.

서비스 기획 + 파트 배분

처음 서비스를 기획하게 된 계기는 수강신청할 때마다 들어가게 되는 '개설교과목 정보 사이트'의 UI가 불편했고, 느린 검색 속도 때문이었다.

프런트 파트에서는 필요한 정보만 담아서 가로 스크롤을 없애고 한 눈에 정보가 들어올 수 있도록, 백엔드 파트는 검색 속도를 빠르게 하고, 담아놓기한 정보가 업데이트 될 경우 알람을 보내는 기능을 추가로 구현하기로 했다.

구현해야 할 서비스

  • 개설교과목정보 크롤러 봇
    • 주기적인 크롤링 기능
    • 알람기능 - mailer
  • REST api
    • 회원가입, 로그인, 수정, 탈퇴 등 회원 Auth에 관련된 api
    • 과목 즐겨찾기 CRUD api
    • 과목 조회 api
    • 인증메일 send 기능 api
  • db 구축
    • user
    • subject - 크롤러 데이터 바탕

이 중 내가 맡은 파트는 크롤러를 제외한 파트였다.

개발환경 및 언어 정하기

크롤러 개발 언어 - 파이썬

다른 팀원이 파이썬으로 크롤링을 한 경험이 있어서 선택하게 되었다.

그리고 진입 장벽이 낮고, 많은 자료가 있는 언어를 선택해야 했기에 자연스럽게 파이썬이 적합하다고 생각했다고 한다.

REST API 개발 언어 - 파이썬 & 프레임워크 - Flask

처음에는 node.jsPython 둘 중 하나를 선택해야 했는데, 크롤링 파트를 파이썬으로 진행하기도 하고, 파이썬을 공부 목적으로 한 번 사용해 보는 것도 좋을 것 같아서 파이썬을 백엔드 언어로 정하게 되었다.

파이썬 웹 개발 프레임워크는 크게 DjangoFlask가 있는데,
Django는 좀 더 무겁고 러닝커브가 있다고 알고 있고, Flask가 공부할 때 더 좋고 빠르게 구축할 수 있다고 한다.
그래서 바로 Flask를 선택하게 되었다.

DB - MySQL

크롤링할 데이터가 테이블 형태로 이루어져 있어 똑같이 옮겼을 때 직관적으로 이해하기 쉽다고 생각했다. 또한 생성될 user 테이블과 과목 테이블에 관계를 부여해서 즐겨찾기 테이블을 쉽게 구현하는데 좋다고 생각했다. 그리고 무엇보다 무료이고, 그에 따라 참고할 수 있는 자료가 많다.

DB 서버 - RDS

도커를 사용하려 했지만, 도커 컨테이너 형태로 Mysql Server를 구축하는 것은 위험한 방식일 수 있다는 글을 읽었다.
AWS RDS를 Freetier로 이용하면 비용 부담 없이 안정적이게 db 서버 운용이 가능하기에 RDS를 택했다.

배포 - Docker

사실 욕심이었다.
그러나 단순히 결과물보다 공부 목적도 있었기에 선택하게 되었다.
예전부터 도커에 대해 궁금증이 있었고, 로컬 개발 환경과 배포 환경을 일치시키는 데에서 매력을 느꼈다.

배포 서버 - EC2

EC2 - 둘 다 사용 경험 있고, 가장 보편적이기 때문. 사실 더 좋은 다른 방안을 쓰려면 거기에 따른 러닝커브가 존재하기 때문에 급한 상황에서 시도해보지 못했다.

→ 학교 동아리 서버로 docker container를 구동하려 시도했지만, super user 권한이 있어야 했다. Super user 권한을 받기까지 시간이 지체되었다. 또한 관리적인 측면에서 AWS EC2 console 또는 Jupyter 환경을 사용하는게 더 간편했다. 비용적인 측면은 물론 학교 동아리 서버가 가성비가 좋겠지만, AWS Freetier 계정으로도 큰 부담없이 서버를 구축 할 수 있어서 AWS EC2를 선택했다.

개발환경 구축, 파일 디렉토리 구조 설정

먼저 도커파일을 만들어 가상환경에서 작업하기 전에 파일 디렉토리를 작성해야 했다.

How to structure a Flask-RESTPlus web service for production builds
이 링크를 참고해서 파일 디렉토리를 구성했다. 정말 좋은 글이라 따로 정리도 할 예정이다.

.
├── app
│   ├── __init__.py
│   ├── main
│   │   ├── config.py
│   │   ├── controller
│   │   │   └── __init__.py
│   │   ├── __init__.py
│   │   ├── model
│   │   │   └── __init__.py
│   │   └── service
│   │       └── __init__.py
│   └── test
│       └── __init__.py
├── manage.py
└── requirements.txt

위와 같이 디렉토리 구조를 작성하게 되었다.

개발환경을 위한 도커파일은 다음과 같이 작성하게 되었다.

FROM python

# Install requirements
RUN pip install --no-cache-dir --upgrade pip
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Add source code
ADD app /home/app
ADD migrations /home/migrations
ADD requirements.txt /home
ADD manage.py /home

WORKDIR /home
# Set environment variables
ENV FLASK_APP=manage.py

# ENTRYPOINT
ENTRYPOINT python manage.py run

db 모델링

1. User

  • 회원정보에 필요한 정보들이 담겨있다.
  • Password는 해싱함수를 이용해 encode되어 저장된다.

2. Subject

  • 크롤링한 데이터를 pandas Dataframe에 담아 그대로 db에 저장했다.
  • 따라서 각 필드별 type 정의가 대부분 text로 엄밀하게 정의하진 않았다.

3. user_subject

  • 사용자가 즐겨찾기 과목을 추가하면 해당 과목의 id가 담기는 테이블이다.
  • 보통은 각 테이블의 primary key를 foreign key로 필드를 가지는게 정석이나, 여러 이슈로 인해 다음과 같이 정의했다. 이슈에 대한 언급은 아래에서 하겠다.

4. issue_report

  • 사용자가 보낸 문의사항, 피드백을 담는 테이블.
  • 유저가 바로 이메일로 보내면 db에 담을 필요는 없지만, 사용자가 우리에게 이메일 발신시에 사용자 로그인 인증이 필요한 이슈가 생겨 db에 담는 방식을 택했다.

5. departments

  • 각 학년도, 학기 마다 학부 명칭, 목록, 학부에 대한 식별자가 조금씩 다른 이슈가 있어 만든 테이블.
  • 18년도~21년도 2학기 까지의 학부목록, 학부id를 각 컬럼에 담고있다.

문제점, 해결방법

사용자의 정보가 담겨 있는 user테이블과 과목 정보가 담겨 있는 subject테이블Many-to-many relationship이다. 위에서도 언급했듯이 즐겨찾기 테이블(user_subject 테이블)은 primary key를 foreign key로 참조하여 만들어야 한다.

그러나 새로운 학기가 시작될 경우 subject 테이블이 새로 생성이 되기 때문에 그 때마다 관계를 새로 정의해주어야 하는 문제가 발생했다. 동적으로 관계를 정의할 수 있다면 좋겠지만, 그 방법을 실패했다.

그리고 subject 테이블이 새로 만들어짐에 따라 join 테이블도 함께 늘어나게 되므로, 회원의 즐겨찾기 과목을 조회하기 위해서 탐색해야 하는 테이블의 개수도 늘어나게 되는 문제가 생긴다.

  • 동적으로 관계를 정의할 수 있는가?
  • user_subject 테이블이 1개가 아닌 여러개가 된다.

그래서 이러한 문제를 다음과 같이 해결했다.

정석적인 join의 방법이 아닌 '이메일', 'subject_id' 두가지 필드를 정의해 참조용 key로 사용했다. subject_id는 직접 만든 과목에 대한 식별자다.

학년도-학기-과목코드-분반
subject_id example
21-2-MAT1002-01

참조 과정은 다음과 같다

  1. 앞의 학년도(21), 학기(2)를 가져와 조회 할 테이블에 대한 정보를 얻는다
    (테이블 명 : s21_2)
  2. 해당 테이블에 대해 과목코드가 일치하는 데이터를 가져온다
    (select * from s21_2 where 과목코드='MAT1002')

크롤러 구축

크롤러 파트는 내 파트가 아니어서 패스하도록 하겠다. 자세한 내용은 여기를 참고.

REST API 구축

프론트와의 지속적인 회의를 통해 필요한 api에 대한 리스트업을 하고 개발을 진행했다.


마주쳤던 이슈들은 대략 다음과 같다.

  • Queue Pool 이슈

    → Flask-SQLAlchemy를 사용했는데,
    코드 상에서 session.close()를 해주지 않아 커넥션 반납이 안 됐음

  • Sleep Connection 이슈

    → 일부 connection들이 Sleep 상태로 머물러 있어 누적시 rds가 터지는 이슈 (해결 방법)

  • 이메일 발송 시도 시 Google에서 막는 이슈

    → 구글 측에서 막은 보안 이슈로, 발신 계정 로그인 시 해결

  • MysqlDb 패키지에는 Pool_recycle 셋팅이 불가능한 이슈

    → subject 조회시 사용된 패키지였는데 그냥 sqlAlchemy 패키지로 통일

    → 대신 교과목 조회시 속도가 살짝 저하되었다

  • 교과목 불러오는 api 호출시 속도 이슈

    • 전체 학부 테이블을 불러오는데 처음에는 지나치게 느렸다(10~20초)
    • 하지만 쿼리문 실행(db connect) 후 데이터 불러오기, response 넘겨주기 두 가지 과정에 대한 속도 측정 결과, response로 넘겨주는데 엄청난 시간이 쓰였다.
    • 기존의 정의한 모델을 사용해 Jsonify 하여 넘겨주는 과정이 지나치게 오래 걸렸던것
    • 패키지의 힘을 빌리지 않고 직접 하드코딩으로 Jsonify를 해 넘겨줬다.
    • 모든 열을 가져오지 않고, 필요한 열만 선별해서 골라서 넘겨주는 것도 속도 향상에 기여했다.
  • 시간 검색옵션 기능 api 구현 중 이슈

    1. 대부분은 요일이 달라도 수업시간은 같았으나, 수업시간이 다른 과목도 존재했다

      → 기존의 DB부터 문제가 있음(예외 고려 못했음)

      → 일단 db에 새로운 필드를 추가해서 예외 상황도 처리했다

    2. 시작시간, 종료시간 사이의 과목을 select한 방법

      strcmp로 구현

서비스 배포 & Hotfix

대부분은 프론트에서의 이슈가 많았지만, 백엔드에서도 추가하거나 수정한 사항들이 있다. 크게 서너 가지가 있지만 그 중 가장 큰 이슈는 토큰 인증 문제였다.

토큰이 없거나, 유효하지 않을 시 response 분기처리

백엔드 api를 만들 때는 토큰을 넘겨받지 않을 경우를 고려하지 않았다. 유효한 토큰인지, 아닌지만 검증하고 아예 받지 않을 때는 고려하지 않고 작업을 했는데, 이게 프런트와 만날 때 문제가 되는 일이 있었다.

백엔드 파트는 무엇보다 분기처리와 가능한 모든 경우의 수를 생각하고 짜야 하는 것이 중요한 것 같다.

추가적으로 갑자기 필요한 api가 생겨서 급하게 만들기도 했는데, 이미 전체적인 틀이 있어서 그렇게 많은 시간이 들지는 않았다.

마무리하며

약 1달간의 프로젝트가 끝났다. 프로젝트를 시작하기 전과 지금을 비교하면 정말 많이 배우고 성장했다. (Docker, Flask, MySQL 전부 다 처음 써 봤으니까) Notion에 정리한 내용들만 봐도 정말 양이 많다.


프로젝트는 프런트3, 백엔드2로 진행했는데 처음에는 내가 맡은 파트를 잘 모르는 부분도 많고 부족해서 1인분을 못하지는 않을까 하는 걱정도 있었지만, 별 탈 없이 잘 마무리되어서 기분이 좋다.

정말 별 거 아닌 오류 때문에 4~5시간을 보내기도 했고, 배포 전 3일동안은 새벽 3시까지 버그 수정을 하기도 했고, 함께 진행한 승우와 스터디룸에서 코딩하다 나와 순대국밥을 먹으며 오늘은 CORS가 날 화나게 했다거나 분기 처리 하나를 잘못 해서 2시간을 다시 코딩했다거나 등등 시시콜콜한 이야기를 나누었던 것들이 벌써 추억으로 자리잡게 된 것 같다.

profile
끙끙대며 배우는 중

4개의 댓글

comment-user-thumbnail
2021년 8월 20일

한 달간 압축적으로 빠르게 성장하신 것 같아 대단합니다😎

1개의 답글
comment-user-thumbnail
2021년 9월 17일

멋지네요 ㅎㅎ

1개의 답글