4P by GN⁺ | ★ favorite | 댓글 3개
  • 버그 신고, 할인 코드 입력, 배송 추적처럼 사람이 ID를 읽고 전달하는 과정에서는 O/0, I/l/1/7 같은 시각적 모호성이 쉽게 입력 오류로 이어짐
  • 혼동은 글꼴과 손글씨에 따라 커지며, 5/S, 2/Z, 8/B, 6/G, 9/q/g처럼 구분이 흐려지는 조합이 반복적으로 나타남
  • 고객지원, 오류 ID, 제품 ID처럼 사람이 직접 다루는 ID에서는 무작정 큰 문자 집합보다 읽기 쉬운 문자 집합을 고르는 편이 실사용 품질에 더 중요함
  • 대소문자를 구분하면 5자 ID에서 418,195,493개 조합을 만들 수 있지만, 구분하지 않으면 5,153,632개로 줄어들어 길이와 안전성의 절충이 필요함
  • 대소문자 구분 ID는 짧게 만들기 좋지만, 일부 서드파티 시스템이나 프로토콜이 대소문자 비구분으로 동작할 수 있어 통합 단계에서 문제가 생길 수 있음

사람이 읽는 ID에서 생기는 문자 혼동

  • ID를 버그 리포트, 할인 코드 입력, 배송 추적 같은 시스템 상호작용에서 적거나 전달할 때, 피할 수 있는 문자 혼동이 사용자 경험을 망칠 수 있음
  • 대표적인 시각적 모호 문자 조합은 다음과 같음
    • O / 0: 슬래시나 점이 없는 0에서는 글자 O와 숫자 0이 비슷해 보일 수 있음
    • I / l / 1 / 7: 대문자 I, 소문자 l, 숫자 1, 숫자 7은 여러 인쇄물과 손글씨에서 구분하기 어려움
    • 5 / S, 2 / Z, 8 / B, 6 / G, 9 / q / g: 특정 글꼴, 스타일화된 폰트, 손글씨에서 혼동될 수 있음
  • 예시 문자열 9qg6G8B2Z5SIl170O는 Arial, Helvetica, Courier, Times, Verdana, Georgia, Tahoma, Impact, Comic Sans 같은 여러 시스템 글꼴에서 비교 대상으로 사용됨
  • Il처럼 일부 조합은 여러 글꼴에서 계속 모호하게 보일 수 있고, 9qg처럼 직접 손으로 쓰면 더 헷갈리는 조합도 있음
    • 특히 문제가 커지는 ID 유형

    • 고객지원에서 주고받는 할인 코드
    • 배송이나 물류에서 쓰는 추적 코드
    • 문제 재현과 문의 처리에 필요한 오류 ID
    • 제품 식별에 쓰이는 제품 ID

대소문자 구분과 문자 집합의 절충

  • ID가 abcABC를 같은 값으로 볼지 여부는 생성 규칙을 정할 때 함께 결정해야 함
  • 대소문자를 구분하면서 시각적으로 모호한 문자를 제외하면 선택 가능한 문자는 53개
  • 대소문자를 구분하지 않으면 선택 가능한 문자는 22개로 줄어듦
  • ID 길이에 따른 가능한 조합 수는 다음과 같음
    • 5자, 대소문자 구분: 53^5 = 418,195,493
    • 5자, 대소문자 비구분: 22^5 = 5,153,632
    • 8자, 대소문자 구분: 53^8 = 62,259,690,411,361
    • 8자, 대소문자 비구분: 22^8 = 54,875,873,536
  • 결국 선택지는 짧지만 혼동 가능성이 큰 ID길지만 읽기 쉬운 ID 사이의 균형임
  • 대소문자를 모두 쓰면 언젠가 대소문자 비구분 서드파티 시스템이나 프로토콜에서 예상 밖 동작을 만날 수 있음
    • 한 상용 시스템은 사용자가 iDid를 서로 다른 ID로 선택할 수 있게 했지만, 존재하지 않는 ID 조회 시 대소문자 비구분 매칭을 수행해 잘못된 데이터를 반환했음
    • 해당 버그에는 “편의”를 위한 동작이라는 답이 돌아왔음

문자 하나가 아니라 조합도 피해야 할 수 있음

  • 문자 조합 자체가 다른 문자처럼 보이는 경우도 있음
    • rnm처럼 보일 수 있음
    • vvw처럼 보일 수 있음
  • 이런 이유만으로 문자를 대거 제외하면 선택 가능한 문자 집합이 너무 줄어들 수 있으므로, 생성 단계에서 특정 조합만 피하는 방식이 더 현실적일 수 있음
  • 말로 전달하는 상황에서는 음성 유사성도 함께 고려할 수 있음
    • 예를 들어 bp는 소리 내어 말할 때 비슷하게 들릴 수 있음

참고할 만한 기존 방식

  • Crockford의 Base32는 모호한 문자를 같은 값으로 디코딩하고, 우발적인 외설 표현 문제도 고려함
  • Open Location Code23456789CFGHJMPQRVWX 문자 집합을 사용함
    • 이 문자 집합은 시각적으로 모호한 문자를 피하고 일반 언어의 단어 철자도 피하도록 선택됨
    • 다만 6G, 9Q를 모두 포함함

댓글과 토론

발음까지 고려한건 정말 경이롭네요

Hacker News 의견들
  • 직장에서 수백만 대 기기에 일련번호를 붙여 출하했는데, 헷갈리는 문자나 숫자를 전혀 제외하지 않아 고객들이 제대로 읽는 데 크게 고생한 적이 있음
    고객이 말한 값을 바탕으로 가능한 모든 오타 조합을 만들어 공장 데이터베이스와 맞는 것만 보여주는 정규식 스크립트를 만들어야 했고, 이후 날짜 같은 다른 정보를 대조해 실제 일련번호를 추정했음
    더 아이러니한 건 몇몇 자릿수는 절대 바뀌지 않았고, 어떤 자리는 공장 구분용으로 0, 1, 2만 쓰면 됐기 때문에 애초에 전체 문자 집합이 필요 없었다는 점임. 마치 8천조 대를 만들 거라고 믿었던 듯함

    • 일련번호에서 사업 정보가 새는 것을 막는 게 유익하거나 적어도 유익하다고 여겨지는 경우가 많음
      예를 들어 제품에 1, 2, 3처럼 순차 번호를 붙이면 적은 표본만으로도 전체 판매량을 꽤 쉽게 추정할 수 있음. 유효한 일련번호를 추측하기 어렵게 만들어 환불 악용 같은 일을 막는 데 도움이 될 때도 있음
      물론 이런 우려가 있어도 읽기 어려운 문자를 피하는 조치를 같이 할 수 있고, 오히려 어떤 번호 체계를 쓸지 누군가 생각하고 있다는 뜻이라면 이런 문제를 더 잘 인식했어야 함. 실제로는 누군가 30초쯤 고민하고 “자릿수를 이만큼 쓰면 절대 안 떨어지겠네, 끝” 했을 가능성이 커 보임
    • 생각해 보니 Apple 일련번호에 모음이 없는 이유도 이 때문이거나 적어도 한 요인일 수 있겠음
      기기 일련번호에는 자음과 숫자만 쓰는 것 같음
  • 인코딩은 사용자에 따라 달라져야 함. Base32, 특히 Crockford와 RFC 4648은 짧게 표현하기 좋고 모호하지 않은 알파벳과 그 이유도 잘 갖추고 있음
    하지만 사용자가 소리 내어 말해야 한다면 s/key RFC 1751처럼 “TIDE ITCH SLOW REIN RULE MOT” 같은 단어 목록 표현이 나을 수 있음
    직접 단어 목록을 만들면 안 됨. 관용구, 동음이의어, 방언 등 숨어 있는 함정이 끝도 없음. “wet clam butterfly” 같은 대형 사고를 의도치 않게 만들지 말아야 함

    • 안타깝게도 그 예시는 “TIED HITCH SLOE REIGN RULE MOW”로도 들릴 수 있음. 패리티 비트 2개만으로는 이 해독이 틀렸다고 확신할 수도 없음
      이 예시가 나온 RFC 1751 [0]은 구두 전달용 인코딩을 상정하지 않았고, 사용자가 더 쉽게 “읽고, 기억하고, 입력”하게 만드는 목적임
      전문가끼리 구두로 전달한다면 대문자 26개만 쓰고 NATO 음성 문자표에 의존하는 선택이 합리적임. 하지만 훈련받지 않은 사용자에게 잡음 많은 구두 환경에서 코드를 받아내는 문제는 여전히 풀리지 않은 문제임
      [0] https://datatracker.ietf.org/doc/html/rfc1751
    • 1994년에 나온 것이니 어느 정도 감안해야겠지만, 그래도 꽤 우스울 정도로 나쁜 RFC임
      “키가 있는 메시지 다이제스트 알고리즘 MD5를 사용해야 하며, 충분히 강하다”는 대목부터 웃김
      “대부분의 사람이 읽고, 기억하고, 입력하기 어렵다”까지는 좋았는데, “영어 단어는 사람들이 기억하고 입력하기 훨씬 쉽다”로 감. 대부분의 사람은 영어를 모른다는 문제가 있지만, 단어 목록을 바꾸면 되지 않나 싶었더니 “상호운용성 때문에 언어별 사전을 따로 두는 것은 바람직하지 않다”고 함
      결국 전 세계가 영어 알파벳 26자는 배웠으니 단어 몇 개쯤 추가해도 괜찮다는 식인데, char Wp[2048][4] = […] 안에는 초급자에게 적합한 흔한 단어가 아니라 “WAD, BESS, MERT…” 같은 것들이 들어 있음. 심지어 “ORR? AGEE EGAN HAAS!!”, “GAUL FLAM! DRAB!”까지 나옴
    • 이런 종류의 ID는 뭐라고 부르는지 궁금함
  • 이 얘기를 보니 예전 일이 떠오름. 아픈 날 몸이 아픈 걸 잊으려고 임의 진법 산술을 하는 장난감 모듈을 만들었고, 쉬워서 CPAN에 올렸음
    그 모듈이 https://metacpan.org/pod/Math::Fleximal
    내가 만든 사소한 것들 중 이건 절대 지원 요청이 들어오지 않을 거라고 생각했는데 실제로 들어왔음. 이유는 16진수를 영숫자 코드로 바꾸는 예제를 넣어뒀는데, 누군가 긴 숫자를 읽기 쉬운 코드로 바꾸는 데 그대로 쓰자는 멋진 생각을 했기 때문임
    모듈은 잘 동작했지만, 이게 어딘가의 프로덕션에 들어갔다는 사실이 꽤 황당했음

  • 글에서는 손글씨로도 구분하기 어려운 문자를 피하자고 강조하는데, 예시 표에는 숫자 7이 들어 있음. 누군가의 7과 1을 구분하기 어려웠던 일이 셀 수 없이 많음
    7에 가로획을 그으면 도움이 되지만 많은 사람이 그렇게 쓰지 않아서, 7인지 세리프가 달린 1인지 확신하기 어려울 때가 있음

    • 글에서는 소리로 구분하기 어려운 “B”(Bravo)와 “P”(Papa)도 언급했지만, 훨씬 비슷하게 들리는 “F”(Foxtrot)와 “S”(Sierra) 는 빠져 있음
      둘은 거의 구분되지 않을 때가 있음. NATO/항공 표준 알파벳(Alpha, Bravo, Charlie, Delta...)을 쓸 수도 있지만, 고객층이 아주 엄격히 제한되어 있지 않다면 큰 도움이 안 됨. 이런 조합도 피하는 편이 좋음
      ID 문자열이 조금 길어지더라도 문자를 읽고 말하고 듣는 능력을 최대화하는 편이 훨씬 낫고, 시간과 짜증을 훨씬 많이 줄여줌
    • 손글씨에서 1이 7처럼 보이는 경우는 겪어본 적이 없음. 보통은 I나 l이 1과 헷갈림
      어떤 손글씨 스타일에서 1이 7과 비슷해지는지 궁금함. 7의 윗가로획만으로도 충분히 구분될 것 같음
    • 앞부분에는 빠졌지만, “시각적으로 모호한 사전” 섹션에는 1과 7이 둘 다 들어 있지 않음
  • 대문자와 소문자를 둘 다 쓰면 언젠가 대소문자를 구분하지 않는 제3자 시스템이나 프로토콜에 당할 가능성이 큼
    실제로 사용자가 대소문자를 구분하는 ID를 고를 수 있게 해 iDid를 서로 다른 값으로 허용하면서도, 존재하지 않는 ID를 조회하면 대소문자 무시 매칭을 해서 엉뚱한 데이터를 반환하는 상용 시스템을 본 적이 있음
    이 버그를 보고했더니 “편의를 위한 기능”이라고 답했음

  • Nintendo Switch에서 DLC 일련번호를 입력할 때 모호한 문자 키가 화면 키보드에서 비활성화되어 있었는데, 꽤 괜찮은 사용자 경험이라고 느꼈음
    이는 일련번호가 애초에 모호한 문자 없이 생성된다는 뜻임. 이 UX가 운영체제에 내장된 것인지, 아니면 플레이하던 게임인 Mario + Rabbids Sparks of Hope에만 있던 것인지는 잘 모르겠음

  • 오픈소스 비밀번호 관리자 KeepassXC는 비밀번호를 더 읽기 쉽게 하려고 색을 사용함. 대문자, 소문자, 숫자, 기호 같은 문자 종류마다 다른 색을 쓰는 방식임
    아주 단순한 아이디어지만, 특히 무작위 비밀번호에서는 이미 가독성 높은 글꼴을 쓰고 있더라도 큰 도움이 됨

    • Bitwarden도 모호하지 않은 글꼴과 세 가지 색을 씀. 문자는 기본색, 숫자는 파란색, 기호는 빨간색인데 정말 좋음
      비밀번호 중심 소프트웨어가 아무 색 구분 없이 모호한 글꼴로 문자를 렌더링하는 걸 허용하는 건 이해하기 어려움
    • KeepassXC 비밀번호 생성기에서는 제외 문자 목록도 쉽게 추가할 수 있음
      TV 리모컨 같은 인터페이스로 긴 비밀번호를 입력하다가 l1|I를 헷갈렸다는 걸 깨닫는 순간이 너무 화나서 직접 제외해 두고 있음
    • 색각 이상이 있는 입장에서는 이 아이디어가 싫음
  • 평소 자주 마주치는 문제를 다룬 글이라 읽기 좋았음
    종이에 2단계 인증 백업 코드를 적을 때마다 o/0, v/u, 5/S 같은 문자를 지나칠 때 불안해짐. 그래서 이런 문자는 일부러 다르게 보이도록 약간 꾸며 쓰게 됐음
    “음성 유사성” 부분을 보니 와이파이 비밀번호를 고를 때가 떠오름. 한 문장으로 공유해도 모호하지 않게, 초등학교 3학년도 철자를 쓸 수 있고 자음이 여러 개 들어간 흔한 단어를 원했고 결국 “vacation”을 골랐음

    • 내 규칙은 모든 숫자 아래에 점을 찍는 것임. 이렇게 하면 5/S, 0/O, 8/B 같은 문제가 해결됨. 실제로 문제가 되는 쌍은 각자의 손글씨에 따라 달라짐
      정말 확신이 안 서면 NATO/항공 알파벳 [1]도 덧붙임. 예를 들어 U가 있으면 U에서 시작해 대각선으로 Uniform이라고 적음
      약간의 규율만 필요함. 10년 넘게 이렇게 해왔고 2단계 인증 코드를 잃어버린 적이 한 번도 없음
      [1] NATO와 항공 코드의 실제 차이에 대한 꼬투리 잡기는 안전하게 /dev/null로 보내도 됨
    • 이런 걸 손으로 종이에 적는 사람이 있다는 게 믿기지 않음
      머리가 병목됨
  • 이런 대화가 좋음. 최첨단이거나 흥미진진한 주제는 아닐 수 있지만, 사람과 기계 모두의 삶을 더 쉽게 만드는 데 상당한 의미와 힘이 있음
    이런 것들은 잘했을 때 오히려 아무도 알아차리지 못하는 모범 사례 영역이기도 함. 세부 사항에 대한 성의와 진정성이 “당연히 그래야 하는 것”으로 묶여 특별히 칭찬받지 못하는 건 아쉬운 현실임

  • 글의 실수를 짚자면, 9qg6G8B2Z5SIl170O (ariel)에서 글꼴 이름은 Ariel이 아니라 Arial임. 인어공주는 여기 없음