Files
document-haness/docs/n+1liner/tech-log-studio/jpa-feed-query-performance/question/question-promote-feed-visible-to-cqrs.md
T
DongHyeonkaandClaude Fable 5.1 62520a4dce docs(n+1liner): adopt the decomposition contract and strip evaluative prose
- 계약 채택 — schema 2 → 4. 독자 질문, 후보 36건(PROMOTE 24 · MERGE_INTO 8 ·
  KEEP_IN_SSOT 4), 종류별 칸, kind:slug 관계. 저장소 github-project/ca-tmpl @ 761384d
  (문서가 인용한 IT 7종이 그 커밋에 있다)
- 재선별 결과 기존 24건이 전부 살아남았다. 측정이 Reference 안에 들어 있었지만 독립성 검사를
  통과하지 못해 Case 로 빼지 않았다
- 평문 칸의 백틱 제거, 「기준선」을 실제 이름으로, frontmatter 에 source·sourceRevision
- 리뷰 39건 반영 — 설명 뒤에 붙은 평가·예고·되풀이·독자 오해 가정을 지웠다. 사실을 담은
  절반이 있는 문장은 평가만 뺐다

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

6.1 KiB

id, kind, slug, title, topic, topicName, topicName, project, status, studio, questionStatus, sourceRevision, source
id kind slug title topic topicName topicName project status studio questionStatus sourceRevision source
5088ce14-b096-41d3-abba-64b7afb48bb9 QUESTION promote-feed-visible-to-cqrs feed_visible을 Production CQRS로 승격할 것인가 jpa-feed-query-performance JPA 피드 조회 성능 — N+1 진단과 조회 전략의 진화 JPA 피드 조회 성능 Liner N + 1문제 게시 전 https://hyeonworks.com/studio/documents/5088ce14-b096-41d3-abba-64b7afb48bb9/edit OPEN n+1liner-lab@2026-08
final/document.md#15-5
final/document.md#17-3
final/document.md#18

id: 5088ce14-b096-41d3-abba-64b7afb48bb9 kind: QUESTION slug: promote-feed-visible-to-cqrs title: feed_visible을 Production CQRS로 승격할 것인가 topic: jpa-feed-query-performance topicName: JPA 피드 조회 성능 — N+1 진단과 조회 전략의 진화 topicName: JPA 피드 조회 성능 project: Liner N + 1문제 status: 게시 전 studio: "https://hyeonworks.com/studio/documents/5088ce14-b096-41d3-abba-64b7afb48bb9/edit" questionStatus: OPEN sourceRevision: n+1liner-lab@2026-08 source:


feed_visible을 Production CQRS로 승격할 것인가

사용자별 가시성을 미리 펼친 feed_visible 테이블을 두자 피드 조회가 커버링 인덱스 하나만 읽는 단일 스캔으로 바뀌었다. 이 테이블을 상시 유지하려면 원본이 바뀔 때마다 투영을 갱신해야 하고, 그러면 쓰기와 분리된 물리 읽기 저장소를 두는 풀 CQRS(Command Query Responsibility Segregation, 명령과 조회를 다른 모델로 나누는 설계)로 넘어간다. 그 비용을 이번 범위에서 바로 받아들일지는 따로 판단해야 할 일로 두었다.

관계

  • Feed Visibility Query Pattern 단일 OR, UNION 분해, 사전계산 세 방식을 같은 결과 집합으로 비교했다.
  • 현재 Read Model은 CQRS-lite로 유지한다 이번 범위에서는 저장소를 나누지 않고 읽기 모델만 분리하기로 했다.
  • Visibility OR이 Keyset Index를 깨뜨린 문제 keyset으로 푼 페이지 조회에 가시성 세 분기를 얹자 BitmapOr와 재정렬이 다시 나타났고, 사전계산은 이것을 없애려던 방식이다.

사실

  • 사전계산 조회는 feed_visible 커버링 인덱스 하나만 읽었다. OR도 조인도 정렬도 없이 20행에 접근했고 buffers는 1이었다.
  • 단일 OR은 세 분기를 BitmapOr로 합쳐 후보 1,500을 훑은 뒤 정렬로 상위 20을 냈고 buffers는 122였다. UNION 분해는 요청할 때마다 세 분기를 각각 스캔했고 buffers는 200으로 단일 OR보다 컸다.
  • 세 방식은 seed 2,000에서 뷰어 user008에게 같은 20개 항목을 반환했다.
  • 통합 쿼리에서 부모 선택을 사전계산으로 두면 가장 깊은 페이지에서 ix_feed_visible 인덱스 범위로 19행만 읽었다. 단일 OR로 두면 가시성 세 분기와 멘션 조건을 다시 계산하며 200행을 훑었다.
  • feed_visible은 FeedVisibilityIT와 FeedCrownIT 안에서 만들고 지우는 실험용 테이블이라 상시 유지하지 않는다.
  • 지금 읽기 경로는 쓰기와 같은 저장소를 쓰면서 읽기 전용 포트·DTO·쿼리만 분리한 CQRS-lite다.

가정

  • 고트래픽 읽기에서는 세 방식의 조회 비용 차이가 실제 부하로 나타난다.
  • 상시 유지하면 피드·멘션·가시성이 바뀔 때마다 투영을 다시 써야 한다.
  • 투영이 어긋나면 사용자가 볼 수 없어야 할 항목이 화면에 나오거나 볼 수 있는 항목이 빠진다.

미지수

  • 지금 트래픽을 단일 OR이나 UNION 분해로 감당할 수 있는가. 사전계산이 필요해지는 읽기 요청량이 어디부터인가.
  • 쓰기 변경을 투영으로 어떻게 옮기는가. 도메인 이벤트로 보내는가, 아웃박스 테이블을 거치는가.
  • 투영 갱신이 늦어졌을 때 지연을 어디까지 허용하는가.
  • 가시성이 바뀌는 사건이 무엇인가. 아이템 공개 범위 변경, 멘션 추가·삭제, 사용자 삭제까지 포함하는가.
  • 저장 공간은 사용자 수만큼 늘어나는데 그 크기를 감당할 수 있는가.
  • 투영이 원본과 어긋났을 때 무엇으로 발견하고 어떻게 되돌리는가.
  • 승격이 현재 계약의 범위를 넘는다는 것은 확인했고, 계약과 가드레일의 어느 조항을 어떻게 고칠지는 아직 정하지 않았다.

제약

  • 현재 계약은 별도 물리 읽기 저장소를 에스컬레이션 대상으로 두었다. 승격하려면 계약을 먼저 개정해야 한다.
  • 가시성은 보안에 걸린 조건이라 투영이 어긋나면 볼 수 없어야 할 항목이 노출된다. 그래서 동기화 지연 허용치를 느슨하게 잡을 수 없다.
  • 지금까지 잰 값은 모두 단일 스레드 로컬 측정이라, 고트래픽에서 어느 방식이 필요한지는 이 값으로 답할 수 없다.

선택지

1. 현재 범위를 유지하고 요청 시 조회로 푼다

단일 OR이나 UNION 분해로 요청할 때마다 조회한다. 투영을 두지 않으니 동기화 비용도, 원본과 어긋날 위험도 없다.

요청마다 가시성 세 분기를 다시 풀기 때문에 조회 비용은 줄지 않는다. 로컬에서 잰 buffers는 단일 OR 122, UNION 분해 200이었고 고트래픽에서 이 값이 어떤 부하가 되는지는 재지 않았다.

2. 사전계산을 상시 유지하는 읽기 저장소로 승격한다

쓰기 변경을 투영에 반영하고 조회는 투영만 읽는다. 세 방식 가운데 이것만 buffers 1로 20행을 읽었다.

대신 동기화 경로, 지연 허용치, 정합성 검증, 복구 절차를 모두 설계해야 하고 계약도 함께 개정해야 한다.

3. 일부만 사전계산한다

접근이 잦은 구간만 투영으로 유지하고 나머지는 요청할 때 조회한다.

두 조회 경로를 함께 운영해야 하고, 어느 구간을 투영에 둘지 무엇을 보고 가를지 먼저 정해야 한다.

다음 검증

  1. 부하 테스트로 지금의 조회 방식이 목표 트래픽을 견디는지 확인한다.

  2. 가시성이 바뀌는 사건을 모두 열거하고 각각이 투영의 어느 행을 건드리는지 정리한다.

  3. 사용자 수와 아이템 수를 곱해 투영이 몇 행까지 커지는지 계산한다.

  4. 동기화 지연을 어디까지 허용할지 정한다. 가시성이 보안 조건이라 이 값을 정해야 동기화 방식을 고를 수 있다.

  5. 투영과 원본이 어긋났는지 확인하는 방법과 되돌리는 절차를 정의한다.

  6. 위 다섯 가지 결과를 보고 계약을 개정할지 판단한다.