페이징(Paging)은 대량의 데이터를 한 번에 모두 가져오지 않고, 일정한 단위(페이지)로 나누어 나누어 처리하는 기법입니다. 이를 통해 성능을 개선하고, 메모리 사용을 최적화하며, 사용자에게 보다 나은 경험을 제공합니다. 예를 들어, 데이터베이스에 1000개의 데이터가 있다면, 이를 한 번에 모두 가져오지 않고, 한 페이지에 10개씩 나누어 보여주는 것이 페이징입니다.
페이징의 주요 개념
- 페이지 번호 (Page Number):
- 데이터를 나누었을 때, 각 묶음을 식별하는 번호입니다. 보통 1페이지, 2페이지, 3페이지 등으로 표현되며, 사용자가 원하는 페이지를 선택할 수 있습니다.
- 페이지 크기 (Page Size):
- 한 페이지에 표시할 데이터의 개수입니다. 예를 들어, 페이지 크기가 10이라면, 한 페이지에 10개의 데이터가 표시됩니다.
- 오프셋 (Offset):
- 데이터의 시작점을 의미합니다. 즉, 몇 번째 데이터부터 조회할지 결정하는 값입니다. 오프셋은
(pageNumber - 1) * pageSize
로 계산됩니다.
- 데이터의 시작점을 의미합니다. 즉, 몇 번째 데이터부터 조회할지 결정하는 값입니다. 오프셋은
- 리미트 (Limit):
- 한 번에 조회할 데이터의 개수를 의미합니다. 보통 페이지 크기와 같은 값으로 설정됩니다.
페이징의 필요성
- 성능 최적화:
- 대량의 데이터를 한 번에 모두 가져오면, 처리 속도가 느려지고 시스템에 부하가 발생할 수 있습니다. 페이징을 통해 필요한 데이터만 부분적으로 가져옴으로써 성능을 향상시킬 수 있습니다.
- 메모리 절약:
- 모든 데이터를 한 번에 메모리에 적재하지 않고, 필요한 만큼씩 가져오기 때문에 메모리 사용을 줄일 수 있습니다.
- 사용자 경험 개선:
- 웹 애플리케이션이나 앱에서는 한 번에 많은 데이터를 보여주는 것이 아니라, 스크롤을 내리거나 페이지를 이동하면서 데이터를 추가로 불러오는 방식이 사용자에게 더 친숙합니다.
페이징의 동작 방식
페이징은 주로 데이터베이스에서 이루어지며, SQL이나 JPQL, QueryDSL과 같은 쿼리 언어를 사용하여 구현됩니다.
페이징의 예
예를 들어, 100명의 사용자가 있는 데이터베이스에서 10명씩 페이징 처리를 한다고 가정하면:
- 1페이지: 1번 사용자부터 10번 사용자까지 조회
- 2페이지: 11번 사용자부터 20번 사용자까지 조회
- 3페이지: 21번 사용자부터 30번 사용자까지 조회
JPQL로 구현한 페이징 예시
public List<Member> getMembers(int pageNumber, int pageSize) {
EntityManager em = emf.createEntityManager();
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m ORDER BY m.name ASC", Member.class);
// 페이징 처리
query.setFirstResult((pageNumber - 1) * pageSize); // 시작 위치 설정
query.setMaxResults(pageSize); // 한 페이지에 보여줄 최대 결과 수 설정
return query.getResultList();
}
이 코드에서:
setFirstResult()
는 조회할 데이터의 시작 위치(오프셋)를 설정합니다.setMaxResults()
는 한 페이지에 표시할 데이터의 개수를 설정합니다.
QueryDSL을 사용한 페이징 예시
public List<Member> getMembersByQueryDSL(int pageNumber, int pageSize) {
EntityManager em = emf.createEntityManager();
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember member = QMember.member;
return queryFactory.selectFrom(member)
.orderBy(member.name.asc())
.offset((pageNumber - 1) * pageSize) // 오프셋 설정
.limit(pageSize) // 페이지 크기 설정
.fetch();
}
결론
- 페이징은 대량의 데이터를 페이지 단위로 나누어 처리하는 방식으로, 성능 최적화와 메모리 관리, 사용자 경험 개선을 목적으로 사용됩니다.
- 페이징을 구현하는 방법으로는 SQL, JPQL, QueryDSL 등 다양한 쿼리 언어를 사용할 수 있으며, 이를 통해 데이터를 효율적으로 조회하고 관리할 수 있습니다.
@EntityGraph
는 JPA(Java Persistence API)에서 N+1 문제를 해결하거나, 지연 로딩(Lazy Loading)을 제어하기 위해 엔티티 그래프(Entity Graph)를 사용하는 방법입니다. 이를 통해 JPQL이나 기본 쿼리로 엔티티를 조회할 때 연관된 엔티티들을 함께 조회하도록 설정할 수 있습니다.
N+1 문제
- N+1 문제란, 한 번의 쿼리로 데이터 N개를 가져온 후, 그와 관련된 데이터를 다시 N번 각각 조회하는 방식으로 추가 쿼리가 발생하는 문제입니다. 이 문제는 성능에 큰 영향을 미치기 때문에, 이를 해결하기 위해
@EntityGraph
를 사용할 수 있습니다.
@EntityGraph 개념
- 엔티티 그래프(Entity Graph)는 프록시로 인해 발생하는 지연 로딩(Lazy Loading)을 피하고, 원하는 엔티티와 연관된 엔티티를 즉시 로딩(Eager Loading)으로 가져오도록 설정하는 방법입니다.
- 이를 통해 연관된 엔티티들을 명시적으로 함께 조회할 수 있습니다.
@EntityGraph 사용 예시
기본 사용법
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Team team;
// getter, setter
}
Member
엔티티는 Team
과 ManyToOne
관계로 맺어져 있으며, 기본적으로 FetchType.LAZY
를 사용하여 지연 로딩을 합니다.
@EntityGraph 적용 예시
public interface MemberRepository extends JpaRepository<Member, Long> {
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
}
- 위 코드에서
@EntityGraph(attributePaths = {"team"})
을 사용하여Member
와 연관된Team
엔티티를 함께 조회합니다. 이를 통해 지연 로딩이 아닌 즉시 로딩으로 데이터를 가져옵니다. - 즉,
Member
를 조회할 때 N+1 문제를 방지하고 한 번의 쿼리로Team
엔티티도 함께 조회하게 됩니다.
@EntityGraph 속성
attributePaths
: 함께 조회할 연관된 엔티티의 경로를 명시합니다. 위 예시에서는team
엔티티를 함께 조회하도록 설정했습니다.
JPQL과 함께 사용
@EntityGraph
는 JPQL 쿼리에도 적용할 수 있습니다. 예를 들어, JPQL 쿼리에서 @EntityGraph
를 사용하여 특정 연관 엔티티를 즉시 로딩하도록 할 수 있습니다.
@Query("SELECT m FROM Member m")
@EntityGraph(attributePaths = {"team"})
List<Member> findMembersWithTeams();
이렇게 하면, Member
와 Team
이 한 번의 쿼리로 함께 조회됩니다.
결론
@EntityGraph
는 JPA에서 지연 로딩으로 인해 발생하는 N+1 문제를 해결하고, 필요한 연관된 엔티티를 즉시 로딩할 수 있도록 돕는 유용한 어노테이션입니다.- 이를 사용하면 성능 최적화에 도움이 되며, JPQL과 함께 사용할 수도 있습니다.
'Everyday Study' 카테고리의 다른 글
2024.09.25(수) { IP 어드레스, 서브넷 마스크, 대칭 암호화, MAC 어드레스 } (0) | 2024.09.25 |
---|---|
2024.09.24(화) { 오프셋 기반 페이징의 문제점 } (0) | 2024.09.24 |
2024.09.20 (금) { WHERE 절, HAVING 절, GROUP BY, ORDER BY } (0) | 2024.09.23 |
2024.09.19(목) { 컬렉션 값 타입, CROSS JOIN } (0) | 2024.09.19 |
2024.09.12(목) { 복합키 클래스( @IdClass, @EmbeddedId ) } (0) | 2024.09.12 |