/* * 저널을 **상한까지만 센다** — 세는 것도 읽는 것이다 (2026-08-22). * * ── 왜 (실측) ─────────────────────────────────────────────────────────────── * 조건에 맞는 행을 전부 세는 질의는 행을 실어 오지 않아도 비싸다. 개발 저널에서 트윈 하나가 358만 * 행이었고 그 한 번이 **5.32초**였다. 색인만 상한까지 읽으면 같은 조건에서 **0.04초**다(130배). * * 그 5초 동안 이벤트 루프는 다른 요청을 처리하지 못한다 — 이력 화면 한 번, 준비도 화면 한 번이 * 사용자에게는 「눌렀는데 아무 반응이 없다」로 나타난다. 준비도 화면은 그런 세기를 **넷** 했다. * * ── 규율 ──────────────────────────────────────────────────────────────────── * 세는 목적이 둘 중 하나다. * ① **판정** — 「한 건이라도 있나」. 그때는 하나만 찾으면 끝이다(`anyJournalRow`). * ② **표시** — 사람에게 수를 보인다. 그때는 상한까지 세고, 넘으면 **모른다고 말한다**(`countUpTo`). * * 상한을 넘었을 때 상한값을 전체로 적으면 화면이 거짓을 말한다. 그래서 `capped` 를 함께 낸다 — * 없는 것과 모르는 것을 가르는 이 프로젝트의 규율이 세기에도 같이 적용된다. */ import type { ObjectLiteral, Repository } from 'typeorm' /** 표시용 세기의 기본 상한 — 화면이 한 번에 보여 줄 범위를 훨씬 넘는 값. */ export const JOURNAL_COUNT_CAP = 10_000 export interface CappedCount { /** 센 수. `capped` 면 상한까지만 센 값이므로 **전체가 아니다**. */ count: number /** 상한을 넘었나 — 그때 전체 수는 모르는 것이 사실이다. */ capped: boolean } /** * 상한까지만 세기 — 키 컬럼만 읽으므로 행 본문을 만들지 않는다. * * `count()` 를 쓰지 않는 이유: 그것은 상한을 받지 않는다(조건에 맞는 전부를 센다). */ export async function countUpTo( repo: Repository, where: any, cap = JOURNAL_COUNT_CAP ): Promise { const rows = await repo.find({ where, select: ['id'] as any, take: cap + 1 }) return rows.length > cap ? { count: cap, capped: true } : { count: rows.length, capped: false } } /** * 한 건이라도 있나 — **판정에는 수가 필요 없다.** * * 예전에는 전부 센 뒤 `> 0` 을 봤다. 그 답을 얻는 데 358만 행을 세고 있었다. */ export async function anyJournalRow(repo: Repository, where: any): Promise { const rows = await repo.find({ where, select: ['id'] as any, take: 1 }) return rows.length > 0 }