From 8bf059a96cf7d3b8f46046af825770013b81d43f Mon Sep 17 00:00:00 2001 From: sehooh5 Date: Wed, 11 Feb 2026 14:42:47 +0900 Subject: [PATCH 1/2] add seed to grad posts --- app/grad_2025/schemas.py | 1 + app/grad_2025/service.py | 13 +++++++++++-- app/posts/repository.py | 18 ++++++++++-------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/grad_2025/schemas.py b/app/grad_2025/schemas.py index 4fb5433..c4c4d40 100644 --- a/app/grad_2025/schemas.py +++ b/app/grad_2025/schemas.py @@ -20,6 +20,7 @@ def from_grad_2025(cls, grad_2025: Grad2025): class Grad2025Cursor(APISchema): id: int + seed: str | None = None def encode(self) -> str: payload = self.model_dump_json().encode() diff --git a/app/grad_2025/service.py b/app/grad_2025/service.py index 2f8e122..99669fc 100644 --- a/app/grad_2025/service.py +++ b/app/grad_2025/service.py @@ -1,3 +1,5 @@ +import uuid + from app.category.enums import Category from app.common.schemas import Page from app.posts.repository import PostRepository @@ -28,8 +30,15 @@ async def search_posts( categories: list[Category] | None = None, univ_majors: list[str] | None = None, ) -> Page[PostCompactRead]: - cursor_id = cursor.id if cursor else None + if cursor and cursor.seed: + seed = cursor.seed + cursor_id = cursor.id + else: + seed = str(uuid.uuid4()) + cursor_id = None + posts = await self.post_repository.find_grad_2025_posts( + seed=seed, cursor=cursor_id, limit=limit + 1, categories=categories, @@ -38,7 +47,7 @@ async def search_posts( has_next = len(posts) > limit if has_next: posts = posts[:-1] - next_cursor = Grad2025Cursor(id=posts[-1].id).encode() + next_cursor = Grad2025Cursor(id=posts[-1].id, seed=seed).encode() else: next_cursor = None compact_posts = [PostCompactRead.from_post(post) for post in posts] diff --git a/app/posts/repository.py b/app/posts/repository.py index e813967..6a9850e 100644 --- a/app/posts/repository.py +++ b/app/posts/repository.py @@ -296,12 +296,17 @@ async def find_univ_major_by_post_id(self, *, post_id: int) -> Grad2025 | None: async def find_grad_2025_posts( self, *, + seed: str, cursor: int | None, limit: int, categories: list[Category] | None = None, univ_majors: list[str] | None = None, ) -> list[Post]: - """post_grad_2025_table에 존재하는 Post만 조회합니다.""" + """post_grad_2025_table에 존재하는 Post만 조회합니다. 시드 기반 랜덤 정렬.""" + random_order = func.md5(func.concat(seed, Post.id.cast(String))).label( + "random_order" + ) + stmt = ( select(Post) .options( @@ -310,19 +315,16 @@ async def find_grad_2025_posts( ) .join(post_grad_2025_table, Post.id == post_grad_2025_table.c.post_id) .where(Post.deleted_at.is_(None)) - .order_by(Post.created_at.desc(), Post.id.desc()) + .order_by(random_order, Post.id.desc()) .limit(limit) ) if cursor is not None: - # 커서 id의 created_at을 가져와서 그 이전 것들만 조회 - cursor_created_at = ( - select(Post.created_at).where(Post.id == cursor).scalar_subquery() - ) + current_random = func.md5(func.concat(seed, str(cursor))) stmt = stmt.where( or_( - Post.created_at < cursor_created_at, - and_(Post.created_at == cursor_created_at, Post.id < cursor), + random_order > current_random, + and_(random_order == current_random, Post.id < cursor), ) ) From 841a370e32fb41d0ccfff565ae3e2e7a4488ad94 Mon Sep 17 00:00:00 2001 From: sehooh5 Date: Wed, 11 Feb 2026 14:58:10 +0900 Subject: [PATCH 2/2] using cursor not cursor_id --- app/grad_2025/service.py | 9 ++------- app/posts/repository.py | 7 ++++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/grad_2025/service.py b/app/grad_2025/service.py index 99669fc..1196b28 100644 --- a/app/grad_2025/service.py +++ b/app/grad_2025/service.py @@ -30,16 +30,11 @@ async def search_posts( categories: list[Category] | None = None, univ_majors: list[str] | None = None, ) -> Page[PostCompactRead]: - if cursor and cursor.seed: - seed = cursor.seed - cursor_id = cursor.id - else: - seed = str(uuid.uuid4()) - cursor_id = None + seed = cursor.seed if cursor and cursor.seed else str(uuid.uuid4()) posts = await self.post_repository.find_grad_2025_posts( seed=seed, - cursor=cursor_id, + cursor=cursor, limit=limit + 1, categories=categories, univ_majors=univ_majors, diff --git a/app/posts/repository.py b/app/posts/repository.py index 6a9850e..9c550da 100644 --- a/app/posts/repository.py +++ b/app/posts/repository.py @@ -8,6 +8,7 @@ from app.common.schemas import FeedCursor from app.database.deps import SessionDep from app.grad_2025.models import Grad2025 +from app.grad_2025.schemas import Grad2025Cursor from app.users.models import User from app.utils.dependency import dependency @@ -297,7 +298,7 @@ async def find_grad_2025_posts( self, *, seed: str, - cursor: int | None, + cursor: Grad2025Cursor | None, limit: int, categories: list[Category] | None = None, univ_majors: list[str] | None = None, @@ -320,11 +321,11 @@ async def find_grad_2025_posts( ) if cursor is not None: - current_random = func.md5(func.concat(seed, str(cursor))) + current_random = func.md5(func.concat(seed, str(cursor.id))) stmt = stmt.where( or_( random_order > current_random, - and_(random_order == current_random, Post.id < cursor), + and_(random_order == current_random, Post.id < cursor.id), ) )