DOCS/troubleshooting/250819_happybell80_타임존버그및스크롤.md

4.2 KiB

이전 대화 불러오기 타임존 버그 및 스크롤 위치 문제 해결

날짜: 2025-08-19
작업자: happybell80 & Claude
관련 프로젝트: rb10508_micro, frontend-customer, robeing-gateway

오전 12시 30분

문제 1: 이전 대화 불러오기 버튼이 작동하지 않음

증상

  • "이전 대화 불러오기" 버튼 클릭 시 빈 배열 반환
  • /gateway/api/history?before=2025-08-18T02:22:30.707Z&limit=30{"messages":[],"has_more":false}
  • 8월 16, 17일 대화가 분명히 존재하는데도 못 가져옴

원인 분석

  1. 프론트엔드 시간 변환 문제

    • 서버가 보낸 2025-08-18T11:22:30.707422 (타임존 없음)
    • JavaScript new Date() → 로컬 시간(KST)으로 해석
    • toISOString() → UTC로 변환하며 -9시간 → 2025-08-18T02:22:30.707Z
  2. 서버 타임존 비교 버그 (핵심 원인)

    # rb10508_micro/app/core/memory/storage.py
    msg_time = datetime.fromisoformat(timestamp_str)  # naive datetime
    before_time = datetime.fromisoformat(before.replace('Z', '+00:00'))  # aware datetime
    if msg_time >= before_time:  # TypeError 발생!
    
    • Python 에러: can't compare offset-naive and offset-aware datetimes
    • 265번 라인 except: continue로 에러 무시 → 모든 메시지 건너뜀

해결 방법

서버 코드 수정 - 모든 datetime을 UTC aware로 통일

def to_utc_aware(dt_str: str) -> datetime:
    """모든 datetime 문자열을 UTC aware datetime으로 통일하는 순수 함수"""
    if not dt_str:
        return None
    dt = datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
    return dt.replace(tzinfo=timezone.utc) if dt.tzinfo is None else dt

# 사용
msg_time = to_utc_aware(timestamp_str)
before_time = to_utc_aware(before)

문제 2: Gateway 우회 접근

증상

  • 초기: /rb10508/api/history 직접 호출 (JWT 검증 우회)
  • 수정 후: /gateway/api/history 정상 호출

해결 방법

  • .gitea/workflows/deploy.yml 수정
  • 서버 .env 파일에서 환경변수 읽도록 변경
# 기존
export VITE_ROBEING_API_URL=https://ro-being.com/rb10508

# 수정
if [ -f /home/admin/frontend-customer/.env ]; then
  export $(cat /home/admin/frontend-customer/.env | grep -v '^#' | xargs)
fi

문제 3: 이전 대화 로드 시 스크롤이 맨 아래로 이동

증상

  • 이전 대화 불러오기 → 스크롤이 맨 아래로 → 방금 불러온 대화 안 보임

원인

  • useEffectmessages 변경 시마다 scrollToBottom() 실행

해결 방법

스크롤 위치 기억 방식 (카톡과 동일)

const oldHeight = messagesContainerRef.current?.scrollHeight || 0;
// ... 메시지 로드 ...
requestAnimationFrame(() => {
  if (messagesContainerRef.current) {
    messagesContainerRef.current.scrollTop += 
      (messagesContainerRef.current.scrollHeight - oldHeight);
  }
});

교훈

1. 타임존은 명시적으로 처리

  • 서버는 항상 타임존 정보 포함 (Z 또는 +09:00)
  • naive datetime 절대 사용 금지
  • 비교 전 모든 datetime을 동일 타임존으로 통일

2. 예외 처리는 구체적으로

  • except: 같은 광범위한 예외 처리 금지
  • 최소한 except Exception as e: + 로깅
  • 타임존 에러를 "데이터 없음"으로 착각하는 상황 방지

3. 함수형 프로그래밍 원칙 준수

  • to_utc_aware() 같은 순수 함수로 분리
  • 부작용 없이 테스트 가능한 코드
  • 하드코딩 없는 재사용 가능한 로직

4. 스크롤 UX는 세심하게

  • 새 메시지: 맨 아래로 스크롤
  • 이전 대화: 현재 위치 유지
  • requestAnimationFrame으로 DOM 업데이트 타이밍 맞추기

5. 디버깅 순서

  1. 브라우저 Network 탭에서 실제 요청/응답 확인
  2. 서버 로그로 에러 확인
  3. 간단한 테스트 코드로 로직 검증
  4. 수정 후 전체 플로우 재테스트

최종 결과

  • 타임존 버그 해결 → 8월 16, 17일 대화 정상 로드
  • 스크롤 위치 유지 → 사용자 경험 개선
  • JWT 인증 경로 정상화 → 보안 강화

작성 시간: 2025년 8월 19일 오전 12시 30분
관련 커밋: rb10508_micro(d6e9044), frontend-customer(9e01f72)