Code › tail-villain

언어 스위처가 문제가 아니었다

다국어 지원을 붙이며 드러난 로컬 UI 상태와 계정 설정, 대시보드 역할 분리 문제

언어 스위처를 눌렀는데 화면이 바뀌지 않는 것처럼 보였다.

기능이 아예 동작하지 않는 건 아니었다. 상태는 바뀌고 있었고, 일부 컴포넌트는 새 언어를 읽고 있었다. 문제는 화면 곳곳에 아직 하드코딩된 문구가 남아 있다는 데 있었다. 사용자는 내부 상태를 보지 않는다. 버튼을 눌렀는데 눈앞의 문장이 그대로면, 그냥 고장 난 것으로 느낀다.

그날 작업은 한국어와 영어를 지원하는 일이었지만, 막상 해보니 번역 파일을 추가하는 일보다 상태의 경계를 정하는 일이 더 중요했다. 무엇은 잠깐 바뀌는 UI 언어이고, 무엇은 계정에 저장되는 선호 언어인지 분리해야 했다.


처음에는 RPG 느낌이 강한 UI 실험을 따로 하고 있었다.

테일빌런은 빌런 면접관이라는 강한 캐릭터가 있고, 어두운 톤의 UI도 잘 맞는다. 하지만 그 실험에서 나온 문구와 세계관을 그대로 메인 제품에 합치면 위험했다. 재사용할 수 있는 레이아웃이나 다국어 구조는 가져올 수 있지만, 메시지 톤까지 같이 가져오면 제품이 갑자기 다른 방향으로 보일 수 있기 때문이다.

그래서 실험은 따로 보존하고, 메인에는 필요한 인프라만 옮겼다. 언어 provider, dictionary, switcher 같은 뼈대는 가져오되, 문구는 기존 제품의 톤에 맞춰 다시 정리했다. 이 구분이 없었으면 “다국어 지원” 작업이 어느새 “제품 톤 변경” 작업으로 섞였을 것이다.

코드에서는 작은 차이처럼 보이지만, 제품에서는 꽤 큰 차이다. 기능을 옮기는 것과 목소리를 옮기는 것은 다르다.


가장 먼저 걸린 건 언어 스위처의 책임이었다.

헤더에 있는 스위처는 빠르게 화면 언어를 바꿔보는 장치에 가깝다. 사용자가 한국어로 보다가 영어 UI가 어떻게 보이는지 확인할 수도 있고, 세션 중에 잠깐 언어를 바꿔볼 수도 있다. 이건 로컬 UI 상태에 가깝다.

반면 계정의 선호 언어는 조금 다르다. 다음에 로그인했을 때 어떤 언어로 시작할지, 서버에 저장해둘 기본값에 가깝다. 이 값은 프로필 설정에서 바꾸는 게 자연스럽다.

처음에는 이 둘이 섞였다. 글로벌 스위처를 누르는 순간 계정 선호 언어도 같이 바뀌는 흐름이 있었고, 이건 나중에 이상한 경험을 만들 수 있었다. 잠깐 영어로 확인해본 것뿐인데 계정 기본 언어가 바뀌어버리면, 사용자는 다음 접속 때 왜 언어가 바뀌었는지 이해하기 어렵다.

그래서 책임을 나눴다. 헤더 스위처는 로컬 UI 언어를 바꾸고, 프로필 설정은 계정에 저장되는 선호 언어를 바꾼다. 같은 “언어 변경”처럼 보이지만, 하나는 지금 화면의 상태이고 다른 하나는 사용자의 설정이다.

상태는 어디에 저장되는지가 곧 의미가 된다.


스위처가 고장 난 것처럼 보였던 이유도 결국 같은 문제였다.

상태는 바뀌는데 화면 대부분이 반응하지 않으면 사용자는 기능이 실패했다고 느낀다. 다국어 지원은 dictionary를 하나 만들고 끝나는 일이 아니었다. 대시보드, 로드맵 생성, 로드맵 상세, 인터뷰 시작, 인터뷰 세션처럼 사용자가 실제로 지나가는 흐름 전체가 같은 언어 상태를 읽어야 했다.

특히 인터뷰 화면에서는 언어가 더 민감하다. 테일빌런은 단순한 설정 페이지가 아니라, 사용자가 압박 상황을 연습하는 제품이다. 질문과 버튼, 피드백 문구가 섞여 보이면 몰입이 깨진다. 빌런이 날카롭게 질문하고 있는데 주변 UI가 다른 언어로 남아 있으면, 제품이 하나의 경험으로 느껴지지 않는다.

그래서 주요 로그인 이후 화면을 따라가며 하드코딩된 문자열을 걷어냈다. 완벽한 번역 품질을 만드는 것보다 먼저 필요한 건, 사용자가 언어를 바꿨을 때 화면이 확실히 반응한다는 감각이었다.

눈에 보이지 않는 상태 변경은 기능이 아니다. 사용자에게는 화면이 바뀌어야 기능이다.


같은 날 대시보드 구조도 손봤다.

기존 대시보드는 로드맵 목록과 진행 상태와 다음 행동이 한 화면에 섞여 있었다. 처음에는 편해 보였지만, 정보가 늘어나자 화면의 역할이 흐려졌다. 사용자가 로그인한 뒤 가장 먼저 봐야 하는 건 “내 로드맵이 몇 개 있는가”보다 “지금 무엇을 하면 되는가”에 더 가깝다.

그래서 /dashboard는 개요 화면으로 남기고, 로드맵 목록은 /dashboard/roadmaps로 분리했다. 대시보드는 진행도, 지금 복습할 것, 약한 토픽, 다음 행동을 보여주는 곳이 되고, 로드맵 라이브러리는 로드맵을 관리하는 곳이 됐다.

화면을 나누니 컴포넌트도 덜 억지스러워졌다. 오른쪽 패널이 길게 늘어나거나, sticky 레이아웃이 어색하게 스크롤을 만드는 문제를 단순 CSS로만 고치려 하면 계속 땜질이 된다. 애초에 한 화면이 너무 많은 일을 하고 있으면, 레이아웃 문제처럼 보이는 것도 정보 구조 문제일 수 있다.

진행 바와 레이더 차트도 이 흐름 안에서 들어갔다. 보여줄 데이터가 생겼으니 시각화를 붙인 게 아니라, 대시보드가 “다음에 무엇을 할지”를 알려주는 화면이 되어야 했기 때문이다. 로드맵 커버리지, 토픽 완료, 연습 커버리지, 복습 상태, 면접 성과 같은 값은 숫자로만 나열하면 잘 읽히지 않는다. 대시보드라면 상태를 한눈에 보이게 해야 했다.


이 작업에서 남은 감각은 꽤 단순하다.

다국어 지원은 번역 문제가 아니라 상태 문제였다. 언어가 어디에서 바뀌고, 어디에 저장되고, 어떤 화면이 그 변화를 읽어야 하는지 정해야 했다. 그 경계가 흐리면 사용자는 스위처가 고장 났다고 느끼고, 개발자는 상태는 정상인데 왜 안 보이지 하고 로그를 보게 된다.

대시보드도 비슷했다. 화면이 어수선한 이유가 컴포넌트가 못생겨서가 아닐 때가 있다. 한 화면에 서로 다른 책임이 섞여 있으면, 아무리 카드와 차트를 다듬어도 사용자는 다음 행동을 찾기 어렵다.

테일빌런은 빌런이 질문을 잘 던지는 제품이어야 하지만, 그 전에 사용자가 길을 잃지 않아야 한다. 언어 스위처든 대시보드든 결국 같은 문제였다. 제품이 알고 있는 상태를 사용자가 이해할 수 있는 형태로 보여주는 것.