Post

Kickytime – 풋살 매칭 서비스 (프로젝트 개요)

풋살 매칭 서비스 Kickytime의 배경, 목표, 아키텍처, 설계를 정리한 개요

Kickytime – 풋살 매칭 서비스 (프로젝트 개요)

한눈에 보기: 관리자가 경기를 개설하면 사용자가 참여/취소하는 풋살 매칭 서비스. AWS Cognito로 인증하고, Spring Boot + React + PostgreSQL 기반으로 구현했습니다.

0. 메타 정보

  • 프로젝트명: Kickytime
  • 프로젝트 기간: 2025년 8월 01일 ~ 2025년 8월 17일
  • 팀원: 박민지(팀장), 하영현, 이혜민, 구태연, 임정우

1. 프로젝트 개요

1-1. 프로젝트 배경

많은 사람들이 풋살 경기에 참여하고 싶어하지만, 경기를 개설하거나 참여할 수 있는 플랫폼이 부족하다. 기존 오프라인/커뮤니티 중심 매칭은 번거롭고, 참여자 현황 관리도 어렵다.

Kickytime“관리자가 개설한 경기 정보를 바탕으로 사용자들이 참여/취소할 수 있는 시스템” 을 통해 매칭 생성·참여·관리의 번거로움을 줄이고, 실시간 참여 현황 파악을 쉽게 합니다.

1-2. 프로젝트 목표

  • 관리자 전용 경기 개설/삭제 기능 제공
  • 일반 사용자 경기 참여/취소 기능 제공
  • 인원 제한, 중복 참여 방지 등 유효성 검증
  • AWS Cognito 기반 인증 적용
  • 향후 확장을 고려한 모듈형 설계

2. 주요 기능 (요약)

2-1. 사용자 기능

  • 매칭 목록 확인(제목/시간/장소/정원/현재인원)
  • 매칭 참여 신청(정원·중복 검사)
  • 매칭 참여 취소(본인만 가능)
  • 회원가입 시 이메일 필수 + 이메일 검증(Cognito)
  • 가입 시 프로필 사진 기본 이미지 설정

2-2. 관리자 기능

  • 매칭 개설(시간·장소·정원 등 입력)
  • 매칭 삭제(참여자 정보 포함 일괄 삭제)

3. 핵심 로직 플로우 (요약 다이어그램)

3-1. 회원가입 & 로그인 (Cognito)

sequenceDiagram
  autonumber
  participant U as User (Browser)
  participant C as AWS Cognito (User Pool)
  participant B as Backend (Spring Boot)

  U->>C: SignUp(email/password)
  C-->>U: Verification email
  U->>C: Verify email
  U->>C: SignIn
  C-->>U: ID/Access Token (JWT)
  U->>B: POST /api/users/signin-up\nAuthorization: Bearer <JWT>
  B->>C: (optional) JWK/SDK로 토큰 검증
  B->>B: users 테이블에 cognito_sub 기준 Upsert
  B-->>U: 200 OK (User 정보)

3-2. 매칭 참여

sequenceDiagram
  autonumber
  participant U as User
  participant B as Backend
  participant DB as PostgreSQL

  U->>B: POST /api/matches/{id}/participants (JWT)
  B->>DB: 현재 참가자 수 < max_players ?
  B->>DB: (match_id, user_id) 중복 여부 확인
  alt 유효성 통과
    B->>DB: match_participants INSERT
    B-->>U: 200/201 참여 성공
  else 실패
    B-->>U: 409/404 등 오류
  end

3-3. 매칭 취소

sequenceDiagram
  autonumber
  participant U as User
  participant B as Backend
  participant DB as PostgreSQL

  U->>B: DELETE /api/matches/{id}/participants (JWT)
  B->>DB: 본인 참여 여부 확인
  alt 본인 참여 맞음
    B->>DB: match_participants DELETE
    B-->>U: 204/200 취소 완료
  else 아님
    B-->>U: 403/404 오류
  end

4. 아키텍처 & 설계

4-1. 전체 아키텍처(개요)

Kickytime 네트워크 아키텍처 두 AZ, ECS/EC2 App 서버 혼용, RDS, NAT×2, TGW, SSM 구조

4-2. 데이터 모델(ERD)

erDiagram
  USERS ||--o{ MATCH_PARTICIPANTS : has
  MATCHES ||--o{ MATCH_PARTICIPANTS : has
  USERS ||--o{ MATCHES : creates

  USERS {
    BIGINT id PK
    VARCHAR email "unique, cognito와 일치"
    VARCHAR nickname "unique"
    VARCHAR cognito_sub
    BOOLEAN email_verified
    VARCHAR image_url "기본: /images/default-profile.png"
    ENUM role "USER|ADMIN (default USER)"
    ENUM rank "Beginner|... (default Beginner)"
    TIMESTAMP created_at
    TIMESTAMP updated_at
  }

  MATCHES {
    BIGINT id PK
    VARCHAR location
    DATETIME match_time
    INT max_players
    ENUM match_status "OPEN|FULL|CLOSED|CANCELED (default OPEN)"
    BIGINT created_by FK "-> USERS.id"
    TIMESTAMP created_at
    TIMESTAMP updated_at
  }

  MATCH_PARTICIPANTS {
    BIGINT id PK
    BIGINT match_id FK "-> MATCHES.id"
    BIGINT user_id FK "-> USERS.id"
    TIMESTAMP joined_at
    UNIQUE match_user_unq "(match_id, user_id)"
  }

테이블 설명(요약)

  • USERS: Cognito 최초 로그인 시 cognito_sub 기준으로 upsert. password는 MVP에선 사용하지 않을 수 있음(null 허용).
  • MATCHES: 관리자만 생성/삭제. 상태 값으로 운영 정책 반영(예: 정원 도달 시 FULL).
  • MATCH_PARTICIPANTS: (match_id, user_id) UNIQUE로 중복 참여 방지.

4-3. 핵심 API 요약(발췌)

구분메서드경로설명권한
사용자POST/api/users/signin-up최초 로그인 UpsertUSER
사용자GET/api/users/me내 프로필 조회USER
경기GET/api/matches경기 목록 조회USER
경기POST/api/matches경기 개설USER
경기GET/api/matches/me내가 참여한 경기USER
경기POST/api/matches/{matchId}/participants경기 참여USER
경기DELETE/api/matches/{matchId}/participants참여 취소USER
경기(관리)DELETE/api/matches/{matchId}경기 삭제ADMIN
사용자(관리)POST/api/admin/users/backfill-cognito?confirm=falseCognito 백필ADMIN

응답 코드 권장: 생성 201, 취소/삭제 204, 중복/정원초과 409, 인증/권한 문제 401/403.


부록 A. 상세 스키마 표 (원문 발췌)

users

컬럼명타입설명
idBIGINT (PK)사용자 ID
emailVARCHAR로그인 ID 이메일(고유), Cognito와 일치
nicknameVARCHAR별칭, 화면 표시용(고유, 중복 x)
cognito_subVARCHARCognito 사용자 고유 식별자(sub)
email_verifiedBOOLEAN이메일 검증 여부
passwordVARCHAR비밀번호 (향후 로컬 로그인 확장 시; Cognito만 쓰면 null)
roleENUMUSER / ADMIN (기본값: USER)
image_urlVARCHAR프로필 사진 경로, 기본값 예: /images/default-profile.png
rankENUMBeginner 등(기본값: Beginner)
created_atTIMESTAMP가입일자
updated_atTIMESTAMP수정일자

MVP 단계에서는 Cognito 로그인을 사용하므로 password는 사용하지 않거나 null 허용으로 둡니다.

matches

컬럼명타입설명
idBIGINT (PK)매칭 ID
locationVARCHAR장소
match_timeDATETIME경기 시간
max_playersINT최대 인원 수
match_statusENUMOPEN, FULL, CLOSED, CANCELED (기본값: OPEN)
created_byBIGINT (FK)관리자 ID (users 참조)
created_atTIMESTAMP개설일시
updated_atTIMESTAMP수정일시

match_participants

컬럼명타입설명
idBIGINT (PK)참여 ID
match_idBIGINT (FK)매칭 ID (matches 참조)
user_idBIGINT (FK)사용자 ID (users 참조)
joined_atTIMESTAMP참여 일시
UNIQUE(match_id, user_id)제약중복 참여 방지

부록 B. 예시 cURL (발췌)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 로그인/업서트
curl -X POST http://localhost:8080/api/users/signin-up \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

# 경기 목록
curl -X GET http://localhost:8080/api/matches \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

# 경기 참여
curl -X POST http://localhost:8080/api/matches/10/participants \
  -H "Authorization: Bearer <ACCESS_TOKEN>"

# 참여 취소
curl -X DELETE http://localhost:8080/api/matches/10/participants \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
This post is licensed under CC BY 4.0 by the author.