DOCS/troubleshooting/250811_happybell80_Gateway필드변환문제.md
happybell80 158ea808e6 docs: Frontend 빌드 환경변수 문제 트러블슈팅 추가
- Gitea Actions에서 VITE_ROBING_API_URL 누락 발견
- 빌드 시점 환경변수 미적용으로 기본값 사용
- 근본 원인 해결 방법 문서화
2025-08-11 21:46:40 +09:00

4.9 KiB

Gateway 필드 변환 및 보안 문제 해결

오전 10:58

문제 1: 헬스체크 엔드포인트 표준화

상황:

  • 각 서비스마다 다른 헬스체크 엔드포인트
  • nginx/k8s 표준인 /healthz 필요

해결:

# rb10508_micro/app/main.py
@app.get("/healthz")
async def healthz():
    """단순 헬스체크 - 서비스 생존 확인만"""
    return {"status": "ok"}
  • 루트 레벨에 /healthz 추가
  • API 프리픽스 없이 접근 가능
  • 빠른 응답 (< 100ms)

문제 2: UUID 형식 오류

원인:

  • Frontend가 username(happybell80) 전송
  • Gateway가 UUID 형식 기대
  • invalid UUID 'happybell80': length must be between 32..36 characters

해결:

# robeing-gateway/app/database.py
async def get_robing_info(username: str):
    """username으로 직접 조회"""
    query = text("""
        SELECT ... 
        FROM workspace_members wm
        JOIN users u ON wm.user_id = u.id
        WHERE u.username = :username
    """)
  • users 테이블 JOIN으로 username → UUID 변환
  • UUID 체크 로직 제거 (Frontend는 항상 username 사용)

문제 3: nginx 직접 프록시 vs Gateway 라우팅

현재 구조 분석:

# 원래 설계 (문서)
Frontend → nginx(/gateway/) → Gateway(8100) → rb10508_micro

# 실제 nginx 설정
location ^~ /rb10508/ {
    proxy_pass http://192.168.219.52:10508/;  # 직접 프록시
}
location ^~ /gateway/ {
    proxy_pass http://localhost:8100/;  # Gateway 서비스
}

문제점:

  • /rb10508/는 Gateway 우회
  • messagetext 필드 변환 안됨
  • 422 Unprocessable Entity 에러

해결 방안 검토:

  1. nginx 모든 요청을 Gateway로 → Gateway 부하 문제
  2. rb10508_micro가 두 필드 모두 지원 → 실용적

문제 4: 사용자 인증 검증 부재 (보안 위험)

현재 상황:

// 누구나 헤더 조작 가능
fetch('/gateway/api/chat', {
  headers: {'X-User-Id': 'admin'}  // 위장 가능!
})

위험:

  • 사용자 위장 가능
  • 로빙 접근 권한 우회
  • 데이터 유출 위험

필요한 조치:

  • JWT 토큰 검증 로직 추가
  • auth-server와 연동
  • 실제 사용자 확인 필수

교훈

1. 헬스체크 설계

  • 표준 엔드포인트 사용 (/healthz)
  • 빠른 응답 우선 (복잡한 체크는 별도 엔드포인트)
  • 루트 레벨 배치로 프리픽스 문제 회피

2. 식별자 설계

  • Frontend는 사용자가 이해하는 값 사용 (username)
  • Backend에서 내부 ID 변환
  • UUID는 내부용으로만 사용

3. Gateway 아키텍처

  • 모든 요청을 Gateway로 보내면 병목 발생
  • 실용적 접근: 서비스가 유연하게 처리
  • 점진적 마이그레이션 전략 필요

4. 보안 최우선

  • 헤더 기반 인증은 위험
  • JWT 토큰 검증 필수
  • 신뢰 경계 명확히 설정

현업 패턴

Gateway 부하 해결:

  1. Service Mesh: 각 서비스에 Sidecar 프록시
  2. Gateway 클러스터링: 여러 Gateway 인스턴스
  3. 하이브리드: 중요 요청만 Gateway, 나머지 직접

실용적 해결:

class MessageRequest(BaseModel):
    text: Optional[str] = None
    message: Optional[str] = None
    
    def get_text(self):
        return self.text or self.message
  • 두 경로 모두 지원
  • Gateway 유무와 관계없이 작동
  • 마이그레이션 유연성 확보

다음 작업

  1. 즉시: rb10508_micro에 message 필드 지원 추가
  2. 긴급: JWT 토큰 검증 구현
  3. 계획: Gateway 스케일링 전략 수립
  4. 장기: Service Mesh 도입 검토

오후 9:42

문제 5: Frontend 빌드 시 환경변수 미적용

증상:

  • .env 파일에 VITE_ROBING_API_URL=https://ro-being.com/gateway 설정됨
  • 하지만 빌드된 JS 파일에는 하드코딩 기본값 /rb10508 사용
  • 결과: nginx가 Gateway 우회하여 직접 프록시

원인 발견:

# .gitea/workflows/deploy.yml
- name: Build application
  run: |
    export VITE_API_URL=http://localhost:8001
    # VITE_ROBING_API_URL 누락!
    npm run build

해결:

export VITE_API_URL=http://localhost:8001
export VITE_ROBING_API_URL=https://ro-being.com/gateway  # 추가
npm run build

교훈

  1. 빌드 환경변수 체크

    • CI/CD 스크립트에서 모든 환경변수 설정 확인
    • .env 파일이 있어도 빌드 시점에 로드되는지 검증
    • 빌드된 결과물에서 실제 값 확인
  2. 기본값 함정

    • 하드코딩 기본값은 디버깅을 어렵게 만듦
    • 환경변수 미설정 시 명확한 에러가 낫다
  3. 근본 원인 vs 임시방편

    • rb10508_micro 수정은 임시방편
    • Gitea Actions 수정이 근본 해결
    • 구조적으로 올바른 해결 우선
  4. 서버 관리자 관점

    • Gateway가 이미 모든 기능 제공
    • 직접 프록시는 필드 변환 누락
    • Gateway 경유가 정석