랜덤 레코드 가져오기
고민이 많았던 부분이다. 미니프로젝트 진행 중 테이블 전체 중 하나의 랜덤 column만 가져와야하는 요구사항이 있어서 어떻게 가져와야할지 생각을 많이 했다.
요구사항은 다음과 같았다.
1. 랜덤한 게시글 1개를 사용자에게 보내준다.
2. 이렇게 보고 난 글은 랜덤조회 방법으로 다시는 볼 수 없다.
사실 내가 만든 요구사항이었다. 되는지 안 되는지도 모르고 막 질러놨더니 그 업보가 나에게 돌아왔다. 사람은 말을 아끼고 살아야 한다.
하지만 내뱉은 말은 주워담을 수 없는 법. 그래서 했다.
처음에 생각했던 방법은 다음과 같았다.
1. 이미 읽은 게시글을 제외한 게시글을 대상으로 findAll() 한다.
2. 1.의 결과물 중 랜덤한 하나의 레코드만 가져온다.
3. 그 게시글을 읽음 처리한다.
이렇게 하면 무조건 된다. 심플한 방법이다. 동시에 쓰면 안 되는 방법이다. 단 하나의 레코드를 가져오기 위해 DB를 Full-Scan 하는 것은 DB에 큰 부하를 주는 일이기 때문이다. 그래서 다른 방법을 고민했다. 하지만 답은 쉽게 나오지 않았다.
생각해보면 이런 기능은 누군가가 분명 구현을 했을 것이다. 인터넷에 없는게 말이 안 된다. 그래서 인터넷을 찾아봤고, 해답을 구했다. 같은 고민을 공유한 선배개발자일지도 모르는 누군가에게 동질감과 더불어 감사의 뜻을 전하고 싶다.
게시글이 사라질지도 모르니 한 마디로 내용을 요약하자면, 페이징 기법을 응용한 방법이다.
레코드를 하나의 페이지당 1개의 게시글을 가지도록 분할하고, 그 중 랜덤한 하나의 페이지만 골라오는 것이다.
페이징이 무엇인지 잘 모르겠다면 아래 게시글을 참고하자.
코드로 구현하면 다음과 같다.
// 조건에 맞는 게시글의 개수를 가져온다.
long qty = postRepository.countByUserNotAndPostIdNotIn(user, readingPostIdList);
// 가져온 개수 중 랜덤한 하나의 인덱스를 뽑는다.
int idx = (int)(Math.random() * qty);
// 페이징하여 하나만 추출해낸다.
Page<Post> postPage = postRepository
.findAllByUserNotAndPostIdNotIn(
user,
readingPostIdList,
PageRequest.of(idx, 1)
);
if (postPage.hasContent()) {
Post post = postPage.getContent().get(0);
}
복잡한 코드도 아니면서, DB에 큰 데이터 덩어리를 배달하도록 요청하는 작업을 시키지 않아도 되도록 잘 만들어진 코드였다. 고맙다. 스택오버플로우!
'내가 배운 것들 > 문제 해결' 카테고리의 다른 글
[SpringBoot] DTO 형식으로 반환해줄 때, NULL값은 숨긴 채로 되돌려주기 (0) | 2021.12.23 |
---|---|
[RegExp] 아이디, 닉네임, 비밀번호 정규식 (1) | 2021.12.23 |
[QueryDSL/MySQL] QueryDSL로 MySQL에서 랜덤한 레코드 가져오기 (0) | 2021.12.22 |
[QueryDSL] Handler dispatch failed; nested exception is java.lang.NoSuchFieldError: TREATED_PATH (0) | 2021.12.22 |
[SpringBoot] Swagger v2를 Jwt 인증 환경에서 사용할 수 있도록 구성하기 (0) | 2021.12.11 |
댓글