django 특강
기본형 데이터 타입들이 immutable, 파이썬에선 list, dict 와 같은 참조형 타입들이 mutable
Immutable 객체 int, float, str, tuple
Mutable 객체 list, dict
수정불가능한 객체 즉 immutable 객체는 기존 객체는 그대로 있고 항상 새로 생성되고 기존 객체는 가비지컬렉터에 의해 자동으로 삭제가 되나 이러한 동작들이 순식간에 이루어지기 때문에 우리는 그냥 덮어씌워 지는구나 착각하는 것
실제로 덮어 씌워지는 경우는 참조형 데이터들을 사용할 때 인데
list, dict 같이 수정 가능한 객체 mutable 객체의 경우는 해당 주소값을 직접 가지고 있는게 아닌 가리키고 있는 것이기 때문에 안의 원소를 변경할 경우
다른 인스턴스에도 영향을 미치게 된다.
a = ["python2", "python3"]
print(a)
b = a
print(b)
a.append("python4")
print(f'a : {a}')
print(f'b : {b}')
>>> ['python2', 'python3']
>>> ['python2', 'python3']
>>> a : ['python2', 'python3', 'python4']
>>> b : ['python2', 'python3', 'python4']
a 에 append로 데이터를 추가 했을 때 b에도 영향이 가는 것을 볼 수 있다. append 아니고 그냥 등호로 사용하게 되면 다시 새로운 선언을 하는 거기 때문에
immutable 처럼 새로 생성되고 이전 객체는 가비지컬렉터에 자동으로 삭제가 되간한다.
파라미터 *args/**kwargs(argument/keywordargument)
*args
받은 인자에 초과가 되는 부분들을 args 가 갯수에 상관없이 다 받아줌 list들을 풀어서 사용할 때 유용함
**kwargs
Key=Value 형태로 인자를 줄 수 있음 dict들을 풀어서 사용할 때 유용함
class People:
def __init__(self, **kwargs):
self.info = kwargs
self.name = kwargs.get('name')
self.age = kwargs.get('age')
self.is_worked = kwargs.get('is_worked')
john = People(name='john', age=25, is_worked=True)
print(john.info)
print(john.name, john.age, john.is_worked)
class Human:
def __init__(self, *args):
self.info = args
self.name = args[0]
self.age = args[1]
self.is_worked = args[2]
peter = Human('peter', 23, True)
print(peter.info)
print(f'name : {peter.name}, age: {peter.age}, is_worked : {peter.is_worked}')
USER.objects.create(
**request.POST
)
stacktrace
에러 볼 때 함수안에 함수안에 함수안에 함수로 되어있는 구조를 스택트레이스라고 부른다함
venv 활용 (vscode 환경 설정)
python -m venv venv # venv 생성
venv/Scripts/active # 가상환경 접속
requirements 활용
pip freeze 한 결과물을 requirements.txt 에 저장해서 ( pip freeze > requirements.txt )
pip install -r requirements.txt 이용해서 환경변수에 패키지 설치
코드 컨벤션
코드를 짤 때 하는 약속!
Pacal - UserLoginView - 클래스 만들 때
Camel - userLoginView - 파이썬에선 카멜은 안쓴다.
Snake - user_login_view - 클래스 아닐 경우
상수는 전부 대문자
리스트는 복수형으로 이름을 지정해주어야한다. 아니면 list라고 명시해주기
https status code
2xx normal
3xx redirect
4xx client error
5xx sever error
Project Refectoring
발표전 AWS에 배포해둔 사이트가 엄~청 느려져서 AWS인스턴스를 재시작했음에도 속도가 약간은 나아졌으나 크게 나아지지않아 원인을 확인해보니 추천시스템이 메인페이지로 이동할 때마다 실행 되기 때문에 Star 테이블의 데이터가 쌓이면 쌓일 수록 서버에 부하가오고 속도가 매우 느려졌었다...
기본적으로 테이블 구성 자체를 좀 더 세분화 해서 만들었다면 보다 가벼웠을 수도 있을거란 생각이 들었지만 근본적으로는 추천시스템이 계속해서 작동하는 것 때문이라고 생각이 든다.
특히 DB데이터를 토대로 DataFrame을 만들었어야 했으니 속도가 느려질만하다. 그리고 우리가 아직 모르지만 짜놓은 코드들 중에서 지워지지않고 메모리에 쌓이는 코드들도 있었을 거라 생각이 든다.
발표 후 튜터님들의 피드백
> 이를 해결하기 위해 유사도 검사 부분까지는 서버가 최초 실행될 때 한번만 진행되도록하기.
> 유저 접속이 가장 드문 새벽 시간대에 DB의 데이터를 토대로 갱신되도록 해주기.
위의 방법은 DB 데이터를 토대로 실시간 추천을 해주는 현재 시스템에는 적합하지 않지만 그래도 추천시스템 자체를
클래스와 함수를 이용해서 정리해보고, 타이머를 둬서 1분 마다 갱신 될 수 있도록 하는 시스템으로 변경해보려한다.
그렇게 기존에 있던 함수들을 Recommand클래스를 정의해서 그 안에 집어넣고 함수들에 self 인자를 추가해줘야만 해서 self 인자를 추가하고 실행을 해봤더니

타입에러가 발생하면서 작동이 되지않았다.

원인의 위치코드... 창호튜터님께 여쭤본 결과로는 저렇게 사용하는게 맞지만 한가지 하지않은 것이 있었다!
바로 클래스를 import 해오기만 하고 선언하지 않았던 것

user_based_collab 에 Recommand 클래스의 인스턴스를 생성해서 가져와서 사용하도록 만들었다.
여기에 이제 추가로 Recommand 클래스를 싱글톤 패턴으로 적용하도록 만들었다.
파이썬에서 싱글톤 패턴을 적용하는 방법이 되게 많았는데 어노테이션을 이용해서 하는 방식이 확실하게 알기 쉽고 사용하기도 편한 것 같다. 싱글톤 코드자체를 100% 이해가 잘안되긴해서 매직매서드나 그런 부분들을 좀 더 공부해야겠다.
# recommadation/recommand.py
def singleton_dec(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
@singleton_dec
class Recommand:
def __init__(self):
# 현재 DB에 맞게 동기화
restaurants = pd.DataFrame(Restaurant.objects.all().values())
stars = pd.DataFrame(Star.objects.all().values())
restaurants.columns = ['restaurant_id',
'restaurant_name',
'restaurant_address',
'restaurant_image',
'restaurant_count',
'restaurant_avg_score',
'restaurant_category_id']
stars.columns = ['star_id', 'star_date', 'restaurant_id', 'user_id', 'star_avg_score', 'star_count']
# print(restaurants)
# print(stars)
# 데이터프레임을 출력했을때 더 많은 열이 보이도록 함
pd.set_option('display.max_columns', 10)
pd.set_option('display.width', 300)
# restaurant_id를 기준으로 restaurants 와 starts 를 결합함
restaurant_stars = pd.merge(restaurants, stars, on='restaurant_id')
# print(restaurant_stars)
# user별로 restaurant에 부여한 star 값을 볼 수 있도록 pivot table 사용
restaurant_user = restaurant_stars.pivot_table('star_avg_score', index='user_id', columns='restaurant_name')
# 평점을 부여안한 영화는 그냥 0이라고 부여
self.restaurant_user = restaurant_user.fillna(0)
# print(restaurant_user)
from sklearn.metrics.pairwise import cosine_similarity
# 유저와 유저 간의 코사인 유사도를 구함
user_based_collab = cosine_similarity(self.restaurant_user, self.restaurant_user)
# print(user_based_collab)
# 위는 그냥 numpy 행렬이니까, 이를 데이터프레임으로 변환
self.user_based_collab = pd.DataFrame(user_based_collab, index=self.restaurant_user.index, columns=self.restaurant_user.index)
# print(user_based_collab)
def recommandation(self, login_user_id):
# 유사도 메인페이지에 출력을 위해서 보여주기 위한 유사도 높은 유저 추출
similar_top10 = self.user_based_collab[login_user_id].sort_values(ascending=False)[:10]
# 로그인 유저와 비슷한 유저를 내림차순으로 정렬한 후에, 상위 10개만 뽑아 그 중 유사도가 가장 높은 유저만 추출
similar_user = self.user_based_collab[login_user_id].sort_values(ascending=False)[:10].index[1]
# print(similar_user)
# 해당 유저가 좋아했던 음식점를 평점 내림차순으로 출력
result = self.restaurant_user.query(f"user_id == {similar_user}").sort_values(ascending=False, by=similar_user, axis=1)
# print(result)
result_list = []
for re in result:
result_list.append(re)
# print(result_list)
return result_list, similar_user, similar_top10
최초 인스턴스 생성 때만 DB에있는 데이터를 가져와 user_based_collab을 만들고 그 이후로는 만들어진 인스턴스로 계속 사용하기 때문에 현재 우리가 만들어놓은 서비스에서는 음식점 평가 즉시 내용이 갱신되어야하는데 그렇지 않아 적절하지 않은 서비스이지만 한번 만들어보며 싱글톤 패턴을 이해해보고싶었다.
예상대로 서버를 재시작하면 갱신된 결과가 제대로 적용되나 실시간으로는 적용되지않는 것으로 싱글톤이 잘 작동되고 있다는 것을 알 수 있었다.
이제 다음으로 해볼 건 일정 시간 마다 저 인스턴스의 __init__ 메서드를 실행 시켜 초기화 시켜서 현재 DB와 동기화 되도록 하는 것이다.
창호튜터님께 여쭤봤을땐 django에서 실행할지, os(windows, linux)에서 실행할지 따라 나뉜다고한다.
일정시간마다 함수를 실행 시켜주는 방법으로 여러가지 찾아본 결과
django 자체 패키지로 threding 이나 schedule 패키지를 이용하던가 django-crontab 을 이용하는 방법 중 고민했는데
django 자체 패키지를 사용하는 경우는 뭔가 리소스를 많이 잡아먹을 것 같아서 상록튜터님께서 crontab을 이용해는 것을 추천해주셨다.

설정을 다하고 python manage.py crontab add 입력을 하였으나 해당 모듈이 없다고 오류가 발생해서 pip install fcntl 해보고 실행해봤지만 마찬가지로 안되서 찾아보니 windows라서 안된다고한다.. AWS 서버에서 사용하게 될 일 있을 때 사용 해봐야겠다

그래서 윈도우 스케쥴러를 다시 한번 찾아봤다.. 이건 진짜로 내컴퓨터에서 직접 실행 시키는 그런 방법이었다. 아무래도 그런 방식들 보단 일단 django 내부에서 해결 할 수 있도록 threading 패키지를 이용해서 간단하게 작성해보았다.
패키지를 다운 받고, import 를 해온 뒤 __init_메서드 마지막에 한줄만 추가해주니 타이머에 따라 10초마다 갱신이 되는 것을 확인 할 수 있었다.
import threading
...
threading.Timer(10, self.__init__).start()
하지만! 근본적인 해결은 아직 되지않았다. 서버 재시작 후 최초 메인 페이지 접근 시 걸리는 시간이다.
이를 해결하기 위해서 여기까지 달려왔다. 이제 남은건 서버 시작 할 때 인스턴스를 만들어주는 것이다.

project 폴더의 __init__.py 에 클래스 import 받아와서 선언을 함으로써 인스턴스를 생성하도록 만들었다.
# TODAY_LUNCH/__init__.py
from recommandation.recommand import Recommand
user_based_collab = Recommand()
그 이후로 서버를 시작하는데 시간이 조금 더 걸리지만 메인페이지를 갈때의 속도는 느려짐 없이 확실하게 작동이된다!!

이번 리팩토링을 통해서 성능개선을 한번 시켜보았다. 테이블 구성을 더욱 세밀하게 하는 방식으로도 성능개선이 가능했을 거라 생각은 들지만 이번 기회로 싱글톤이나, threading, crontab 까지 공부해볼 수 있었다.
추가로 내가 담당한 파이썬 파일들을 pylint도 한번 돌려보았는데..

6.73점.. 고쳐야할 점이 매우 많다..;;; 그나마 다른 팀원의 코드도 섞여있어서 이런 점수를 받은게 아닐까 싶다..

추천시스템은 아예 0점이다.. 뭔가 버그가 낫나 싶긴하다.. 오류메세지들을 보면서 시간 여유되면 오류 하나씩 확인해서 수정해봐야겠다.

크롤링해온건 3.81점 0점은 버그가아닌것같다..
'회고록(TIL&WIL)' 카테고리의 다른 글
WIL 2022.06.20 DRF/ Custom User, login/out, 역참조, Serializer (0) | 2022.06.21 |
---|---|
WIL 2022.06.20 DRF/ venv환경 구성, 모델링옵션, REST API, POSTMAN, ORM (0) | 2022.06.20 |
TIL 2022.06.14 TODAY_LUNCH (KPT 회고) (0) | 2022.06.14 |
TIL 2022.06.13 TODAY_LUNCH (bug_fix, refectoring, README) (0) | 2022.06.13 |
TIL 2022.06.09 TODAY_LUNCH (랭킹TOP5 비동기식으로 재구현, 팝오버(부트스트랩)) (0) | 2022.06.09 |