사용자 위치 변경에 대한 기능 수정

2025. 3. 11. 17:17·개발일지

기존 로직은 사용자의 위치 전송 시 레디스에 저장된 값을 수정하는 식이었다.

이 방식은 반경 내 주변 사용자 조회 기능에서 주변 사용자가 많을 때 문제가 될 수 있는 로직이다.

    1. 먼저, 사용자 위치 변경 요청은 5분 마다 일어나는 폴링 방식인데 요청할 때마다 수정을 하면 DB 부하가 커진다.

    2. 주변 사용자 조회 기능에서 반경 내 모든 사용자의 카테고리별 랭킹 정보(종료한 최근 계획을 기준으로 할 일의 카테고리 별로 얼마나 많은 사용자가 했는지에 대한 정보, 예를 들어 사용자 1이 종료한 최근 계획에서 '독서' 카테고리에 대해서 할 일 2개를 했으면 '독서' 카테고리에 회원 수가 1이 증가됨)

 

이전 고찰에서 사용자의 위치 정보를 저장하는 위치를 Redis 에서 Mysql(RDB)로 바꾸었다. 바꾼 이유는 해당 포스트에서 확인할 수 있다.

그리고 2번을 위해서 집계 테이블을 추가했었다. 

 

위치 정보의 저장 위치

Mysql의 기존 테이블 Member 테이블에 Location 컬럼을 추가한다. 해당 컬럼의 타입은 Point 로 경위도 좌표를 저장한다.

 

CREATE TABLE `temp_member` (
  ...,
  `location` point /*!80003 SRID 4326 */ DEFAULT NULL,
  ...
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

 

사용자의 위치 변경

위치 데이터의 저장 위치를 Redis 에서 RDB 로 옮긴 후 5분마다 일어나는 사용자의 위치 변경에 대하여 DB 부담이 늘어났음을 인지하였다. Redis는 인 메모리DB 이고 단일 스레드의 이벤트 루프 방식(요청이 몰려도 이벤트 루프 내에서 순차적으로 처리) 으로 처리하기 때문에 크게 문제가 되지 않았지만 RDB는 아니다. RDB 는 디스크 I/O 를 통해 데이터를 처리하고 데이터 ACID를 보장하기 위해 트랜잭션을 관리해야 한다. 그리고 어플리케이션 레벨에서 DB 연결을 위한 제한된 커넥션 풀을 가지고 있기 때문에 5분 마다 폴링은 DB 성능에 문제를 일으킬 수 있다.

 

이에 대한 해결책은 해당 기능의 특징인 사용자의 정확한 위치는 중요하지 않는다에서 나온다. 해당 기능의 중요한 대상은 사용자의 위치가 아니고 사용자들이 수행한 Todo(할 일)이다. 

사용자의 위치 변경 요청 시에 기존 위치와 새로운 위치와의 GeoHash(7단계) 을 통해 같은 값을 가지면 수정하지 않고 다른 값을 가질 때 수정하게 한다. GeoHash(7단계)는 지구(구)를 평면으로 투영하여 일정한 크기의 격좌(셀로) 나누어 문자열을 부여하는 값이고 7단계는 하나의 격좌를 일정의 크기의 격좌로 나누는 행위를 7번 반복하여 7개의 문자열을 갖는다. 

 

Point prevLocation = member.getLocation();
Point newLocation = geometryFactory.createPoint(new Coordinate(location.y_coordinate(), location.x_coordinate()));

// 기존 GeoHash
GeoHash prevGeoHash = GeoHash.withCharacterPrecision(prevLocation.getX(), prevLocation.getY(), 7);

// 새로운 위치에 대한 GeoHash
GeoHash curGeoHash = GeoHash.withCharacterPrecision(location.y_coordinate(), location.x_coordinate(), 7);

// 기존 위치와 새로운 위치의 geohash(7)이 동일한 경우 종료
if(curGeoHash.equals(prevGeoHash)){
    return;
}

 

기존 위치 데이터와 새로운 위치 데이터의 geohash 값이 다른 경우 집계 테이블 업데이트

위치 데이터를 변경해야 하는 상황이라면 집계 테이블에도 변경을 해야 한다.

집계 테이블의 스키마는 다음과 같다.

CREATE TABLE `cell_category_count` (
  `category_id` bigint NOT NULL,
  `cell_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `count` int NOT NULL,
  ...
  PRIMARY KEY (`category_id`,`cell_id`),
  CONSTRAINT `FK8s2nvmutjh8h1xaityc034591` FOREIGN KEY (`category_id`) REFERENCES `category` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

 

먼저, 최근 종료한 계획의 할 일을 조회한다. 할 일들의 카테고리를 확인한다.

이전 위치의 geohash + 카테고리 id 에 해당하는 집계 테이블의 레코드의 count를 1 감소시킨다.

바뀐 위치의 geohash + 카테고리 id 에 해당하는 집계 테이블의 레코드의 count를 1 증가시킨다.

 

 // 종료한 최근 계획의 할 일 조회
 Plan recentPlan = planRepository.findTopByMemberIdOrderByCreatedAtDesc(memberId)
                .orElseThrow(PlanNotFoundException::new);
 List<Todo> recentTodos = todoRepository.findByPlanAndIsCompleted(plan, true);
 
 // 카테고리별 개수 파악
 Map<Long, Integer> categoryCountMap = countCategory(recentCompletedTodos);
 
 // 집계 테이블 업데이트
 categoryCountMap.entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .forEach( entry -> {
            cellCategoryCountRepository.upsertCellCategoryCount(prevGeoHash.toBase32(), entry.getKey(), -entry.getValue());
            cellCategoryCountRepository.upsertCellCategoryCount(curGeoHash.toBase32(), entry.getKey(), entry.getValue());
        });

 

동시성 문제 데드락 문제를 해결하기 위해서 categoryId 순서대로 집계 테이블을 변경을 했고 mysql 의 upsert 기능을 활용해 조회->수정 하는 것이 아닌 바로 수정하게 했다. 하지만, 해당 코드에는 셀 변경에 대한 데드락 문제가 아직 존재한다. 사용자 A가 셀 id a -> b로 변경을 하고 사용자 B가 셀 id b -> a 로 변경을 하는 상황일 때 사용자 A, B 가 각각 a, b 에 락을 얻고 다음 자원을 요청하는 상황에서 데드록일 발생한다. 이에 대한 해결책에 대한 고민은 추후에 한다. 아마 분산락을 처리하여 해결할 것 같은데 분산락에 대한 이해 와 구현 능력이 부족하여 다음으로 미룬다.

'개발일지' 카테고리의 다른 글

Realtime API 응답 EventType에 따른 처리 구분  (0) 2025.06.23
웹 소켓 핸들러 예외 처리  (0) 2025.06.20
주변 사용자 조회 로직 변경  (0) 2025.03.11
[소프티어 부트캠프] 지도(공유) 기능에 대한 고찰  (1) 2025.03.11
'개발일지' 카테고리의 다른 글
  • Realtime API 응답 EventType에 따른 처리 구분
  • 웹 소켓 핸들러 예외 처리
  • 주변 사용자 조회 로직 변경
  • [소프티어 부트캠프] 지도(공유) 기능에 대한 고찰
khw7385
khw7385
khw7385 님의 블로그 입니다.
  • khw7385
    khw7385 님의 블로그
    khw7385
  • 전체
    오늘
    어제
    • 분류 전체보기 (43)
      • 코딩테스트 (7)
      • 자바 (3)
      • 스프링 (3)
      • cs (7)
        • 자료구조 (3)
        • 알고리즘 (1)
        • 객체지향 (3)
      • 개발일지 (6)
        • 트러블슈팅 (1)
      • 데이터베이스 (3)
        • Redis (2)
        • MySQL (1)
      • 기타 (2)
      • devops (6)
      • LG CNS AM INSPIRE (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
khw7385
사용자 위치 변경에 대한 기능 수정
상단으로

티스토리툴바