- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동 - book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서) - 빈 폴더 제거 (journey/assets/*)
4.8 KiB
4.8 KiB
Gateway 필드 변환 및 보안 문제 해결
오전 10:58
문제 1: 헬스체크 엔드포인트 추가
상황:
- 각 서비스마다 다른 헬스체크 엔드포인트
해결:
# 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_robeing_info(username: str):
"""username으로 직접 조회"""
query = text("""
SELECT ...
FROM workspace_member wm
JOIN user 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 우회message→text필드 변환 안됨- 422 Unprocessable Entity 에러
해결 방안 검토:
- nginx 모든 요청을 Gateway로 → Gateway 부하 문제
- rb10508_micro가 두 필드 모두 지원 → 실용적
문제 4: 사용자 인증 검증 부재 (보안 위험)
현재 상황:
// 누구나 헤더 조작 가능
fetch('/gateway/api/chat', {
headers: {'X-User-Id': 'admin'} // 위장 가능!
})
위험:
- 사용자 위장 가능
- 로빙 접근 권한 우회
- 데이터 유출 위험
필요한 조치:
- JWT 토큰 검증 로직 추가
- auth-server와 연동
- 실제 사용자 확인 필수
교훈
1. 헬스체크 설계
- 빠른 응답 우선 (복잡한 체크는 별도 엔드포인트)
- 루트 레벨 배치로 프리픽스 문제 회피
2. 식별자 설계
- Frontend는 사용자가 이해하는 값 사용 (username)
- Backend에서 내부 ID 변환
- UUID는 내부용으로만 사용
3. Gateway 아키텍처
- 모든 요청을 Gateway로 보내면 병목 발생
- 실용적 접근: 서비스가 유연하게 처리
- 점진적 마이그레이션 전략 필요
4. 보안 최우선
- 헤더 기반 인증은 위험
- JWT 토큰 검증 필수
- 신뢰 경계 명확히 설정
현업 패턴
Gateway 부하 해결:
- Service Mesh: 각 서비스에 Sidecar 프록시
- Gateway 클러스터링: 여러 Gateway 인스턴스
- 하이브리드: 중요 요청만 Gateway, 나머지 직접
실용적 해결:
class MessageRequest(BaseModel):
text: Optional[str] = None
message: Optional[str] = None
def get_text(self):
return self.text or self.message
- 두 경로 모두 지원
- Gateway 유무와 관계없이 작동
- 마이그레이션 유연성 확보
다음 작업
- 즉시: rb10508_micro에 message 필드 지원 추가
- 긴급: JWT 토큰 검증 구현
- 계획: Gateway 스케일링 전략 수립
- 장기: Service Mesh 도입 검토
오후 9:42
문제 5: Frontend 빌드 시 환경변수 미적용
증상:
- .env 파일에
VITE_ROBEING_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_ROBEING_API_URL 누락!
npm run build
해결:
export VITE_API_URL=http://localhost:8001
export VITE_ROBEING_API_URL=https://ro-being.com/gateway # 추가
npm run build
교훈
-
빌드 환경변수 체크
- CI/CD 스크립트에서 모든 환경변수 설정 확인
- .env 파일이 있어도 빌드 시점에 로드되는지 검증
- 빌드된 결과물에서 실제 값 확인
-
기본값 함정
- 하드코딩 기본값은 디버깅을 어렵게 만듦
- 환경변수 미설정 시 명확한 에러가 낫다
-
근본 원인 vs 임시방편
- rb10508_micro 수정은 임시방편
- Gitea Actions 수정이 근본 해결
- 구조적으로 올바른 해결 우선
-
서버 관리자 관점
- Gateway가 이미 모든 기능 제공
- 직접 프록시는 필드 변환 누락
- Gateway 경유가 정석