[PonsLink] 한 번 놓친 연결에서 PonsLink가 시작된 이유
PonsLink를 만들기 시작한 이유는 기술 호기심보다 불편함에 더 가까웠다. 누군가를 다시 만나야 하는 순간이 오면, 지난 대화의 맥락을 처음부터 복구해야 했다. 메시지를 찾고, 파일을 다시 보내고, 왜 만나려 했는지 다시 설명하는 일이 반복됐다.
처음에는 이 문제를 “좋은 회의 링크가 없어서”라고 짧게 정리했다. 틀렸다. 링크는 쉽게 만들 수 있었다. 진짜 문제는 연결이 끊긴 뒤에 맥락이 같이 사라진다는 점이었다.
그래서 PonsLink의 출발점은 영상 통화 데모가 아니라, 요청이 방에 도착하기 전 단계를 제품으로 묶는 일이 됐다. 이 글은 그 첫 판단을 다시 적는다.

연결은 됐는데 일이 끝나지 않았다
실시간 방이 붙어도 일의 끝은 방이 아니었다. 사용자는 이런 흐름을 기대했다.
- 왜 만나고 싶은지 남긴다
- 상대가 수락하거나 일정을 고른다
- 같은 맥락을 가진 채로 방에 들어간다
- 통화 후에도 무엇이 남았는지 추적할 수 있다
방만 있으면 3번만 해결된다. 1·2·4가 비면 연결은 됐는데 일이 안 끝난다. 나는 이 간극을 “기능 부족”이 아니라 요청-세션-기록의 단절로 보기 시작했다.
상담·협업 제품에서 이 단절은 특히 비싸다. 상대는 이미 용건을 한 번 말했다고 생각하고, host는 빈 방에서 다시 질문해야 한다. 기술적으로는 connected 상태인데, 업무적으로는 아직 시작도 못 한 상태다. 이 간극을 UI 문구 하나로 메울 수 없다고 판단했다.
연결 성공 지표만 보면 제품이 괜찮아 보일 수 있다. 방에 들어왔고, ICE가 붙었고, 영상이 보인다. 그런데 host의 다음 행동은 “그래서 무슨 일로 오셨죠?”다. 그 순간 실시간 기술은 이미 제 역할을 끝냈고, 제품의 진짜 숙제는 이제 시작된다. 나는 이 순서를 뒤집고 싶었다.

서버에 모든 것을 맡기려던 유혹
초기 유혹은 단순했다. 서버가 방 상태와 미디어와 파일까지 다 들고 있으면 구현이 쉬워 보였다. 권한도 서버, 기록도 서버, 전송도 서버. “한 곳에서 통제한다”는 말은 안정감처럼 들린다.
하지만 그 구조는 두 가지를 동시에 키웠다.
- 운영 비용과 장애 반경
- “방이 곧 제품”이라는 잘못된 중심
PonsLink가 풀려던 것은 중계 인프라 자랑이 아니었다. 누가 어떤 맥락으로 들어오는지였다. 서버가 필요한 곳은 권한·정책·복구 기준이었고, 모든 바이트를 서버가 가질 필요는 없었다.
특히 파일과 미디어를 서버 경유로 통일하면, 나중에 개인정보·보관 기간·삭제 요청 같은 운영 질문이 한꺼번에 들어온다. 연결 제품이 저장 제품의 책임까지 떠안는 순간, 로드맵이 왜곡된다. 그래서 “서버가 할 일”과 “서버가 가져야 할 데이터”를 처음부터 분리해 적기 시작했다.
이 분리가 깔끔하지 않으면 팀 안 대화도 꼬인다. 누군가는 latency를 말하고, 누군가는 보관 정책을 말하고, 누군가는 방 UI 문구를 말한다. 모두 중요한데 같은 백로그에 섞이면 우선순위가 정치가 된다. 제품 경계를 문장으로 먼저 고정해야 엔지니어링 경계도 따라온다.

브라우저 한계가 드러난 지점
브라우저 mesh로 미디어를 붙이면 작은 방에서는 충분히 동작한다. 문제는 한계가 “연결 실패”로만 나타나지 않는다는 점이었다.
- 모바일에서 권한 화면이 길어지면 상대는 빈 방을 본다
- 새로고침은 새 peer처럼 행동한다
- 탭 백그라운드 전환이 track을 멈춘다
- 파일과 영상은 같은 WebRTC 이름 아래 전혀 다른 실패 모드를 가진다
이 지점에서 “방 UI 안에 기능을 더 붙이자”는 반사신경이 위험해진다. 기능이 늘수록 설명해야 할 상태가 늘고, 설명하지 못한 상태는 불안으로 읽힌다.
또한 브라우저 차이는 기능 표보다 문장 표로 드러난다. 같은 “파일 받기” 버튼이라도 Chrome과 Safari에서 사용자가 기다려야 하는 이유가 다르다. 방 제품이 그 차이를 모두 흡수하려 하면, 회의 화면이 전송 콘솔로 변한다. 이 관찰이 나중에 PonsWarp 분리
한계를 숨기면 데모는 예뻐지고 운영은 지옥이 된다. 나는 반대로 가기로 했다. 한계를 제품 문장의 재료로 쓰자. 사용자가 지금 권한 화면에 있는지, 네트워크 경로를 찾는지, 상대를 기다리는지 말할 수 있다면, 브라우저는 약점이 아니라 설계 제약으로 다루어진다.
방을 제품의 중심에서 한 칸 옆으로 옮기다
다시 잡은 경계는 이랬다.
| 단계 | 책임 | 실패 시 사용자 문장 |
|---|---|---|
| 요청 접수 | 용건·맥락 수집 | 무엇을 요청했는지 다시 쓸 필요 없음 |
| 수락/일정 | host 판단 | 언제 가능한지 보임 |
| 세션 권한 | 입장 가능 여부 | 왜 못 들어가는지 설명 |
| 실시간 방 | 미디어·협업 | 연결 복구 중인지 보임 |
| 후속 기록 | 남길 것 | 다음에 이어서 할 수 있음 |
room은 독립 서비스가 아니라 요청이 통과하는 한 단계가 된다. 이렇게 보면 연결 실패도 제품 전체 붕괴가 아니라, 특정 단계의 복구 사건이 된다.
이 표가 생긴 뒤로는 기능 우선순위가 바뀌었다. “더 예쁜 방”보다 “요청이 권한으로 바뀌는 경로”가 먼저였다. public desk에서 요청이 들어오고, host가 확인하고, 필요하면 booking 상태가 붙고, 그 결과가 session authority로 변환된다. room은 그 authority를 소비한다. 통화가 끝나면 기록이나 후속 액션으로 다시 돌아간다.
이 흐름을 인정하면 빈 방 링크를 기본 경로로 두면 안 된다는 결론이 나온다. 링크는 여전히 필요하지만, 링크만으로 제품이 시작되면 안 된다. 요청 없는 입장은 예외 경로여야 하고, 예외 경로의 UX는 복구용이어야 한다. 기본 경로와 예외 경로를 같은 화면 밀도로 대우하면 운영이 영원히 복구 모드에 산다.
구현에서 남긴 불변조건
이후 구현에서 지키려 한 조건은 단순했다.
- 요청 없는 빈 방은 기본 경로가 아니다
- signaling/BFF/TURN은 숨기되, 실패는 숨기지 않는다
- 파일 전송처럼 리듬이 다른 기능은 방 안의 장식으로 때우지 않는다
- 사용자가 보는 문장과 내부 상태 이름을 분리한다
- “연결됨”과 “일 가능”을 같은 배지로 표시하지 않는다
특히 상태 분리가 중요했다. 내부적으로는 connecting 하나여도, 사용자는 “카메라 권한”, “상대 대기”, “네트워크 우회”를 다르게 느낀다. 상태 머신을 문서화하기 전에 UI를 붙이면 제품 얼굴이 먼저 망가진다.
운영 관점에서도 불변조건로그에 room id만 남기고 request id가 없으면, 장애가 났을 때 “어느 요청의 방이 죽었는지”를 복구하기 어렵다. 실시간 제품의 관측 단위를 peer connection이 아니라 요청-세션 축으로 잡아야 사후 분석이 된다.
불변조건을 문서로만 남기면 금방 깨진다. 코드 리뷰 질문으로 바꿔 두는 편이 낫다. “이 변경은 요청 없는 입장을 더 쉽게 만드는가?”, “실패 문장이 내부 상태 이름을 그대로 노출하는가?”, “파일 경로가 방 상태 머신에 새 결합을 만드는가?” 이 질문들이 통과하지 못하면 기능이 좋아 보여도 경계를 무너뜨린다.
마치며
PonsLink는 “실시간 방을 만든 이야기”로 시작하고 싶지 않았다. 한 번 놓친 연결을 다시 붙일 때, 맥락이 같이 돌아와야 한다는 불편에서 시작했다.
지금 다시 보면 첫 질문은 이것이었다. 방이 먼저인가, 요청이 먼저인가.
나는 요청이 먼저라고 답했고, 그 답이 이후 WebRTC 지옥과 PonsWarp 분리 연결 기술은 문을 연다. 그 문 앞에 어떤 용건을 세워 둘지는 제품이 정해야 한다.
다음에 같은 문제를 본다면, 방 UI 스케치보다 요청-권한-기록 표를 먼저 쓰겠다. 그 표가 비어 있으면 실시간 기능은 아무리 화려해도 공중에 뜬 문이다. 요청이 먼저라는 원칙을 실제 화면 동선으로 옮기면 순서가 바뀐다. 랜딩에서 바로 “방 만들기”를 강조하기보다, 용건을 남기고 host가 확인할 수 있는 입구를 앞에 둔다. 방이 열리는 시점은 권한이 생긴 뒤다. 이 순서를 지키면 빈 방 대기가 줄고, host의 첫 질문이 “누구세요?”에서 “남겨 주신 용건 기준으로 진행할까요?”로 바뀐다.
물론 긴급 통화처럼 요청을 건너뛰어야 하는 장면은 남는다. 문제는 예외를 기본값으로 승격하는 일이다. 예외 경로는 짧게, 기본 경로는 맥락이 있게. 그 비대칭를 제품이 의식적으로 유지해야 한다. 모든 입장을 똑같이 부드럽게 만들려다 보면, 결국 모든 입장이 맥락 없는 입장이 된다.