From 8580bbb713c8beeca0d0802f8b5b5da92c5efa3f Mon Sep 17 00:00:00 2001 From: jwkim Date: Fri, 19 Jun 2026 11:05:13 +0900 Subject: [PATCH] =?UTF-8?q?Crowd=20Pick=20=EB=B9=84=EC=9C=A8=20=ED=95=A9?= =?UTF-8?q?=EC=9D=B4=20=ED=95=AD=EC=83=81=20100%=20=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=98=EC=98=AC=EB=A6=BC=20=EB=B3=B4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Arena.tsx | 12 ++++++------ frontend/src/lib/format.ts | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/Arena.tsx b/frontend/src/components/Arena.tsx index 7926ad1..91cc7ee 100644 --- a/frontend/src/components/Arena.tsx +++ b/frontend/src/components/Arena.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { outcomeLabel, pct, kickoffDisplay, winProb } from "@/lib/format"; +import { outcomeLabel, pctParts, kickoffDisplay, winProb } from "@/lib/format"; import { type Lang, dict, teamShort } from "@/lib/i18n"; import type { Outcome, @@ -542,15 +542,15 @@ export default function Arena({ Crowd Pick{" "} {t.crowdSub} - {/* 비율은 분포 합 기준(=total 과 동일해야 정상) — 구 데이터 불일치 시에도 100% 이내로 표시 */} + {/* 비율은 분포 합 기준 + 합이 항상 100% 되도록 보정(최대 잔여 방식) */}
{(() => { - const sum = crowd.teamAWin + crowd.draw + crowd.teamBWin; + const [a, d, b] = pctParts([crowd.teamAWin, crowd.draw, crowd.teamBWin]); return ( <> - - - + + + ); })()} diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts index 00911b8..0714526 100644 --- a/frontend/src/lib/format.ts +++ b/frontend/src/lib/format.ts @@ -55,6 +55,27 @@ export function pct(part: number, total: number): number { return Math.round((part / total) * 100); } +// 여러 비율을 정수 %로 — 합이 항상 정확히 100 (최대 잔여 방식). +// 각자 따로 반올림하면 99/101 이 나오는 문제를 방지. +export function pctParts(parts: number[]): number[] { + const total = parts.reduce((s, p) => s + p, 0); + if (!total) return parts.map(() => 0); + const raw = parts.map((p) => (p / total) * 100); + const floor = raw.map(Math.floor); + let remainder = 100 - floor.reduce((s, v) => s + v, 0); + // 소수부가 큰 순서로 남은 %를 1씩 분배 + const order = raw + .map((v, i) => ({ i, frac: v - Math.floor(v) })) + .sort((a, b) => b.frac - a.frac); + const out = [...floor]; + for (const { i } of order) { + if (remainder <= 0) break; + out[i] += 1; + remainder -= 1; + } + return out; +} + // "방금 전 / 3분 전 / 2시간 전 / 5일 전" — 댓글 상대시간 export function relativeTime(iso: string, lang: Lang = "ko"): string { const then = new Date(iso).getTime();