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; } /**