From 6f568558d4441e0af9e4b8a309733d88bbf08511 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Sun, 3 May 2026 21:04:08 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=8F=99=EB=84=A4=20=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=20=EC=B6=94=EC=B2=9C=20=EC=84=B1=EB=8A=A5=20=EA=B0=9C=EC=84=A0?= =?UTF-8?q?=20-=20=EB=B0=94=EC=9A=B4=EB=94=A9=20=EB=B0=95=EC=8A=A4=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EB=B0=8F=20=EC=9C=84=EC=B9=98=20=EC=9D=B8?= =?UTF-8?q?=EB=8D=B1=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - findAllWithLocation() 전체 조회 대신 바운딩 박스(BETWEEN) 쿼리로 DB 레벨 필터링 - Member 엔티티에 (latitude, longitude) 복합 인덱스 추가 - PriorityQueue 제거 후 stream sorted + limit(10) 으로 단순화 Co-Authored-By: Claude Sonnet 4.6 --- .../co/readingtown/member/domain/Member.java | 5 +++- .../member/repository/MemberRepository.java | 18 +++++++++-- .../member/service/RecommendationService.java | 30 ++++++++++++++----- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/member/src/main/java/kr/co/readingtown/member/domain/Member.java b/member/src/main/java/kr/co/readingtown/member/domain/Member.java index 20b8bfa..7bcf634 100644 --- a/member/src/main/java/kr/co/readingtown/member/domain/Member.java +++ b/member/src/main/java/kr/co/readingtown/member/domain/Member.java @@ -11,7 +11,10 @@ @Entity @Getter -@Table(name = "members") +@Table( + name = "members", + indexes = @Index(name = "idx_member_location", columnList = "latitude, longitude") +) @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Member extends BaseEntity { diff --git a/member/src/main/java/kr/co/readingtown/member/repository/MemberRepository.java b/member/src/main/java/kr/co/readingtown/member/repository/MemberRepository.java index 42ac365..7a66de0 100644 --- a/member/src/main/java/kr/co/readingtown/member/repository/MemberRepository.java +++ b/member/src/main/java/kr/co/readingtown/member/repository/MemberRepository.java @@ -7,8 +7,7 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; - +import java.math.BigDecimal; import java.util.List; public interface MemberRepository extends JpaRepository { @@ -37,9 +36,22 @@ public interface MemberRepository extends JpaRepository { @Query(""" SELECT m FROM Member m - WHERE m.latitude IS NOT NULL + WHERE m.latitude IS NOT NULL AND m.longitude IS NOT NULL AND m.isOnboarded = true """) List findAllWithLocation(); + + @Query(""" + SELECT m + FROM Member m + WHERE m.latitude BETWEEN :minLat AND :maxLat + AND m.longitude BETWEEN :minLon AND :maxLon + """) + List findMembersInBoundingBox( + @Param("minLat") BigDecimal minLat, + @Param("maxLat") BigDecimal maxLat, + @Param("minLon") BigDecimal minLon, + @Param("maxLon") BigDecimal maxLon + ); } diff --git a/member/src/main/java/kr/co/readingtown/member/service/RecommendationService.java b/member/src/main/java/kr/co/readingtown/member/service/RecommendationService.java index f6a3756..964a8a9 100644 --- a/member/src/main/java/kr/co/readingtown/member/service/RecommendationService.java +++ b/member/src/main/java/kr/co/readingtown/member/service/RecommendationService.java @@ -119,20 +119,32 @@ public List recommendBooks(Long memberId) { public List recommendLocalMembers(Long memberId) { Member currentMember = memberRepository.findById(memberId) .orElseThrow(MemberException.NotFoundMember::new); - + if (currentMember.getLatitude() == null || currentMember.getLongitude() == null) { return List.of(); } - - List allMembers = memberRepository.findAllWithLocation(); - - return allMembers.stream() + + // 바운딩 박스 계산 (반경 1km) + final double RADIUS_KM = 1.0; + final double LAT_DEGREE_PER_KM = 1.0 / 110.574; + double currentLat = currentMember.getLatitude().doubleValue(); + double currentLon = currentMember.getLongitude().doubleValue(); + double latDelta = RADIUS_KM * LAT_DEGREE_PER_KM; + double lonDelta = RADIUS_KM / (111.320 * Math.cos(Math.toRadians(currentLat))); + + BigDecimal minLat = BigDecimal.valueOf(currentLat - latDelta); + BigDecimal maxLat = BigDecimal.valueOf(currentLat + latDelta); + BigDecimal minLon = BigDecimal.valueOf(currentLon - lonDelta); + BigDecimal maxLon = BigDecimal.valueOf(currentLon + lonDelta); + + List allMembers = memberRepository.findMembersInBoundingBox(minLat, maxLat, minLon, maxLon); + + List result = allMembers.stream() .filter(member -> !member.getMemberId().equals(memberId)) - .filter(member -> member.getLatitude() != null && member.getLongitude() != null) .map(member -> { double distance = calculateDistance( - currentMember.getLatitude().doubleValue(), - currentMember.getLongitude().doubleValue(), + currentLat, + currentLon, member.getLatitude().doubleValue(), member.getLongitude().doubleValue() ); @@ -141,6 +153,8 @@ public List recommendLocalMembers(Long memberId) { .sorted(Comparator.comparing(LocalMemberRecommendationDto::distanceKm)) .limit(10) .collect(Collectors.toList()); + + return result; } /**