# 마지막 문자가 Σ인 Unicode 사용자 이름으로 프로필 페이지 로드 시 오류 발생

**URL:** https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182
**Category:** Bug
**Created:** [12월 13, 2020, 11:24오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182 "2020-12-13T23:24:31Z")
**Posts on this page:** 17
**Page:** 2

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 17, 2020, 7:37오전 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/21 "2020-12-17T07:37:19Z")

</div>

먼저 도움 주셔서 정말 감사합니다! 어쨌든 흥미로운 버그네요.

문제는 라우트가 사용 가능하지 않아서 데이터베이스를 통해 사용자 이름을 변경해야 할 수도 있다는 겁니다. 😅

쿼리를 제공해 주실 수 있을까요?

> [@sam](#):
>
> 또한 `허용되는 유니코드 사용자 이름 문자` 설정을 사용하여 사용자 이름에서 Σ의 사용을 금지할 수 있습니다. 이렇게 하면 이 문제가 다시 발생하지 않도록 보장할 수 있습니다.

그것은 불가능하다고 생각합니다. 그렇게 하면 그리스에서 Σ로 시작하는 많은 이름들이 금지되어 해당 사용자의 Facebook 로그인이 사실상 깨질 테니까요. 특정 정규식 패턴을 금지할 수 있을까요? 그러면 Σ가 적어도 끝부분에 위치하도록 보장할 수 있을 것 같습니다.

---

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 17, 2020, 12:45오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/22 "2020-12-17T12:45:54Z")

</div>

다른 회원 한 명이 정확히 동일한 문제로 가입했습니다. 제가 여기서 강조하려는 점은, 우리에게는 이것이 경계 사례(edge-case)가 아니라는 것입니다. 그리스어 이름 중 이름 끝에 Σ(또는 ς)를 사용하는 이름이 실제로 수천 개나 됩니다.

그리고 불행히도, 우리의 주요 연령층(40세 이상)을 고려하면, 페이스북에 이름을 전부 대문자로 적어둔 회원들이 매우 많습니다 (😅) . 따라서 페이스북 로그인을 사용하면 이름이 사용자 이름 필드에 그대로 복사됩니다…

---

<div class="post-metadata">

### Author: ![gerhard](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/gerhard/32/119479_2.png) [@gerhard](https://meta.discourse.org/u/gerhard)
#### Post date: [12월 17, 2020, 3:34오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/23 "2020-12-17T15:34:57Z")

</div>

> [@sam](#):
>
> 흥미롭게도 Postgres도 여기서 실패합니다:

Postgres에 대해서는 걱정하지 않으셔도 됩니다. SQL에서는 항상 `username_lower`를 비교해야 하며 `LOWER()`에 의존해서는 안 됩니다. `username_lower`는 단순히 사용자명의 소문자 버전이 아니기 때문입니다. 우리는 유니코드 정규화도 적용하고 있습니다.

> [@sam](#):
>
> 아마도 `username_lower`를 계산하는 내부 Discourse 메서드에서 이 특이한 경우를 특별 처리해야 하지 않을까요?

동의합니다. 지금은 `User.normalize_username`에 우회 처리를 추가하는 것으로 충분할 것입니다. Ruby 버그 관련 토론에서 이미 논의가 이루어지고 있으며, 쉬운 해결책이 없어 보입니다. 하지만 우리는 운이 좋습니다. 사용자명의 마지막 문자만 확인하면 되기 때문입니다. 이는 완전한 문장에서 처리하는 것보다 훨씬 쉽습니다.

---

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 17, 2020, 3:51오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/24 "2020-12-17T15:51:55Z")

</div>

> [@gerhard](#):
>
> 사용자 이름의 마지막 문자를 확인

음… 이 사용자 이름은 어떻게 처리될까요?

대문자: ΧΡΗΣΤΟΣ\_ΠΑΝΑΓΙΩΤΑΚΟΠΟΥΛΟΣ  
소문자: χρηστος\_παναγιωτακοπουλος 또는 χρηστοσ\_παναγιω타κοπουλος

NodeJs는 이를 올바르게 처리하지만, 사용자 이름의 마지막 문자만 확인한다면 문제가 지속될 것입니다.

 ![Screenshot 2020-12-17 at 17.50.13](https://global.discourse-cdn.com/meta/original/3X/c/e/ce68d752a904266cda62bb4063baa8f9ae95077f.png)

* * *

(그리스어 남성 이름의 거의 모든 것, 이름과 성 모두, Σ로 끝납니다)

---

<div class="post-metadata">

### Author: ![gerhard](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/gerhard/32/119479_2.png) [@gerhard](https://meta.discourse.org/u/gerhard)
#### Post date: [12월 17, 2020, 3:57오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/25 "2020-12-17T15:57:36Z")

</div>

맞아. 그래도 전체 구현보다는 훨씬 쉬울 거야. 밑줄이나 대시, 그리고 숫자 같은 특정 기호만 신경 쓰면 되니까. 충분히 해볼 만할 것 같아.

---

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 17, 2020, 4:27오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/26 "2020-12-17T16:27:53Z")

</div>

이 특정 버그 논의에서 벗어나고 있다는 건 알지만, 이 문제를 생각할 때마다 이를 피할 수 있다는 사실을 무시할 수 없습니다.

discourse에서 사용자를 userId로 참조하는 API 경로와 username으로 참조하는 경로가 혼재되어 있다는 것을 발견했습니다. userId를 중심으로 더 일관되게 만들어서는 안 될까요?

예를 들어, 현재 카테고리/태그에서처럼 URL에 username과 userId를 모두 포함하는 방식(`https://meta.discourse.org/u/chrispanag/4387`)을 도입하는 건 어떨까요?

그냥 아이디어입니다 😅

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [12월 18, 2020, 8:13오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/28 "2020-12-18T20:13:20Z")

</div>

> [@gerhard](#):
>
> Postgres에 대해 걱정할 필요는 없습니다. SQL에서는 항상 `username_lower`를 비교해야 하며, `LOWER()` 함수에 의존해서는 안 됩니다. `username_lower`는 단순히 사용자 이름의 소문자 버전이 아니기 때문입니다. 유니코드 정규화도 적용하고 있습니다.

이 작업을 위해서는 정리 작업이 필요할 것입니다. `LOWER()` 호출이 몇 군데 있는 것을 분명히 확인했습니다.

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [12월 20, 2020, 10:00오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/29 "2020-12-20T22:00:40Z")

</div>

> [@chrispanag](#):
>
> 지금 카테고리/태그에서 일어나는 것과 비슷한 것을 구현하는 건 어떨까요?

지난 몇 년간 이 문제가 계속 제기되어 왔습니다: [Update quotes and mentions when username is changed - #10 by sam](https://meta.discourse.org/t/update-quotes-and-mentions-when-username-is-changed/39836/10?u=sam)

2016년에 @eviltrout은 이에 반대했는데, 요즘 그의 입장이 어떻게 변했는지 확실하지 않습니다.

어쨌든, 저는 Discourse에서 이 PR으로 우회 해결책을 마련했습니다:

> <https://github.com/discourse/discourse/pull/11540>
>
> Final sigma is not lower cased correctly in Ruby causing issues with routing.
> 
> T…his works around the issue by downcasing all usernames containing a sigma using JS.

이 PR은 시그마(σ)로 시작하는 사용자 이름을 소문자로 변환하여 Facebook의 신규 가입 문제를 처리합니다. 즉, Spiros의 사용자 이름과 최종 시그마(ς)를 사용하는 다른 사용자들의 이름만 소문자로 수정하면 이 문제가 장기적으로 해결될 것입니다.

(PR 병합 대기 중)

---

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 20, 2020, 11:17오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/30 "2020-12-20T23:17:05Z")

</div>

감사합니다 @sam! 정말 큰 도움이 되었습니다.

> [@sam](#):
>
> 즉, Spiros의 사용자 이름과 파이널 시그마를 가진 다른 사용자들의 이름을 소문자로 변경하면 이 문제가 장기적으로 해결될 것입니다.

Rails 콘솔에서 이를 수행할 수 있는 방법이 있나요? (사용자가 손상되어 있어 관리자 패널을 통해 처리할 수 있는 방법이 없거든요…)

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [12월 20, 2020, 11:35오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/31 "2020-12-20T23:35:04Z")

</div>

> [@chrispanag](#):
>
> 레일즈 콘솔에서 이걸 할 수 있는 방법이 있나요? (유저가 깨져서 관리자 패널로는 도저히 할 수 없거든요…)

네, 이렇게 하면 됩니다

```plaintext
./launcher enter app
rails c
u = User.find_by(username: 'ΣΠΥΡΟΣ')
u.username = 'σπυρος'
u.username_lower = 'σπυρος'
u.save!`
```

---

<div class="post-metadata">

### Author: ![eviltrout](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/eviltrout/32/5275_2.png) [@eviltrout](https://meta.discourse.org/u/eviltrout)
#### Post date: [12월 21, 2020, 4:55오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/32 "2020-12-21T16:55:19Z")

</div>

여전히 모든 곳에 id를 사용하는 것을 좋아하지는 않지만, 그럴 만한 이유가 많은 경우들이 있다는 점은 이해합니다.

그런 경우에는 URL에 넣을 수 있는 어떤 값이든 username이라고 가정하고, `id-username` 같은 형태를 선호하겠습니다. 라우터가 이를 무시해도 괜찮습니다. 하지만 링크를 공유할 때, 어떤 것을 링크하고 있는지 최소한 감은 잡을 수 있을 테니까요.

---

<div class="post-metadata">

### Author: ![Falco](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/falco/32/179432_2.png) [@Falco](https://meta.discourse.org/u/Falco)
#### Post date: [12월 21, 2020, 5:51오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/33 "2020-12-21T17:51:25Z")

</div>

PostgreSQL의 경우, 적절한 정렬 규칙(collation)을 설정하면 문제가 해결되는 것으로 보입니다:

```plaintext
➜ discoursesmall git:(master) psql -d discourse_development
psql (13.1 (Ubuntu 13.1-1.pgdg20.10+1))
Type "help" for help.

discourse_development=# SELECT lower('ΣΠΥΡΟΣ');
 lower  
--------
 σπυροσ
(1 row)

discourse_development=# SELECT lower('ΣΠΥΡΟΣ' COLLATE "und-x-icu"); 
 lower  
--------
 σπυρος
(1 row)

```

출처: [PostgreSQL: Re: BUG #15805: Problem with lower function for greek sigma (Σ) letter](https://www.postgresql.org/message-id/5d80ac2d-3f1f-4d60-88a8-5602eb9978a3%40manitou-mail.org)

---

<div class="post-metadata">

### Author: ![chrispanag](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/chrispanag/32/105853_2.png) [@chrispanag](https://meta.discourse.org/u/chrispanag)
#### Post date: [12월 21, 2020, 9:55오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/34 "2020-12-21T21:55:53Z")

</div>

사실 ‘ß’ → ‘SS’ (독일어)와 같이 유사한 변환을 수행하는 다른 유니코드 문자들도 있습니다.

Discourse가 이러한 문자들을 어떻게 처리하는지 살펴보는 것도 흥미로울 것입니다.

또한 이 리소스도 확인해 보실 수 있습니다:

> **[text-transform CSS property - CSS | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/text-transform)**
>
> The text-transform CSS property specifies how to capitalize an element's text. It can be used to make text appear in all-uppercase or all-lowercase, or with each word capitalized. It also can help improve legibility for ruby.

---

<div class="post-metadata">

### Author: ![sam](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/sam/32/102149_2.png) [@sam](https://meta.discourse.org/u/sam)
#### Post date: [12월 21, 2020, 10:28오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/35 "2020-12-21T22:28:19Z")

</div>

> [@eviltrout](#):
>
> 그 경우 `id-username` 형태를 더 선호합니다.

개인적으로 사용자를 위한 Stack Overflow 스타일의 라우팅을 매우 좋아합니다:

`https://stackoverflow.com/users/17174/sam-saffron`

Discourse에서는 다음과 같은 형태가 될 것입니다:

`https://meta.discourse.org/u/17174/sam-saffron`

이 방식은 케이크도 먹고 남도 먹는, 일석이조의 효과를 줄 수 있습니다. 물론 "URL 어딘가에 17174 같은 ID가 있는 것을 싫어합니다. 사용자 이름은 안정적이니까요"라는 반론도 충분히 이해합니다.

그럼에도 불구하고, 우리는 지금까지 기존 라우팅 방식으로 잘 유지해 왔으며, 몇 년에 한 번 정도 엣지 케이스가 발생하는 수준입니다.

---

<div class="post-metadata">

### Author: ![lucasbasquerotto](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lucasbasquerotto/32/133376_2.png) [@lucasbasquerotto](https://meta.discourse.org/u/lucasbasquerotto)
#### Post date: [12월 22, 2020, 12:18오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/36 "2020-12-22T12:18:52Z")

</div>

위에서 설명한 대로, **적어도 사용자 프로필 페이지에서는** 사용자 이름 대신 id를 사용하거나(또는 둘 다 사용하되 id에만 의존하는 방식) 사용하는 것이 도움이 될 것 같습니다. 이렇게 하면 사용자 이름에 문제가 발생했을 때, 관리자(Admin)가 rails 콘솔에서 명령어를 실행할 필요 없이 Discourse UI를 통해 사용자 이름을 변경할 수 있습니다.

---

<div class="post-metadata">

### Author: ![gerhard](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/gerhard/32/119479_2.png) [@gerhard](https://meta.discourse.org/u/gerhard)
#### Post date: [12월 22, 2020, 1:43오후 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/37 "2020-12-22T13:43:25Z")

</div>

> [@chrispanag](#):
>
> 사실 ‘ß’ → ‘SS’ (독일어)와 같이 유사한 변환이 있는 다른 유니코드 문자들이 있습니다.

우리가 신경 써야 할 것은 소문자 변환 시 JavaScript와 Ruby 구현 방식이 다른 문자들뿐입니다. 반대 방향은 아닙니다.

독일어 소문자 "ß"를 대문자로 변환하는 규칙은 없습니다. “SS”, “SZ” 또는 새로운 대문자 “ẞ”(네, 미묘한 차이가 있습니다)가 될 수 있습니다. 이 과정을 역으로 수행하는 것은 "ẞ"에 대해서만 가능하며, 이는 Ruby와 JavaScript에서 모두 올바르게 작동합니다.

* * *

저는 다음을 수행해야 한다고 생각합니다:

1. 즉각적인 문제를 해결하기 위해 @sam의 우회 방법을 병합합니다.
2. `LOWER(username)`를 SQL 쿼리에서 제거합니다. 이는 단순히 잘못된™ 관행이기 때문입니다(예: 유니코드 정규화 누락).
3. Ruby가 근본적인 문제를 수정하기를 바랍니다.
4. 장기적으로: 루트에 사용자 ID를 추가하는 방안을 고려해 보겠습니다. 가장 어려운 부분은 인용과 멘션을 어떻게 처리할지 파악하는 것일 것입니다.

---

<div class="post-metadata">

### Author: ![znedw](https://avatars.discourse-cdn.com/v4/letter/z/b5e925/32.png) [@znedw](https://meta.discourse.org/u/znedw)
#### Post date: [2월 23, 2021, 9:33오전 UTC](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182/38 "2021-02-23T09:33:50Z")

</div>

관련이 있을지 확실하지 않지만, 유니코드 이름을 가진 몇몇 사용자의 기록이 /log에 흩어져 있습니다.

```plaintext
ActionView::Template::Error (No route matches {:action=>"show", :controller=>"users", :username=>"ζηεδψ"}, possible unmatched constraints: [:username])

```

[이전 페이지](https://meta.discourse.org/t/unicode-username-with-as-the-final-char-leads-to-an-error-loading-profile-page/173182.md?page=1)
