STAGE_101
Service Page :www.stage101.shop
Dev Environment : Next.js, TypeScript, FastAPI, AWS
Project Introduction: 소극장 예약 페이지
Project Type: 개인 프로젝트
"One space. One moment. Infinite possibilities."
STAGE_101은 기획부터 개발, 디자인까지 혼자서 진행한 풀스택 개인 프로젝트입니다.
평소 영화 감상과 평론을 즐기던 취향에서 출발해,
소극장 기반 영화 예약 플랫폼이라는 아이디어로 발전하게 되었고
단순히 상영작 정보를 보여주는 것이 아니라,
날짜별 예매가 가능한 소극장 조회부터 티켓 결제까지 실제 사용자 흐름을 고려한 구조로 설계·구현했습니다.
Project Overview
- 프로젝트명 : STAGE_101
- 한줄 설명 : 하나의 공간에서 무한한 가능성을 단 한 번의 경험으로 STAGE_101
- 반응형 디자인 : 모든 페이지는 최소 430px을 기준으로 반응형 구현되어 모바일, 데스크탑 모두 최적화 되었습니다.
- 스켈레톤 UI : 데이터 로딩 시간이 길어질 때 사용자에게 빈 화면 대신 스켈레톤 UI를 제공하여 더 나은 UX 제공
- 개발 기간 : 10주 정도 진행 되었습니다. / 리펙터링 기간 : 약 3주
👥 팀 구성 및 역할
| 역할 | 이름 | 담당 업무 |
|---|---|---|
| 👨💻 프론트엔드 | 박우석 | 전체 페이지 설계 UI/UX 흐름 구현 |
| 🧑💻 백엔드 | 박우석 | FastAPI 기반 사용자 인증, 결제 및 예약 시스템 구현 |
| 🎨 디자이너 | 박우석 | 사용자 흐름에 맞춘 UI/UX 설계 |
| 📝 기획 | 박우석 | STAGE_101 관련 컨셉과 기능 설계 |
⛳ 프로젝트 목표
- 다양한 기능을 직접 구현하며 기술 스펙트럼을 확장 시키기 위함
- 실제 서비스처럼 유저 흐름이 완성된 플랫폼을 목표
- 단순히 개발 만의 목적이 아닌 기획&설계 관점에서도 비지니스 서비스 극대화
- 사용자의 경험과 동선을 중심에 두고 기능을 설계
- 리펙터링 목표 : FastAPI & AWS 사용 및 공부
🚶♂️➡️유저 플로우


📌 내가 사용한 기술 스택과 라이브러리 선택 이유
(프로젝트 내에 자세한 설명이 추가되어 있습니다.)
| 구분 | 기술 | 선택 이유 |
|---|---|---|
| Front-end | React | 컴포넌트 기반 구조로 사용성과 유지보수성 뛰어나 UI 개발에 유리 |
| Next.js | 라우팅, SSR/SSG, 성능 최적화 기능 포함 → 구조화에 유리 | |
| Language | TypeScript | 정적 타입 검사로 안정성 확보, 코드 오류 줄임 |
| Back-end | FastAPI | 기존에 써봤던 Django보다 가볍고 트렌디한 백엔드 경험 |
| DB | PostgreSQL | 복잡한 사용자 대화 및 로그 데이터를 구조화하여 안정적으로 관리하기 위함 |
| Library | Zustand | 간단하게 상태 관리 가능, Redux보다 사용성 높음 |
| React-Query | 비동기 데이터 처리, 캐싱, 리페치 등 안정적인 API 연동 | |
| Zod | 스키마 기반 validation으로 안정성 확보 | |
| Framer-Motion | 자유로운 애니메이션 구현 및 전환 제어 | |
| Calendar | React-calendar | 간단한 일정 선택 UI 구현 (DayPicker보다 직관적) |
| API | Google Identity | Auth 연동 + 간편 로그인 구현 |
| Kakao SDK | 카카오 로그인 연동 | |
| Payments | Toss Payments | 국내 결제 최적화 솔루션 |
| Styling | Tailwind CSS | 빠른 개발과 유지보수, 스타일드 컴포넌트보다 간결 |
| Deployment | AWS EC2, RDS, S3 | 유연한 배포 환경과 확장 가능한 인프라를 구축 |
Performance
Auth 로직 Route가 아닌 Server Actions 사용
- 클라이언트에서 fetch 호출 제거로 인증 요청의 응답 속도 25% 개선
- 인증 로직이 서버 파일 내에서 실행되어 클라이언트 측 보안성 향상
장바구니 선택 삭제 , 결제 시 Promise.all로 병렬 처리
- 선택 삭제 기능은 Promise.all을 활용해 동시에 여러 항목을 병렬 처리
- 순차 처리 대비 평균 삭제 응답 시간이 약 50% 이상 단축
데이터 로딩 지연에 따른 Skeleton UI 적용
- 리뷰 모델 3.5초 로딩 → Skeleton UI 적용으로 사용자 혼란 최소화
- 상점 페이지 (3초), 상품 상세 페이지 (1.5초) 에도 Skeleton UI 적용
- 마이페이지(티켓/리뷰 등)에도 Skeleton UI 적용하여 시각적 피드백 제공
결제 페이지 접근 제어 & UX 안정성 확보
- 뒤로 가기 방지: pushState + popstate → 세션 안내 처리
- 직접 접근 방지: sessionStorage의 allowPaymentsAccess 플래그 적용
- 중복 호출 방지: paymentDone 세션 기록으로 자동 리디렉션 처리
Project Details
Sign In, Sign Up / Server Action


CSR & Server Actions
사용자 입력이 많은 인증(Auth)에서는 SSR보다 CSR이 더 적합하며, Server Actions를 활용해 민감한 로직을 서버에서 처리하여 보안성을 높였습니다.
RHF, Zod
Zod를 사용해 유효성 검사를 동기적으로 처리하고, 런타임 타입 체크를 병행하여 안정성 확보 RHF를 통해 입력 필드 단위로 상태를 관리해 불필요한 리렌더링을 줄이고 성능을 개선
Remember Email
사용자가 매번 동일한 이메일을 입력하지 않도록 이메일 저장 체크 기능을 추가해 반복적인 입력 부담을 줄였습니다.
Social Login
소셜 로그인 기능을 도입해 Kakao 또는 Google로 로그인할 수 있게하여 플랫폼 이용 진입 장벽을 낮췄습니다.
Home / Server Component

Server Component
Home는 서비스의 첫 진입점이라 SEO/초기 로딩이 가장 중요했습니다. Next.js Server Component로 주요 콘텐츠를 서버에서 미리 렌더링해 검색엔진 노출과 초기 페인트 속도를 개선했고, 클라이언트 번들 크기를 줄여 상호작용 가능 시점 까지의 체감 성능을 안정화했습니다.
Graph lib / CSR
그래프 구현을 위해 Chart.js, ApexCharts, Recharts를 실제로 적용해 비교했고, 화면 목적에 따라 최적 선택이 달라진다는 차이를 느꼈습니다. ApexCharts는 도넛(KPI) 표현과 테마/legend/tooltip 완성도가 높아 TopReservation에 사용했으며, SSR 미지원이라 dynamic import(ssr:false)로 처리했습니다. Chart.js는 축/라벨/툴팁 제어가 강점이라 TopReview 막대 차트에 적용했고(축 라벨은 축약, 툴팁은 풀네임), Recharts는 React UI 결합과 반응형 구성이 쉬워 굿즈 랭킹 모달에서 차트+리스트/링크를 함께 구성했습니다.
Now Showing / SSR
Now Showing은 SEO/초기 노출이 중요한 영역이라 서버에서 데이터를 선패칭해 initialData로 주입했습니다. 이후 캐러셀 애니메이션, 스켈레톤 전환, 예매 클릭 등 상호작용은 CSR로 처리했습니다.
Review, Write a Review / Client Component


Modal & Framer-Motion
Framer-Motion을 활용해 리뷰 작성 모달을 부드럽게 전환하여 사용자 경험을 향상시켰습니다. 여러 모달이 겹칠 때 슬라이드 애니메이션으로 구분되며, CSR 방식으로 사용자 인터랙션에 빠르게 대응합니다.
Blur UI
비로그인 사용자는 리뷰 일부가 블러 처리되어 표시되며, 로그인 유도로 자연스러운 접근을 유도합니다. 시각적 제한을 통해 컨텐츠 보호와 사용자 경험을 동시에 고려했습니다.
Unified Ticket-Based UI
STAGE_101의 핵심 아이덴티티인 ‘티켓’ 디자인을 리뷰, 결제 내역, 미리보기 등 전반에 적용하여 UI의 시각적 통일성과 오프라인 연계 가능성을 고려한 경험을 제공합니다.
Infinite Scroll
Tanstack Query의 useInfiniteQuery를 활용해 한 번에 모든 데이터를 불러오지 않고, 더보기 버튼을 누를 때마다 일정 데이터를 불러오게했습니다.
Live Ticket Preview
입력값을 useState로 관리하고, 해당 값을 티켓 형태의 컴포넌트에 바로 반영하여 실시간 미리보기를 구현했습니다. 작성 중에도 최종 리뷰 레이아웃을 즉시 확인할 수 있도록 했습니다.
Seat Reservation

30분간 임시 예약
좌석을 고르면 FastAPI 기반의 백엔드에서 30분 동안 유효한 임시 예약 정보가 DB에 저장됩니다. 결제가 완료되지 않으면, 30~35분 사이에 자동으로 예약 정보가 삭제되어 해당 좌석은 다시 예약 가능 상태로 전환됩니다.
FastAPI + WebSocket 실시간 반영
FastAPI와 WebSocket을 활용해, 특정 좌석이 예약되면 모든 사용자에게 실시간으로 해당 좌석이 비활성화된 상태로 표시됩니다. 이로 인해 사용자 혼선을 줄이고, 좌석 선택부터 결제까지의 흐름에 안정성과 신뢰도를 높였습니다.
Payments / Server Component


결제 완료 페이지 - Server Component(SSR) + 인증 쿠키 전달
Next.js Server Component에서 headers()로 인증 쿠키를 받아 API 요청에 그대로 전달해, 결제 내역이 '내 계정 소유'인 경우에만 조회되도록 처리했습니다. 단건 결제 히스토리(id)로 payment_key를 확보한 뒤, 동일 payment_key의 전체 구매 항목을 다시 조회해 주문 요약/목록을 서버에서 확정 렌더링합니다. 조회 실패/비정상 접근은 notFound()로 차단해 잘못된 URL 접근이나 위변조 시도를 최소화했습니다.
결제 중복 방지 + 예약/좌석 조회 + QR 세션 연동(입장 인증)
결제 완료 리다이렉트로 전달되는 searchParams(orderId, paymentKey, reservationId 등)를 서버에서 검증하고, 필수 값이 없으면 즉시 redirect('/') 처리합니다. 이후 해당 예약(reservationId)에 대해 이미 'paid' 결제가 존재하는지 먼저 확인하고, 없을 때만 결제 레코드를 생성해 중복 생성을 방지했습니다(포인트 적립도 서버에서 계산). 마지막으로 예약 정보에서 좌석을 조회하고, reservationId 기반 QR 세션을 불러와 입장용 QR을 제공하여 결제 완료 → 입장 인증까지 자연스럽게 이어지도록 구성했습니다.
Shop / Server Component

상품 리스트 SSR 렌더링 (Server Component + metadata)
상품 목록은 Server Component에서 선패칭(fetchShopsServer)해 초기 화면에 바로 렌더링했습니다. 페이지 메타데이터(title/description)와 함께 노출되어 SEO/초기 로딩을 확보했고, 로딩 구간은 loading.tsx 스켈레톤으로 UX를 보완했습니다.
상품 수량 조절 입력 UX 개선 (React State & Input Validation)
수량 조절을 위한 버튼과 직접 입력 기능을 useState와 조합하여 구현하고, 비정상 값 입력 시 Math.max(1, value)를 통해 유효성 검사로 보정했습니다.
비회원 장바구니 접근 제어 (Zustand + 모달 연동)
userId 유무로 로그인 여부를 확인하고, 비로그인 상태에서는 LoginModal을 띄워 인증을 유도했습니다. 바로 로그인 페이지로 리다이렉트하지 않은 이유는, 사용자가 상품 정보를 탐색·비교하는 흐름을 끊지 않고 현재 컨텍스트에서 '로그인 필요'를 안내하기 위해서입니다. Zustand로 로그인/모달 상태를 관리해 자연스럽게 전환되도록 구성했습니다.
AWS 인프라 구성 및 배포
S3
유저 프로필 및 공연 포스터 이미지 저장소로 사용. FastAPI에서 presigned URL 기반 업로드 처리.
RDS (PostgreSQL)
주요 비즈니스 데이터를 RDS PostgreSQL에 저장하고 SQLAlchemy ORM으로 트랜잭션을 안정적으로 관리.
EC2
프론트(Next.js)와 백엔드(FastAPI)를 각각 EC2에 배포하고, 성능 개선을 위해 t3.medium 사양으로 업그레이드.
ALB
AWS 인프라 구성 과정에서 Application Load Balancer와 ACM 인증서를 활용해 HTTPS 환경을 구축했습니다. ALB Listener 규칙을 통해 HTTP 요청을 HTTPS로 리다이렉트하도록 설정하여 보안 통신을 적용했습니다.
Route53
가비아에서 도메인을 구매한 뒤 Route53으로 DNS를 이전하여 ALB와 매핑했습니다. 가비아가 동일한 TLD 기준으로 AWS보다 훨씬 저렴한 비용(약 500원대)으로 도메인을 제공하여 초기 인프라 비용을 효과적으로 절감할 수 있었습니다.
회고
📌 배운 점
이번 리팩토링 프로젝트를 통해 단순히 기능을 구현하는 수준을 넘어, 서비스 전체의 구조와 동작 원리를 스스로 설계하며 개발하는 경험을 할 수 있었습니다. 기존에는 Supabase·Vercel·Django처럼 기본적인 구조가 이미 갖춰진 환경에서 개발을 진행해 플랫폼이 제공하는 틀 안에서 필요한 부분만 채워 넣으면 되었지만, FastAPI + AWS 인프라를 직접 구성하면서는 모든 요소를 제가 직접 설계해야 했습니다. 라우팅, 데이터 흐름, 인증 구조, 배포 방식, 로드밸런싱까지 하나하나 스스로 선택하고 구축하면서 “왜 이런 구조가 필요한가”를 근본적으로 고민할 수 있었고, 이 과정에서 백엔드와 인프라에 대한 이해도가 크게 높아졌습니다. 특히 문제가 발생했을 때 원인을 프레임워크가 아니라 내가 설계한 구조 자체에서 찾아야 하는 상황을 겪으며 서비스 전체를 바라보는 시야가 넓어진 것을 느꼈습니다.
🌀 아쉬웠던 점
이번 프로젝트에서 가장 아쉬웠던 부분은 기존 Supabase 기반 구조를 FastAPI와 PostgreSQL로 이전하는 과정에서, 초기 설계의 중요성을 크게 체감하게 된 점입니다. 이전 구조는 급하게 구현된 테이블 스키마와 일관성이 부족한 API 구조가 많아 FastAPI 기반으로 변환하는 과정에서 "왜 이런 방식으로 구현했지?"라는 고민을 수없이 하게 되었고, 이를 수정하는 데 예상보다 많은 시간이 들었습니다. 이 경험을 통해 백엔드 기술을 무엇으로 사용하든, 결국 초기에 데이터 모델과 API 흐름을 얼마나 탄탄하게 잡아두는지가 프로젝트 전체의 유지 보수성과 확장성을 좌우한다는 것을 실감했습니다. 다음 프로젝트에서는 ERD 설계와 데이터 흐름 정의를 먼저 명확히 하고, 구조적으로 일관된 설계를 기반으로 개발을 진행해 중간에 불필요한 리팩토링이 반복되지 않도록 더 체계적으로 접근하려고 합니다.
