포스트

[TIL] 사용자 흐름에 맞춰 ERD 다듬기

최종 기획서 작성 중 크루, OCR 결과, AI 피드백, 랭킹 테이블을 사용자 흐름에 맞춰 다시 연결하고 ERD 구조를 정리한 기록입니다.

For the English version of this post, see here.
[TIL] 사용자 흐름에 맞춰 ERD 다듬기

오늘 한 일

  • 최종 기획서 작성
    • 운영진 피드백 반영

    • 피드백에 대한 우리 팀의 생각 반영

  • ERD 수정사항 정리
    • 피드백 반영

    • 와이어프레임과 비교

  • ERD 수정사항 반영

ERD 수정

  • TodayPoor 프로젝트의 1차 기획 피드백을 바탕으로 서비스 방향성과 ERD 구조를 다시 정리함

  • 1차 수정

    image

    변경 항목기존 구조변경 구조변경 이유
    그룹 용어 변경GROUP, GROUP_MEMBERCREW, CREW_MEMBER프론트 와이어프레임에서 “크루”라는 용어를 사용하므로 FE/BE/기획서 용어를 통일하기 위해 변경
    그룹 FK명 변경group_idcrew_id테이블명이 CREW로 변경되었기 때문에 FK명도 일관성 있게 수정
    크루 설명 추가없음CREW.description 추가와이어프레임의 크루 카드에 설명 문구가 필요하므로 크루 소개/설명 정보를 저장하기 위해 추가
    POOR_TITLE 제거POOR_TITLE 테이블 존재테이블 삭제타이틀을 고정값으로 관리하지 않고 AI가 매번 생성하는 방식으로 변경했기 때문에 별도 타이틀 테이블이 불필요해짐
    AI 결과 필드 추가AI_RESULT.output_text 중심title, roast_message, mode 추가AI가 수상 타이틀과 독설/피드백 문구를 함께 생성하므로 결과를 구분해 저장하기 위해 추가
    AI 모델명 필드명 변경model_namemodel필드명을 더 간결하게 만들기 위해 변경
    토큰 사용량 분리token_usageinput_token, output_token입력 토큰과 출력 토큰을 분리해 AI 사용량을 더 정확히 기록하기 위해 변경
    프롬프트 버전 추가없음prompt_version 추가프롬프트를 코드에서 관리하더라도 어떤 버전의 프롬프트로 생성된 결과인지 추적하기 위해 추가
    AI 호출 상태 추가없음status 추가AI 호출 성공/실패 여부를 저장하기 위해 추가
    AI 에러 코드 추가없음error_code 추가AI 호출 실패 시 원인을 추적하고 디버깅하기 위해 추가
    OCR 관계 변경OCR_RESULT.expense_idEXPENSE.ocr_result_idOCR 결과는 먼저 생성되고, 사용자가 수정/확정한 최종 소비 데이터가 EXPENSE에 저장되므로 EXPENSE가 OCR 결과를 참조하도록 변경
    OCR 추출값 추가OCR_RESULT.raw_text만 저장extracted_merchant, extracted_amount 추가OCR이 최초 인식한 가맹점/금액과 사용자가 수정한 최종 값을 비교할 수 있도록 추가
    이미지 URL 제거EXPENSE.image_url삭제OCR 인식 후 원본 이미지는 더 이상 필요하지 않으므로 DB에 저장하지 않기 위해 제거
    소비-크루 관계 필수화crew_id nullable 가능EXPENSE.crew_id NOT NULL사용자는 반드시 크루에 가입한 상태에서 소비 기록을 등록하도록 정책을 정했기 때문에 필수값으로 설정
    랭킹-AI 결과 관계 필수화ai_result_id nullable 가능RANKING_RESULT.ai_result_id NOT NULL크루원들의 MVP 순위는 AI 결과를 필수로 포함하기 때문에 NOT NULL로 설정
    랭킹 주기 명확화ranking_date만 존재ranking_date를 일간 기준으로 사용서비스가 하루 단위 MVP/랭킹을 제공하므로 랭킹 날짜를 일간 기준으로 관리
    • 이번 ERD 수정에서는 프론트 와이어프레임 용어와 백엔드 도메인명을 맞추고, AI가 타이틀과 피드백을 생성하는 구조에 맞춰 POOR_TITLE을 제거함

    • 또한 OCR 최초 인식 결과와 사용자가 수정한 최종 소비 데이터를 분리하기 위해 OCR_RESULTEXPENSE의 관계를 재정리함

    • 이를 통해 MVP 서비스 흐름에 더 적합한 데이터 구조로 개선함

  • 2차 수정

    image

    변경 항목기존 구조변경 구조변경 이유
    랭킹 유형 추가RANKING_RESULT에 랭킹 종류 구분 없음ranking_type 추가날짜별 랭킹 결과와 랜덤 주제 기반 랭킹 결과를 구분하기 위해 추가
    랭킹 타입 enum 정의없음DAILY, RANDOM_TOPIC프론트 와이어프레임에서 일간 결과와 랜덤 주제 결과를 모두 보여주므로 결과 유형을 명확히 구분
    회원 정보 동의 여부 추가USER에 동의 여부 없음is_agreed 추가회원가입 시 개인정보 및 서비스 이용 관련 동의 여부를 저장하기 위해 추가
    크루별 AI 모드 추가AI 모드가 결과 테이블에만 존재CREW.ai_mode 추가크루마다 AI 피드백 분위기/강도를 설정할 수 있도록 하기 위해 추가
    AI 결과 모드 유지AI_RESULT.mode 존재그대로 유지AI 결과가 어떤 모드로 생성되었는지 이력으로 남기기 위해 유지
    enum 값 명시enum provider, enum visibility처럼 타입만 표시enum 후보값을 Mermaid에 문자열로 명시ERD만 봐도 어떤 enum 값이 사용되는지 이해할 수 있도록 하기 위해 추가
    • 추가로
      • 처음에는 크루(CREW) 단에서 AI 모드를 설정하니까, AI_RESULT도 CREW의 ai_mode를 FK처럼 참조해야 하는 거 아닌지 고민함
        • 하지만, 두 값의 역할이 달랐음

        • CREW.ai_mode는 ‘현재 크루에서 설정해둔 AI 모드 값’이고,

        • AI_RESULT.mode는 ‘해당 AI 결과가 생성될 당시 실제 사용된 모드 값’임

        → 즉, 크루의 ai_mode는 이후에 변경될 수 있지만, 이미 생성된 AI 결과는 생성 당시 어떤 모드로 만들어졌는지 그대로 유지되어야 함

        • 따라서 AI_RESULT가 CREW.ai_mode를 직접 참조(FK)하기보다는, AI 결과 생성 시 사용된 mode 값을 AI_RESULT에 그대로 저장하는 방식이 더 적절하다고 판단함

배운 점

  • 단순히 테이블만 그리는 것이 아니라, 실제 사용자 흐름, FE 화면 구조, 데이터 책임, AI 결과 관리, 운영 및 디버깅, 미래 확장 가능성까지 함께 고려해야 제대로 된 ERD가 나온다는 걸 알 수 있었다.

  • 처음에는 테이블을 많이 만들수록 좋아 보였지만, 오히려 데이터의 책임과 흐름이 명확한 구조가 더 중요하다는 걸 느꼈다.

  • 오늘 논의한 내용들을 바탕으로 앞으로는 API 설계와 실제 서비스 흐름까지 연결해서 고민해봐야겠다.