Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Socket: ✨ Managing user session status on a socket server #176

Merged
merged 40 commits into from
Oct 17, 2024

Conversation

psychology50
Copy link
Member

@psychology50 psychology50 commented Oct 17, 2024

작업 이유

  • 클라이언트의 세션 상태를 다음과 같이 구분합니다.
  • 자세한 내용은 블로그에 작성
상황 상태 처리
사용자가 앱을 실행 중이지만, 채팅 관련 뷰(내가 가입한 채팅방 리스트 뷰 or 임의의 채팅방 뷰)를 보고 있지 않음. ACTIVE_APP • 푸시 알림
사용자가 앱을 실행 중이며, 내가 가입한 채팅 리스트 뷰를 보고 있음. ACTIVE_CHAT_ROOM_LIST • 메시지 전달
사용자가 앱을 실행 중이며, 임의의 채팅방 뷰를 보고 있음. ACTIVE_CHAT_ROOM_{chat_room_id} • 메시지 전달 • 다른 채팅방 메시지는 푸시 알림
사용자가 앱을 백그라운드로 실행함. (앱을 종료하지 않고 나가거나, 화면을 끄거나, 메뉴 화면으로 이동) BACKGROUND • 푸시 알림
사용자가 앱을 종료함. INACTIVE • 푸시 알림
사용자가 절전 모드(방해 금지 모드)를 사용함.   중요 알림에 대해서는 기기 설정을 무시하고 보내거나, 배터리 잔량에 따라 동작을 조정할 필요가 있다면...? 너무 복잡한 기능이라 일단 제외
일정 시간 내 ping을 수신하지 못 함. INACTIVE • 푸시 알림
사용자가 로그아웃을 함. INACTIVE • X
사용자가 회원탈퇴를 함. 사용자 상태 제거 • X
사용자가 앱을 삭제함. offline 유지 • X

작업 사항

1️⃣ User Status

@RequiredArgsConstructor
public enum UserStatus implements LegacyCommonType {
    ACTIVE_APP("1", "앱 활성화"),
    ACTIVE_CHAT_ROOM_LIST("2", "채팅방 리스트 뷰"),
    ACTIVE_CHAT_ROOM("3", "채팅방 뷰"),
    BACKGROUND("4", "백그라운드"),
    INACTIVE("5", "비활성화"),
    ;

    ...
}
  • 사용자 상태는 총 5개로 구분합니다.

2️⃣ 사용자 세션

public class UserSession implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    private String deviceId;
    private String deviceName;
    @Convert(converter = UserStatusConverter.class)
    private UserStatus status;
    private Long currentChatRoomId;
    private LocalDateTime lastActiveAt;

    ...
}
  • redis에 저장되는 사용자 세션 정보는 다음과 같습니다.
    • deviceId, deviceName: 나중에 사용자 기기 FCM 토큰 식별을 위해 필요한 값.
    • status: 사용자의 현재 상태
    • currentChatRoomId: 사용자가 채팅방 뷰를 보고 있을 때, 해당 채팅방 아이디
    • lastActiveAt: 사용자의 메시지(박동 검사 포함)를 수신할 때마다 업데이트되는 값.

사실 deviceId는 필요없을 수 있습니다.
어차피 socket-relay에서 채팅방에 가입한 user_id 리스트를 통해, 각 유저의 deviceId를 불러올테니까요.
그런데 UserSession에 사용자 식별 가능한 값이 하나도 없어, 일단 불안하여 추가했습니다.


3️⃣ 사용자 상태 활성화

  • Connect 프레임 수신 시, 사용자의 상태를 ACTIVE_APP으로 전환합니다.

4️⃣ 사용자 뷰 상태 추적

@Slf4j
@Controller
@RequiredArgsConstructor
public class StatusController {
    private final StatusService statusService;

    @MessageMapping("status.me")
    @PreAuthorize("#isAuthenticated(#principal)")
    public void updateStatus(UserPrincipal principal, StatusMessage message, StompHeaderAccessor accessor) {
        statusService.updateStatus(principal.getUserId(), principal.getDeviceId(), message, accessor);
    }
}
  • 사용자가 SEND 프레임을 status.me path로 전송하여, 상태를 변경합니다.

5️⃣ 사용자 상태 비활성화

  • Disconnect 프레임 수신 시, 사용자의 상태를 INACTIVE로 전환합니다.

6️⃣ 사용자 활동 시간 업데이트

필요가 없다고 판단하여, 박동 검사에 대해 사용자 활성 시간을 갱신하는 로직은 추가하지 않았습니다.
이유는 제 블로그에서 발췌한 고찰로 퉁치겠습니다.

이 내용을 작성하다가, 생각해보니 정말 필요할까 싶어서 제외해버렸다.
 
요지는 사용자가 포그라운드에서 아무것도 안 하고, 가만히 있는 경우에 발생한다.
어떠한 뷰 이동도 없으니, 사용자의 마지막 활동 시간은 변하지 않을 것이다.
처음에는 이게 문제가 된다고 생각해서, 박동 검사를 할 때마다 활동 시간을 업데이트 하려는 interceptor를 추가하려 했다.
 
그러나 Spring WebSocket은 IEFT 권장 사항에 따라, 박동 검사 interval을 25초로 잡고 있으며
이는 모든 사용자 세션마다 25초 주기로 redis에 부하를 줘야 한다는 이야기가 된다.
 
그래서 이 시점에서 한 번 다시 생각해보게 되었는데, 사용자가 아무런 액션을 취하지 않을 때 마지막 활동 시간을 업데이트 해줘야 할 이유가 존재할까?
서비스 정책마다 다를 수 있겠지만, 내 서비스 같은 경우엔 이게 전혀 의미가 없는 행위였다.
그리고 애초에 포그라운드로 계속 냅둔다고 한들, 어쨌든 한 번이라도 백그라운드로 전환하면 상태는 알아서 바뀔 것이고, 앱을 종료해도 마찬가지.
아무런 액션도 취하지 않는 사용자의 "활동 시간을 갱신한다"라는 전제부터가 잘못 되었다고 생각하게 되었다.
 
의미도 없는데 애꿎은 redis만 괴롭힐 뻔..

리뷰어가 중점적으로 확인해야 하는 부분

  • 사용자 상태를 관리하는 접근법이 적절하다고 보는지?

발견한 이슈

  • receipt 헤더가 존재하면, 언제나 응답을 돌려줘야 하는데, 이게 안 됨.
    • interceptor에서 clientOutboundChannel 의존성을 주입해서 해결하려 했으나, 순환 참조에 걸려서 실패......
  • redis를 반드시 7.4로 업그레이드 해야합니다.
    • hash로 user session을 저장하는데, 7.4의 HEXPIRE 명령어가 있어야만 필드 별로 ttl을 걸 수 있습니다.
    • Lettuce에서 HEXPIRE 명령어를 사용할 수 있는 메서드를 제공해주지 않아, 네이티브로 처리하려고 했으나 이상한 에러가 발생합니다. 그래서 일단 Lua Script를 이용해서 처리했습니다.

@psychology50 psychology50 added the enhancement New feature or request label Oct 17, 2024
@psychology50 psychology50 self-assigned this Oct 17, 2024
@psychology50 psychology50 changed the title Socket: ✨ Managing user session states on a socket server Socket: ✨ Managing user session status on a socket server Oct 17, 2024
@psychology50 psychology50 merged commit 9067ce7 into dev Oct 17, 2024
1 check passed
@psychology50 psychology50 deleted the feat/PW-589-update-socket-user-status branch October 17, 2024 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant