DOCS/troubleshooting/250809_happybell80_username시스템구축.md
happybell80 99acb38162 docs: Username 시스템 구축 트러블슈팅 문서
- 사용자 식별 문제 해결 (User → happybell80)
- 프론트엔드 리팩토링 및 빌드 오류
- 로빙 엔드포인트 불일치 해결
- JWT 토큰 설계 및 username 매핑 전략
2025-08-09 18:12:13 +09:00

5.9 KiB

Username 시스템 구축 및 사용자 식별 개선

작성일: 2025년 8월 9일 작성자: happybell80

오후 5시 30분

사용자 식별 문제 - "User"만 표시되는 현상

문제 상황

  • 로그인 후 사용자명이 "User"로만 표시
  • happybell80(실제 사용자 ID)이 표시되지 않음
  • 로빙과 통신 시 사용자 식별 불가능

원인 분석

  1. 데이터 구조 혼란

    Google Email: goeun2dc@gmail.com
    User ID (UUID): aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
    Username: happybell80 (저장할 곳 없음)
    
  2. JWT 토큰 문제

    • 토큰에 email, username 정보 누락
    • {"sub": "unknown"}만 포함
  3. DB 스키마 문제

    • users 테이블에 username 필드 없음
    • 이메일과 사용자 ID 매핑 불가능

해결 과정

1단계: DB 스키마 변경
-- users 테이블에 username 필드 추가
ALTER TABLE users ADD COLUMN username VARCHAR(50) UNIQUE;

파일 수정:

  • app/models/user.py: username 필드 추가
  • migrations/add_user_workspace_tables.sql: 스키마 업데이트
2단계: Auth-server Username 처리
# app/providers/gmail.py
# 이메일 → username 매핑
email_to_username = {
    'goeun2dc@gmail.com': 'happybell80',
    'eagle0914@gmail.com': 'eagle0914',
}
username = email_to_username.get(user_email, user_email.split('@')[0])

# JWT 토큰 생성
jwt_token = create_access_token(data={
    "sub": username,  # username을 subject로 사용
    "email": user_email,
    "name": user_name,
    "username": username
})
3단계: 프론트엔드 표시 개선
// auth-context.tsx
const username = payload.username || userId;
setUser({
  id: username,  // username을 ID로 사용
  email: email,
  name: name
});

// user-menu.tsx
const displayName = user?.name || user?.id || user?.email?.split('@')[0] || 'User';

결과

  • 로그인 시 "happybell80" 정상 표시
  • 게임 API 호출 시 올바른 user_id 전달
  • 로빙과 사용자 정확한 매핑

오후 6시 00분

프론트엔드 리팩토링 및 빌드 오류 해결

문제 상황

  1. 중복된 로그인 UI 코드
  2. 프로필 클릭 시 로그아웃 실행
  3. wouter의 useNavigate 없음 오류
  4. 로그인 후 페이지 리다이렉트 문제

해결 과정

UserMenu 컴포넌트 통합

생성: src/components/user-menu.tsx

  • 로그인/프로필 UI 통합
  • DropdownMenu로 로그아웃 분리
  • Header와 GameLayout에서 재사용
export function UserMenu({ variant = 'default', showName = true }) {
  // 로그인 버튼 또는 프로필 드롭다운
  if (!isAuthenticated) {
    return <Button onClick={() => setShowLoginDialog(true)}>로그인</Button>;
  }
  
  return (
    <DropdownMenu>
      <DropdownMenuTrigger>
        {/* 프로필 이미지/아이콘 */}
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem onClick={logout}>로그아웃</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
wouter 라우팅 수정
// 변경 전
import { useNavigate } from "wouter";  // 에러!
const [, navigate] = useNavigate();

// 변경 후
import { useLocation } from "wouter";
const [, setLocation] = useLocation();
setLocation('/game');  // 페이지 이동
페이지 리로드 제거
// 변경 전
window.location.reload();  // 현재 페이지 잃음

// 변경 후
window.history.replaceState(null, '', window.location.pathname);

결과

  • 코드 50% 감소
  • 빌드 오류 해결
  • UX 개선 (프로필 드롭다운, 페이지 유지)

오후 6시 30분

로빙 엔드포인트 불일치 문제

문제 상황

Gateway → /api/chat 요청
rb10508_micro → /api/message만 지원
결과: 404 Not Found

해결 과정

rb10508_micro 엔드포인트 추가
# app/api/endpoints.py
@router.post("/chat")
async def chat_endpoint(request: MessageRequest):
    """Gateway/nginx 호환 chat 엔드포인트 - /message의 alias"""
    return await message_endpoint(request)
포트 충돌 해결
  • rb10508_test 컨테이너 제거
  • rb10508_micro만 10508 포트 사용

결과

  • Gateway와 로빙 통신 정상화
  • 404 에러 해결

교훈 및 베스트 프랙티스

1. 사용자 식별 설계

  • Username은 시스템 전체에서 일관되게 사용
  • UUID는 내부용, Username은 표시용
  • 이메일은 로그인용으로만 사용

2. JWT 토큰 설계

{
  "sub": "happybell80",      // 주 식별자
  "email": "goeun2dc@gmail.com",
  "name": "김종태",
  "username": "happybell80"   // 명시적 username
}

3. 컴포넌트 재사용

  • 중복 UI는 즉시 컴포넌트화
  • Props로 변형 가능하게 설계
  • 한 곳에서 관리

4. 라우터 호환성

  • wouter는 경량 라우터 (useLocation만 제공)
  • react-router-dom과 API 다름
  • 라이브러리 특성 먼저 확인

5. 포트 관리

  • 컨테이너별 고유 포트 할당
  • network_mode: host 사용 시 주의
  • 재배포 시 포트 해제 대기 필요

6. 점진적 마이그레이션

  • DB 스키마 변경은 신중하게
  • 기존 데이터 호환성 고려
  • 하드코딩 → DB 조회로 단계적 전환

최종 시스템 구성

사용자 플로우

  1. Google OAuth 로그인 (goeun2dc@gmail.com)
  2. Username 매핑 (happybell80)
  3. JWT 토큰 발급 (username 포함)
  4. 프론트엔드 표시 (김종태 or happybell80)
  5. 로빙 API 호출 (user_id: happybell80)

데이터 매핑

Email → Username → Display Name
goeun2dc@gmail.com → happybell80 → 김종태
eagle0914@gmail.com → eagle0914 → Eagle

핵심 파일

  • auth-server/app/models/user.py - username 필드
  • auth-server/app/providers/gmail.py - 매핑 로직
  • frontend-customer/src/components/user-menu.tsx - 통합 UI
  • frontend-customer/src/contexts/auth-context.tsx - 토큰 처리