Skip to content

docs(S15P11A705-119): Feed 정책·계약 정본 정합화 — MVP 후보 3채널·점수 구성·이벤트 계약 - #21

Merged
colosair merged 2 commits into
mainfrom
docs/S15P11A705-119-feed-contract
Jul 29, 2026
Merged

docs(S15P11A705-119): Feed 정책·계약 정본 정합화 — MVP 후보 3채널·점수 구성·이벤트 계약#21
colosair merged 2 commits into
mainfrom
docs/S15P11A705-119-feed-contract

Conversation

@colosair

Copy link
Copy Markdown
Member

요약

MVP Feed에서 Place category·region을 제외하기로 확정한 계약(back#74 · P42)에 팀 공용 정본을 맞춘다. 정본의 Feed 서술이 옛 4채널·w_geo_cat 공식에 머물러 있어 back 구현자가 정본만 읽으면 잘못된 계약을 얻는 상태였다.

정책·계약 문서만 바꾼다. 런타임 구현 서술은 추가하지 않았고 코드·DB 변경도 없다.

Jira: S15P11A705-119 (상위 -111)

⚠️ 병합 순서 — back#74보다 먼저 병합하면 안 된다

이 PR의 내용은 back#74가 확정한 계약을 정본에 옮긴 것이다. back#74는 아직 Draft이고 리뷰 대기 중이므로 리뷰에서 계약이 바뀌면 이 PR도 따라가야 한다.

  • back#74 병합 후에 이 PR을 병합한다.
  • back#74에서 수치·규칙이 바뀌면 이 PR을 먼저 갱신한다.

변경 사항

static/05_AI_설계.md — 14장 재구성 (핵심)

기존 14장은 「Feed 책임 / 사용하는 AI 데이터 / 금지 사항」 세 절뿐이라 후보 채널·점수 구성·API 계약이 어디에도 없었다. 정본만으로 계약을 재구성할 수 있게 여섯 절로 나눴다.

  • 14.1 소유 경계 (신설) — 정책·계약은 AI 파트, 런타임 구현은 Backend 파트. 구현 명세와 어긋나면 이 문서가 정본임을 명시했다.
  • 14.2 후보 채널 (신설) — 최신 발행·팔로우·무작위 3종, 기본 배분 100·80·20. core.place에 category·region 컬럼이 없다는 근거(06 §2.3)를 함께 적어 "왜 뺐는지"를 남겼다. AI Keyword 없는 Collection도 후보 포함 + keywords: [].
  • 14.3 점수 계산 (신설) — 요청 시점 deterministic 산술. 팔로우 0.500 / Keyword 0.375 / 최신성 0.125 + 노출 패널티. w_geo_cat 제거 후 비례 정규화한 값임을 밝혔다.
  • 14.4 Feed가 사용하는 AI 데이터 — 타인 특징·본인 Profile 양쪽에서 Place region·category 제거. 제외는 조회 쿼리에서 수행할 것과 응답에 소유자 식별자를 넣지 않을 것을 추가했다.
  • 14.5 금지 사항 — 기존 내용 + 요청 시점 벡터 유사도 계산·벡터 검색 후보 생성 금지.
  • 14.6 API 계약 (신설) — 경로·기본 size 20·opaque cursor·상세는 공통 Collection API 재사용·requestId 별도 필드·이벤트 배열 상한 100.
  • 14.7 — back/docs가 관리하는 범위를 명시적으로 열거해 경계를 분리했다.

부수적으로 문서 헤더와 핵심 원칙 15에 "구현은 back, 정책·계약은 이 문서"를 반영했고, 15.2 MVP 제외에 category·region 기반 후보·가중치를 넣었다.

static/05_AI_설계.md — 17장 인덱스 정정

Backend 파트 표가 feed/feed-recommendation.md·ai/ai-integration.md를 가리키고 있었으나 실제 경로는 docs/ai/spec/ 아래다(back dev 기준). 표의 10개 항목을 실경로로 고치고, 결정 근거인 ai/proposals/P42-feed-mvp-without-place-metadata.md를 추가했다. Feed 문서에는 대응하는 14장 절 번호를 달았다.

static/08_API_명세.md — 10장

  • 10.1size 기본 20과 opaque cursor가 공통 계약(1.4)을 따른다는 점, requestId가 Feed Session 식별자라는 점, PUBLIC만 반환한다는 점, 소유자 식별자를 응답에 넣지 않는다는 점을 규칙으로 올렸다. 추천 정책의 정본이 05 14장임을 링크했다.
  • 10.2 — 요청 형식을 단일 이벤트 객체 → requestId + events 배열로 정정했다. 기존 예시는 requestId가 이벤트 객체 안에 들어 있어 확정 계약과 달랐다. 배열 상한 100, IMPRESSION 전송 시 400, 개별 항목 부분 실패 허용, 쓰기 전용을 규칙으로 명시했다.

그 외 정본 정합

  • 05-1 §1.3 — Front가 보낼 페이로드 형태(배열 묶음, requestId 바깥 필드)와 100개 상한을 적었다. 기존 서술은 "payload에 requestIdposition을 포함"뿐이라 배열 형식임을 알 수 없었다.
  • 05-1 §1.5 — 입력 크기 상한 표에 events 배열 100개 행을 추가했다. 이 표가 서버 방어 상한의 단일 등록처다.
  • 02 §9 — 후보 3채널·점수 구성·규칙 기반이라는 사용자 관점 정책과 파트 소유 경계를 추가했다.
  • 04 §5 — 타인 특징은 PUBLIC만, 응답에 소유자 식별 값 없음을 공개정책에 못 박았다.
  • 10 — MVP 포함에 3채널·점수 구성을, 제외에 category·region 기반 후보·가중치를 넣었다.

배경

P42의 결정은 데이터 모델과의 정합이다. core.place에는 category_group_code를 저장하지 않고(값이 비거나 거칠어서), 지역은 주소 문자열에서 표시용으로만 파생한다. 이 상태에서 category·region을 후보 채널·점수 입력으로 쓰려면 DTO·DB·Front 계약과 백필이 함께 필요하다. MVP에서는 감당할 범위가 아니라고 판단해 두 신호를 빼고, 남은 세 항을 비례 정규화해 상대 순서를 보존했다(합 1.0 유지).

리뷰 포인트

  1. 가중치 0.500 / 0.375 / 0.125가 정본에 들어가도 되는가. back#74는 이 값들을 "설정값이며 튜닝 대상"으로 규정한다. 정본에는 기본값으로 적고 감쇠 함수·계수·윈도우 같은 세부는 back 문서로 넘겼다. 정본이 튜닝 때마다 따라 바뀌지 않게 하려는 선택인데, 아예 값을 빼고 back만 두는 편이 낫다고 보면 알려달라.
  2. 10.2 요청 형식 변경. 단일 객체 → 배열은 Front에 실제 영향이 있는 계약 변경이다. 아직 구현 전이라 지금 정정하는 편이 싸다고 판단했다. 05-1 §1.3에 Front가 볼 형태를 함께 적었다.
  3. 17장 Backend 표의 ai/* 항목 경로도 함께 고쳤다. Feed 항목만 ai/spec/로 고치면 같은 표 안에서 접두사가 갈려 더 헷갈린다고 봤다. Feed 범위를 넘는 수정이므로 원치 않으면 되돌리겠다.
  4. 08 §1.4의 "명세상 상한 없음"은 손대지 않았다. back은 커서 size에도 CursorPage.MAX_SIZE 100을 적용하지만, 이는 Feed만이 아니라 전 엔드포인트에 걸리는 공통 계약이라 이 PR 범위 밖으로 뒀다. 아래 후속 참조.

테스트 / 검증

문서 전용 변경이라 실행 가능한 테스트가 없다. 대신 다음을 확인했다.

  • back#74docs/ai/spec/feed-{recommendation,scoring,event,profile-cache}.mdP42 전문을 원격에서 읽고 대조 — 후보 3채널·배분 100/80/20, 가중치 0.500/0.375/0.125, 배열 상한 100, requestId 별도 필드, 경로·기본 size 20 모두 일치
  • 17장 인덱스의 각 경로를 back dev 트리와 대조 — 11개 항목 모두 실재 확인
  • static/ 전체에서 Feed|피드|추천·region|category|지역 재검색 — 남은 서술이 새 계약과 충돌하지 않음을 확인 (06 §2.3, 08 §721은 이미 정합)
  • 14장 참조(14.x·14장) 링크가 재구성 후 절 번호와 맞는지 확인
  • 렌더링 확인 — 로컬 프리뷰 미실행. GitHub 렌더링으로 확인 예정

미결 / 후속

  • back#74 병합 대기. 리뷰에서 계약이 바뀌면 이 PR을 갱신한다.
  • 커서 size 서버 방어 상한(100)의 정본 등록. 08 §1.4·§13의 "명세상 상한 없음"과 back의 CursorPage.MAX_SIZE = 100이 어긋나 있다. Feed 범위가 아니라 별도 티켓으로 다룬다.
  • back#63 공개·소유자 경계 통합은 이 PR에 흡수하지 않았다. /feed/collections/{collectionId}/shelf(08 §2.7)도 그대로 뒀다.

관련: back#74 (선행) · S15P11A705-125 · S15P11A705-120(Spring 구현)

MVP Feed에서 Place category·region을 제외한 확정 계약(back#74)에
정본을 맞춘다. 런타임 구현 서술은 추가하지 않는다.

- 05 §14를 소유 경계·후보 채널·점수 구성·경계·금지·API 계약으로 재구성
- 05 §14.2에서 Place region·category를 후보·Profile 입력에서 제거
- 05 §17 Backend 인덱스를 실제 경로(ai/spec/*)로 정정하고 P42 추가
- 08 §10.1 size·cursor·requestId·소유자 비노출 규칙 명시
- 08 §10.2 이벤트 요청을 requestId + events 배열 형식으로 정정 (상한 100)
- 05-1 §1.3·§1.5, 02 §9, 04 §5, 10에 같은 계약 반영

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
back#74 리뷰 7번 반영입니다.

§8.1 GET /api/core/v1/feed/collections/{collectionId}/shelf의 size가 10으로
남아 있어, 같은 Feed 네임스페이스에서 /feed/collections는 20, shelf는 10으로
갈렸습니다. CursorPage.DEFAULT_SIZE(20)에 맞추고, size가 공통 커서 계약
(기본 20 · 상한 100 · normalizeSize 보정)을 따른다는 근거를 함께 적습니다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@colosair
colosair marked this pull request as ready for review July 29, 2026 02:14
@colosair
colosair merged commit aae1cec into main Jul 29, 2026
@colosair
colosair deleted the docs/S15P11A705-119-feed-contract branch July 29, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant