[DB] 탁구 리그전을 위한 ERD를 어떻게 설계해야 할까?

한강섭·2026년 1월 23일

탁굿

목록 보기
1/1
post-thumbnail

탁구 리그전(대회)을 개최하고 결과를 표시해주는 서비스를 만들기 위한 ERD 설계.
대회의 특성을 살펴보자면 많은 부수에 따른 리그(경기)들이 있고 그 리그 안에는 각각의 경기가 속하게 된다.

이 사진은 3부라는 리그 경기의 예선전이다. 여기에서 3등을 하게되면 예선 탈락, 1,2 등을 하게 되면 본선으로 가게된다.

이렇게 결과가 본선으로 가게되면 예선전에서는 풀리그 였지만 토너먼트로 경기의 특성이 바뀌고 남자 3부의 결과가 나오게 된다.

이런 특성을 가지고 있기에 대회, 경기, 게임 이 3단계로 구분을 하였고, 복식과 단체전을 커버하기 위해서 그룹테이블을 별도로 만들어 관리하였다.


유저

탁구 리그전 서비스를 이용하는 사용자의 기본 정보를 관리한다. 인증 정보와 탁구 실력을 나타내는 rating(부수) 을 함께 저장하여 사용자 식별과 경기 매칭의 기준으로 활용한다.

CREATE TABLE user (
    user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(255) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    username VARCHAR(50) UNIQUE NOT NULL,
    real_name VARCHAR(100) NOT NULL,
    gender ENUM('MALE', 'FEMALE') NOT NULL,
    birth_date DATE NOT NULL,
    region VARCHAR(100),
    phone_number VARCHAR(20),
    rating INT DEFAULT 12 COMMENT '0: 선수부, 1~12: 1부~12부',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
  • email과 username은 UNIQUE 제약으로 중복 방지
  • 물리적 삭제 대신 소프트 삭제 방식 권장 (기록 보존)

대회

탁구 대회의 최상위 개념으로 전체 이벤트 정보를 관리한다. 한 명의 주최자가 여러 대회를 만들 수 있으며, 하나의 대회는 여러 경기 단계(competition)를 포함할 수 있다.

CREATE TABLE tournament (
    tournament_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tournament_name VARCHAR(200) NOT NULL,
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    location VARCHAR(200) NOT NULL,
    organizer_id BIGINT NOT NULL,
    entry_fee DECIMAL(10, 2) DEFAULT 0,
    event_info TEXT COMMENT '경기 종목 정보',
    prize_info TEXT COMMENT '시상 정보',
    description TEXT,
    status ENUM('DRAFT', 'REGISTRATION_OPEN', 'REGISTRATION_CLOSED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED') DEFAULT 'DRAFT',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (organizer_id) REFERENCES user(user_id) ON DELETE RESTRICT
);

organizer_id의 ON DELETE RESTRICT로 주최자 삭제 방지
status로 대회 생명주기 관리
event_info와 prize_info는 TEXT 타입으로 자유롭게 작성 가능


참가자

tournament와 user를 연결하는 중간 테이블로, 누가 어느 대회에 참가했는지 기록한다. 참가 신청부터 최종 순위까지의 전체 과정을 추적한다.

CREATE TABLE tournament_participant (
    participation_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tournament_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    participation_status ENUM('REGISTERED', 'CONFIRMED', 'WITHDRAWN', 'DISQUALIFIED') DEFAULT 'REGISTERED',
    final_rank INT,
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (tournament_id) REFERENCES tournament(tournament_id) ON DELETE RESTRICT,
    FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE RESTRICT,
    UNIQUE KEY unique_participation (tournament_id, user_id)
);
  • (tournament_id, user_id) 조합으로 UNIQUE 제약, 중복 참가 방지
  • ON DELETE RESTRICT로 기록 보존
  • final_rank는 대회 완료 후 업데이트

경기

하나의 대회 안에서 진행되는 경기 단계를 관리한다. 예선전, 본선, 결승 토너먼트처럼 대회는 여러 단계로 구성될 수 있으며, 각 단계마다 다른 경기 방식과 참가 형식을 가질 수 있다.

CREATE TABLE competition (
    competition_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    tournament_id BIGINT NOT NULL,
    competition_name VARCHAR(200) NOT NULL COMMENT '예선, 본선, 결승 토너먼트 등',
    competition_type ENUM('ROUND_ROBIN', 'SINGLE_ELIMINATION', 'DOUBLE_ELIMINATION', 'GROUP_STAGE') NOT NULL,
    match_format ENUM('SINGLES', 'DOUBLES', 'TEAM') NOT NULL COMMENT '단식, 복식, 단체전',
    competition_order INT NOT NULL COMMENT '대회 내 진행 순서',
    has_groups BOOLEAN DEFAULT FALSE COMMENT '조 편성 여부',
    players_per_group INT COMMENT '조별 인원수',
    description TEXT,
    status ENUM('SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED') DEFAULT 'SCHEDULED',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (tournament_id) REFERENCES tournament(tournament_id) ON DELETE RESTRICT
);
  • 동일 tournament 내에서 competition_order로 순차적 진행 관리
  • match_format에 따라 match_participant 테이블에 저장되는 인원수가 달라짐
  • 애플리케이션 레벨에서 match_format에 맞는 참가자 수 검증 필요

그룹 (복식, 단체전)

조별 경기를 위한 그룹 정보를 관리한다. competition에서 has_groups가 TRUE일 때 사용되며, A조, B조와 같이 참가자를 나누어 조별 리그를 진행할 수 있다.

CREATE TABLE `group` (
    group_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    competition_id BIGINT NOT NULL,
    group_name VARCHAR(50) NOT NULL COMMENT 'A조, B조 등',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (competition_id) REFERENCES competition(competition_id) ON DELETE RESTRICT,
    UNIQUE KEY unique_group (competition_id, group_name)
);
  • (competition_id, group_name) 조합으로 UNIQUE 제약
  • group은 MySQL 예약어이므로 백틱(`)으로 감싸서 사용
  • 조가 필요 없는 competition에서는 사용되지 않음

참여자

특정 조에 속한 참가자를 관리한다. 조별 리그에서 어떤 사용자가 어느 조에 배정되었는지, 조 내에서의 최종 순위는 어떻게 되었는지 기록한다.

CREATE TABLE group_participant (
    group_participant_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    group_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    group_rank INT COMMENT '조 내 최종 순위',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES `group`(group_id) ON DELETE RESTRICT,
    FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE RESTRICT,
    UNIQUE KEY unique_group_participant (group_id, user_id)
);
  • (group_id, user_id) 조합으로 UNIQUE 제약
  • user_id를 직접 참조하여 구조 단순화
  • group_rank는 조별 리그 완료 후 업데이트

매치 (게임)

실제 경기 정보를 저장한다. 두 팀(또는 두 명)이 대결하는 개별 경기의 일정, 결과, 세트 스코어를 관리하며, 토너먼트의 경우 대진표 구조도 표현한다.

CREATE TABLE `match` (
    match_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    competition_id BIGINT NOT NULL,
    group_id BIGINT COMMENT '조별 경기인 경우 조 ID',
    match_round INT COMMENT '토너먼트의 경우 라운드 (8강=8, 4강=4, 결승=2)',
    match_number INT COMMENT '같은 라운드 내 경기 번호',
    court_number INT COMMENT '코트 번호',
    status ENUM('SCHEDULED', 'IN_PROGRESS', 'COMPLETED', 'CANCELLED', 'NO_SHOW') DEFAULT 'SCHEDULED',
    winner_side ENUM('SIDE_A', 'SIDE_B', 'DRAW') COMMENT '승리 팀',
    side_a_sets INT COMMENT 'A팀이 획득한 세트 수',
    side_b_sets INT COMMENT 'B팀이 획득한 세트 수',
    total_sets INT COMMENT '총 진행된 세트 수',
    next_match_id BIGINT COMMENT '토너먼트에서 다음 라운드 경기',
    notes TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (competition_id) REFERENCES competition(competition_id) ON DELETE RESTRICT,
    FOREIGN KEY (group_id) REFERENCES `group`(group_id) ON DELETE RESTRICT,
    FOREIGN KEY (next_match_id) REFERENCES `match`(match_id) ON DELETE SET NULL
);
  • next_match_id로 토너먼트 대진표 구조 표현
  • 세트 스코어를 간단하게 기록 (예: 3:1이면 side_a_sets=3, side_b_sets=1, total_sets=4)
  • match는 MySQL 예약어이므로 백틱(`)으로 감싸서 사용
  • ON DELETE SET NULL로 next_match_id는 참조하는 경기 삭제 시 NULL 처리

게임 참가자

각 경기에 참여하는 선수들을 관리한다. 단식은 양쪽 1명씩, 복식은 2명씩, 단체전은 3명 이상이 참여하며, side로 양 팀을 구분하고 participant_order로 팀 내 순서를 관리힌다.

CREATE TABLE match_participant (
    match_participant_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    match_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    side ENUM('SIDE_A', 'SIDE_B') NOT NULL,
    participant_order INT COMMENT '복식이나 단체전에서 순서',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (match_id) REFERENCES `match`(match_id) ON DELETE RESTRICT,
    FOREIGN KEY (user_id) REFERENCES user(user_id) ON DELETE RESTRICT
);
  • 같은 match_id에 여러 명이 참여 가능
  • competition의 match_format에 따라 참가자 수 검증 필요
    SINGLES: 각 side당 1명 (총 2명)
    DOUBLES: 각 side당 2명 (총 4명)
    TEAM: 각 side당 3명 이상
  • participant_order로 복식에서 1번, 2번 선수 구분 가능
profile
기록하고 공유하는 개발자

4개의 댓글

comment-user-thumbnail
2026년 1월 24일

1개의 답글
comment-user-thumbnail
2026년 2월 2일

온라인 탁구게임 만들어주세요

1개의 답글