우테코 5기

[하루스터디] 어드민 페이지 개발하기 - 기본적인 통계 기능 구현

teo_99 2023. 10. 1. 22:09

현재는 데이터 조회를 위해 매번 데이터베이스에 접근해야 하는 구조입니다. 또한 데이터베이스는 인바운드 네트워크 설정으로 인해서 우테코 캠퍼스에서만 접근이 가능한 상태입니다. 따라서 어드민 페이지에서 데이터 조회를 지원한다면 어디에서든지 쉽게 데이터를 확인할 수 있을 것입니다.

 

Spring Data의 Pageable 사용하기

Spring Data의 Pageable을 활용하면 페이징 기반의 조회를 손쉽게 구성할 수 있습니다. 

@GetMapping("/members")
public ResponseEntity<List<AdminMemberResponse>> findMembers(Pageable pageable) {
    List<AdminMemberResponse> members = adminService.findMembers(pageable);
    return ResponseEntity.ok(members);
}

컨트롤러에서는 위처럼 Pageable 인터페이스를 파라미터로 선언합니다. 파라미터로 Pageable을 선언하는 경우에는 아래와 같이 페이지 정보, 페이지 크기, 정렬 조건등을 쿼리 파라미터로 담아 요청을 보냈을 때에 대한 처리가 가능해집니다.

GET /members?page=1&size=10&sort=name,desc

 

Spring Data JPA를 사용하는 경우에는 위 Pageable 인터페이스를 그대로 JpaRepository의 인자로 사용할 수 있습니다. JpaRepository가 인터페이스 상속을 하고 있는 PagingAndSortingRepository에 접근해보면, 페이징 기반의 findAll 메소드를 제공하고 있는 것을 확인할 수 있습니다. 

현재는 조회 조건이 필요하지 않으므로 Pageable을 인자로 받는 findAll 메소드를 그대로 사용해서 서비스를 구현했습니다.

// AdminService.java
public List<AdminStudyResponse> findStudies(Pageable pageable) {
    return studyRepository.findAll(pageable)
            .map(AdminStudyResponse::from)
            .toList();
}

 

 

오늘 개설된 스터디 수 & 완료된 스터디 수 구하기

기본적인 페이징 기반의 조회는 구현했으므로 보다 세부적인 통계를 제공해보도록 하겠습니다. 사실 지금부터 구현하는 기능들이 비즈니스적으로 의미가 있는 정보들이기 때문에 핵심이라고 볼 수 있겠습니다. 

 

오늘 개설된 스터디 수가 얼마나 되는지, 그리고 완료된 스터디 수는 얼마나 되는지를 제공하는 기능을 구현해보도록 하겠습니다. 

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

현재 하루스터디의 경우 모든 엔티티에 대해 BaseTimeEntity를 상속받게 하여 createdDate, lastModifiedDate을 동시에 저장하고 있습니다. 그러므로 해당 필드들을 이용하면 오늘 개설된 스터디 수, 완료된 스터디 수를 쉽게 구할 수 있을 것 같습니다.

 

오늘 개설된 스터디를 구하려면 엔티티가 생성된 시각이 자정 이후인 상태를 가지는 경우만 조회하면 됩니다. 오늘 완료된 스터디의 경우에는 스터디가 엔티티가 마지막으로 수정된 시각이 자정 이후이면서 Step이 DONE인 상태를 가지는 경우를 조회하면 됩니다. 

 

사실 lastModifiedDate와 Step 필드로 '오늘 스터디가 종료되었는지' 판단하는 것은 논란의 여지가 있다고 생각합니다. Study 엔티티가 수정되었다고 해서 그것이 Step 필드라는 보장은 없기 때문입니다.

 

하지만 현재 하루스터디는 비즈니스적으로 보았을 때, Study의 상태는 Step만 변경이 되며 다른 필드들은 한 번 생성이 되고 나면 변경이 이루어지지 않습니다. 

 

또한, lastModifiedDate를 사용하지 않고 '오늘 종료된 스터디'를 구하고자 한다면 추가적인 컬럼을 부여하거나 다른 추가 테이블을 만들어야 하는데 어드민 개발 초기에 이런 리소스를 투자하는 것은 불필요하다고 판단했습니다.

 

구현 자체는 Spring Data JPA의 쿼리 메소드로 조회 조건을 지정해주고 서비스에서 자정 시간을 계산해 넣어줌으로써 쉽게 완료할 수 있었습니다.

// StudyRepository.java
Page<Study> findAllByCreatedDateBetween(Pageable pageable, LocalDateTime before, LocalDateTime after);

Page<Study> findAllByLastModifiedDateBetweenAndStepIs(Pageable pageable, LocalDateTime before,
                                                      LocalDateTime after, Step step);
// AdminService.java
public List<AdminStudyResponse> findStudiesCreatedToday(Pageable pageable) {
    LocalDateTime midnightTime = findMidnightDateTime();

    return studyRepository.findAllByCreatedDateBetween(pageable, midnightTime, LocalDateTime.now())
            .map(AdminStudyResponse::from)
            .toList();
}

private LocalDateTime findMidnightDateTime() {
    return LocalDateTime.now(ZoneId.of("Asia/Seoul"))
            .withHour(0)
            .withMinute(0)
            .withSecond(0)
            .withNano(0);
}

마치며

페이지네이션을 처음 사용해보았지만 Spring Data JPA의 지원 덕에 간단하게 오늘 개설된 / 완료된 스터디 목록을 구할 수 있었습니다. 다음으로는 조금 더 복잡한 통계 정보를 다뤄보도록 하겠습니다.