위젯 렌더링 대작전

2024년 3월 27일 수요일

Today I Learned

날짜

2024년 3월 27일 수요일

내용

열심히 진행했던 스프린트인 리스트 디자이너의 QA작업에 돌입했다.

데이터 갱신 타이밍

위젯에 표시되는 상품들의 실시간성 데이터들은 1시간 간격으로 cron을 이용해 업데이트 하도록 설계했다. 간과한 것은, 새롭게 위젯을 만들 때, 데이터가 생성되야 한다는 것이다. 이 로직이 없으면 새로 만들어진 위젯의 상품들은 다음 크론이 오기 전까지 데이터가 없게 되어 고객이 사용할 수 없다.

관련된 데이터를 업데이트 하는 함수들 crud에 만들어놨었다. 이를 가져와서 위젯을 생성하는 클래스 내에서 호출하도록 구현했다. 모양새가 정말 맘에 들지 않지만.. 현재는 기능을 완성하여 출시하는 것이 중요하다고 생각해서 기능 완성에 중점을 두었고, 여기에는 이상 없다.

또 뺴먹은 부분은 위젯 설정 내에서 데이터를 수집하는 기간을 설정할 수 있는데, 수정 때 데이터를 수집하는 로직을 추가하지 않아 다음 크론 때가 되서야 실제 데이터가 바뀐다는 것… 수정사항을 저장할 때도 데이터를 새로 수집하여 저장하도록 로직을 추가해주었다.

미리보기

이전에 존재했던 위젯과 마찬가지로 어드민 내에서 위젯을 다룰 때 미리보기와 실제 스토어에서 위젯이 출력될 때 호출되는 라우터와 클래스는 동일하다. 프리뷰에선 디자인 옵션등을 query param으로 넘겨줘서 적용한 것을 렌더링하여 반환하고, 실제 스토어에선 옵션에 대한 값이 없어 DB에 저장된 옵션을 적용해 반환한다는 차이가 있을 뿐이었다.

유저가 위젯을 수정할 때, 그때그떄 모든 데이터를 서버에 전송하였고 이를 렌더링하여 반환했다. 이로 인해 기본적으로 성능이 약간 불안했다. 기존 위젯과 동일한 방식임에도 차이가 나는 것은, 위젯에 적용되는 데이터의 차이인 듯 하다. 기존에는 고작 몇 가지(ex. 위젯 내 표시되는 아이템의 수 등)의 데이터만 주고받아 적용했지만 , 지금은 약 50가지 데이터가 전달되어 적용된다. 예를 들어, 위젯에 출력되는 제목에

“가나다”를 추가로 적을 때

  1. 기존 데이터에 제목만 “ㄱ”이 추가된 채로 모든 데이터를 전송
  2. 1 데이터에 제목만 “ㅏ”가 추가된 채로 모든 데이터를 전송
  3. 2 데이터에 제목만 “ㄴ”가 추가된 채로 모든 데이터를 전송

되서 총 6번 렌더링을 요청하게 된다. 프리뷰를 위해 별도의 로직을 구성했다면 좋을텐데.. 만약 이 서비스가 고도화되면 최우선으로 생각해야 할듯 하다.

여기서 더욱 문제가 될 뻔 했던 것은, 실시간 데이터도 프리뷰에서 업데이트되야 했던 것이다. 그럼 매 렌더링 요청때마다, 데이터베이스를 싹 다 뒤져서 6개의 실시간성 데이터를 업데이트하는 함수가 호출되야 한다. 우리 서버가 살려달라고 애원할게 뻔하다. 어쩔 수 없이 프리뷰에선, 실시간성 데이터에 임의의 데이터를 집어넣어주는 방식으로 변경했다.

이로 인해, 렌더링을 위한 클래스를 변경했다. 기존에는 하나의 구체 클래스였지만, 추상클래스를 이용해서 스토어에서 렌더링할 떄와 미리보기에서 렌더링 할때를 분리했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class ListDesignerWidgetRenderer(ABC):
    def __init__(self, data, db):
        self.data = data
    
    @abstractmethod
    async def render_widget(self):
        pass

    @abstractmethod
    def get_products(self, list_designer_widget_id, db):
        pass

    @abstractmethod
    def return_widget(self, template, products, data):
        pass

# 스토어 렌더링
class ListDesignerWidgetStoreRenderer(ListDesignerWidgetRenderer):
    def __init__(self,data,db) -> None:
        self.data = data
        self.db = db

    # 위젯 렌더링 메서드
    async def render_widget(self):
	       ...

    # 표시할 상품들을 순서대로 가져오는 메서드
    def get_products():
		     ...

    # jinja2로 템플릿에 데이터 적용하여 반환하는 메서드
    async def return_widget():
		    ...
        
   # 스토어 렌더링
class ListDesignerWidgetStoreRenderer(ListDesignerWidgetRenderer):
    def __init__(self,data,db) -> None:
        self.data = data
        self.db = db

    # 위젯 렌더링 메서드
    async def render_widget(self):
	       ...

    # 표시할 상품들을 순서대로 가져오는 메서드
    def get_products():
		     ...

    # jinja2로 템플릿에 데이터 적용하여 반환하는 메서드
    async def return_widget():
		    ...

회고

좀 되라!