[P2P 프로토콜] DHT와 BitTorrent식 peer discovery를 다시 보기
파일 전송 버튼을 눌렀는데 전송률 그래프가 움직이지 않는 장면을 먼저 떠올린다. 사용자는 “느리다”고 말하지만, 실제로는 아직 보낼 상대를 찾지 못한 상태일 수 있다. bytes가 흐르기 전에도 P2P는 이미 실패할 수 있다. 아래에서는 P2P을 중심으로, 깨진 가정과 다시 그은 경계를 순서대로 남긴다.

peer discovery가 가린 실제 문제
DHT는 모든 피어를 전부 아는 구조가 아니다. 가까운 key 공간을 따라 물어보고, 그 과정에서 살아 있는 피어 후보를 조금씩 좁힌다. 이 사실을 제품 문구에 그대로 노출할 필요는 없지만, 내부 상태에서는 숨기면 안 된다. | 항목 | 맡는 일 | 흔한 오해 | 남겨야 할 단서 | |---|---|---|---| | tracker | 시작 시 peer list를 빠르게 알려 준다 | 중앙 주소록이 있으면 P2P가 아니라는 오해 | 응답 지연, 빈 목록 비율, abuse 차단 기록 | | DHT | 가까운 key 공간을 따라 피어를 물어본다 | 모든 피어를 전부 알고 있다는 오해 | 질의 깊이, bootstrap 성공 여부, 후보 수 | | magnet link | 파일 위치보다 content identifier와 발견 힌트를 건넨다 | 링크만 있으면 바로 받을 수 있다는 오해 | info hash, tracker 힌트, 사용자 대기 시간 | | bootstrap node | 처음 들어온 노드가 네트워크를 찾게 해 준다 | 한 번만 넣으면 끝난다는 오해 | 실패율, 재시도 횟수, 차단된 네트워크 조건 | 사용자는 peer discovery라는 말을 몰라도 된다.
대신 지금 상대를 찾는 중인지, 후보를 다시 확인하는 중인지, 초대가 만료되었을 수 있는지 알아야 한다. “P2P 연결 중”이라는 한 문구는 편하지만, 실패했을 때 다음 행동을 주지 못한다. 이 기준은 비용에도 영향을 준다. discovery가 늦어서 사용자가 여러 번 재시도하면 전송 byte는 적어도 서버 요청과 지원 문의는 늘어난다. 반대로 tracker를 너무 강하게 믿으면 abuse 대응 비용이 커진다.
그래서 발견 계층은 성능 최적화 이전에 운영 비용을 어디에 둘지 정하는 문제다.

처음에는 중앙 서버 없이 다 될 줄 알았다
물론 이 선택이 DHT를 포기한다는 뜻은 아니다. 나중에 swarm 규모가 생기면 DHT나 더 넓은 발견 계층을 볼 수 있다. 다만 그때도 출발점은 같다. 어떤 피어를 누구에게 물어봤고, 실패했을 때 어떤 대안을 보여 줬는지가 남아 있어야 한다. 배포 순서도 이 경계에 맞춘다. 먼저 tracker 응답과 빈 목록 비율만 계측한다. 다음에는 bootstrap 실패를 별도 문구로 분리한다. 마지막에 magnet link의 힌트가 오래되었거나 peer list 검증이 실패한 경우를 다른 재시도 경로로 보낸다.
한 번에 전부 바꾸면 어디서 좋아졌는지 알 수 없다. 마지막 기준은 서버 유무가 아니다. 책임이 어디로 이동하는지, 그 이동을 제품 화면과 로그가 설명할 수 있는지가 더 중요하다. DHT와 BitTorrent식 peer discovery를 다시 읽은 이유도 여기에 남는다. P2P를 더 순수하게 만드는 기술을 찾기보다, 피어를 찾지 못한 순간을 운영 가능한 이름으로 바꾸는 일이다. 처음에는 연결이 붙은 뒤의 DataChannel이나 TCP 전송만 생각했다.
BitTorrent를 다시 읽고 나서야 순서가 바뀌었다. 파일 조각을 누가 갖고 있는지, 어떤 피어가 살아 있는지, 처음 들어온 노드가 누구에게 물어봐야 하는지 정하지 못하면 전송 계층은 출발하지 못한다. *전송 실패처럼 보이는 화면을 발견 실패, 빈 peer list, bootstrap 차단, 검증 실패로 나눠 보기 위한 실패 지도.

DHT의 구성요소
PonsLink나 PonsWarp에 붙인다면 peer discovery는 파일 전송 버튼보다 먼저 시작된다. 버튼은 하나여도 내부에서는 초대 확인, tracker 조회, DHT 또는 directory 후보 확인, 조각 검증이 다른 시각에 일어난다. 이 순서를 지우면 롤백도 어렵다. tracker 경로를 탄 사용자를 모르면 점진 배포, 비용 제한, abuse 대응도 같이 흐려진다. PonsWarp 같은 브라우저 제품에서는 완전한 공개 DHT보다 signaling 기반 초대와 제한된 peer directory가 먼저 현실적이다.
브라우저 탭은 오래 살아 있는 node가 아니고, 탭이 닫히면 주소록의 한 점도 바로 사라진다. 작은 제품이 거대한 DHT를 흉내 내면 구현은 흥미롭지만 운영 단서가 부족해질 수 있다. tracker가 있으면 시작은 빠르다. 대신 운영자는 주소록의 신뢰, abuse 대응, 차단 정책을 책임져야 한다. DHT를 쓰면 중앙 의존은 줄어들지만 bootstrap, 악성 피어 필터링, 긴 대기 시간, 개인 정보 노출 가능성을 다시 봐야 한다.
책임이 서버에서 사라진 것이 아니라 다른 위치로 이동한 것이다. | 사용자에게 보일 문장 | 내부 상태 | 다음 행동 | 보존할 데이터 | |---|---|---|---| | 상대를 찾는 중입니다 | tracker 요청 또는 DHT 질의 시작 | 잠시 대기 | 요청 시각, 후보 수 | | 초대 정보가 오래되었을 수 있습니다 | magnet link 힌트 불일치 | 새 초대 요청 | info hash, 초대 생성 시각 | | 연결 후보를 다시 확인합니다 | peer list 검증 | 재시도 또는 다른 경로 제안 | 실패한 후보, 검증 결과 | | 네트워크에서 발견이 막혔습니다 | bootstrap 실패 또는 차단 | relay/초대 기반 경로 안내 | bootstrap 주소, 실패 코드 |
지원 문의를 읽을 때도 같은 기준을 쓴다. “상대가 안 떠요”라는 문장은 전송 실패가 아니라 발견 실패일 가능성이 높다. 이때 운영자가 확인할 첫 줄은 업로드 속도가 아니라 peer 후보 수와 첫 응답 시각이다. 그래프가 비어 있는 시간을 계측해야 P2P가 느린지, 아직 P2P가 시작되지 않았는지 구분할 수 있다. 내가 피하고 싶은 화면은 “연결 실패” 하나로 닫히는 화면이다. 그 문구는 개발자에게도 사용자에게도 너무 늦은 정보다. 발견 단계가 나뉘면 실패가 조금 길어 보일 수 있지만, 대신 재시도 버튼과 지원 로그가 같은 원인을 가리킨다. P2P를 설명할 때 서버/클라이언트/사용자 중 누구 문장으로 말하는지 먼저 고르면 논쟁이 줄었다.
peer를 찾을 수 있는 조건
상태 이름은 구현 편의보다 운영 편의에 가깝다. 예를 들어 discovering 하나로 남기면 대시보드는 단순해 보인다. 하지만 tracker 응답이 비었는지, DHT bootstrap이 막혔는지, magnet link가 가리키는 info hash와 실제 조각 검증이 어긋났는지 알 수 없다. 사용자는 모두 같은 기다림으로 느끼지만 고치는 사람은 다른 파일을 열어야 한다. peer discovery는 전송의 부속 기능처럼 보이지만 실제 제품에서는 먼저 실패한다. tracker가 늦게 답하면 peer list가 비고, bootstrap node를 모르면 DHT는 시작되지 않는다. magnet link에 info hash가 있어도 그 hash를 가진 피어를 찾지 못하면 사용자는 아무것도 받지 못한다. 또 하나의 현실 조건은 브라우저 수명이다. 데스크톱 앱이나 서버 노드와 달리 브라우저 탭은 오래 살아 있다는 약속을 하기 어렵다. 백그라운드로 밀리거나 닫히면 peer list의 한 점이 사라진다. 그래서 작은 제품에서는 공개 DHT보다 제한된 directory, 초대 재발급, 실패 문구를 먼저 다듬는 편이 실제 사용자에게 더 정직하다. *처음 연결 버튼 뒤에 tracker, DHT, magnet link, bootstrap node가 어떤 순서로 걸리는지 확인하기 위한 흐름 노트.
discovery가 실패하는 경우
실패 문구도 이 순서에 맞춰 짧게 둔다. 후보가 없을 때는 상대가 아직 열지 않았을 가능성을 말하고, bootstrap이 막혔을 때는 네트워크를 바꾸거나 초대를 새로 만들 수 있음을 말한다. 조각 검증이 실패했을 때는 보안을 이유로 다시 확인한다고 설명한다. 같은 빨간 오류라도 사용자가 할 수 있는 일이 달라야 한다. 이 문제를 “P2P 연결 실패”라고 한 줄로 묶으면 운영자가 볼 수 있는 단서가 사라진다.
같은 대기 화면이라도 원인은 다르다. tracker가 비었는지, DHT 질의가 돌고 있는지, 상대 피어가 사라졌는지, 조각 검증이 아직 끝나지 않았는지 나눠야 한다. 운영 로그에는 기술 용어와 사용자 문구를 같이 남긴다. 내부 이벤트는 tracker.requested, dht.bootstrap_failed, magnet.hint_mismatch, peer_list.empty처럼 갈라지고, 화면에는 사용자가 고를 수 있는 행동만 남는다. 용어를 쉬운 말로 바꾸는 일보다 중요한 것은 책임을 흐리지 않는 일이다.
측정값도 전송량보다 앞쪽에 둔다. peer 후보 수, 첫 응답까지 걸린 시간, bootstrap 실패율, tracker 응답 지연, 빈 peer list 비율을 분리한다. 이 숫자들이 있어야 발견 실패를 전송 실패와 구분할 수 있다. 이 표는 UI 문구를 예쁘게 만들기 위한 장식이 아니다. 같은 문의가 들어왔을 때 지원 로그와 제품 화면이 같은 사건을 가리키게 하려는 장치다. 사용자가 본 문장과 내부 이벤트 이름이 연결되어야 “P2P가 느리다”는 말을 전송률, 발견 시간, 후보 검증 실패로 다시 풀 수 있다.
'[P2P 프로토콜 DHT와 BitTorrent식 peer discovery를 다시 보기'을 관통하는 질문은 이 섹션에서도 같았고, 답만 더 구체적이 됐다.
측정 가능한 신호
사용자 화면에서는 이 복잡함을 모두 보여 줄 필요가 없다. 다만 “다시 시도” 버튼이 무엇을 다시 하는지는 정해야 한다. tracker를 다시 묻는 것인지, bootstrap 주소를 바꾸는 것인지, 초대 링크를 새로 만드는 것인지, peer list 검증만 반복하는 것인지가 다르면 기다림의 의미도 다르다. 같은 버튼이라도 내부 행동이 다르면 로그 이름을 나눈다. 내부 상태를 나눌 때도 너무 많은 이름을 만들 필요는 없다.
처음에는 tracker-waiting, peer-list-empty, bootstrap-failed, magnet-hint-mismatch, piece-verifying 정도면 충분하다. 중요한 것은 이름의 개수가 아니라 화면 문구, 로그, 재시도 정책이 같은 경계를 보게 하는 일이다. 이렇게 나누면 제품 판단도 조금 차분해진다. tracker를 쓰는 것은 중앙 서버를 부끄럽게 남기는 일이 아니라 빠른 시작과 abuse 대응을 맡길 위치를 고르는 일이다. DHT를 쓰는 것은 서버를 지우는 일이 아니라 bootstrap과 악성 피어 필터링을 다른 곳에서 책임지는 일이다.
또 하나 놓치기 쉬운 부분은 보안과 남용 대응이다. peer를 찾는 과정에서는 IP, 접속 시간, 후보 목록처럼 조심해야 할 단서가 생긴다. 공개 DHT를 바라볼수록 악성 피어가 거짓 후보를 뿌리거나 특정 info hash 주변을 긁어 갈 가능성도 같이 본다. 작은 실험이라도 후보를 그대로 믿지 않고, 검증 실패와 차단 이유를 따로 남겨야 한다. DHT 실험은 피어 몇 개를 띄우고 조각이 오가는지 보는 데서 끝나지 않는다.
bootstrap 주소가 틀렸을 때, tracker가 늦게 답할 때, 같은 info hash를 가진 피어가 서로 다른 조각을 들고 있을 때를 따로 만들어야 한다. 발견이 늦으면 전송률 그래프는 시작도 못 한다. 그래서 처음 적용 후보는 “DHT 전체”가 아니라 발견 상태의 분리다. 초대 링크로 시작하는 경로, 제한된 directory로 찾는 경로, tracker가 비었을 때 다시 안내하는 경로를 나눠 두면 나중에 DHT를 붙이더라도 어디가 바뀌었는지 알 수 있다.
적용할 때 체크할 것
여기서 잡은 질문은 하나다. 피어를 어디서, 누구 책임으로 찾을 것인가? tracker와 DHT는 서로를 지우는 선택지가 아니라, 발견 책임을 어디에 남길지 보여 주는 두 방식에 가깝다. 이 질문을 먼저 놓으면 구현 순서가 바뀐다. 전송 최적화보다 발견 상태의 이름을 먼저 만들고, 성공 데모보다 빈 peer list를 어떻게 설명할지 먼저 정한다. 그래야 나중에 DHT를 붙여도 기존 tracker 흐름과 비교할 기준이 생긴다.
- 처음 참여한 피어가 bootstrap node를 모르면 네트워크는 시작되지 않는다. - DHT는 모든 피어를 전부 아는 구조가 아니라 가까운 key 공간을 따라 물어보는 구조다. - tracker가 있으면 시작은 빠르지만 운영자는 주소록의 신뢰와 abuse를 책임져야 한다. - magnet link는 파일 위치가 아니라 content identifier와 발견 경로를 같이 건네는 약속에 가깝다. - 피어 발견은 개인 정보, 네트워크 정책, 차단 대응, 악성 피어 필터링과 바로 연결된다.
BitTorrent가 흥미로웠던 지점은 tracker와 DHT가 “중앙 vs 분산”의 단순한 대립이 아니라는 점이었다. tracker는 빠른 시작을 돕고, DHT는 tracker가 약하거나 사라졌을 때 swarm이 피어를 더듬어 찾게 한다. 어느 쪽도 발견 책임을 없애지는 않는다. *구현 전에 tracker, DHT, magnet link, bootstrap node의 책임을 한 번 더 갈라 보기 위한 점검표.
마치며
이 글에서 확인한 경계는 다음 변경의 기준으로 남겼다.