Spring Boot

효율적인 데이터 로딩: Spring Boot과 MyBatis로 구현하는 페이징 처리

청춘고양이 2024. 6. 21. 16:16
반응형

1. 서론

페이징 처리란? 페이징 처리는 많은 양의 데이터를 한 번에 모두 가져오는 대신, 일부만 가져와서 화면에 보여주는 기법입니다. 이를 통해 성능을 개선하고 사용자의 경험을 향상시킬 수 있습니다.

왜 페이징 처리가 필요한가? 대량의 데이터를 한 번에 처리하면 메모리와 성능에 큰 부담을 주게 됩니다. 페이징 처리를 통해 필요한 데이터만 가져와서 처리함으로써 이러한 문제를 해결할 수 있습니다.

2. 페이징 처리의 기본 개념

페이징의 기본 원리

  • 데이터를 일정한 크기로 나누어 각 페이지에 해당하는 부분만 가져오는 방식입니다.
  • 클라이언트에서 요청하는 페이지 번호와 한 페이지당 항목 수를 기준으로 데이터베이스에서 필요한 부분만 쿼리합니다.

주요 용어

  • 페이지(Page): 데이터를 나누는 단위입니다.
  • 오프셋(Offset): 현재 페이지의 시작 지점을 나타냅니다.
  • 한 페이지당 항목 수: 한 페이지에 표시할 데이터의 개수입니다.

3. 페이징 처리 구현 예제

Spring Boot와 MyBatis를 이용한 페이징 처리

4. 코드 예제

Controller

@RestController
@RequestMapping("/api/qna")
public class QnaController {

    private final QnaService qnaService;

    @Autowired
    public QnaController(QnaService qnaService) {
        this.qnaService = qnaService;
    }

    @GetMapping("/")
    public ModelAndView getIndex(
            @RequestParam(value = "tab", required = false, defaultValue = "help") String tab,
            @RequestParam(value = "page", required = false, defaultValue = "1") int page) {
        ModelAndView modelAndView = new ModelAndView("qna/index");

        int totalArticles = qnaService.getArticleTotalCount(tab);
        PagingModel pagingModel = new PagingModel(totalArticles, page);
        
        List<QnaArticleEntity> articles = qnaService.getArticlesByTab(tab, pagingModel);
        
        modelAndView.addObject("articles", articles);
        modelAndView.addObject("pagingModel", pagingModel);
        
        return modelAndView;
    }
}

Model (Model로 PagingModel로 만들어 사용하는 방식이 효율적입니다.)

public class PagingModel {
    // 기본적으로 한 페이지에 표시할 행(row)의 개수
    public static final int DEFAULT_ROW_COUNT_PER_PAGE = 20;

    // 전체 행(row)의 개수 (총 게시글의 개수)
    private final int totalRowCount;
    // 요청된 페이지 번호
    private final int requestPage;
    // 한 페이지에 표시할 행(row)의 개수
    private final int rowCountPerPage;

    /**
     * 생성자: 전체 행(row)의 개수와 요청된 페이지 번호를 받아서 PagingModel 객체를 생성한다.
     * 
     * @param totalRowCount 전체 행(row)의 개수
     * @param requestPage   요청된 페이지 번호
     */
    public PagingModel(int totalRowCount, int requestPage) {
        // 전체 행(row)의 개수를 설정
        this.totalRowCount = totalRowCount;
        // 요청된 페이지 번호가 1보다 작으면 1로 설정
        this.requestPage = requestPage < 1 ? 1 : requestPage;
        // 기본 행(row) 개수를 설정
        this.rowCountPerPage = DEFAULT_ROW_COUNT_PER_PAGE;
    }

    /**
     * @return 현재 페이지에서의 시작 인덱스 (0부터 시작)
     */
    public int getStartIndex() {
        return (requestPage - 1) * rowCountPerPage;
    }

    /**
     * @return 한 페이지에 표시할 행(row)의 개수
     */
    public int getRowCountPerPage() {
        return rowCountPerPage;
    }
}

Service

@Service
public class QnaService {

    private final QnaMapper qnaMapper;

    @Autowired
    public QnaService(QnaMapper qnaMapper) {
        this.qnaMapper = qnaMapper;
    }

    public int getArticleTotalCount(String tab) {
        return qnaMapper.countArticlesByTab(tab);
    }

    public List<QnaArticleEntity> getArticlesByTab(String tab, PagingModel pagingModel) {
        return qnaMapper.selectArticlesByTab(tab, pagingModel.getStartIndex(), pagingModel.getRowCountPerPage());
    }
}

Mapper

@Mapper
public interface QnaMapper {

    @Select("SELECT COUNT(*) FROM qna_articles WHERE category = #{tab}")
    int countArticlesByTab(@Param("tab") String tab);

    @Select("SELECT * FROM qna_articles WHERE category = #{tab} LIMIT #{count} OFFSET #{offset}")
    List<QnaArticleEntity> selectArticlesByTab(@Param("tab") String tab, @Param("offset") int offset, @Param("count") int count);
}

SQL 쿼리

SELECT COUNT(*) FROM qna_articles WHERE category = #{tab};
SELECT * FROM qna_articles WHERE category = #{tab} LIMIT #{count} OFFSET #{offset};

5. 결론

페이징 처리의 이점

  • 성능 향상: 필요한 데이터만 가져와서 처리하기 때문에 성능이 크게 향상됩니다.
  • 사용자 경험 개선: 빠른 응답 시간과 필요한 정보만 보여줌으로써 사용자 경험이 향상됩니다.

페이징 처리 시 주의사항

  • 페이지 번호와 같은 입력 값은 항상 유효성을 검사해야 합니다.
  • 페이지를 이동할 때마다 동일한 쿼리를 반복적으로 실행하지 않도록 캐싱 등을 고려할 수 있습니다.
반응형