42Seoul-LastDance / Backend

2 stars 0 forks source link

[Feature] 알림창 구현점 이슈 #32

Closed SikPang closed 1 year ago

SikPang commented 1 year ago

요약

알림창 구현 방법 정해보아요



내용

알림창으로 방초대, 게임초대 알림 및 상호작용 가능하게 할 예정입니다. dm은 채팅 기능의 통일성을 위해 냅두는게 나을 것 같아요.

-> 해결하려면 서버 메모리 및 db에 저장하여 리스트 송수신

-> 그냥 휘발성으로 해도 저는 상관 없습니다만, 여러분 의견을 들려주세요😉 다른 해결책도 좋습니다~

(친구 초대는 예정대로 따로 탭이 있습니다)



ebang091 commented 1 year ago

저는 메모리에 저장한 뒤 소켓이벤트로 저장된 방, 게임 초대 날려주는 것 좋다고 생각합니다. 취소시에는 삭제로 구현하는 것도 좋다고 생각합니다. (무효화되는 데이터이기 때문에요. ) 한가지 여러분들과 상의하고 싶은 부분은 메모리 구현에서도 DB에 저장해서 영구적으로 보관할 지, 아니면 DM 소켓에 연결되어있는 (서비스에 접속해있는) 동안에만 일시적으로 보관해서 알림을 줄 지 결정하는 부분입니다. 두가지 모두 구현에 어려움은 없으나 제 개인 의견은 영구적으로 저장할 필요는 없다고 생각합니다. 해당 채팅, 게임초대의 존재유무는 상대 유저와 해당 유저 모두 존재할 때 의미가 있기 때문입니다!!

따라서 저는 DM 소켓에 연결되어있는 동안에만 초대된 game, chat 은 이벤트를 통해서 전달하는 것을 제안드리고 싶네요 ㅎㅎ

+하나 질문이 있는데요, 초대시 이벤트 전달까지는 합의가 된 것 같은데, 해당 알림을 수락하면 바로 '채팅방' 혹은 '게임'으로 이동하는 새로운 소켓 이벤트 + 프론트 엔드 상에서의 구현이 이루어질까요?? (구글링 중 링크를 생성하는 예시를 보긴했는데 이건 rest api인 것 같네요 ㅎㅎ)

SikPang commented 1 year ago

전달과 저장 자체는 DM 소켓이 연결됐을 때만 하는 것 좋다고 생각합니다. 근데 새로고침하면 DM 소켓도 재연결 될 예정이에요.. 그럼 서버 메모리도 비워지니 프론트 메모리랑 다를 게 없군요🥲 혹시 백에서 DB를 안 쓰고 이를 해결할 방법이 있을까요?

ebang091 commented 1 year ago

@SikPang 아 그렇군요... 그렇다면 우선은 DB 저장이 가장 좋을 것 같네요!

SikPang commented 1 year ago

브라우저 세션스토리지에 박아두면 브라우저 끌 때까지는 남아있다는군요! 저희가 해볼게여 db에 저장하기는 좀 누추한 데이터라.. 월욜에 이벤트 정해보시죠

  1. Receiver가 브라우저 끄면 초대 데이터 휘발, Sender에게 초대 취소 이벤트 송신
  2. Sender가 초대 취소해도 Receiver에게 취소 이벤트 송신 (저희 데이터에서 해당 초대 삭제)
  3. Receiver가 초대를 수락했을 때, 방이 없다면 무시 (소켓 통신 사이에서 일어나는 시간차이 때문에 꼬일 수도 있을듯 해서요)
Tolerblanc commented 1 year ago

제 개인 의견은 영구적으로 저장할 필요는 없다고 생각합니다. 해당 채팅, 게임초대의 존재유무는 상대 유저와 해당 유저 모두 존재할 때 의미가 있기 때문입니다!!

동의합니다. 초대 자체가 유효할 상황이 그리 많지 않을 것 같아요. (유저 둘 다 온라인, 채팅방이 살아 있거나 게임방이 살아 있어야 함)


혹시 백에서 DB를 안 쓰고 이를 해결할 방법이 있을까요?

소켓 연결 여부에 의존하지 않는 자료구조를 만들면 될 것 같습니다. Map<userId, Array<Invitation> > 처럼 저장해두고, userId의 소켓이 끊겼거나 초대에 대한 이벤트가 발생할 때마다 업데이트 해주는 방식입니다. 말이 쉽지 세부 구현은 제대로 설계를 해봐야 알 것 같습니다 ㅠ inspircd 에서 초대 객체를 따로 관리했었다는게 갑자기 기억나서, 위와 같은 로직을 생각 해보았습니다.

SikPang commented 1 year ago

현준이가 말한 거랑 비슷한 내용인 것 같은데, 초대했을 때와 그 초대를 취소했을 때, 같은 key(+상태)로 프론트에게 보내주시면 데이터 관리가 원활히 될 것 같은데 어떠세요? Sender 이름과 방 종류로는 구분에 한계가 있을 것 같네요🥲

ebang091 commented 1 year ago

@SikPang

현준이가 말한 거랑 비슷한 내용인 것 같은데, 초대했을 때와 그 초대를 취소했을 때, 같은 key(+상태)로 프론트에게 보내주시면 데이터 관리가 원활히 될 것 같은데 어떠세요? Sender 이름과 방 종류로는 구분에 한계가 있을 것 같네요🥲

이 부분은 언제나 가능합니다!

Tolerblanc commented 1 year ago

같은 키 라는 부분을 잘 이해하지 못했어요.. 특정한 초대에 대해 유니크한 키가 존재하고, 해당 키를 가진 초대에 대한 유효성을 백에서 관리하면서 프론트로 넘기는 방식 맞나요?

ebang091 commented 1 year ago

@Tolerblanc 앗 저는 그렇게 이해했어요 ㅋㅋㅋ 다만 프론트랑 공유한다는 점에서

브라우저 세션스토리지에 박아두면 브라우저 끌 때까지는 남아있다는군요! 저희가 해볼게여 db에 저장하기는 좀 누추한 데이터라.. 월욜에 이벤트 정해보시죠

  • 브라우저 종료 이벤트를 react에서 감지하고 이벤트를 서버에 보낼 수 있다면 소켓 연결 해제보다 로그아웃 감지가 정확해질 것 같아요. 한 번 알아볼게요.
  1. Receiver가 브라우저 끄면 초대 데이터 휘발, Sender에게 초대 취소 이벤트 송신
  2. Sender가 초대 취소해도 Receiver에게 취소 이벤트 송신 (저희 데이터에서 해당 초대 삭제)
  3. Receiver가 초대를 수락했을 때, 방이 없다면 무시 (소켓 통신 사이에서 일어나는 시간차이 때문에 꼬일 수도 있을듯 해서요)

백에서도 메모리 관리를 해주면서 프론트의 스토리지에서 id를 싱크 맞추는 걸로 이해했어요 @SikPang 어차피 백이 다 들고있을 바엔 리스트를 드려도 될 것 같기도 한데 .. 이 부분은 프론트가 편하신대로 해도 될 것 같아요

ebang091 commented 1 year ago

senderName, roomName으로 hash 값 만드는 것도 하나의 방법이 될 것 같아요!

SikPang commented 1 year ago

초대 리스트는 프론트에서 관리하고 백은 그때그때 하나씩 전달만 해주는 걸로 생각했어요. 리스트에 있는 걸 지워주려면 특정 key가 필요하니까 요청드렸던 거에요! (초대와 초대취소가 같은 유니크한 문자열 or 숫자이기만 하면 돼요. map의 key로 넣을 예정) 근데 그냥 이렇게 먼저 정해버리기 보다 초대를 수락했을 때 방에 넣어주는 로직이랑 잘 어우러지는지 생각해보고 설계 제대로 해서 진행해야할 것 같네요.. 월욜에 같이 다시 얘기해보시죠!

OZestina commented 1 year ago

소켓 연결 여부에 의존하지 않는 자료구조를 만들면 될 것 같습니다. Map<userId, Array > 처럼 저장해두고, userId의 소켓이 끊겼거나 초대에 대한 이벤트가 발생할 때마다 업데이트 해주는 방식입니다. 해당 방법으로 진행한다면 생각해 볼 만한 부분 공유합니다

  1. 초대(채팅/게임) 받은 유저가 로그아웃하면 초대 삭제
  2. 초대(채팅/게임) 보낸 유저가 로그아웃하면 초대 취소
  3. (초대한 장소인) 게임방/채팅방 없어지면 초대 취소
  4. (초대받은 사람이) 수락 시 게임방/채팅방 존재하는지 및 초대한 유저가 온라인(게임중?) 상태인지 확인하고 처리 1, 2번은 말씀하신 부분으로 비춰보아 동의가 있는 것 같고 3, 4번의 내용도 고려해봐야 할 내용인 것 같습니다.

추가로 해당 방법으로 진행하게 되면 로그아웃이 아닌 브라우저 닫음을 통한 방법으로 서비스 접속이 끊기고 그 후 오랜 시간이 지나고 접속해도 해당 알람이 남아있을 수 있는데 이 부분도 괜찮은지, 괜찮지 않으면 어떻게 처리할 건지도 논의가 필요할 것 같습니다

Tolerblanc commented 1 year ago

브라우저 종료 이벤트가 기존 새로고침으로 소켓이 끊기는 이벤트와 구분된다는 가정하에, 초대 유효성 검증을 다 백에서 하고 프론트에서 요청 결과에 따라 처리하는 로직도 괜찮다고 생각합니다. (이거는 프론트 구현이 어떻게 될 지 잘 몰라서 조심스럽네요) 백에서 {userId, Set<event_id>}, {event_id, Invitation(Sender, Receiver, EventType, isValid)} 형태로 Map 두 개를 들고 있으면, 기존 socketUsersService와 비슷한 형태로 구현 가능하지 않을까 싶네요! 브라우저가 완전히 종료된 시점에만 clear해주면 될 것 같습니다. 근데 이런 구조면 차라리 그냥 DB를 쓰는게 깔끔할 것 같기도...

별개로 event_id를 해시값으로 / 숫자로 관리하는 것 모두 찬성입니다!!

ebang091 commented 1 year ago

초대 리스트는 프론트에서 관리하고 백은 그때그때 하나씩 전달만 해주는 걸로 생각했어요. 리스트에 있는 걸 지워주려면 특정 key가 필요하니까 요청드렸던 거에요! (초대와 초대취소가 같은 유니크한 문자열 or 숫자이기만 하면 돼요. map에 key로 넣을 예정)

저희 Back 은 그 key를 모르니까 senderId, RoomName, requestName 정도라면 고유한 값을 대표할 수 잇을 것 같아서 hash 제안을 드렸던 것 같습니다! 물론 이건 브라우저에서 저장할 때 얘기지만요..! unhash 해서 3개를 다시 꺼내온다면 그것도 정보전달이 되니까 꽤나 간편할수도 있다고 생각했습니닷

@OZestina 네 그런 부분들이 걸려서 아직 못 정하고 있는 것 같아요...!! 말씀 주신 부분은 타임아웃을 통해서도 구현가능하긴 할텐데 기획과도 닿아있는 부분들이기도해서 내일 얘기해보면 좋을 것 같네요 @Tolerblanc 저두 동의합니다ㅋㅋ

Tolerblanc commented 1 year ago

초대가 발생했을 때 실시간으로 전달하기 위해 소켓을 사용하는 거라고 생각했는데, 초대 취소/삭제 같은 것도 실시간으로 처리해줄 필요가 있나요?? 클라이언트에서 특정 초대에 대해 이벤트를 발생 시켰을 때만(수락 / 거절) 검사해도 괜찮다고 생각합니다. 이렇게 처리해도 UX 측면에서 부정적이진 않을 것 같아요.

채팅방 멤버리스트 처럼, 알림창을 눌렀을 때 현재 유효한 이벤트만 백에서 전달해주는 방식이 되겠네요 -> join 처럼 초대 발생시에도 보내줘야 겠네요!!..

검사 로직이 조금 무거워지긴 하겠지만 어차피 다 Map으로 들고 있어서 $O(1)$ 로 검사할 수 있고, 오히려 프론트/백 양쪽으로 데이터를 놓고 이벤트마다 교환해가면서 처리하는 방식이 설계를 더 복잡하게 만드는 것 같아서요. 게임 초대 이벤트의 경우, 초대를 받은 사람이 이벤트를 발생시키지 않으면 초대를 한 사람이 무한정 대기한다는 단점이 생기지만, 이 부분은 따로 처리하거나 그냥 놔두는 것도 좋은 것 같아요.

SikPang commented 1 year ago

현준이가 얘기한 게 조금 헷갈렸는데 두 부분인 것 같네. Sender가 초대 취소했을 때 아무 행동도 하지 않는 것과 데이터를 프론트와 백 두 곳에서 모두 관리하고 이를 동기화 시키는 것은 다른 문제라고 봐.

  1. 데이터를 프론트와 백 두 곳에서 모두 관리하고 이를 동기화 시키는 것 서버에서 유저별 알림창 리스트를 가지고 있고 이를 보내주기만 하는 것이 바람직한지는 모르겠지만 일단 일이 줄어서 좋긴 해 :D

  2. Sender가 초대 취소했을 때 아무 행동도 하지 않는 것 리스트에 변동사항이 있을 경우 이벤트를 보내 리스트를 실시간 갱신시켜 예외처리 및 처리 로직을 최소화 해보자는 생각으로 채팅방, 친구 리스트를 그렇게 구현했고 이어서 계속 통일성 있게 구현을 하고 싶은 마음은 있어. 채팅방 멤버 리스트는 어떻게 돼있는지 모르겠지만...

유저별 알림창 리스트를 서버에서 관리하면서 프론트가 요청하면 리스트를 통째로 주고, 리스트 갱신이 필요할 때 채팅방, 친구 리스트처럼 업데이트 이벤트만 프론트에게 보내주는 식으로 하면 어때? 데이터를 한 쪽으로 몰아넣어서 관리는 편하게 하고, 리스트는 자동 갱신이 되게 하는 거야

Tolerblanc commented 1 year ago

생각해보니까 멤버 리스트도 버튼을 눌렀을 때 뿐만 아니라 누군가 입장/퇴장할 때마다 매번 보내서 실시간 업데이트를 하고 있네요...

유저별 알림창 리스트를 서버에서 관리하면서 프론트가 요청하면 리스트를 통째로 주고, 리스트 갱신이 필요할 때 채팅방, 친구 리스트처럼 업데이트 이벤트만 프론트에게 보내주는 식으로 하면 어때? 데이터를 한 쪽으로 몰아넣어서 관리는 편하게 하고, 리스트는 자동 갱신이 되게 하는 거야

대찬성입니다!!!

OZestina commented 1 year ago

이야기 나온 방식 대로 서버에서 알림리스트(초대리스트) 관리하는 방식으로 진행하겠습니다. socketUsers.service에 하기 map 만들어서 userId별로 관리하겠습니다. (새로고침 후에도 리스트 유지되도록 userId로 관리) private inviteList: Map<number, Map<number, Invitation>> = new Map<number, Map<number, Invitation>>(); //userId, {inviterId, Invitation} export interface Invitation { type: InviteType; chatRoomName: string | undefined; chatRoomType: RoomStatus | undefined; }

작업 진행 예정인 내용도 공유드립니다. Feature : 친구 초대(게임, 채팅)

====[업데이트 완료]================================= 하기 내용 제외하고 모두 진행됐습니다.

ebang091 commented 1 year ago

@Tolerblanc
chatRoom Invite 정해진 내용 공유드립니다!

<invite 로직>

<joinPrivateChatRoom 로직> inviteList 에 존재하는지 확인 후(socketUserService 메소드 만들예정) join 로직을 실행합니다. (memberList에 추가)

Tolerblanc commented 1 year ago

하나의 유저에게 최대 한 개만 초대를 받을 수 있는 것으로 진행하고 있습니다

~~특정 유저가 게임 초대 -> 채팅 초대를 차례로 받는 상황에서는 이전에 받은 게임 초대는 자동으로 거절처리 되는 로직인가요 ?.? 아직 커밋을 안하신 것 같아서 이슈로 여쭈어 봅니다!!~~

socketUsers.service.ts:95 만 봤을때는 처리 안하고 있는 것 같긴한데, 내일 한번 더 확인해보겠습니다!

1대1 기준 초대가 하나 씩 존재하는걸로 이해했습니다!