포스트

[TIL] TodayPoor ERD 피드백 반영 기록

OCR 결과 분리, AI 프롬프트 테이블 제거, 랭킹 결과 구조 변경 등 TodayPoor ERD 피드백을 반영하며 설계를 정리한 글입니다.

For the English version of this post, see here.
[TIL] TodayPoor ERD 피드백 반영 기록

오늘 한 일

  • 피드백 관련 논의

  • 피드백 ERD 반영 및 수정

회의

초기 설계에서는 기능을 빠르게 반영하는 데 집중했다면, 오늘은 각 테이블과 필드가 정말 필요한지, 중복 데이터는 없는지, MVP 단계에서 과한 설계는 아닌지를 중심으로 다시 점검함

  • PRD 기반 ERD 피드백 반영 및 최종 설계
    • OCR 결과 테이블 분리
      • 초기에는 EXPENSE 테이블 안에 ocr_raw_text를 함께 저장함

      • 문제
        • OCR 결과는 소비 내역 자체라기보다는, 소비 내역을 만들기 위한 분석 결과에 가까움

        • OCR 원문은 길이가 길어질 수 있고, 추후 OCR 정확도나 분석 결과를 따로 관리할 가능성도 있음

      • 해결: 그래서 OCR_RESULT 테이블을 분리함
        • 이렇게 분리하면 EXPENSE는 확정된 소비 데이터에 집중하고, OCR_RESULT는 분석 결과를 담당하게 되어 역할이 더 명확해짐
    • AI 프롬프트 테이블 제거
      • 처음에는 AI 기능을 고려해 AI_PROMPT_TEMPLATE 테이블을 추가함

      • 프롬프트 타입, 시스템 프롬프트, 유저 프롬프트 템플릿 등을 DB에서 관리하는 구조였음

      • 문제
        하지만 다시 검토해보니 현재 MVP에서는 다음 기능이 없음

        • 관리자 페이지에서 프롬프트 수정

        • 프롬프트 버전 관리

        • A/B 테스트

        • 운영 중 배포 없이 프롬프트 변경

        즉, 프롬프트를 DB로 관리할 이유가 아직 명확하지 않음

      • 해결: 프롬프트는 백엔드 코드 내부에서 관리하고, AI가 생성한 결과만 DB에 저장하기로 함

        • 최종적으로는 AI_PROMPT_TEMPLATE 테이블을 제거하고, AI_RESULT만 유지
    • RANKING_RESULT의 title_text 제거
      • 초기에는 RANKING_RESULTtitle_text 필드를 둠

      • 문제
        • 하지만 RANKING_RESULT는 이미 title_id를 통해 POOR_TITLE을 참조하고 있어서, 타이틀 이름은 POOR_TITLE.name으로 가져올 수 있음

        • title_text를 따로 저장하면 같은 타이틀 정보가 두 군데에 중복 저장됨

      • 해결: 그래서 title_text는 제거하고, RANKING_RESULT.title_idPOOR_TITLE의 name을 참조하도록 함
    • AI 결과 참조 구조 변경
      • 초기에는 RANKING_RESULTroast_message를 문자열로 바로 저장하려고 함

      • 문제
        • 하지만 독설 문구는 AI가 생성한 결과이므로, 랭킹 결과와 AI 결과를 분리하는 것이 더 적절하다고 판단함
      • 해결: 그래서 RANKING_RESULTAI_RESULT를 참조하도록 수정함
    • invite code 만료일 추가
      • 문제
        • 그룹 초대 기능을 고려했을 때, 초대 코드가 무기한 유지되는 것은 적절하지 않다고 판단
      • 해결: GROUP 테이블에 초대 코드 만료일을 추가
        • 이 필드를 통해 만료된 초대 코드는 사용할 수 없도록 처리할 수 있음
  • 최종 ERD
    ERD Mermaid 코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    
      ```mermaid
      erDiagram
          USER ||--o{ SOCIAL_ACCOUNT : has
          USER ||--o{ GROUP_MEMBER : joins
          GROUP ||--o{ GROUP_MEMBER : has
    
          USER ||--o{ EXPENSE : uploads
          GROUP ||--o{ EXPENSE : contains
    
          EXPENSE ||--|| OCR_RESULT : has
    
          GROUP ||--o{ RANKING_RESULT : has
          USER ||--o{ RANKING_RESULT : ranked
          POOR_TITLE ||--o{ RANKING_RESULT : assigned
    
          RANKING_RESULT ||--|| AI_RESULT : has
    
          USER {
              UUID id PK
              string nickname
              string profile_image_url
              datetime created_at
              datetime updated_at
              datetime deleted_at
          }
    
          SOCIAL_ACCOUNT {
              UUID id PK
              UUID user_id FK
              enum provider
              string provider_user_id
              string email
              datetime connected_at
          }
    
          GROUP {
              UUID id PK
              string name
              string invite_code
              datetime invite_code_expires_at
              UUID owner_id FK
              datetime created_at
              datetime updated_at
              datetime deleted_at
          }
    
          GROUP_MEMBER {
              UUID id PK
              UUID user_id FK
              UUID group_id FK
              enum role
              datetime joined_at
              datetime deleted_at
          }
    
          EXPENSE {
              UUID id PK
              UUID user_id FK
              UUID group_id FK
              enum category
              int amount
              string merchant
              string memo
              string image_url
              enum visibility
              datetime spent_at
              datetime created_at
              datetime updated_at
              datetime deleted_at
          }
    
          OCR_RESULT {
              UUID id PK
              UUID expense_id FK
              text raw_text
              datetime created_at
          }
    
          RANKING_RESULT {
              UUID id PK
              UUID group_id FK
              UUID user_id FK
              UUID title_id FK
              UUID ai_result_id FK
              date ranking_date
              int total_amount
              int rank_no
              datetime created_at
          }
    
          POOR_TITLE {
              UUID id PK
              string name
              string code
              enum condition_type
          }
    
          AI_RESULT {
              UUID id PK
              text input_data
              text output_text
              string model_name
              int token_usage
              datetime created_at
          }
      ```
    

    image

다음 할 일

  • API 명세 작성

  • 프로젝트 공통 부분 설계

    • 공통 응답

    • 에러 처리

    • 코드/커밋 컨벤션