노딩에는 이제 닉네임이 없습니다.
11월 5일 배포부터 새로 들어오는 사람에게 '형용사+단어' 랜덤 닉네임을 만들어 주지 않습니다. 그 자리에는 다녀간 방의 제목 두 개를 엮은 이름이 들어갑니다. "퇴근길의 알고리즘", "양자역학과 월드컵" 같은 이름입니다.
다녀간 방이 적어 이름이 아직 없는 사람은 점 한두 개와 #7Q2K 같은 네 글자 코드로만 보입니다.
닉네임이 왜 문제였나
노딩은 이름도 이메일도 묻지 않는 익명 앱입니다. 처음에는 흔한 방식대로 가입 순간에 랜덤 닉네임을 하나 뽑아 줬습니다.
어떤 사람인지 알 길이 없으니 글 옆의 이름은 장식이었습니다.
다시 뽑을 수 있는 값이라 신고나 차단을 묶어 둘 기준으로도 약했습니다. 설계 문서에도 적어 뒀습니다. "닉은 재추첨으로 신고 회피 가능."
대안은 세 가지였습니다.
- 사람이 직접 닉네임을 입력하게 하기
- 이름을 아예 없애고 글만 보여 주기
- 그 사람의 노드맵을 이름으로 쓰기
직접 입력은 가장 흔한 길입니다. 입력을 받으면 중복 검사, 금칙어 검사, 변경 이력 관리가 따라옵니다. "이름을 묻지 않는다"는 약속도 깨집니다.
글만 보여 주는 건 익명 게시판의 방식입니다. 노딩은 방과 방 사이를 옮겨 다니는 앱이라 "아까 그 사람이 여기도 왔네" 같은 연속성이 필요했습니다. 알림, 차단, 신고가 매달릴 안정적인 키도 있어야 했습니다.
남은 건 노드맵입니다. 노딩은 사용자가 다녀간 방과 그 사이의 이동을 노드맵으로 쌓습니다. 그 사람을 가장 잘 설명하는 건 이미 그 지도였습니다.
그래서 정체성을 "노드맵 3계층"으로 정했습니다.
- 작은 미니 지도(시길)
- 선택적인 이름
- 지도가 없으면 점 하나
그 밑을 숨은 불변 코드가 받칩니다.
어떻게 만들었나
코드가 뼈대입니다
맨 아래에는 publicCode가 있습니다. 기기별 식별자(localId)를 sha256으로 해시한 상위 20비트를 base32 네 글자로 바꾼 값입니다. #7Q2K처럼 생겼고 한 번 정해지면 바뀌지 않습니다.
신고, 차단, 관리자 조회, 알림에서 사람을 가리키는 값입니다. 실제 연결은 내부 식별자가 합니다.
이름은 비거나 바뀔 수 있습니다. 시길은 검색할 수 없는 그림입니다. 그래서 항상 존재하고 검색 가능한 값이 하나 필요했습니다.
export function getDisplayRef(user) {
return user.nodeName || user.publicCode || getPublicCode(user.localId);
}
서버에서 사용자를 표시하는 곳은 모두 이 함수 하나를 거칩니다. 이름이 있으면 이름, 없으면 코드입니다. 코드마저 아직 안 만들어졌으면 그 자리에서 계산합니다.
이름은 중심 노드와 이웃 하나
노드맵의 중심 노드와 거기 연결된 이웃 노드 하나를 연결어로 묶어 만듭니다. 순서는 이렇습니다.
- 노드맵에서 중심 노드를 찾습니다. 연결된 노드가 셋 이상일 때만 중심이 생깁니다.
- 중심 노드의 제목에서 단어 하나를 뽑습니다. "KBO 외국인 선수 영입"이면 "KBO"만 남겨 "귀찮음의 KBO"처럼 짧게 만듭니다. 제목 전체를 이으면 글 본문처럼 읽혀 혼동을 줬기 때문입니다. "모음", "얘기", "근황" 같은 범용어는 재료에서 뺍니다. 긴 한국어 복합어는 앞 세 글자만 씁니다.
- 이웃 노드 중 중심성이 높은 여섯 개를 후보로 둡니다.
localId를 djb2 해시한 씨드로 하나를 고릅니다. 같은 사람은 항상 같은 이웃을 고르고, 다른 사람은 같은 지도라도 다른 이웃을 고릅니다. - 연결어도 씨드로 고릅니다. "의", "와/과", "에서", "속" 네 가지입니다. "와/과"는 앞 글자 받침을 보고 정합니다. 두 단어가 모두 영어면 "of", "&", "at", "in"을 씁니다. 부자연스러워도 문법만 맞으면 그대로 둡니다.
- 후보를 금칙어 검사에 통과시킵니다. 방 제목과 같은 엄격 모드입니다. 통과한 첫 후보가 이름이 되고, 전부 떨어지면 이름 없이 코드로 돌아갑니다.
이름은 24시간 디바운스로 유지합니다. 노드맵이 바뀔 때마다 이름이 흔들리면 "아까 그 사람"을 못 알아보기 때문입니다. 지도를 이루는 노드 집합이 그대로면 다시 만들지 않습니다.
시길은 10분 단위로 갱신합니다.
지도가 없는 사람은 점 하나
설계할 때는 열 중 여섯쯤이 한 번 들렀다 가는 사람이라고 봤습니다.
지금 재 보니 열 중 아홉이 하루만 쓰고 떠나더라구요. 최근 90일, 노드에 들어온 사용자 기준입니다.
이 사람들에게 가짜 이름을 지어 주지 않기로 했습니다. 지도가 없으면 점 하나입니다.
설계 문서에서는 이걸 "정직 불변식"이라고 불렀습니다. 시길과 이름은 이런 지도에만 붙습니다.
- 실제로 있는 지도
- 연결된 지도
- 살아 있는 지도
- 보이는 지도
잘린 이름도 가짜 이름도 없습니다. 지도가 없으면 점과 코드뿐입니다.

이름이 나가기 전에 먼저 막은 것
이름은 방 제목으로 만들어집니다. 성인물로 격리된 방이나 신고로 플래그가 붙은 방의 제목이 재료로 새어 나가면 안 됩니다.
그래서 이름과 시길의 소스가 되는 쿼리에 피드와 같은 노출 필터를 먼저 넣었습니다. 삭제됐거나 삭제 대기 중인 방, 숨긴 방, 플래그 붙은 방은 재료에서 빠집니다.
이 필터가 들어가기 전에는 이름을 바깥에 내보내지 않았습니다.
남은 문제
"퇴근길의 알고리즘"처럼 잘 붙는 이름이 있습니다. 어색하게 붙는 이름도 있습니다.
설계 때부터 "부자연스러워도 문법만 맞으면 그대로"라고 정했습니다. 어색한 재료가 되는 범용어 목록을 손으로 늘리는 중입니다. 완전한 답은 아직 없습니다.
한국어와 영어 단어가 한 이름에 섞이는 경우는 영어 연결어를 따로 둬서 줄였습니다. 영어 방이 많아지면 다시 손봐야 합니다.
서버 응답에는 아직 nickname 필드가 남아 있습니다. 구버전 앱과의 호환 때문입니다. 값은 이미 표시 이름입니다. 필드 이름까지 지우는 건 구버전이 충분히 빠진 뒤의 일입니다.
배포 전에 쌓인 광장 활동 로그는 48시간 동안 옛 닉네임으로 남았다가 사라집니다.
제일 큰 문제는 제품 쪽에 있습니다.
연결된 방이 셋 이상이어야 이름이 생깁니다. 앞서 잰 대로 많은 사람이 그 전에 떠납니다.