NestJS API 무한스크롤(offset)

장유림·2025년 4월 6일

jz_project

목록 보기
4/7

ChatGPT와의 대화

무한 스크롤이란?

사용자가 스크롤을 아래로 내릴 때마다 자동으로 다음 데이터를 불러오는 방식이에요.
페이스북, 인스타그램, 트위터 피드처럼 한 번에 전체 데이터를 불러오는 대신, 조금씩 끊어서 불러오는 구조예요.

Offset 기반 무한 스크롤

클라이언트가 보내는 요청 예시

GET /posts/liked?userId=1&offset=0&limit=5
  • offset=0 몇 번째(0 번째) 데이터부터 가져올지
  • limit=5 몇 개(5 개) 가져올지

EX.

GET /posts/liked?userId=1&offset=5&limit=5

백엔드에서의 처리

async getPostsLiked(userId, sort, offset = 0, limit = 5) {
  // 1. 유저 존재 확인
  const user = await this.usersRepository.findOne({ where: { id: userId } });
  if (!user) throw new Error('사용자를 찾을 수 없습니다.');

  // 2. 정렬 방식 설정
  let orderBy: string;
  switch (sort) {
    case 'oldest': orderBy = 'p.postDatetime ASC'; break;
    case 'newest': orderBy = 'p.postDatetime DESC'; break;
    case 'most_liked':
    case 'least_liked':
      orderBy = 'p.postDatetime DESC'; // 좋아요 기준 정렬은 나중에 JS에서 처리
      break;
    default: orderBy = 'p.postDatetime DESC';
  }

  // 3. 사용자가 좋아요한 post의 ID만 먼저 가져오기
  const postIdRows = await this.dataSource.query(`
    SELECT p.id AS postId
    FROM post p
    INNER JOIN likes l ON l.post_id = p.id
    WHERE l.user_id = ?
    ORDER BY ${orderBy}
    LIMIT ? OFFSET ?
  `, [userId, limit, offset]);

  const postIds = postIdRows.map((row) => row.postId);

  // 4. 각 post의 상세 정보 + 좋아요 수 조회
  const postInfos = await this.getPostInfos(postIds);

  // 5. 좋아요 수 기준 정렬은 JS에서 처리
  if (sort === 'most_liked') {
    postInfos.sort((a, b) => b.likeCount - a.likeCount);
  } else if (sort === 'least_liked') {
    postInfos.sort((a, b) => a.likeCount - b.likeCount);
  }

  // 6. 클라이언트에 응답할 데이터
  return {
    'like-list': postInfos,
    nextOffset: offset + postInfos.length, // 다음 요청할 offset
    hasMore: postInfos.length === limit,   // 더 불러올 게 있는지 여부
  };
}
  • 컨트롤러에서
@Get('liked/:userId')
async getLikedPosts(
  @Request() req: { user: {sub: number} },
  @Query('sort') sort?: 'oldest' | 'newest' | 'most_liked' | 'least_liked',
  @Query('offset') offset?: string,
  @Query('limit') limit?: string
) {
  return this.postsService.getPostsLiked(
    userId,
    sort,
    Number(offset ?? 0), // 기본값 0
    Number(limit ?? 5)   // 기본값 5
  );
}

✅ Number(offset ?? 0)

  • 쿼리 파라미터에서 받은 offset은 보통 문자열(string | undefined)이므로 숫자로 변환해야 합니다.

  • ? 0: 값이 null이나 undefined이면 기본값 0을 사용하겠다는 뜻입니다.

예: offset=10 → '10' → Number('10') → 10

예: offset 없음 → undefined ?? 0 → 0

✅ Number(limit ?? 5)

  • limit도 마찬가지로 없으면 기본값 5 사용합니다.

  • 무한 스크롤에서 한 번에 불러올 항목 수

클라이언트에서 실제 요청 흐름

  1. 첫 요청
GET /posts/liked/1?sort=newest&offset=0&limit=5
  1. 응답:
{
  "like-list": [...5개 데이터...],
  "nextOffset": 5,
  "hasMore": true
}
  1. 다음 요청:
GET /posts/liked/1?sort=newest&offset=5&limit=5
  1. 응답:
  • hasMore: false면 마지막 페이지이므로 더 이상 요청 안 함.

요약

항목설명
limit한 번에 몇 개씩 불러올지
offset몇 번째부터 가져올지(이전까지 본 항목 수)
hasMore아직 불러올 게 있는지
nextOffset다음에 요청할 offset 값
  • JS에서 sort(): likeCount 기준 정렬은 SQL 대신 자바스크립트로 처리
profile
환영합니다

0개의 댓글