Skip to content

Repository files navigation

델타포스 디시 티어리그 · 승격·강등 성적표

델타포스 디시인사이드 대회 결과로 1/2/3티어의 승격·강등을 계산해 보여주는 정적 사이트입니다. 데이터베이스가 없습니다. 성적표 전체가 주소(URL 해시)에 담기므로, 링크 하나만 공유하면 상대도 같은 화면을 봅니다.

개발

npm install
npm run dev        # 개발 서버 (HMR)
npm run build      # dist/ 생성
npm run preview    # 빌드 결과 확인
npm run lint       # ESLint
npm run format     # Prettier
npm run test       # Vitest (점수 계산·URL 인코딩)

점수 계산

종합 점수 = (K/D × 10) + (팀 수 + 1 − 팀 순위) × 2

티어별로 점수 내림차순 정렬한 뒤, 상위 promote명을 위 티어로, 하위 promote명을 아래 티어로 보냅니다. 1티어는 승격이, 3티어는 강등이 없으므로 각 티어 인원은 그대로 유지됩니다.

시즌 1 기준 최고점은 3티어의 에이전트햄부기(64.0, 10킬 2데스 · 우승팀)입니다.

2단 승급·강등

한 시즌에 두 티어를 건너뛰는 규칙입니다. 3티어 선수가 1티어보다 강한데 한 단계씩만 올라가면 다음 시즌에도 여전히 제자리를 못 찾는 문제를 교정합니다.

자격 — 통합 순위 기준. 전체 선수를 점수순으로 줄 세워 티어 정원(= 팀 수)씩 자른 구간이 그 선수의 "실력 티어"입니다. 실제 티어와 실력 티어가 두 단계 이상 벌어지면 자격을 얻습니다. 티어 안에서 1등인지가 아니라 리그 전체에서 그 자리에 있을 자격이 증명되는지를 봅니다.

인원 보존 — 1:1 스왑. 자격자를 그냥 올리면 위 티어 인원이 늘어납니다. 그래서 2단 승급자와 2단 강등자를 짝지어 자리를 맞바꿉니다. 짝이 안 맞으면 적은 쪽에 맞추고, promote도 넘지 않습니다.

슬롯 소비. 2단 이동은 건너뛴 경계를 모두 지나므로, 지나간 경계의 일반 이동 인원을 하나씩 줄입니다. 이 처리가 없으면 2단 이동이 생길 때마다 티어 인원이 어긋납니다.

시즌 1에 적용하면 이렇게 됩니다 — 티어 인원은 7/7/7 그대로입니다.

선수 점수 이동 사유
에이전트햄부기 64.0 3티어 → 1티어 통합 1위. 실력 티어 0
델델델델델 54.0 2티어 → 1티어 일반 승격
ZBCat 18.0 3티어 → 2티어 통합 10위라 1티어 정원 밖
채로희 12.0 1티어 → 2티어 일반 강등
내가중빡이 10.7 1티어 → 3티어 통합 18위. 실력 티어 2

건너뛸 폭은 config/constants.jsJUMP_SIZE입니다.

팀 순위 보너스가 최대 14점이라, 킬이 거의 없는 선수는 소속팀 성적만으로 순위가 갈립니다. 개인 기여를 더 강조하려면 src/lib/scoring.jsTEAM_RANK_WEIGHT를 낮추면 됩니다. 두 계수(KD_WEIGHT, TEAM_RANK_WEIGHT)는 이름 있는 상수라 푸터 문구도 자동으로 따라갑니다.

데이터 형식

데이터는 src/data/seasons.js에 있습니다. 화면에 편집기는 없습니다.

{
  "seasons": [
    {
      "id": "S01", // 상단 드롭다운에 표시되는 이름
      "label": "1회 대회",
      "promote": 2, // 티어별 승격·강등 인원
      "teams": [
        {
          "name": "G팀",
          "rank": 1, // 대회 최종 순위
          // 배열 순서가 곧 티어입니다: [1티어, 2티어, 3티어]
          "players": [
            ["믕봉믕", 11, 4], // [이름, 킬, 데스]
            ["델델델델델", 12, 3],
            ["에이전트햄부기", 10, 2],
          ],
        },
      ],
    },
  ],
}

시즌 추가seasons 배열 뒤에 같은 모양으로 하나 더 붙이면 됩니다. 드롭다운에 자동으로 나타나고, 기본 표시는 배열의 마지막(가장 최근) 시즌입니다.

잘못된 JSON은 무엇이 틀렸는지 알려주고 기본 데이터로 되돌아갑니다. 시즌 id가 겹치면 그것도 잡아냅니다.

구조

계층이 한 방향으로만 의존합니다: configlibhookscomponentsApp. 아래층은 위층을 모르므로, 순수 로직(lib)은 화면 없이 그대로 테스트할 수 있습니다.

src/
  config/constants.js       티어 수·화면 갈래·이동 방향 등 공용 이름 (단일 진실 공급원)
  data/seasons.js           대회 결과 원본. 시즌 추가는 여기만 고칩니다

  lib/scoring.js            점수·순위·다음 시즌 로스터 계산 (순수 함수)
  lib/filters.js            리더보드 필터 규칙 + 드롭다운 항목 목록
  lib/schema.js             주소로 들어온 데이터 검증 (한국어 메시지)
  lib/urlCodec.js           JSON ↔ 압축 해시 (lz-string, 예전 base64 링크 호환)
  lib/format.js             표에 찍을 숫자 자릿수

  hooks/useHashData.js      주소 해시를 상태의 원천으로 삼는 훅
  hooks/useSeasonStandings.js  보고 있는 시즌과 그 계산 결과
  hooks/usePlayerFilters.js    필터 상태 한 덩어리 + 걸러진 목록

  components/ui/            도메인을 모르는 재사용 부품 (Select · Avatar · Logo · icons)
  components/               SiteHeader · TopBar · Leaderboard · NextSeason · TierBadge
  styles/                   tokens → base → layout → components/*.css 순으로 읽힘
artifact/                   claude.ai 아티팩트 게시용 단일 HTML (구버전 디자인, 앱과 별개)

확장할 때 손대는 곳

하고 싶은 일 고칠 파일
시즌 추가 data/seasons.js
점수식 조정 lib/scoring.jsKD_WEIGHT · TEAM_RANK_WEIGHT
티어 수 변경 config/constants.jsTIER_COUNT + styles/tokens.css--tier-*
2단 → 3단 이동 config/constants.jsJUMP_SIZE
필터 추가 lib/filters.js + TopBar<Select> 한 줄
화면 갈래 추가 config/constants.jsVIEW + SiteHeaderNAV_ITEMS
색·서체 변경 styles/tokens.css

화면은 전적 검색 사이트 계열의 다크 리더보드 한 장입니다. 시즌·티어 드롭다운과 이름 검색으로 걸러 보고, 행 왼쪽 색 띠와 맨 오른쪽 칸이 이동 방향을 나타냅니다.

티어 칸은 승격·강등이 적용된 결과를 보여줍니다. 움직인 선수는 1티어 ←2티어처럼 원래 티어를 함께 답니다. 티어 필터도 적용 후 기준입니다.

URL에 저장되는 값

주소에 데이터가 없으면 로드하는 순간 기본 데이터를 해시에 심습니다. 즉 주소창은 항상 데이터를 들고 있습니다.

URL에 들어가는 것은 원본 값입니다 — 선수 이름, 킬, 데스, 소속팀, 팀 순위, 티어(배열 순서), 시즌 정보. 점수와 K/D는 저장하지 않고 매번 계산합니다. 계산 결과까지 URL에 넣으면 나중에 점수식을 고쳤을 때 저장된 값과 화면 값이 어긋나기 때문입니다. 원본만 있으면 언제 열어도 같은 결과가 나옵니다.

주소창이 저장소이므로 길이가 곧 수명입니다. base64는 원본보다 33% 길어져 시즌이 쌓이면 브라우저·메신저의 링크 한계에 걸립니다. 그래서 lz-string으로 압축하고 c~ 접두사를 붙입니다.

시즌 수 압축 (현재) base64 (예전)
1 658자 982자
10 2,748자 9,658자
20 4,344자 19,298자

접두사가 없는 해시는 예전 base64 링크로 보고 그대로 읽으므로, 이미 공유된 링크는 계속 동작합니다.

배포

main에 push하면 GitHub Actions가 lint → build → Pages 배포까지 처리합니다. 저장소 Settings → Pages → SourceGitHub Actions로 한 번 지정해 주세요.

vite.config.jsbase: './' 덕분에 user.github.io/<repo>/ 하위 경로와 커스텀 도메인 루트 양쪽에서 모두 동작합니다.

About

version 0.0.0.1

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages