BookSpace
학생회가 운영하는 학내 학습 공간을 학교 계정으로 예약하고, 예약 현황을 확인·취소할 수 있도록 만든 웹 서비스다. 시간 단위 스터디룸 예약 서비스 BookSpace와 좌석 배치도 기반의 BookSeat를 연결하고, BookSeat에는 복지 물품 재고 조회 및 운영자 관리 기능을 추가했다. 두 서비스는 화면에서 상호 이동할 수 있지만, 제공된 코드상 서로 별도로 구성된 웹 애플리케이션이다.
만들게 된 배경
숭실대학교 AI소프트웨어학부 학생회가 학생들의 학내 스터디룸 및 개인 학습 좌석 이용을 관리하기 위해 마련한 예약 서비스다. 공식 안내자료에서는 기존 슈투데이 앱을 통한 예약을 인정하지 않고 별도 예약 사이트 이용을 안내한다. 학생이 공간별 예약 현황과 이용 규칙을 직접 확인하고, 필요할 때 예약·취소·반납할 수 있도록 웹 화면을 구성했다.
개발 기간
- 스터디룸 예약(BookSpace): 2026년 1~3월 초기 개발, 이후 운영 중
- 좌석 예약(BookSeat): 2026년 9~10월 초기 개발, 이후 지속 보완·운영 중
해결하려던 문제와 목표
공간별로 다른 이용 대상과 운영 시간, 예약 가능 여부를 학생이 확인할 수 있게 하고, 중복 사용 및 예약 충돌을 줄이는 것이 핵심 목표다. 스터디룸은 시간대별 예약, SSU-PACE 개인 좌석은 실제 좌석 배치도 기반의 점유·반납·연장 흐름으로 분리해 제공한다. 학생회 운영자를 위한 예약·좌석 관리 기능과 복지 물품 현황 조회도 함께 다룬다.
주요 기능
- 학교 계정 로그인 및 학부별 이용 구분: 숭실대학교 통합로그인을 거쳐 사용자 학적 정보를 확인한다. BookSeat 서버는 학부에 따라 SSU-PACE I·II 접근 및 예약 권한을 제한한다. 스터디룸 프론트엔드에도 일부 공간의 학부별 이용 제한 화면 로직이 있다.
- 스터디룸 시간대 예약: 정보과학관 테라스 3A·3B·3C, 형남공학관 세미나실 2, 창의관 워크룸 A·B 등을 공간별로 표시한다. 예약된 시간을 구분하고 연속된 1~2시간을 선택해 예약 요청을 보낸다. 프로필에서 예약 내역·상세를 확인하고 취소를 요청할 수 있다.
- 개인 좌석 예약·연장·반납: SSU-PACE I 10석과 SSU-PACE II 6석의 배치도와 좌석별 점유 상태를 제공한다. 기본 이용 시간은 2시간, 종료 30분 전부터 2시간씩 최대 2회 연장할 수 있다. 반납 또는 만료 시 예약 상태를 갱신한다.
- 학생회 관리자 기능: BookSeat는 좌석 운영 상태 변경, 예약 강제 반납, 정기 청소 시간 관리, 예약 현황·처리 기록 조회를 위한 관리 화면과 API를 갖춘다. 스터디룸 프론트엔드에는 공간 운영 여부, 예약 취소, 정기 청소·수선 시간 관리 화면과 API 호출 코드가 있다.
- 복지 물품 현황 및 서비스 연결: BookSeat는 Google Sheets의 물품별 총수량·대여 가능 수량·대여 중 수량을 조회하고 검색·카테고리 필터를 제공한다. 두 예약 서비스 사이에는 상호 이동 링크를 배치했다.
내가 직접 구현하거나 담당한 부분
- 양 서비스의 전반적인 개발 담당: 사용자 예약 흐름과 관리자 기능을 포함한 서비스 전체 개발 범위를 담당했으며, 다른 공동 개발자와 협업했다(본인 확인).
- 프론트엔드: React·TypeScript 기반 공간 목록, 시간/좌석 선택, 예약 현황·상세, 예약 취소·연장·반납, 학부별 이용 조건 및 관리자 화면 개발에 참여했다.
- 백엔드·데이터 처리: BookSeat의 Fastify API, SQLite·Drizzle 기반 예약·세션·권한·운영 데이터 처리, 활성 예약 중복 방지 제약 및 복지 물품 데이터 조회를 포함한 전반적 개발을 담당했다. 스터디룸 백엔드도 구현·운영까지 완료했으나, 해당 서버 코드가 없어 세부 구현 기술은 문서에서 확정하지 않는다.
- 학교 계정 로그인 협업: 학교 인증 연동은 서비스 개발 범위에 포함되지만, USaint 로그인 로직의 보안 점검 및 수정은 백부승이 수행했다. 이 작업을 본인의 단독 구현 성과로 기재하지 않는다.
- 운영·보완: 두 서비스를 운영하고 있으며, 특히 좌석 예약 서비스는 초기 개발 이후 지속 보완 중이다(본인 확인).
팀 구성과 역할
- 스터디룸 예약(BookSpace): 3명 — 오유성, 이민우, 최영진
- 좌석 예약(BookSeat): 2명 — 오유성, 조세진
- 내 역할: 두 서비스의 전반적인 개발 범위 담당(서비스 설계, 화면·기능 개발, 서버 연동 및 운영 전반). 안내자료에 명시된 공동 개발자와 협업
- 협업 기여 구분: USaint 로그인 연동 부분은 정보보호학부 부학생회장 백부승이 보안 점검을 수행하고 인증 로직을 수정함. 다른 공동 개발자의 세부 담당 범위는 미확인
설계 구조 / 기술을 선택한 이유
스터디룸 예약(BookSpace): React·TypeScript·Vite SPA에서 React Router로 공간 목록, 예약 상세, 로그인, 마이페이지, 관리자 페이지를 분리한다. Axios로 공간·예약·사용자·관리자 API를 호출하며, 인증 만료 시 토큰 갱신 후 요청을 재시도하는 클라이언트 로직이 있다. 프론트엔드에는 학교 SSO 결과를 로그인 API로 전달하는 흐름이 구현되어 있다. 스터디룸 백엔드 구현과 운영은 완료되었으나(본인 확인), 서버 소스가 없어 내부 구조와 중복 예약 방지 방식을 코드로 검증할 수는 없다.
개인 좌석 예약(BookSeat): 사용자·관리자 React SPA를 개별 빌드하고, Fastify API가 학교 인증 결과 검증, 학부별 접근 통제, 예약·연장·반납 및 관리자 변경 요청을 처리한다. SQLite와 Drizzle 스키마에 사용자, 인증 세션, 공간·좌석, 예약, 정기 청소 일정, 관리자 처리 기록을 관리한다. 학교 로그인 결과는 서버 콜백에서 검증하며 별도 세션 토큰의 해시만 DB에 저장한다. 예약 생성·변경 시 서버에서 권한과 운영 제약을 다시 검사하고, 활성 예약의 사용자·좌석별 유일성은 DB 인덱스로 보장한다.
BookSeat의 README.md와 배포 스크립트에는 Cloudflare Pages(정적 사용자·관리자 화면) → Cloudflare Tunnel → AWS EC2의 Fastify API → SQLite 구성이 기재되어 있다. 이는 소스·설정으로 확인되는 배포 설계이며 현재 인프라 실행 상태를 직접 검증한 결과는 아니다. 복지 물품 정보는 Google Sheets CSV를 서버에서 읽어 60초 캐시하고, 외부 조회가 실패하면 기존 캐시가 있을 때 마지막 조회 데이터를 표시한다. 각 기술을 채택한 당시의 구체적인 의사결정 이유는 자료에 없어 확인 필요하다.
기술 구성과 확인 범위
- Frontend 공통: React, TypeScript, Vite
- 스터디룸(BookSpace): React Router, Axios, styled-components, Lucide React (Recoil 초기화 코드 존재, 실제 상태 활용 범위는 미확인)
- 좌석 예약(BookSeat) Backend: Node.js, Fastify, SQLite(
better-sqlite3), Drizzle ORM - 인증·외부 연동: 숭실대학교 SmartID SSO 및 Canvas/LMS 학적 정보 조회, Google Sheets CSV 복지 물품 현황 연동(BookSeat)
- Infrastructure — BookSeat 배포 구성 자료 기준: Cloudflare Pages·Tunnel, AWS EC2, systemd (서비스 운영은 확인되었으나 이 구성의 실제 적용 여부는 별도 확인 필요)
- 스터디룸 Backend/DB: 백엔드는 구현 완료·운영 중(사용자 확인). 서버 소스가 제공되지 않아 구체적 기술 스택은 미확인
가장 어려웠던 문제
가장 어려웠던 문제: 좌석 예약 요청이 동시에 들어왔을 때 동일 좌석의 중복 점유 또는 사용자 1명의 복수 활성 예약이 발생하지 않도록 하는 데이터 정합성 문제. 실제 운영 장애가 있었다는 기록은 없으며, 소스에서 확인된 핵심 설계 과제로 선정했다.
문제의 원인: 화면에 표시된 좌석 가능 상태는 조회 시점의 정보이므로, 여러 사용자가 거의 동시에 같은 좌석의 예약을 요청하면 프론트엔드의 예약 가능 여부 확인만으로는 충돌을 방지할 수 없다.
해결 방법:
BookSeat 서버에서 예약 생성 전 좌석 활성화·학부 이용 권한·청소 시간 중복을 검사한다. SQLite reservations 테이블에 status = 'active' 조건의 사용자별·좌석별 Partial Unique Index를 정의하고, 중복 삽입 오류를 사용자 예약 충돌(USER_CONFLICT)과 좌석 선점 충돌(SEAT_CONFLICT)로 구분하여 HTTP 409를 반환하도록 구성했다.
해결 결과:
tests/integration.test.ts에 두 사용자가 같은 좌석에 동시 예약을 시도할 때 한 요청은 201, 다른 요청은 409가 되어야 한다는 통합 테스트가 작성되어 있다. 통합 테스트 코드는 작성되어 있지만 실행 로그·운영 환경 동시성 측정 결과는 제공되지 않았으므로, 테스트 통과 여부나 실운영 개선 수치를 확정하지 않는다. 서비스의 전체 개발 범위를 담당했다는 본인 설명은 확인되지만, 이 로직을 혼자 작성했다고 단정하지 않는다.
최종 결과 / 실제 사용 여부
스터디룸 예약(BookSpace)과 좌석 예약(BookSeat)은 모두 개발을 마치고 실제 운영 중이다(본인 확인). 스터디룸 백엔드도 구현 완료됐고, 좌석 예약 서비스는 2026년 9~10월 초기 개발 이후 계속 보완하고 있다. 안내자료와 소스에서 예약·취소·반납, 관리자 기능, 복지 물품 현황 화면이 확인되지만, 실제 이용자 수·예약 건수·테스트 실행 결과 및 운영 지표는 별도 자료가 없다.
배운 점
두 예약 서비스를 전반적으로 개발·운영하면서, 프론트엔드에 표시되는 예약 가능 상태와 서버·DB가 최종 승인하는 예약 상태를 분리하는 구조를 다뤘다. 특히 좌석 예약에서는 API 검증에 더해 DB의 유일성 제약으로 동시 요청 충돌을 처리하도록 구현했다. 또한 학교 로그인 연동 과정에서 외부 보안 점검과 로직 수정이 이루어지는 협업을 경험했다. 개인적으로 얻은 구체적인 교훈·회고는 별도 확인이 필요하다.
앞으로 개선하고 싶은 점
추가 개선 제안(확정된 개발 계획 아님): 스터디룸 백엔드의 구현·운영 자료와 예약 충돌·권한 검증 테스트를 정리해 유지보수 및 검증 가능성을 높일 수 있다. 스터디룸 안내자료는 이용 후 반납을 예약 취소로 소개하지만, 실제 동작 기준은 코드이며 예약 상세 화면에서 시작 이후 취소 버튼이 숨겨진다. 따라서 안내자료를 구현 규칙에 맞게 수정할 필요가 있다. 운영 로그를 활용한 예약 충돌·오류 현황 분석도 개선 후보이다.