Ably.io 사용 시, 유저 로그인 상태 확인하기

Ably.io로 채팅 기능을 구현하다보면 유저가 메세지를 읽었는지의 유무에 대해 체크해봐야하는 상황이 있다.

Ably가 웹 소켓을 사용해서 채팅을 쉽게 구현하게 해주는 서비스이므로 유저가 소켓에 접속해있는지 아닌지 체크할 방법이 있을 것이라고 생각하고 공식 문서를 확인해봤는데 다행히 있었다. 

 

Presence / REST / Docs | Ably Realtime

Presence events provide clients with information about the status of other clients 'present' on a channel

ably.com

 

공식 문서에서 볼 수 있듯이 presence를 통해 현재 channel에 참여하고 있는 client들의 상태에 대해 체크할 수 있다.

 

Presence의 이벤트에는 Enter, Leave, Update, Present가 있다.

 

해당 이벤트들을 기준으로 생각해보면 채팅방에 입장할 때, ably channel에 subscribe하면서 presence에 enter하면 현재 유저가 온라인임을 알 수 있고, 채팅방에서 퇴장할 때, ably channel에도 unsubscribe되면서 presence도 leave하면 현재 유저가 오프라인임을 서버가 알 수 있다.

 

해당 로직을 코드로 작성하면 아래와 같다. 

 

//front - chat.tsx
useEffect(() => {
	const channel = realtime.channels.get(`chat:${id}`);
    channel.subscribe('message',((data) => 
    	setMessage((prev) => [..prev, data]);
    )
    channel.presence.enter(); //channel의 message를 subscribe하고 해당 channel의 presence에 enter한다.
    return () => {
    	channel.presence.leave(); // 나갈 때, channel의 presenced에서 leave하고 unsubscribe한다.
        channel.unsubscribe();
    }
},[id, realtime]);

 

이제 남은 건 presence의 유저 온라인 유무를 백엔드에서 체크하는 것이다.

 

프론트에서 지정한 ably 채널명(`chat:${id}`)에 맞춰서 channel을 가져와서 channel 내부에 있는 presence 값을 resolve해서 가져오면 백엔드에서 presences 값을 확인할 수 있다. 가져온 presences안에서 clientId가 채팅에 참가해 있는 유저와 같으면 온라인인 것이고, 아니면 오프라인인 것으로 판별할 수 있다.

 

해당 로직을 코드로 작성하면 아래와 같다.

//backend - ablyService
getPresence(
	channelName: string
    ): Promise<Ably.Types.PaginatedResult<Ably.Types.PresenceMessage>> {
	return new Promise((resolve) => 
    	this.rest.channels
        	.get(channelName) // 전달받은 channel명으로 채널을 가져온다.
            .presence.get((_, presences) => resolve(presences)); //가져온 채널의 presence에서 presences 값을 resolve한다.
    );
}

// front에서 지정한 channel 이름으로 presence 정보를 가져온다.
const presences = await this.ablyService.getPresence(`chat:${chatId}`); 

// presences 안의 items를 하나하나 clientId를 채팅의 userId와 대조한다.
const userOffline = presences.items.every(
      	(presence) => presence.clientId !== chat.userId.toString()
      );