할 일 그룹 목록 구현
기본 구현
임시로 구현한 할 일 그룹 목록 페이지를 정식으로 구현해볼게요.
(1) TodoGroupService에 findall() API 구현
할 일 그룹을 조건에 맞게 검색하는 API인 findall() 메서드를 TodoGroupService에 구현할게요. 최소한으로 필요한 검색 조건은 바로 특정 사용자지요.
Result 객체의 scalars()를 사용하고, ScalarResult 객체의 all() 메서드를 사용한 점을 제외하면 기존 select() 구문 생성 및 데이터 질의(querying) 구현과 다르지 않아요. 질의 결과가 복수 개이므로 스칼라 값을 scalars()로 가져오고, 가져온 스칼라 값 모두를 가져온 것이지요. 만약 데이터가 매우 많다면 파티셔닝(partitions())이나 페이징(Paging) 기법을 쓰는 게 좋아요.
구현한 코드 커밋 : afee423
(2) 할 일 그룹 목록 페이지 종단점 함수 구현
앞서 임시로 작성한 코드에 findall()로 할 일 그룹 목록을 가져오는 구현을 추가했어요. 간단하죠?
구현한 코드 커밋 : 17febca
(3) 할 일 그룹 목록 출력
데이터베이스에서 할 일 그룹 목록을 가져왔으니 todo-group-list.jinja2 템플릿 파일을 수정해 할 일 그룹 목록을 나열해볼게요.
잘 동작하죠? 이제 좀 모양이 나오네요! 😀
작성한 코드 커밋 : 2f4cd5c
비동기로 할 일 그룹의 할 일 개수 가져와 표시하기
(1) htmx로 지연 로딩과 부분 렌더링 구현
할 일 그룹명을 출력하고 그 옆에 해당 그룹에 존재하는 할 일 개수를 출력했는데요. 이 코드는 데이터베이스에 부하를 일으키는 동작을 할 가능성이 커요. 단순히 개수만 표시하면 되는데, group.todos|length는 데이터베이스에서 해당 할 일 그룹에 관계 맺은 할 일 데이터를 모두 가져온 후 Python 영역에서 개수를 세기 때문이죠. 불필요한 데이터를 모두 가져오는 부분에서 한 번, 그리고 Python에서 데이터 개수를 세는 부분에서 또 한 번 성능 저하가 일어나는 거예요.
이 부분을 htmx로 단순한 방식으로 개선해볼게요. 방식은 다음과 같아요.
-
할 일 그룹 목록 페이지에 접속
-
할 일 그룹 이름이 화면에 표시되면
-
각 할 일 그룹 별로 할 일 개수를 출력하는 API를 비동기로 호출
-
호출 결과를 할 일 그룹 이름 옆에 표시
htmx를 이용하면 아주 간단하게 구현할 수 있어요. 먼저 pages/todo-group-list.jinja2 템플릿을 수정해 htmx 구현을 적용할게요.
htmx 코드 두 줄만으로 네 단계를 구현했어요. hx-trigger="load delay:250ms"는 해당 HTML Dom 요소가 화면에 드러나면 0.25초 후에 작업을 수행하는 거예요.
htmx가 "partial-todo-group-todos-count"라 이름붙은 URL을 호출해요. 이 HTTP API를 구현해야겠어요.
@tpl.hx() 장식자가 새롭게 등장했어요. hx() 장식자는 FastHX에서 제공하는 메서드로, htmx 요청인 HTTP Request만 허용해요. 이런 설정이 page() 장식자엔 없어요.
(2) 할 일 그룹 별 할 일 개수 가져오기
TodoService에 count_by_group_id() API를 새롭게 추가해야 해요. 이름 그대로 할 일 그룹 ID(기본키 값)으로 할 일 개수를 세는 동작을 하죠. 근데 남의 할 일 그룹 ID를 알면 남의 할 일 개수도 알아낼 수 있잖아요? 그래서 이 API를 호출하는 사용자의 ID(기본키값)도 데이터 질의 검색 조건에 추가할 거예요.
조금 생소할 수 있는데, 다음 SQL 질의문을 작성하는 코드예요.
참고로 의사 코드이며, 실제로 작성하는 SQL문은 다음과 같아요.
SQL count(*)에 해당하는 Python 코드가 func.count()에요. SQLAlchemy에서 제공하는 func 객체는 Function API인데, SQL 함수 구문을 생성해주지요.
Todo.group모델 필드는 TodoGroup 모델에 대한 관계 필드예요. 자료형 각주와 달리 TodoGroup 모델 그 자체는 아니예요. 그래서 Todo.group.user_id == user_id 표현식을 사용하지 못하는데, SQLAlchemy는 has()로 관계 필드에 대한 탐색(lookup) 연산을 수행해줘요. Todo.group.has(TodoGroup.user_id == user_id)는 다음 SQL문을 작성하지요.
개수를 셋으니 이를 템플릿으로 출력합니다. pudding_todo/apps/todo/templates 디렉터리 안에 부분이라는 뜻으로 partial 디렉터리를 만들고, 이 디렉터리 안에 todo-group-todos-count.jinja2 파일로 템플릿을 만들어요.
페이지 렌더링 템플릿은 pages 디렉터리에, 부분 렌더링 템플릿은 partial 디렉터리에 배치한 거죠.
여기까지 진행한 코드 커밋 : 33fdfc1