DOCS/troubleshooting/250827_frontend_backend_user_preferences_mismatch.md
happybell80 0fdf135867 docs: Frontend-Backend preferences 연동 진행 상황 업데이트
- 90% 완료 상태
- Gateway 라우팅, nginx 프록시, JWT 인증 모두 해결
- GET 요청 정상 작동
- PUT 요청 500 에러 디버깅 필요
2025-08-27 22:34:17 +09:00

9.2 KiB

Frontend ActivityPanel과 Backend user_preferences 연동 구현

작성일: 2025-08-27

작성자: happybell80 / 51123 서버 관리자

상태: 🟡 진행중 - Gateway 라우팅 추가 필요

영향: 사용자 설정 기능 연동

최종 업데이트: 2025-08-27 22:00


1. 현재 상황

완료된 작업

  • Backend: robeing-monitor에 preferences API 구현 (GET/PUT)
  • Frontend: localStorage → API 호출로 변경 완료
  • 배포: robeing-monitor 51124:9024에서 실행 중

🔴 남은 문제

  • Gateway 라우팅: /api/preferences를 robeing-monitor로 프록시 안함
  • 현재: 모든 /api/* 요청이 rb8001로만 감

2. 현재 구현 상태

2.1 Frontend (ActivityPanel.tsx)

  • 위치: /frontend-customer/src/components/activity-panel.tsx
  • 구현 완료: UI 컴포넌트 및 로직
  • 데이터 저장: localStorage (목업)
  • 기능:
    • 다중 브리핑 작업 관리 (task.id별)
    • 키워드 추가/삭제
    • 스케줄 설정 (매일/평일/주말/커스텀)
    • 포함 항목 선택 (이메일/뉴스/캘린더/슬랙)

2.2 Backend (user_preferences 테이블)

  • 위치: PostgreSQL main_db
  • 구조:
    CREATE TABLE user_preferences (
      id SERIAL PRIMARY KEY,
      user_id UUID REFERENCES users(id),
      slack_user_id VARCHAR(100),
      news_keywords VARCHAR(128)[],  -- 뉴스 키워드 배열
      email_filter VARCHAR(128)[],    -- 이메일 필터 (미사용)
      briefing_enabled BOOLEAN DEFAULT true,
      briefing_time TIME DEFAULT '09:00',
      updated_at TIMESTAMP DEFAULT NOW()
    );
    

2.3 rb8001 사용 현황

  • dm_skill.py: user_preferences에서 news_keywords 조회
  • 사용자별 맞춤 뉴스: 정상 작동 중
  • 브리핑 시간: briefing_time 사용 중

3. 핵심 문제점

3.1 데이터 모델 불일치

Frontend TaskSettings Backend user_preferences 불일치 내용
keywords: string[] news_keywords VARCHAR(128)[] 호환 가능
scheduleTime: string briefing_time TIME ⚠️ 타입 변환 필요
scheduleType: 'everyday' | 'weekdays' | ... - 필드 없음
scheduleDays: string[] - 필드 없음
includeEmail: boolean - 필드 없음
includeNews: boolean - 필드 없음
includeCalendar: boolean - 필드 없음
includeSlack: boolean - 필드 없음

3.2 다중 작업 관리 불가

  • Frontend: 여러 개의 scheduledTask 관리 (일일 브리핑, 주간 리포트 등)
  • Backend: 사용자당 1개 설정만 저장 가능
  • 영향: "일일 브리핑", "주간 리포트" 등 구분 불가

3.3 Mock 데이터 하드코딩

// ActivityPanel.tsx:155-210
const conversations: ConversationSession[] = [...];  // 하드코딩
const activities: ActivityLog[] = [...];            // 하드코딩  
const scheduledTasks: ScheduledTask[] = [...];      // 하드코딩
  • 실제 데이터 조회 API 없음
  • conversation_logs 테이블 조회 엔드포인트 필요

3.4 해결됨: API 구현

  • robeing-monitor: preferences API 완전 구현
  • GET /api/preferences/{user_id}: 조회
  • PUT /api/preferences/{user_id}: 업데이트
  • 고정값 반환: 미구현 필드는 기본값 제공

3.5 🔴 Gateway 라우팅 문제

# robeing-gateway/main.py:407
@app.get("/api/{path:path}")  # 모든 GET → rb8001로
# /api/preferences도 rb8001로 가서 404 발생

필요한 수정:

  • /api/preferences/* → robeing-monitor(9024)로 프록시
  • JWT에서 UUID 추출하여 전달

3.6 실시간 동기화 부재

  • localStorage 기반으로 다른 디바이스와 동기화 안됨
  • WebSocket이나 polling 구현 없음

3.7 권한 검증 누락

  • user_preferences 수정 시 본인 확인 로직 필요
  • 다른 사용자 설정 수정 가능한 보안 문제

4. 해결 방안

4.1 단기 해결책 (최소 수정)

  1. Backend 스키마 확장

    ALTER TABLE user_preferences ADD COLUMN schedule_type VARCHAR(20);
    ALTER TABLE user_preferences ADD COLUMN schedule_days VARCHAR(10)[];
    ALTER TABLE user_preferences ADD COLUMN include_email BOOLEAN DEFAULT true;
    ALTER TABLE user_preferences ADD COLUMN include_news BOOLEAN DEFAULT true;
    ALTER TABLE user_preferences ADD COLUMN include_calendar BOOLEAN DEFAULT false;
    ALTER TABLE user_preferences ADD COLUMN include_slack BOOLEAN DEFAULT false;
    
  2. robeing-monitor에 CRUD API 추가

    GET  /api/preferences/{user_id}
    PUT  /api/preferences/{user_id}
    GET  /api/conversations/{user_id}?limit=10
    GET  /api/activities/{user_id}?limit=10
    
  3. ActivityPanel localStorage → API 호출 변경

4.2 중기 해결책 (구조 개선)

  1. scheduled_tasks 테이블 생성

    CREATE TABLE scheduled_tasks (
      id SERIAL PRIMARY KEY,
      user_id UUID REFERENCES users(id),
      task_type VARCHAR(50),  -- 'daily_briefing', 'weekly_report' 등
      title VARCHAR(255),
      schedule_type VARCHAR(20),
      schedule_days VARCHAR(10)[],
      schedule_time TIME,
      settings JSONB,  -- 유연한 설정 저장
      enabled BOOLEAN DEFAULT true
    );
    
  2. Frontend와 Backend 인터페이스 통일

    • 공통 TypeScript 타입 정의
    • API 응답 형식 표준화

4.3 장기 해결책 (완전 재설계)

  1. 마이크로서비스 분리

    • user-preferences-service 별도 구현
    • GraphQL 도입 검토
  2. 실시간 동기화

    • WebSocket 구현
    • 설정 변경 시 실시간 반영

5. 구현 현황 (2025-08-27 22:00)

완료

  • Backend API: robeing-monitor에 구현 완료
  • Frontend 수정: API 호출 코드 추가, 자동 저장
  • UI 처리: 미구현 필드 비활성화 (opacity-50)
  • 배포: 51124:9024 정상 작동

🟡 진행중

  • Gateway 라우팅: preferences 전용 프록시 추가 필요

⚠️ 작동 방식

  • 현재: Frontend → Gateway(8100) → rb8001 → 404
  • 필요: Frontend → Gateway(8100) → robeing-monitor(9024)

6. 아키텍처 상세

데이터 흐름

Frontend (브라우저)
    ↓ 
[자동 저장 트리거]
    ↓
fetch('/api/preferences/{UUID}')  # Gateway 경유
    ↓
Gateway (8100) + JWT 인증
    ↓
[라우팅 필요: preferences → 9024]
    ↓
robeing-monitor (9024)
    ↓
PostgreSQL user_preferences 테이블

UUID 체계

  • JWT sub: UUID (1e16e9d5-59f3-54da-a661-8abeabff4230)
  • localStorage: user_id에 UUID 저장
  • API 경로: /api/preferences/{UUID}

7. 관련 파일

Frontend

  • /home/happybell/projects/ivada/frontend-customer/src/components/activity-panel.tsx

Backend

  • /home/happybell/projects/ivada/robeing-monitor/app/api/items.py
  • /home/happybell/projects/ivada/robeing-gateway/app/main.py
  • /home/happybell/projects/ivada/rb8001/app/skills/dm_skill.py

문서

  • /home/happybell/projects/ivada/DOCS/troubleshooting/250826_slack_id_column_standardization.md

8. Gateway 라우팅 추가 코드

# /robeing-gateway/app/main.py (line 407 앞에 추가)
@app.api_route("/api/preferences/{path:path}", methods=["GET", "PUT"])
async def proxy_preferences(
    path: str, 
    request: Request,
    x_user_id: str = Depends(get_verified_user)
):
    """Proxy preferences to robeing-monitor"""
    monitor_url = "http://192.168.219.52:9024"
    full_path = f"/api/preferences/{path}"
    
    if request.method == "GET":
        response = await http_client.get(f"{monitor_url}{full_path}")
    elif request.method == "PUT":
        body = await request.json()
        response = await http_client.put(
            f"{monitor_url}{full_path}", 
            json=body,
            headers={"X-User-Id": x_user_id}
        )
    
    return response.json() if response.status_code == 200 else {"error": "Failed"}

9. 결론

90% 완료 - PUT 요청 500 에러 해결 필요

  • Backend API
  • Frontend 수정
  • Gateway 라우팅
  • nginx 프록시 (/gateway/ 사용)
  • JWT 인증
  • GET 요청 (200 OK)
  • PUT 요청 (500 에러 - robeing-monitor 문제)

10. 구현 과정 (2025-08-27)

Phase 1: Backend API 구현 (20:00)

  • robeing-monitor에 preferences API 추가
  • 기존 컬럼만 사용 (news_keywords, briefing_time)
  • 확장 필드는 고정값 반환

Phase 2: Frontend 수정 (21:00)

  • localStorage → API 호출 변경
  • 자동 저장 방식 구현
  • 미구현 필드 UI 비활성화

Phase 3: Gateway 라우팅 (22:00)

  • preferences 전용 라우팅 추가
  • robeing-monitor(9024)로 프록시

Phase 4: 경로 문제 해결 (22:30)

  • 처음: 직접 호출 (http://192.168.219.52:9024) - Mixed Content 에러
  • 다음: /api/preferences - nginx가 frontend-base(8000)로 전달
  • 해결: /gateway/api/preferences - Gateway(8100) 경유

Phase 5: JWT 인증 추가 (22:40)

  • Frontend에서 Authorization 헤더 누락 발견
  • localStorage의 'token' || 'auth_token' 사용
  • Bearer 토큰 형식으로 전달

현재 상태 (22:45)

  • GET /api/preferences/{user_id}: 200 OK
  • PUT /api/preferences/{user_id}: 500 에러 (robeing-monitor 내부 문제)

최종 수정: 2025-08-27 22:45 상태: robeing-monitor PUT 처리 디버깅 필요