# 2026-01-17 작업 내역 - BC-Game Up/Down WebSocket 및 모바일 레이아웃

## 📋 작업 요약
1. WebSocket 서버 완전 정상화 (주기적 게임 상태 브로드캐스트)
2. 모바일 반응형 레이아웃 구현
3. 차트 표시 문제 해결 시도
4. Nginx 캐싱 설정 최적화

---

## ✅ 완료된 작업

### 1. WebSocket 서버 수정 및 안정화

**문제점:**
- Timer가 실행되지 않아 게임 상태 브로드캐스트가 작동하지 않음
- 이전 버전은 `IoServer::factory()`만 사용했으나 periodic timer 미작동

**해결 방법:**
```php
// backend/websocket/server.php

// React event loop 생성
$loop = LoopFactory::create();

// Socket 생성
$webSock = new \React\Socket\Server("{$HOST}:{$PORT}", $loop);
$webServer = new IoServer(
    new HttpServer(
        new WsServer($gameWs)
    ),
    $webSock,
    $loop
);

// Periodic timer 추가 (1초마다 게임 상태 브로드캐스트)
$loop->addPeriodicTimer($GAME_TICK_INTERVAL, function() use ($gameWs) {
    echo "[Timer] Periodic timer triggered\n";
    $gameWs->broadcastGameState();
});

// Event loop 실행
$loop->run();
```

**결과:**
- ✅ WebSocket 연결 성공
- ✅ 1초마다 게임 상태 자동 브로드캐스트
- ✅ 클라이언트에 실시간 데이터 전송 확인
- ✅ 서버 안정성 확보

**서버 실행 명령:**
```bash
cd C:\laragon\www\bc-game-updown\backend\websocket
php server.php
```

**서버 로그 예시:**
```
[WebSocket] Game and Bet managers initialized
[WebSocket] Server initialized
[Server] WebSocket server started successfully
[Server] Listening on ws://0.0.0.0:8080
[Server] Periodic broadcast enabled (every 1s)

[Timer] Periodic timer triggered
[WebSocket] Broadcasting game state to 1 clients
[WebSocket] New connection: 57
[WebSocket] Welcome message sent to 57
[WebSocket] Message from 57: subscribe
[WebSocket] Client 57 subscribed with session session_xxxx
```

---

### 2. 프론트엔드 모바일 레이아웃 구현

**파일:** `frontend/apps/up-down/src/app/app.tsx`

**기존 문제:**
- 고정 너비 (`calc(100% - 380px)`, `w-96`) 사용으로 모바일 레이아웃 깨짐
- Status 사이드바가 모바일에서도 표시되어 공간 낭비

**변경 사항:**
```tsx
{/* 레이아웃: 모바일은 세로 1열, 데스크톱은 메인+사이드바 */}
<div className="flex flex-col lg:flex-row gap-4 w-full">
  {/* 메인 게임 영역 - 모바일 중앙 정렬 */}
  <div className="w-full lg:flex-1 max-w-md mx-auto lg:max-w-none">
    <div className="bg-white dark:bg-dark-secondary rounded-lg shadow-lg overflow-hidden">
      {/* 헤더: BTC 선택, 타이머 등 */}
      <div className="p-3">
        <Header />
      </div>

      {/* Benefit: Up 승리 / Down 승리 금액 */}
      <div>
        <Benefit />
      </div>

      {/* 차트 영역 (타이머 오버레이) */}
      <div className="relative w-full" style={{ height: '400px' }}>
        {isChartLoading && (
          <div className="absolute inset-0 bg-gray-100 dark:bg-gray-800 flex items-center justify-center z-10">
            <div className="text-center">
              <div className="inline-block animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent mb-3"></div>
              <p className="text-gray-600 dark:text-gray-400">차트 로딩 중...</p>
            </div>
          </div>
        )}
        <div className="w-full h-full">
          <Chart />
        </div>
      </div>

      {/* 통계: 라운드 정보, 총 금액, 플레이어 수 등 */}
      <div>
        <Stastics />
      </div>

      {/* 배팅 영역: 금액 입력, Up/Down 버튼 */}
      <div className="p-3">
        <Betting user={user} />
      </div>
    </div>
  </div>

  {/* 상태 사이드바 - 데스크톱에서만 표시 */}
  <div className="hidden lg:block w-96 flex-shrink-0">
    <Status />
  </div>
</div>
```

**레이아웃 구조:**
- 📱 **모바일 (< 1024px):**
  - 세로 1열 레이아웃
  - 최대 너비 제한 (`max-w-md`)으로 중앙 정렬
  - Status 사이드바 완전히 숨김 (`hidden lg:block`)
  - 순서: Header → Benefit → Chart → Stastics → Betting

- 💻 **데스크톱 (≥ 1024px):**
  - 가로 2열 레이아웃 (`flex-row`)
  - 메인 영역 (flex-1) + Status 사이드바 (w-96)

**차트 로딩 개선:**
```tsx
// 0.5초 후 로딩 스켈레톤 제거
useEffect(() => {
  const timer = setTimeout(() => {
    setIsChartLoading(false);
  }, 500);
  return () => clearTimeout(timer);
}, []);

// Runtime 준비 시에도 로딩 완료
const handleRuntimeReady = () => {
  console.log('✅ Runtime ready!');
  setIsChartLoading(false);
};
```

---

### 3. Nginx 캐싱 설정 최적화

**파일:** `C:\laragon\etc\nginx\sites-enabled\gr.ezywallets.com.conf`

**문제:**
- JS/CSS 파일이 1년간 캐싱되어 업데이트 반영 안 됨
- HTML 파일도 캐싱되어 새 빌드 파일 참조 불가

**수정된 설정:**
```nginx
# HTML files - no caching
location ~* \.html$ {
    expires -1;
    add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate";
}

# Static files caching (images, fonts)
location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# CSS and JS - shorter cache (5 minutes for development)
location ~* \.(css|js|wasm)$ {
    expires 5m;
    add_header Cache-Control "public, must-revalidate";
}
```

**Nginx 재시작:**
```bash
cd C:\laragon\bin\nginx\nginx-1.27.3
nginx.exe -t           # 설정 테스트
nginx.exe -s reload    # 재시작
```

---

## 📁 주요 파일 경로

### Backend (PHP)
- `C:\laragon\www\bc-game-updown\backend\websocket\server.php` - WebSocket 서버 진입점
- `C:\laragon\www\bc-game-updown\backend\websocket\GameWebSocket.php` - 게임 WebSocket 핸들러
- `C:\laragon\www\bc-game-updown\backend\websocket\GameWebSocket-simple.php` - 테스트용 간소화 버전

### Frontend (React/TypeScript)
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\app\app.tsx` - 메인 App 컴포넌트
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\components\header.component.tsx`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\components\benefit.component.tsx`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\components\betting.component.tsx`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\components\status.component.tsx`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\components\stastics.component.tsx`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\src\services\BackendWebSocket.ts`

### Nginx 설정
- `C:\laragon\etc\nginx\sites-enabled\gr.ezywallets.com.conf` - 사이트 설정
- `C:\laragon\bin\nginx\nginx-1.27.3\conf\nginx.conf` - 메인 설정

### 환경 변수
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\.env`
- `C:\laragon\www\bc-game-updown\frontend\apps\up-down\.env.production`

---

## 🔧 개발 워크플로우

### 1. 프론트엔드 빌드
```bash
cd C:\laragon\www\bc-game-updown\frontend
npm run build
```

**빌드 결과:**
- `frontend/dist/apps/up-down/index.html`
- `frontend/dist/apps/up-down/assets/index-[hash].js`
- `frontend/dist/apps/up-down/assets/index-[hash].css`
- `frontend/dist/apps/up-down/assets/chart_core_bg-[hash].wasm`

### 2. WebSocket 서버 실행
```bash
cd C:\laragon\www\bc-game-updown\backend\websocket
php server.php
```

**백그라운드 실행 (이미 실행 중):**
- PID 확인: `tasklist | findstr "php.exe"`
- 서버 종료: `taskkill //F //PID [PID번호]`

### 3. 브라우저 캐시 삭제 (업데이트 반영)
- **방법 1:** Ctrl+Shift+Delete → 캐시 삭제
- **방법 2:** F12 → 새로고침 우클릭 → "캐시 비우기 및 강력 새로고침"
- **방법 3:** 시크릿 모드 (Ctrl+Shift+N)

---

## 🐛 알려진 이슈

### 1. Chart 컴포넌트 표시 문제
**증상:**
- 차트가 화면에 렌더링되지 않음
- Header, Benefit, Stastics도 보이지 않음
- Betting 영역만 표시됨

**시도한 해결 방법:**
- Chart 컴포넌트에 명시적 높이 설정 (400px)
- 로딩 스켈레톤 추가
- 디버그 배경색으로 영역 확인

**현재 상태:**
- Chart 컴포넌트가 렌더링을 막고 있을 가능성
- 임시로 Chart를 주석 처리하면 다른 컴포넌트들은 보임

**다음 단계:**
- Chart 라이브러리 초기화 과정 확인
- WASM 로딩 상태 확인
- 에러 로그 분석 필요

### 2. 브라우저 캐싱 문제
**증상:**
- 시크릿 모드에서도 오래된 JS 파일 로드 (`index-BAw0DFe7.js` 대신 `index-g6dq0_H9.js`)

**해결:**
- Nginx 캐싱 설정 수정 완료
- JS/CSS 캐시 시간 1년 → 5분으로 단축

---

## 📊 WebSocket 데이터 흐름

```
클라이언트 (브라우저)
    ↓ wss://gr.ezywallets.com/ws
Cloudflare Proxy (443)
    ↓ Upgrade: websocket
Nginx (443 → 8080)
    ↓ ws://127.0.0.1:8080
PHP WebSocket Server (GameWebSocket.php)
    ↓ 1초마다 브로드캐스트
모든 연결된 클라이언트
```

**메시지 타입:**
1. `welcome` - 연결 성공
2. `subscribed` - 구독 완료
3. `game.state` - 게임 상태 (1초마다)
   ```json
   {
     "type": "game.state",
     "round_id": 123,
     "round_number": 45,
     "status": "BETTING",
     "countdown": 30,
     "start_timestamp": 1234567890000,
     "start_price": 95254.4104,
     "total_up_amount": 0.00,
     "total_down_amount": 0.00,
     "total_up_players": 0,
     "total_down_players": 0,
     "timestamp": 1234567891000
   }
   ```
4. `bet.placed` - 베팅 알림
5. `round.result` - 라운드 결과

---

## 🔐 보안 설정

### SSL/TLS
- Cloudflare Full Mode
- Origin Certificate: `ezywallets.pem`, `ezywallets.key`
- TLS 1.2, TLS 1.3

### Cloudflare 프록시
- ✅ Proxy ON (보안상 필수)
- WebSocket 지원 포트: 443, 2053, 8443
- 현재 사용: 443 (표준 HTTPS)

---

## 📝 환경 변수

### `.env` (개발)
```env
VITE_WS_URL=wss://gr.ezywallets.com/ws
VITE_API_URL=https://gr.ezywallets.com/api
```

### `.env.production`
```env
VITE_WS_URL=wss://gr.ezywallets.com/ws
VITE_API_URL=https://gr.ezywallets.com/api
```

---

## 💡 추가 개선 사항

### 성능 최적화
- [ ] Chart 지연 로딩 (dynamic import)
- [ ] 코드 스플리팅 (현재 613KB 단일 번들)
- [ ] 이미지 최적화

### UI/UX
- [ ] 차트 타이머 오버레이 (참고 이미지처럼)
- [ ] 모바일 터치 제스처
- [ ] 다크모드 애니메이션

### 기능
- [ ] 베팅 히스토리
- [ ] 실시간 알림
- [ ] 라운드 결과 애니메이션

---

## 🚀 배포 체크리스트

- [x] WebSocket 서버 안정화
- [x] 프론트엔드 빌드
- [x] Nginx 설정 최적화
- [x] SSL/TLS 설정 확인
- [ ] Chart 표시 문제 해결
- [ ] 모바일 테스트 완료
- [ ] 데스크톱 테스트 완료
- [ ] 크로스 브라우저 테스트

---

## 📞 연락처 및 참고

**프로젝트 경로:** `C:\laragon\www\bc-game-updown`
**도메인:** `https://gr.ezywallets.com`
**WebSocket:** `wss://gr.ezywallets.com/ws`

**현재 실행 중인 프로세스:**
- Nginx: C:\laragon\bin\nginx\nginx-1.27.3
- PHP WebSocket: 백그라운드에서 실행 중
- MySQL: C:\laragon\bin\mysql\mysql-8.0.30-winx64

---

**마지막 업데이트:** 2026-01-17 21:30
**작성자:** Claude Code Assistant
