Skip to content

Redis GEO (유저 위치 저장 및 검색)

나경호(Na Gyeongho) edited this page May 27, 2025 · 2 revisions

관련 문서

근처에 있는 사용자 조회 기능: Redis Geospatial가 적합한 선택일까?


Redis geo

Redis Geo는 Redis 3.2 버전부터 추가된 지리 공간 데이터를 처리하기 위한 특수 기능입니다. 이 기능을 사용하면 위도와 경도 좌표를 저장하고, 지리적 거리 계산, 반경 내 요소 검색 등의 작업을 매우 효율적으로 수행할 수 있습니다.

Redis Geo 주요 명령어

  1. GEOADD: 지리 공간 데이터 저장

    GEOADD key longitude latitude member [longitude latitude member ...]
    

    예: GEOADD locations 126.9780 37.5665 "seoul"

  2. GEODIST: 두 위치 사이의 거리 계산

    GEODIST key member1 member2 [unit]
    

    예: GEODIST locations seoul busan km

  3. GEORADIUS: 특정 좌표 기준 반경 내 멤버 검색

    GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
    

    예: GEORADIUS locations 126.9780 37.5665 10 km

  4. GEORADIUSBYMEMBER: 저장된 멤버 기준 반경 내 검색

    GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
    

    예: GEORADIUSBYMEMBER locations seoul 20 km

  5. GEOHASH: 위치의 geohash 문자열 반환

    GEOHASH key member [member ...]
    

    예: GEOHASH locations seoul

작동 방식

Redis Geo는 내부적으로 정렬 집합(sorted set) 자료구조를 사용하며, 위치 정보를 geohash로 변환하여 저장합니다.

Geohash는 위도와 경도를 결합한 문자열로, 비슷한 위치는 비슷한 해시 값을 가지는 특성이 있습니다. 이를 통해 Redis는 매우 빠른 지리 공간 검색을 제공할 수 있습니다.

사용 예시 (Java)

// Spring Data Redis 사용 예시
@Service
public class LocationService {

    private final RedisTemplate<String, String> redisTemplate;

    public LocationService(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 사용자 위치 저장
    public void saveUserLocation(String userId, double longitude, double latitude) {
        redisTemplate.opsForGeo().add("user:locations",
            new Point(longitude, latitude), userId);
    }

    // 반경 내 사용자 검색
    public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> findNearbyUsers(
            double longitude, double latitude, double radiusInKm) {

        Distance distance = new Distance(radiusInKm, Metrics.KILOMETERS);
        Circle circle = new Circle(new Point(longitude, latitude), distance);

        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
            .newGeoRadiusArgs()
            .includeDistance()
            .sortAscending();

        GeoResults<RedisGeoCommands.GeoLocation<String>> results =
            redisTemplate.opsForGeo().radius("user:locations", circle, args);

        return results.getContent();
    }
}

Redis Geo 기능은 위치 기반 서비스, 지리적 근접 검색, 위치 추적 등의 기능을 구현할 때 매우 유용합니다. 메모리 기반 데이터베이스인 Redis의 특성상 매우 빠른 응답 시간을 제공하며, 대규모 위치 데이터를 효율적으로 처리할 수 있습니다.

API 엔드포인트 구현

  1. 위치 업데이트 API:
POST /api/location/update
Request: {
  latitude: float,
  longitude: float,
  accuracy: float (optional)
}

  1. 근처 사용자 조회 API:
GET /api/users/nearby
Parameters:
  - radius: int (미터 단위, 기본값 500m)
  - limit: int (최대 반환 수, 기본값 50)

백엔드 처리 로직

  1. 위치 업데이트 시:

    // 1. Redis에 위치 정보 저장
    redisTemplate.opsForGeo().add(
      "user:locations",
      new Point(longitude, latitude),
      userId.toString()
    );
    redisTemplate.expire("user:locations", 24, TimeUnit.HOURS);
    
    // 2. 일정 시간마다 또는 조건에 따라 PostgreSQL에도 저장
    userLocationRepository.save(new UserLocation(userId, point, now()));
  2. 근처 사용자 조회 시:

    // Redis에서 반경 내 사용자 검색
    GeoResults<RedisGeoCommands.GeoLocation<String>> results =
      redisTemplate.opsForGeo().radius(
        "user:locations",
        new Circle(
          new Point(longitude, latitude),
          new Distance(radius, Metrics.METERS)
        )
      );
    
    // 결과 필터링 및 사용자 정보 조회
    List<String> nearbyUserIds = results.getContent().stream()
      .map(result -> result.getContent().getName())
      .filter(id -> !id.equals(currentUserId))
      .collect(Collectors.toList());
    
    return userRepository.findAllByIdIn(nearbyUserIds);

성능 최적화 방안

  1. Redis 캐싱 전략:
    • 자주 접근하는 지역에 대한 결과를 추가로 캐싱
    • 키 형식: nearby:{geohash}:radius:{r}
  2. 위치 업데이트 최적화:
    • 의미 있는 위치 변화가 있을 때만 업데이트 (예: 50m 이상 이동)
    • 백그라운드 작업으로 DB 동기화
  3. 부하 분산:
    • 지역별 샤딩 (geohash 기반)
    • 읽기 전용 복제본으로 조회 부하 분산
Clone this wiki locally