MMA LiveSim
DraftKings용 무료 UFC DFS 최적화와 MMA 전투 시뮬레이터, 실시간 승률·전후 기대값·소유율·최적 라인업 제공
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기볼링 동호회 점수 기록 · 팀 배분 · 핸디 · 내기 정산 · 티어 근거 서비스
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗정모 현장에서 운영진이 휴대폰으로 팀을 나누고 점수만 입력하면, 핸디·승패·내기 정산·에버리지·티어 근거가 자동으로 계산되는 웹앱. 회원은 로그인 없이 링크로 들어와 자기 점수, 낼 금액, 승률, 상대 전적을 확인합니다.
🔗 서비스: https://bowling-recorder.vercel.app ·
main에 푸시하면 Vercel이 자동으로 배포합니다.
볼링 동호회를 운영하며 매 정모마다 반복되던 불편 네 가지에서 시작했습니다.
| # | 문제 | 왜 문제였나 |
|---|---|---|
| 1 | 점수 수기 기록 | 정모가 끝나면 운영진이 스프레드시트에서 참석자를 한 명씩 찾아 게임별 점수를 손으로 옮겨 적었다. 실수가 잦고 시간이 오래 걸렸다. |
| 2 | 티어 기준이 불명확 | 에버리지로 티어를 나누지만 "최근에 확 늘었다", "누구는 누구를 못 이긴다" 같은 운영진의 암묵지가 크게 작용했다. 근거가 남지 않아 결정이 설명되지 않았다. |
| 3 | 내기 정산이 번거로움 | 매 게임 팀전으로 게임비 내기를 하는데, 누가 몇 판 졌고 누구 몫까지 내야 하는지 계산이 복잡했다. 팀이 셋이거나 인원이 다르면 더 복잡해졌다. |
| 4 | 팀 배분과 핸디 계산 | 에버가 비슷한 사람끼리 가위바위보로 팀을 나누고, 핸디표를 보고 팀별 핸디를 손으로 합산했다. |
| 문제 | 해결 |
|---|---|
| 1. 수기 기록 | 참석 체크 → 팀 구성 → 휴대폰으로 점수 입력(3자리 치면 다음 사람으로 자동 이동, 입력 즉시 저장). 정모를 마감하면 구글 시트로 자동 내보내기. |
| 2. 티어 기준 | 티어를 자동으로 바꾸지 않습니다. 대신 전체 에버·최근 20게임 폼·상대 전적을 한 화면에 보여주고 승급/강등 후보만 표시합니다. 운영진이 바꿀 때는 사유를 반드시 기록합니다. 암묵지가 데이터로 남습니다. |
| 3. 내기 정산 | 점수가 들어오면 순위와 사람별 낼 금액이 바로 계산되고, 카톡에 붙여넣을 정산 텍스트를 한 번에 복사합니다. |
| 4. 팀 배분 | 에버 순으로 묶어 팀별로 한 명씩 랜덤 배정(가위바위보 자동화) 또는 직접 구성. 팀원은 드래그로 옮기고, 같은 팀 안에서 순서를 바꾸면 상대방이 바뀝니다. 상대방 티어 차이로 핸디 자동 계산, 팀·개인 핸디와 꼭두각시 점수는 합의로 고칠 수 있습니다. |
flowchart LR
A[새 정모] --> B[참석 체크]
B --> C[팀 구성<br/>랜덤 · 직접]
C --> D[점수 입력]
D --> E[게임 결과<br/>순위 · 낼 사람]
E -->|팀 그대로 / 새로 섞기| C
E --> F[정모 마감]
F --> G[정산 · 카톡 복사]
F --> H[구글 시트 자동 갱신]
동호회에서 실제로 쓰던 규칙을 코드로 옮겼습니다. 계산은 모두 lib/domain/의 순수 함수이고 단위 테스트가 붙어 있습니다.
| 결정 | 이유 |
|---|---|
| 스프레드시트를 버리지 않고 "DB + 시트 자동 내보내기" | 시트를 직접 DB로 쓰면 느리고 구조가 바뀌면 깨집니다. 데이터는 DB에 두고, 운영진이 익숙한 시트는 마감 때마다 기존 엑셀과 같은 모양의 점수기록지 탭(한 사람 한 줄, 점수를 가로로)으로 덮어씁니다. |
| 티어는 자동으로 바꾸지 않고 "근거 + 후보 + 사유 기록" | 티어에는 점수 말고도 운영진 판단이 들어갑니다. 기계가 정하면 신뢰를 잃고, 사람이 정하면 근거가 사라집니다. 둘 다 피하려고 근거는 시스템이, 결정은 사람이 하고, 그 이유를 이력으로 남겼습니다. |
| 후보 기준은 "티어 기준 중앙값의 중간" | "위 티어 최저 / 아래 티어 최고 에버" 기준도 시도했습니다. 그런데 실제 데이터에서 2·3티어 범위가 겹쳐 서로가 서로를 후보로 만드는 문제가 있어 되돌렸습니다. 중앙값은 극단값 한두 명에 덜 흔들립니다. |
| 랜덤 배정 = 가위바위보 자동화 | 완전 랜덤이면 실력 차가 커집니다. 기존 관행(비슷한 사람끼리 가위바위보)을 그대로 옮겨, 묶음이 곧 "상대방"이 되도록 했습니다. 직접 구성 모드도 둡니다. |
| 핸디는 상대방 간 차이, 팀은 순 핸디만 | 동호회 실제 규칙입니다. 팀원 핸디를 그냥 더하면 숫자가 커져 헷갈리므로, 받는 팀에 차이만 보여줍니다. |
| 핸디는 "합의 조정" 허용 + 기본값 함께 저장 | 현장에서는 상황에 따라 합의로 핸디를 바꿉니다. 팀 핸디, 개인 핸디, 꼭두각시 점수 모두 막지 않되 원래 값을 함께 남겨 나중에 왜 숫자가 다른지 알 수 있게 했습니다. |
| 티어·핸디는 게임 확정 시점 값으로 스냅샷 | 나중에 티어나 핸디표가 바뀌어도 과거 게임의 결과와 정산이 바뀌면 안 됩니다. |
| 에버·순위·정산은 저장하지 않고 점수에서 매번 계산 | 점수 하나를 고치면 관련 숫자가 모두 자동으로 맞춰집니다. 불일치가 생길 여지를 없앴습니다. |
| 운영진은 공용 비밀번호, 회원은 로그인 없음 | 운영진 2~3명, 회원 40여 명인 동호회에 개인 계정은 과합니다. 회원은 단톡 링크로 들어와 이름만 고르면 됩니다. 고른 이름은 DB가 아니라 그 기기의 쿠키에 1년간 기억되고, 로그인이 아니라 "누구 기준으로 볼지"일 뿐입니다. 쓰기 권한은 운영진 세션(서명된 쿠키, 30일)에만 있고 서버에서 매번 확인합니다. |
| 모바일 우선, 입력은 밝고 차분하게 / 결과는 어두운 화면 | 실제 사용 장소는 볼링장 레인 앞입니다. 입력 화면은 큰 숫자 칸과 단계형 흐름(실수 방지), 게임 결과·정산은 다 같이 들여다보는 어두운 화면으로 나눴습니다. 팀은 색 대신 A·B·C 글자로 구분합니다. |
| DB는 브라우저에서 접근 불가 (RLS 전부 차단) | 모든 읽기·쓰기는 서버에서 비밀 키로만 합니다. 공개 링크로 회원 화면을 열어도 DB에는 직접 닿지 않습니다. |
| 엑셀 기록은 에버리지에만 사용 | 기존 엑셀에는 날짜와 팀 정보가 없습니다. 그래서 564게임은 에버·최근 폼 계산에만 쓰고, 승률·상대 전적은 앱에서 기록한 게임부터 쌓습니다. |
구글 연동은 googleapis 없이 직접 호출 | 이미 쓰는 jose로 서비스 계정 토큰을 만들어 Sheets REST API를 부릅니다. 무거운 의존성을 줄였습니다. |
| 미니게임 점수는 서버에서 다시 계산 | 앱이 보낸 점수를 믿지 않고 투구마다 쓰러뜨린 핀 수를 받아, 끝까지 친 정상 게임인지 확인한 뒤 점수를 계산해 랭킹에 남깁니다. 이름은 기기에서 고른 회원이라 재미용 랭킹입니다. |
| 1/N 금액은 100원 단위 올림 | 현금·송금 모두 100원 미만은 번거롭습니다. 차액은 정산 화면에 따로 표시합니다. |
처음 화면(/)에서 회원 / 운영진으로 나눠 들어갑니다. 이미 이름을 고른 기기는 바로 회원 홈으로 가고, 회원 홈의 「처음 화면」 버튼으로 언제든 돌아올 수 있습니다.
운영진 (/admin) | 회원 (/home, 로그인 없음) |
|---|---|
| 홈: 새 정모 만들기, 진행 중 정모, 승급·강등 후보 | 홈: 처음엔 이름 고르기(검색·초성) → 최근 정모의 내 점수·순위·낼 금액, 내 기록 요약. 「다른 떼굴러로 보기」로 이름 바꾸기 |
| 정모: 참석 체크 → 팀 구성(드래그) → 점수 입력 → 결과 → 마감·정산·시트 반영, 정모 삭제 | 결과: 정모별 게임 결과, 전체 점수표, 정산 |
| 회원: 목록(승급·강등 후보 배지), 상세(근거·상대 전적·티어 변경 + 사유) | 내 기록: 에버·최근 폼·승률·상대 전적·전체 게임 기록·엑셀 이전 기록 |
| 설정: 핸디표, 기본 게임비, 최근 N게임, 구글 시트, 로그아웃 | 랭킹: 전체 에버 / 최근 폼 / 승률 / 미니게임 |
| 미니게임: 3D 레인에서 공을 밀어 던지는 10프레임 볼링 (밝은 / 락 볼링장), 끝나면 게임 랭킹 |
기획 문서는 docs/에 있습니다. mockups/index.html은 초기 기획 때의 시안이라 지금 화면(애플 스타일 리디자인)과는 다릅니다.
supabase/migrations/app/(public)/ 회원 화면 (홈 · 결과 · 내 기록 · 랭킹)
app/admin/ 운영진 화면 (로그인, 정모, 회원, 설정)
components/ 공용 UI (하단 탭, 헤더·버튼, 게임 결과·정산, 회원 기록)
lib/domain/ 순수 계산 규칙 + 테스트 (팀 배분, 핸디, 순위, 정산, 통계, 티어)
lib/minigame/ 미니게임 물리·점수·스와이프 계산 + 테스트
lib/db/ Supabase 조회
lib/sheets/ 구글 시트 내보내기
scripts/ 기존 엑셀 가져오기
supabase/ DB 마이그레이션
docs/ 요구사항 · 흐름/디자인 · 구현 계획
npm install
cp .env.example .env.local # 값 채우기 (아래 표)
npm run dev # http://localhost:3000
npm test # 도메인 규칙 테스트
| 환경 변수 | 설명 |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase 프로젝트 URL |
SUPABASE_SECRET_KEY | Supabase secret 키 (서버 전용) |
ADMIN_PASSWORD | 운영진 공용 비밀번호 |
SESSION_SECRET | 세션 쿠키 서명 키 (32자 이상 임의 문자열) |
GOOGLE_SERVICE_ACCOUNT_EMAIL, GOOGLE_PRIVATE_KEY | (선택) 구글 시트 내보내기용 서비스 계정 |
supabase/migrations/의 SQL을 순서대로 적용합니다.node --env-file=.env.local scripts/import-xlsx.mjs "기록.xlsx"
티어표 시트(이름·티어)와 점수입력 시트(이름·점수)를 읽습니다.