[TIL] TodayPoor ERD 피드백 반영 기록
OCR 결과 분리, AI 프롬프트 테이블 제거, 랭킹 결과 구조 변경 등 TodayPoor ERD 피드백을 반영하며 설계를 정리한 글입니다.
For the English version of this post, see here.
오늘 한 일
피드백 관련 논의
피드백 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_RESULT에title_text필드를 둠- 문제
하지만
RANKING_RESULT는 이미title_id를 통해POOR_TITLE을 참조하고 있어서, 타이틀 이름은POOR_TITLE.name으로 가져올 수 있음title_text를 따로 저장하면 같은 타이틀 정보가 두 군데에 중복 저장됨
- 해결: 그래서
title_text는 제거하고,RANKING_RESULT.title_id이POOR_TITLE의 name을 참조하도록 함
- AI 결과 참조 구조 변경
초기에는
RANKING_RESULT에roast_message를 문자열로 바로 저장하려고 함- 문제
- 하지만 독설 문구는 AI가 생성한 결과이므로, 랭킹 결과와 AI 결과를 분리하는 것이 더 적절하다고 판단함
- 해결: 그래서
RANKING_RESULT가AI_RESULT를 참조하도록 수정함
- invite code 만료일 추가
- 문제
- 그룹 초대 기능을 고려했을 때, 초대 코드가 무기한 유지되는 것은 적절하지 않다고 판단
- 해결:
GROUP테이블에 초대 코드 만료일을 추가- 이 필드를 통해 만료된 초대 코드는 사용할 수 없도록 처리할 수 있음
- 문제
- OCR 결과 테이블 분리
- 최종 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 } ```
다음 할 일
API 명세 작성
프로젝트 공통 부분 설계
공통 응답
에러 처리
코드/커밋 컨벤션
