1. 프로젝트 개요 및 개발 배경
이번 학기 데이터베이스 프로젝트로 교내 피트니스 센터 프로그램 관리 시스템을 구현했다.
우리 학교의 GX 프로그램 신청 사이트를 직접 다시 설계하고 만들어 보는 것이 목표였다.
기존 신청 시스템은 몇 가지 구조적인 문제를 안고 있었다.

첫째, 강좌를 찾기 위해 센터 → 분류 → 프로그램 → 강좌 순으로 단계마다 선택을 강제하는 계층형 구조여서, 사용자가 원하는 강좌를 찾으려면 여러 페이지를 반복적으로 비교해야 했다.
둘째, 시간대가 겹치는 두 강좌를 동시에 신청해도 별도의 경고 없이 결제까지 진행되는 검증 부재 문제가 있었다.
셋째, 신청·취소·변경 절차가 지나치게 복잡했다.
본 프로젝트는 이러한 문제를 개선하여 보다 직관적이고 효율적인 신청 시스템을 구현하는 것을 목적으로 한다. 나는 이 프로젝트에서 물리적 테이블 설계와 프론트엔드 구현을 담당했으며, 이 글에서는 두 파트를 중심으로 개발 과정을 정리한다.
2. 개발 과정
2.1 논리 설계 후 물리 모델로 전환
데이터베이스 설계는 개념적 → 논리적 → 물리적 순서로 진행된다. 앞선 단계에서 ERD와 정규화를 통해 엔티티와 관계를 정의해 두었다면, 물리적 설계는 이를 실제 DBMS에서 동작하는 형태로 구체화하는 단계다.
개념적 설계와 논리적 설계는 팀원들이 해서 보내주었고, 물리적 설계는 다음 순서로 진행했다.

- 논리 모델을 바탕으로 물리 다이어그램(Physical ERD)을 작성한다.
- 대상 DBMS를 선정한다. 본 프로젝트에서는 MySQL을 선정했다.
- 각 속성에 구체적인 데이터 타입과 크기를 지정한다. (예: INT, VARCHAR(50))
- 논리 설계 단계에서 정의한 1:N, 1:1 관계를 실제 외래키(FK) 컬럼으로 어떻게 구현할지 구체화한다.
설계 도구로는 협업을 위해 ERDCloud를 사용했다. ERDCloud에서 물리 테이블 구조를 완성하면 DDL 스크립트(CREATE TABLE 문)를 생성할 수 있는데, 이를 그대로 사용하지 않고 검토 과정을 거친 뒤 DBMS에서 실행했다.
개념·논리 설계 단계에서 추상적으로 보이던 속성들이, 데이터 타입과 크기를 직접 지정하는 순간 구체적인 제약을 갖게 된다. 예를 들어 VARCHAR(50)은 최대 50자까지만 저장할 수 있으므로, 각 속성마다 적정 크기를 판단하는 과정이 필요했다.
2.2 DDL 작성 및 기본키 설계
테이블은 member, account, program, course, apply 다섯 개로 구성했다. 회원 테이블의 DDL은 다음과 같다.
CREATE TABLE member (
member_id BIGINT NOT NULL AUTO_INCREMENT,
student_id VARCHAR(20) NOT NULL,
password VARCHAR(128) NOT NULL,
name VARCHAR(50) NOT NULL,
role VARCHAR(20) NOT NULL,
status VARCHAR(20) NOT NULL,
PRIMARY KEY (member_id),
UNIQUE KEY uq_student_id (student_id)
);
기본키 설계 과정에서 한 가지 시행착오가 있었다. 초기에는 기본키를 복합키로 설정했으나, 단일 속성만으로도 행을 충분히 식별할 수 있어 복합키로 묶을 필요가 없었다. 이에 모든 테이블의 기본키를 단일키로 수정했다.
이 수정은 정규화 측면에서도 의미가 있었다. 복합키가 없으면 기본키의 일부에만 종속되는 부분 함수 종속 문제가 발생하지 않으므로, 모든 테이블이 자동으로 2NF를 충족하게 된다. 결과적으로 단일키로의 전환은 데이터 모델의 정합성을 높이는 결과로 이어졌다.
외래키에는 다음과 같이 참조 무결성 제약을 부여했다.
CONSTRAINT fk_apply_course
FOREIGN KEY (course_id) REFERENCES course(course_id)
ON DELETE RESTRICT
ON UPDATE CASCADE
ON DELETE RESTRICT를 지정한 이유는, 회원이 탈퇴했거나 강좌가 삭제되었음에도 이를 참조하는 예약 데이터만 남는 유령 데이터 현상을 방지하기 위해서다. 부모 행이 없는 자식 행의 발생을 원천적으로 차단하여 테이블 간 연관 관계의 일관성을 유지하도록 했다.
2.3 테이블 구조 개선
물리 설계 단계에서 가장 크게 변경한 부분은 거대 테이블의 분리다.
초기 설계에서는 gx_program 테이블 하나가 프로그램 이름, 가격, 요일, 시작·종료 시간, 강사명, 정원, 현재 인원, 카테고리 등 지나치게 많은 속성을 포함하고 있었다. 그러나 하나의 프로그램은 여러 시간대로 개설될 수 있다. 예를 들어 동일한 'Flow 스트레칭' 프로그램이 월수 8시 강좌와 화목 5시 강좌로 동시에 운영될 수 있다. 즉, 프로그램(Program) 하나에 여러 개의 강좌(Course)가 대응되는 1:N 구조가 더 자연스럽다.
이에 따라 테이블을 다음과 같이 분리했다.
- Program: 프로그램 고유 정보 (이름, 카테고리, 센터 타입, 가격, 난이도 등)
- Course: 실제 개설 강좌 정보 (강사명, 요일, 시작·종료 시간, 정원, 잔여석, 상태, 버전 등)
CREATE TABLE course (
course_id BIGINT NOT NULL AUTO_INCREMENT,
program_id BIGINT NOT NULL,
name VARCHAR(100) NULL,
instructor_name VARCHAR(50) NULL,
day_of_week VARCHAR(20) NULL,
start_time TIME NULL,
end_time TIME NULL,
max_capacity INT NOT NULL,
current_capacity INT NOT NULL DEFAULT 0,
available_seats INT NOT NULL,
status VARCHAR(20) NULL, -- ACTIVE | CANCELLED
version BIGINT NOT NULL DEFAULT 0,
PRIMARY KEY (course_id),
CONSTRAINT fk_course_program
FOREIGN KEY (program_id) REFERENCES program(program_id)
);
분리 과정에서 available_seats(잔여석), version(동시성 제어용) 등의 컬럼을 추가했다. 예약 테이블인 apply에도 status(IN_CART / PENDING_PAYMENT / PAYMENT_COMPLETED / CANCELLED / REFUNDED), expires_at(결제 만료 시각) 등의 속성을 보강했다. 테이블을 분리한 결과 데이터 중복이 줄었고, 화면에서 강좌 정보를 표현하기에도 더 적합한 구조가 되었다.
2.4 프론트엔드 구성
프론트엔드는 다음과 같은 도구로 구성했다.
- HTML: 단일 HTML 파일
- JavaScript: Fetch API를 통한 백엔드 통신
먼저 서비스의 대표 컬러는 학교 컬러로 하여 화면 전반의 시각적 톤을 통일했다. HTML을 처음 써봐서, 기능별로 페이지를 나누지 않고 한 번에 구현하였다.
2.5 페이지 흐름 설계
화면을 개별적으로 구성하는 데 그치지 않고, 사용자의 동선을 기준으로 페이지를 연결했다.
회원가입/로그인 → 강좌 검색·필터 → 검색 결과 카드 → 수강 신청 → (완료) 마이페이지 이동
특히 두 가지를 중점적으로 고려했다.

첫째, 다중 필터 검색 UI다. 기존 시스템처럼 센터부터 순차적으로 선택하도록 강제하지 않고, 센터·시간대·요일·종목 등의 조건을 체크박스로 동시에 선택하여 검색하도록 구성했다. 백엔드가 이 조건들을 동적 쿼리로 처리하므로, 프론트엔드는 선택된 조건을 한 번에 전달하는 역할만 담당한다.

둘째, 수강 신청 완료 후 마이페이지로의 자동 이동이다. 신청 완료 시 사용자가 신청 여부를 별도로 확인할 필요 없도록, 완료 직후 예약 내역 화면으로 이동하게 했다. 사소한 처리이지만 사용성 측면에서 의미가 있다고 판단했다.
3. 회고 및 배운 점
이번 프로젝트를 통해 데이터베이스 설계를 논리 설계에서 물리 설계, DDL 작성, 실제 동작 검증에 이르기까지 전 과정을 직접 경험할 수 있었다.
기본키를 복합키로 잘못 설정했다가 수정한 일, 설정한 인덱스가 LIKE 질의에서 활용되지 않은 일, 더미 데이터에서 정상이던 화면이 실제 API 연동에서 동작하지 않은 일 등 여러 시행착오가 있었다. 그러나 이러한 과정은 각 설계 결정이 왜 필요한지를 구체적으로 이해하는 계기가 되었다.
협업 측면에서도 Git을 활용해 커밋·푸시·풀을 수행하고 충돌을 해결하면서, 사전에 최신 변경 사항을 반영하는 습관의 중요성을 체감했다. 5주라는 비교적 짧은 기간이었으나, 이론으로만 접하던 데이터베이스 개념을 실제 시스템 구현에 적용해 봄으로써 많은 것을 얻을 수 있었던 프로젝트였다.
'공부중 > 데이터베이스' 카테고리의 다른 글
| [데이터베이스] 6주차 실습 (1) | 2026.04.11 |
|---|---|
| [데이터베이스] 6주차 강의 요약 (0) | 2026.04.11 |
| [데이터베이스] 5주차 실습 (0) | 2026.04.04 |
| [데이터베이스] 5주차 강의 요약 (0) | 2026.04.01 |
| [데이터베이스] 4주차 강의 요약 (0) | 2026.03.27 |