728x90
기간: 11월 22일 ~ 12월 13일
안녕하세요, 엘리스 AI트랙 2기 레이서 블로거 송성곤입니다. 이번 포스팅에서는 엘리스 최종 프로젝트에서 3주간 개발을 하며 배운 것, 느낀 것에 대해 정리하 공유해보려고 합니다.
1. 협업(소통)에서
'이거 말해줘야 될 것 같은데...' 는 빨리 말하자
- 명세서와 스토리보드 작성 후에 실제로 개발을 하다보니 프론트에 돌려주는 리턴값의 형태가 좀 달라졌다. 일단 말을 해드렸어야 됐는데, 곧 있으면 어차피 API개발이 다 되니까 그냥 실제 데이터도 한번 받아보실겸 API개발 다 하고 한번에 말씀드리려고 했다.
- 하지만 프론트엔드에서 뭔가 에러가 있어서 디버깅 중이었는데 갑자기 아웃풋까지 바뀌어서 다시 돌려달라고 말씀하셨다. 아웃풋은 바뀔 수 있다. 그런데 바뀔 걸 알면서 나중에 자기 할 일 다하고 알려주는 것이나, 바뀐 버전을 말 없이 배포한 게 기분이 나쁘셨던 것으로 짐작된다.
- 앞으로는 공유도 자주하고 명세서 최신화도 빨리빨리 할 수 있도록 해야겠다. (선 명세, 보고 후 개발)
2. FastAPI, Python에서
code formatter의 중요성
- 희지님께서 백엔드 분들도 린트나 포맷터를 적용해서 코드 스타일을 통일시키는 것이 어떠냐고 말씀해주셨다. 이전 프로젝트에서는 한번도 안 써보긴 했는데 새로 도입해보기로 했다.
- pre-commit hook을 설정하여, 커밋을 시도할 때 마다 isort → black → flake8 순으로 점검하게 설정하였다.
- 가끔 isort랑 black이랑 서로 의견이 달라서 커밋을 무한히 하게 되는 상황도 있고, flake8은 max-length에 굉장히 예민해서 커밋이 잘 안 될 때도 있었다. 이럴 땐 black만 통과하더라도 커밋에 —no-verify옵션을 주어 그냥 통과시켜주었다.
- 결과적으로 코드 가독성이 굉장히 좋아졌다. 앞으로도 자주 쓸 것 같다.
session, connection pool
- 이전까지 flask로 프로젝트를 할 때는 flask-sqlalchemy를 아래의 방법대로 사용했고 저 db변수를 다른 CRUD관련 함수에서 import하여 사용하였다.
## main.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
## crud.py
db.session.query(User).all()
- 그런데 fastapi의 튜토리얼에서는 아래와 같은 방식으로 진행했다.
## database.py
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
## crud.py
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/", response_model=List[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users
- 사실 생각해보면 flask도 하나의 connection을 가지고 그 안에서 session을 쓰고 닫고 하고 있는 방식인데, 새삼 fastapi에서 저렇게 보니 “매 연결마다 db를 연결하잖아?!”라는 생각이 들어서 기원님께서 main에서 Sessionlocal()을 한번만 하도록 리팩토링을 해주셨다.
- 하지만 당연히 위 방법이나 아래 방법이나 크게 다르지 않고, 리소스도 많이 소요하는 작업이 아니었다.
- 물론 리팩토링한 코드도 당연히 작동되지 않았다. (왜냐면 매 작업마다 세션이 필요한데 하나로 하다니...)
- 아래는 내가 db, session, connection pool에 대해 너무 헷갈려서 이것저것 고민하다 질문했던 내용과 답변이다! 부디 도움되시길.
422 unprocessible entity error
- python 웹개발 톡방에서도 본 결과, fastAPI에서 종종 보이는 에러인듯 하다.
- fastapi가 pydantic을 사용하고, 요청과 응답 데이터에 대한 규칙이 엄격해서 발생하곤 한다.
- request나 response의 형태가 내가 타입 힌팅으로 지정한 클래스(혹은 데이터타입)과 일치하는지 확인해보시길!
Form 태그에서 데이터 받는 법
- 예시를 위해 요청 parameter를 그대로 돌려주는 api를 만들었다.
- 만약 요청을 잘 처리했다면 응답으로 "123", 123, 123.0 이 돌아올 것이다.
- 평소에 짜던 다른 API처럼 코드를 작성하면 이렇게 작성할 것이다. (내가 그랬다)
- 하지만 응답으로는 내가 요청한 데이터가 아니라 default로 지정한 값들이 돌아오고 있다.
- 위처럼 fastapi에서 Form을 import하고 default값을 그 안에 넣어줘야한다.
- 이제 정상적으로 요청한 값이 돌아오고 있다.
인공지능에서..
tensorflow hub사용하면서 메모리가 자꾸 터지던 문제
- 우리는 tensorflow hub에서 arbitary style transfer모델을 load하여 사용하였다.
def save_transfer_image(
content_image_path: str, style_image_path: str, save_path: str
) -> BytesIO:
try:
content_image = tf_read(content_image_path)
style_image = tf_read(style_image_path)
# hub_module = hub.load('style_transfer')
hub_handle = (
"https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2"
)
hub_module = hub.load(hub_handle)
- 기존 방식을 보면 style transfer를 작동시킬 때 마다 모델을 불러온다.(하지만 이게 문제일 줄은 몰랐다.)
- 'local 변수니까 잠깐 쓰고 다시 메모리에서 내리겠지?' 라고 생각했기 때문이다.
- 하지만 계속 GPU 메모리에 남아있었던 것이다..
- hub.load를 함수 밖으로 빼 한번만 작동시킴으로서 해결되었다...(허무)
728x90
'교육, 대외활동, 봉사 > 엘리스 AI 트랙 2기' 카테고리의 다른 글
[엘리스] 엘리스의 수료식! 데모데이 (0) | 2021.12.27 |
---|---|
[엘리스] 엘리스에서의 마지막 프로젝트(AI 프로젝트) - 기획편 (0) | 2021.11.22 |
[엘리스] AI트랙 레이서들을 위한 <레이서 홈커밍 데이> (0) | 2021.11.08 |
[엘리스] 데이터 분석 웹 서비스 프로젝트를 하며 느낀 점, 앞으로 공부할 것 (0) | 2021.10.24 |
[엘리스] 팀 프로젝트 1~2주차 기록 (0) | 2021.10.10 |
댓글