AI 기반 이력서 생성 및 포트폴리오 관리 & 개발자 네트워킹 플랫폼
Why this project?
개발자 커뮤니티에서 멘토링이나 피어 리뷰를 위한 자연스러운 연결 수단이 부족했습니다. 기존 플랫폼은 이력서 관리와 네트워킹이 분리되어 있어, 프로필을 확인한 뒤 바로 대화를 시작하기 어려웠습니다. 프로필 기반 매칭부터 대화까지 하나의 플랫폼에서 자연스럽게 이어지는 구조가 필요했습니다.
Key Features
커피챗 신청-승인 프로세스와 실시간 채팅을 논리적으로 분리하여, 커피챗 승인이 채팅방 생성의 선행 조건이 되도록 접근 제어 로직을 설계했습니다. 승인되지 않은 관계에서는 채팅 채널 자체가 생성되지 않으므로, 비인가 접근을 구조적으로 차단합니다. 도메인 분리를 통해 커피챗 로직 변경이 채팅 로직에 영향을 주지 않도록 결합도를 낮췄습니다.
WebSocket으로 실시간 메시지 브로드캐스팅을 처리하고, Prisma REST API로 메시지를 DB에 영속화하는 이중 구조를 설계했습니다. WebSocket은 즉시성을, REST API는 데이터 안정성을 각각 담당하여, 메시지가 실시간으로 전달되면서도 DB에 안전하게 저장되는 구조를 구현했습니다.
DB 내 LastReadMessageId를 사용자별로 관리하고, 서버 사이드에서 UnreadCount를 계산하여 단일 진실 소스(Single Source of Truth)를 유지했습니다. 클라이언트에서 카운트를 관리하면 다중 디바이스/탭 환경에서 불일치가 발생하므로, 서버 연산 기반으로 일관된 상태를 보장하는 방식을 선택했습니다.
Tech Decisions
실시간성이 필요한 메시지 전송은 WebSocket, 영속화와 조회는 REST API로 분리하여 각 프로토콜의 강점을 활용했습니다.
TypeScript와의 자연스러운 타입 통합으로 개발 생산성을 높이고, 팀 전체가 일관된 DB 접근 패턴을 사용할 수 있었습니다.
팀 프로젝트에서 feature/develop/release/hotfix 브랜치 전략으로 병렬 개발 시 충돌을 최소화하고 릴리스 품질을 관리했습니다.
Problem Solving
Problem
초기 설계에서 커피챗 승인 여부와 관계없이 채팅 채널이 미리 생성되어 있었고, 승인 전 사용자가 URL을 직접 입력하면 채팅이 가능해지는 접근 제어 취약점이 발견되었습니다. 인증(로그인)은 되어 있지만 인가(권한)가 제대로 작동하지 않는 상태였습니다.
Approach
이 문제를 "도메인 경계가 명확하지 않아서 발생한 구조적 결함"으로 판단했습니다. 단순히 채팅 진입 시 승인 여부를 체크하는 것이 아니라, 커피챗 도메인과 채팅 도메인을 논리적으로 분리하고, 채팅방 생성 자체를 커피챗 승인의 후행 이벤트로 설계했습니다. 승인 이벤트가 발생해야만 채팅 채널이 생성되므로, 비인가 접근을 체크 로직이 아닌 구조로 원천 차단할 수 있었습니다.
Result
승인된 관계에서만 채팅이 가능한 안전한 접근 제어를 구현했고, 도메인 분리를 통해 향후 커피챗 로직 변경이 채팅에 영향을 주지 않는 유연한 구조를 확보했습니다.
Problem
클라이언트 측에서 미읽음 메시지 수를 각각 관리하고 있었는데, 같은 사용자가 여러 탭이나 디바이스에서 접속하면 UnreadCount가 서로 다르게 표시되는 데이터 불일치 문제가 발생했습니다. 한쪽에서 읽어도 다른 쪽에 반영되지 않았습니다.
Approach
이 문제의 본질은 "상태의 진실 소스가 분산되어 있다"는 것이었습니다. 클라이언트가 각자 카운트를 관리하는 방식을 폐기하고, 서버에서 LastReadMessageId를 기준으로 UnreadCount를 매번 계산하여 응답하는 구조로 전환했습니다. 상태를 저장하는 것이 아니라 조회 시점에 계산하는 방식이므로, 어떤 디바이스에서 접근하든 항상 동일한 결과를 보장합니다.
Result
다중 디바이스/탭 환경에서도 일관된 미읽음 수를 보장하는 구조를 확립했습니다.
Outcomes
Links