Skip to content

Latest commit

 

History

History
188 lines (157 loc) · 8.13 KB

architecture.md

File metadata and controls

188 lines (157 loc) · 8.13 KB

PinLog Architecture

Runtime Boundaries

flowchart TB
    Browser[React Browser Client] --> Edge[Cloudflare Tunnel / Ingress]
    Edge --> Backend[Spring Boot Backend]
    Browser --> Kakao[Kakao Maps]
    Backend --> Redis[(Redis)]
    Backend --> Core[(PostgreSQL core)]
    Backend --> Kafka[Kafka]
    Backend --> AI[FastAPI AI Service]
    Kafka --> AI
    AI --> Derived[(PostgreSQL ai + pgvector)]
    AI --> Models[Embedding / LLM / Vision]
Loading
경계 소유 데이터와 책임
Frontend 화면·라우팅·서버 상태 캐시·탭 간 Refresh 조정. Credential을 저장하지 않습니다.
Backend 사용자 인증, Core 도메인, 권한·공개 범위, AI 요청과 최종 응답 조립을 소유합니다.
AI ai 스키마의 파생 데이터와 모델 호출을 소유하며 core 스키마를 읽지 않습니다.
Infra 이미지 공급망, Kubernetes 선언 상태, Secret, NetworkPolicy, 관측·백업을 소유합니다.

신뢰 경계

  • 브라우저는 FastAPI AI 서버나 PostgreSQL에 직접 접근하지 않습니다.
  • Backend는 인증된 사용자의 Core 원문과 소유권을 확인하고 AI에는 처리에 필요한 식별자와 제한된 입력만 전달합니다.
  • AI 애플리케이션 DB Role은 ai 스키마만 읽고 씁니다. core 스키마는 search path와 권한에서 제외합니다.
  • 외부에 공개되는 Collection 응답은 Context 원문과 내부 Member·Record·Context ID를 포함하지 않습니다.

Authentication Sequence

sequenceDiagram
    actor User
    participant Browser
    participant Back as Spring Security
    participant Provider as Google / Kakao / Naver
    participant Redis

    User->>Browser: 소셜 로그인 선택
    Browser->>Back: OAuth 인가 시작
    Back->>Provider: 인가 요청
    Provider-->>Back: Authorization Code
    Back->>Provider: 사용자 정보 확인
    Back->>Redis: 회전형 Refresh 세션 저장
    Back-->>Browser: HttpOnly Access·Refresh + CSRF Cookie
    Browser->>Back: API 요청 + CSRF Header
    Back-->>Browser: 인증된 응답
Loading

Access·Refresh Token은 JavaScript가 읽을 수 없는 HttpOnly Cookie로 전달합니다. 상태 변경 요청은 별도 CSRF Cookie와 Header가 일치해야 합니다. Access 만료 시 Frontend가 Refresh를 한 번 호출하고, 여러 탭의 동시 회전은 Promise 공유와 Web Locks로 직렬화합니다. 이전 Refresh Token 재사용은 회원 단위 세션 폐기로 이어집니다.

Context Analysis Sequence

sequenceDiagram
    actor User
    participant Front
    participant Back
    participant DB as PostgreSQL
    participant Queue as Kafka
    participant AI
    participant Model
    User->>Front: 장소와 Context 저장
    Front->>Back: Record / Context 요청
    Back->>DB: core Context + PENDING 상태 저장
    Back->>Queue: Context 처리 이벤트 발행
    Back-->>Front: 저장 결과 반환
    Queue->>AI: 처리 이벤트 전달
    AI->>DB: 상태 사전 검사·작업 선점
    AI->>Model: Embedding·Keyword 판정
    Model-->>AI: 파생 결과
    AI->>DB: 상태 잠금·CANCELLED 재검사
    AI->>DB: 파생 데이터 저장, COMPLETED 전이
Loading

모델 호출은 DB 트랜잭션 밖에서 수행합니다. 결과 저장 직전에 상태를 다시 확인하므로 처리 중 삭제·교체된 Context의 늦은 결과가 검색에 섞이지 않습니다.

상태와 복구

상황 처리
최초 Embedding·Keyword 처리 PENDING → PROCESSING → COMPLETED
Embedding 완료 후 Keyword 실패 Embedding 결과를 재사용하고 Keyword 단계부터 재개
일시적인 외부 API·DB 오류 PROCESSING을 유지하고 만료 후 Scheduler가 회수
재시도 한도에 도달한 영구 오류 해당 단계를 FAILED로 전이
처리 중 Context 삭제·수정 CANCELLED 전이 후 늦은 결과를 저장하지 않음
삭제된 Embedding is_deleted = true로 검색 대상에서 즉시 제외

Personal Search Sequence

sequenceDiagram
    actor User
    participant Front
    participant Back
    participant AI
    participant DB as PostgreSQL
    User->>Front: 자연어 질의
    Front->>Back: 개인 기록 검색
    Back->>AI: 사용자 범위 + 질의
    AI->>DB: exact cosine + 상태·profile 필터
    AI->>AI: 재작성·Keyword·문자열 신호 결합
    AI-->>Back: recordId 후보와 신뢰도
    Back->>DB: Core 원문·소유권 확인
    Back-->>Front: Record 검색 결과
Loading

AI는 Core 원문을 직접 조회하지 않습니다. Backend가 인증된 사용자의 소유권과 공개 범위를 다시 확인한 뒤 화면 응답을 만듭니다.

검색 신호 구성

flowchart LR
    Query[사용자 질의] --> Rewrite[선택적 질의 재작성]
    Rewrite --> Vector[pgvector exact cosine]
    Query --> Lexical[본문 문자열 검색]
    Vector --> Merge[Record 단위 후보 결합]
    Lexical --> Merge
    Merge --> Keyword[Keyword 재정렬]
    Keyword --> Judge[선택적 LLM 관련도 판정]
    Judge --> Back[Backend 소유권·원문 검증]
Loading

벡터 검색은 user_id, is_deleted, 분석 상태, Embedding Profile을 먼저 제한합니다. 여러 Context가 일치해도 Record마다 최고 유사도 Context 하나만 후보로 남깁니다. 재작성·Keyword·문자열·LLM 신호는 각각 독립적으로 끌 수 있으며 실패하면 기본 검색으로 복귀합니다.

Image-based Place Suggestion

sequenceDiagram
    actor User
    participant Front
    participant Back
    participant AI
    participant Vision
    participant Kakao

    User->>Front: 대화 캡처 이미지 선택
    Front->>Back: multipart 이미지 업로드
    Back->>AI: 내부 장소 제안 요청
    AI->>Vision: 장소명·지역·맥락 단서 추출
    Vision-->>AI: 구조화된 단서
    AI->>Kakao: 장소 후보 검색
    Kakao-->>AI: 장소 후보
    AI-->>Back: 후보별 상태·경고·Context 제안
    Back-->>Front: 사용자가 확인할 후보
    User->>Front: 장소와 Context 확정
Loading

이미지는 후보 생성에만 사용하고 영구 저장하지 않습니다. Vision 결과를 바로 Record로 만들지 않고 Kakao 장소 후보와 사용자의 최종 확인을 거쳐 잘못된 장소 저장을 방지합니다. 외부 API 일부가 실패하면 후보별 경고를 함께 반환해 사용자가 직접 검색으로 이어갈 수 있습니다.

Delivery and Operations

flowchart LR
    PR[Pull Request] --> CI[Test / Lint / Build]
    CI --> Image[Immutable Image]
    Image --> GHCR[GHCR]
    GHCR --> Update[Infra Image Candidate]
    Update --> Argo[Argo CD]
    Argo --> K3s[k3s Cluster]
    K3s --> Observe[Prometheus / Grafana / Loki]
    K3s --> Backup[PostgreSQL Backup]
Loading

운영 상세는 ../infra/docs/architecture.md와 각 Runbook을 기준으로 합니다.

운영 경계

  • 선언 상태: 환경별 ApplicationSet·Helm values가 배포 구성을 소유하고 Argo CD가 live 상태를 맞춥니다.
  • 불변 이미지: Backend·Frontend·AI 이미지는 commit SHA로 식별하며 같은 태그를 덮어쓰지 않습니다.
  • Secret: 평문은 Git에 저장하지 않고 Sealed Secret의 암호문만 선언 상태에 포함합니다.
  • 네트워크: Cloudflare Tunnel과 Traefik만 공개 진입점을 제공하며 AI·DB·Redis는 내부 통신으로 제한합니다.
  • 관측: Prometheus가 애플리케이션·클러스터 메트릭을 수집하고 Grafana·Loki가 대시보드와 로그 탐색을 제공합니다.
  • 복구: PostgreSQL 백업은 생성·검증·외부 보관 경계를 분리하고 복원 절차를 Runbook으로 관리합니다.

Source Documents