현재 진행 중인 토이 프로젝트에는 아래와 같이 쪽지 기능이 있다.
message

보통 Web Socket을 통해 구현을 한다고 한다.
하지만 나는 Socket을 제대로 익히지 않았으므로, 우선 없이 구현해보려 한다.

Message & Room

가장 먼저 떠오른 방식은, Room 즉, 쪽지 방의 개념이다.
현재 서비스 중인 에브리타임 같은 App에 보면, 쪽지 방이 존재한다.
해당 방을 나가지 않는 이상 상대방과의 쪽지가 채팅처럼 이어지는 시스템이다.

무작정 나에게 오는 쪽지를 모두 목록화 해서 응답에 주면,
Client 입장에서 매우 불편할 것이기에 다음과 같은 시스템을 구현하기로 했다.

따라서 아래와 같이 먼저 Message Entity를 만들어주었다.
KakaoTalk_20230130_192317484

관계의 차수

첫번째 고려해야 할 점, 과연 Member Entity와 어떻게 연관 관계를 맺을 것인가?
각 Client는 여러 개의 쪽지를 받고 쓸 수 있으므로, 우선 1:N 관계로 정의했다.
하지만, 송신자와 수신자의 구분은 필요하므로 아래와 같이 따로 선언을 해 놓았다.
KakaoTalk_20230130_193041641
KakaoTalk_20230130_193111083

Fetch Join 문제

두번째 고려해야 할 점, Message를 DB에서 찾아올 때, 어떤 값을 통해 검색할 것인가?
앞서, 모든 쪽지 방을 Room Id로 구분해 놓기로 했었다.
그럼 개별적인 방은 DAO(Repository) 를 통해 DB에서 Room Id를 통해 불러오면 된다.
KakaoTalk_20230130_213310981

하지만 내가 대화했던 모든 쪽지의 목록을 보이기 위해선 Room Id 만으로는 불가능하다.

  1. 그럼 어떤 방식으로 내가 대화했던 쪽지의 가장 최신 내용을 띄워 목록을 보내줄까?
    • 이는 SQL의 group by`를 이용해 해결해보려고 한다.
  2. 또한, 해당 목록에 나랑 대화 중인 상대의 이름이 떠야 하는데, 어떻게 가져올까?
    • 내가 보낸 사람인지 받은 사람인지 어떻게 알며, 상대의 이름을 불러올지가 관건.
    • 이 부분이 가장 핵심적으로 고민해봐야 할 부분인 것 같다.

2번에 대한 해답은 일단 아래와 같이 생각했다.
1번에 대한 Query에서 Member와 Fetch Join을 해서 Nickname과 사진을 찾아주자.
KakaoTalk_20230203_202812979
KakaoTalk_20230203_212521284

하지만 두 번째 사진과 같이 오류를 뱉었다.
조금 검색을 해보니 Fetch Join에는 일대다 관계를 여러번 쓸 수 없다고 한다.
이로 인해 발생하는 에러인데, Fetch Join을 이용하지 않고 구현할 방법을 궁리해야할 것 같다.

임시방편의 수립

우선, 임시 방편으로 아래와 같이 기능은 하도록 구현해 놓았다.
Fetch join 없이 Query를 날리기 때문에, N+1 Query의 위험이 있다고 생각한다.
KakaoTalk_20230204_004042535
KakaoTalk_20230204_004118058

직접 아래와 같이 Member를 DB에 만들어 놓고 실험을 해보았다.
1 DB멤버

쪽지를 보낸 사람이 1명만 있을 때는 아래의 2개 Query만 날아갔다.
쪽지 1개만 있을 때 쿼리

하지만 쪽지를 8명 모두에게 보내거나 받았을 때, 아래의 추가 Query 1개만 날아갔다.
쪽지 8개 있을 때 추가 쿼리

분명히 Join을 하지 않은 상태에서 Member에 접근을 했다.
그로 인한 부작용으로 N+1 Query가 발생할 것이라고 예상을 했는데,
왜 Member에 대한 1개의 Query만 추가로 날아가게 된 것일까?

확인을 해보니 서비스 로직에서 findMessageList() 함수에,
매개 변수로 findedMember.getNickname()이 들어가고 있는데,
해당 동작을 지역 변수로 빼니 마지막 Query가 날아가지 않았다.

Overload의 이용

세번째 고려해야 할 점, 처음 쪽지를 보낼 때는 방이 생성이 되어있지 않다는 점.
처음에 쪽지를 보내는 API에 대한 요청 URL과,
이후에 방이 생성된 후, 쪽지를 보내는 API에 대한 요청 URL을 따로 두려고 계획했다.

아래와 같이 Controller 단에서 Mapping 경로를 구분한다.
KakaoTalk_20230203_214710205

이후 Service 단에서 다음과 같이 Method를 Overload 하여 구분한다.
KakaoTalk_20230203_214816301

  • 방이 생성되지 않은 경우
    • 현재 방의 최대값을 구하여 새 방을 생성한다.
  • 방이 이미 있는 경우
    • Front단에서 넘겨받은 방 번호로 해당 방에 쪽지를 전송한다.

위의 세 단계를 통해서 쪽지 기능을 우선 완성할 수 있었다.
성능에 대한 개선과 고찰을 계속 해나갈 예정이다.