155 lines
8.5 KiB
Markdown
155 lines
8.5 KiB
Markdown
# 베이지안 프레젠테이션 BallQuiz 페이지 리디자인
|
||
|
||
**날짜**: 2025-12-16
|
||
**작성자**: happybell80
|
||
**관련 파일**:
|
||
- `bayesian-presentation/frontend/src/pages/BallQuizPage.tsx`
|
||
- `bayesian-presentation/backend/app/services/state_service.py`
|
||
- `bayesian-presentation/backend/main.py`
|
||
|
||
---
|
||
|
||
## 문제 상황
|
||
|
||
### 1. Section 구조 복잡성
|
||
- Section 1(초기 확률 입력)과 Section 2(공 꺼내기)가 분리되어 불필요한 단계 추가
|
||
- 발표자가 두 번에 나눠 입력해야 하는 불편함
|
||
|
||
### 2. 셀 수정 불가 문제
|
||
- 초기 확률과 회차별 확률이 읽기 전용으로 표시되는 경우 발생
|
||
- `isRoundCompleted` 조건으로 인해 일부 셀만 수정 가능
|
||
|
||
### 3. 포트 충돌
|
||
- 백엔드를 포트 8000에서 실행하려고 시도
|
||
- 포트 8000은 다른 서비스(goosefarminvestment)가 사용 중
|
||
- nginx 설정은 포트 3001로 프록시 설정되어 있음
|
||
|
||
### 4. 기본 section 설정 오류
|
||
- Section 1 제거 후에도 백엔드 기본 section이 1로 유지
|
||
- 프론트엔드에서 section 2로 시작하도록 변경했으나 백엔드와 불일치
|
||
|
||
### 5. 입력 필드 UX 문제 (2025-12-17 추가)
|
||
- **입력값 유실**: 페이지 새로고침/재연결 시 입력한 확률이 사라짐 (서버에는 저장되지만 화면에 표시 안 됨)
|
||
- **입력 중 덮어쓰기**: 입력 중 서버 `state_update`가 오면 아직 저장되지 않은 입력값이 사라짐
|
||
- **리셋 버튼 미작동**: 리셋 후에도 테이블의 입력값이 그대로 남아있어 초기화되지 않음
|
||
|
||
### 6. 베이지안 업데이트 로직 문제 (2025-12-17 추가)
|
||
- **독립적 확률 변환**: 각 회차마다 확률(%)을 독립적으로 베타분포로 변환하여 이전 회차 정보가 누적되지 않음
|
||
- **그래프 색상 구분 부족**: 초기와 회차별 그래프 색상이 동일하여 변화 추이 파악 어려움
|
||
- **피크 값 미표시**: 5회차까지 입력 완료 후 최종 확률 추정값(피크)이 표시되지 않음
|
||
- **하이라이트 테두리 과도**: 공을 꺼낸 후 활성화된 셀의 테두리가 과도하게 강조됨
|
||
- **종합 행 미표시**: 종합 행의 회차별 값이 "-"로만 표시되어 평균 확률을 확인할 수 없음
|
||
|
||
---
|
||
|
||
## 해결 방안
|
||
|
||
### Section 1 제거 및 통합
|
||
- `BallQuizPage.tsx`: Section 1 렌더링 코드 제거 (141-259줄)
|
||
- Section 2에 초기 확률과 회차별 확률 입력 통합
|
||
- "시뮬레이션 시작" 버튼 제거
|
||
|
||
### 모든 셀 수정 가능하도록 변경
|
||
- `BallQuizPage.tsx:174-219`: `isRoundCompleted` 조건 제거, 모든 회차 셀을 항상 input 필드로 표시
|
||
- `BallQuizPage.tsx:120-164`: 초기 확률도 항상 input 필드로 표시
|
||
- 사용자 입력값 우선 반영 로직 개선
|
||
|
||
### 포트 충돌 해결
|
||
- `main.py`: 백엔드 실행 포트를 3001로 변경
|
||
- nginx 설정 확인: `/bayesian-api/` → `http://localhost:3001/`
|
||
- 포트 8000은 다른 서비스 사용 중이므로 사용 불가
|
||
|
||
### 기본 section 설정
|
||
- `state_service.py:26`: 기본 section을 1 → 2로 변경
|
||
- `state_service.py:207`: `reset_state`에서도 section 2로 초기화
|
||
- 프론트엔드와 백엔드 일치
|
||
|
||
### 입력 필드 UX 개선 (2025-12-17)
|
||
- **서버 상태로 로컬 상태 동기화**: `BallQuizPage.tsx:25-86` - `useEffect`로 서버 `state_update` 수신 시 `participantInputs` 동기화
|
||
- **입력 중 필드 보호**: `BallQuizPage.tsx:22` - `focusedInputRef`로 포커스 중인 필드는 서버 업데이트로 덮어쓰지 않음
|
||
- **자동 저장**: `BallQuizPage.tsx:94-120` - `useEffect` + debounce(300ms)로 입력값 자동 저장
|
||
- **리셋 시 로컬 상태 초기화**: `BallQuizPage.tsx:26-29` - `estimates`가 빈 객체일 때 `participantInputs`도 빈 객체로 초기화
|
||
|
||
### 베이지안 업데이트 로직 개선 (2025-12-17)
|
||
- **누적 odds 변환**: 각 회차마다 입력한 확률(%)을 odds로 변환하여 이전 alpha, beta에 누적
|
||
- 예: 초기 50% → alpha=1, beta=1 → 1회차 60% → alpha=2.5, beta=2 → 2회차 65% → alpha=4.36, beta=3
|
||
- 5회차까지 진행 시 alpha + beta > 10
|
||
- **그래프 색상 점진적 변화**: 초기 연보라(`rgba(200, 180, 255, 0.8)`)에서 5회차 진한 보라(`rgba(92, 0, 230, 0.9)`)로 점진적 변화
|
||
- **5회차 피크 값 표시**: 5회차까지 입력 완료 시 Beta 분포 피크 공식 `(alpha - 1) / (alpha + beta - 2)`로 계산하여 그래프 하단에 표시
|
||
- **하이라이트 테두리 제거**: 활성화된 셀의 테두리 제거, 배경색(`bg-primary/5`)과 그림자(`shadow-sm`)만 유지
|
||
- **종합 행 회차별 값 표시**: 각 회차별 참가자들의 평균 확률 계산하여 확률(%)과 odds(x:1) 형식으로 표시
|
||
- **초기 그래프 항상 표시**: 초기값이 없어도 기본값 50%로 그래프 표시
|
||
- **입력하지 않은 회차 그래프 미표시**: 해당 회차에 입력이 없으면 그래프를 그리지 않음
|
||
|
||
---
|
||
|
||
## 구현 완료
|
||
|
||
- Section 1 제거 완료
|
||
- 42개 셀(7명 × 6컬럼) 모두 수정 가능
|
||
- 포트 3001로 백엔드 실행
|
||
- 기본 section 2로 통일
|
||
- 입력 필드 UX 개선 완료 (2025-12-17)
|
||
- 새로고침/재연결 시 입력값 유지
|
||
- 입력 중 서버 업데이트로 덮어쓰기 방지
|
||
- 자동 저장으로 입력값 유실 방지
|
||
- 리셋 버튼 정상 작동
|
||
- 베이지안 업데이트 로직 개선 완료 (2025-12-17)
|
||
- 누적 odds 변환으로 이전 회차 정보 누적
|
||
- 그래프 색상 점진적 변화 (연보라 → 진한 보라)
|
||
- 5회차 피크 값 표시 (그래프 하단)
|
||
- 하이라이트 테두리 제거
|
||
- 종합 행 회차별 평균 확률 표시
|
||
- 초기 그래프 항상 표시 (기본값 50%)
|
||
- 입력하지 않은 회차 그래프 미표시
|
||
|
||
---
|
||
|
||
## 교훈
|
||
|
||
### 포트 사용 전 확인 필수
|
||
- **원인**: nginx 설정과 백엔드 포트를 확인하지 않고 포트 8000 사용 시도
|
||
- **교훈**:
|
||
- 포트 사용 전 `netstat` 또는 `ss` 명령어로 사용 중인 포트 확인
|
||
- nginx 설정 파일에서 프록시 포트 확인 (`/etc/nginx/sites-enabled/default`)
|
||
- 다른 서비스와 충돌하지 않는 포트 사용
|
||
- **원칙**: `312_writing-principles.md` - 확인된 사실만 기록
|
||
|
||
### 프론트엔드/백엔드 기본값 일치 확인
|
||
- **원인**: Section 1 제거 후 프론트엔드만 section 2로 변경, 백엔드는 section 1 유지
|
||
- **교훈**:
|
||
- 상태 관련 기본값 변경 시 프론트엔드와 백엔드 모두 확인
|
||
- `state_service.py`의 초기값과 프론트엔드 기본값 일치 확인
|
||
- **원칙**: `311_FastAPI_구조_원칙.md` - 상태 관리 일관성
|
||
|
||
### 사용자 요구사항 명확히 이해
|
||
- **원인**: "엑셀처럼 수정 가능하게" 요구사항을 여러 번 반복 요청
|
||
- **교훈**:
|
||
- 읽기 전용 셀(`isRoundCompleted` 조건)을 제거하고 모든 셀을 항상 input 필드로 표시
|
||
- 사용자가 "엑셀처럼"이라고 표현할 때는 모든 셀을 클릭해서 바로 수정 가능해야 함
|
||
- **원칙**: 사용자 의도 파악 후 구현
|
||
|
||
### 브라우저 직접 확인 습관화
|
||
- **원인**: 코드 수정 후 브라우저에서 직접 확인하지 않고 가정으로 진행
|
||
- **교훈**:
|
||
- Playwright를 사용한 자동화 테스트로 브라우저 상태 확인
|
||
- 코드 변경 후 반드시 실제 브라우저에서 동작 확인
|
||
- **원칙**: `AGENTS.md` - 직접 확인 후 진행
|
||
|
||
### 서버 상태 리셋 시 로컬 상태도 초기화 필수
|
||
- **원인**: `reset_state`로 서버 `estimates`는 `{}`로 초기화되지만, 프론트엔드 `useEffect` 동기화 로직에서 빈 객체일 때 early return하여 `participantInputs`가 초기화되지 않음
|
||
- **교훈**:
|
||
- 서버 상태가 빈 객체로 리셋될 때는 로컬 상태도 명시적으로 초기화해야 함
|
||
- `useEffect`에서 early return 전에 빈 객체 체크 후 로컬 상태 초기화 로직 추가
|
||
- **원칙**: `313_React_구조_원칙.md:100-104` - 프론트엔드/백엔드 상태 동기화
|
||
|
||
### 베이지안 업데이트는 누적 방식으로 구현
|
||
- **원인**: 각 회차마다 확률을 독립적으로 베타분포로 변환하여 이전 회차 정보가 누적되지 않음
|
||
- **교훈**:
|
||
- 베이지안 업데이트는 이전 정보를 누적하는 방식이므로, 각 회차 입력값을 odds로 변환하여 alpha, beta에 누적
|
||
- 확률(%)을 odds로 변환: `odds = prob / (100 - prob)`, `alpha = odds`, `beta = 1`
|
||
- 누적: `cumulativeAlpha += odds.alpha`, `cumulativeBeta += odds.beta`
|
||
- 5회차까지 진행 시 alpha + beta > 10이 되어 그래프가 더 뾰족해짐
|
||
- **원칙**: 베이지안 추론의 본질은 사전 정보와 관찰 정보를 결합하는 것
|
||
|