누구나 쉽게 배우는 티켓팅! '쉽게, 티켓'
쉽게, 티켓
easy-ticket-e7da7.web.app
프로젝트 주제
누구나 티켓팅을 쉽게 배울 수 있도록 하자!
주제 선정 배경
현재 다양한 이벤트, 공연, 스포츠 경기 등의 티켓 예매는 대부분 온라인에서 이루어지고 있습니다.
그러나 인터넷 사용에 익숙하지 않은 어르신분들 또는 사회적 약자들은 이러한 온라인 티켓팅에서 어려움을 겪고 있으며, 결과적으로 문화생활을 즐길 기회를 얻지 못하는 경우가 많습니다.
'쉽게, 티켓'은 이러한 문제를 해결하고자 했습니다. 사용자들이 티켓팅 과정을 연습하면서 다양한 난이도의 문제를 경험하여 실제 상황에서의 대응 능력을 기를 수 있도록 도와줍니다. 디지털 격차 없이 누구나 예매 과정을 쉽게 배울 수 있습니다.
프로젝트 일정 (기간)
2024.07.09 - 2024.09.13 (9주)
팀 소개
이공계 전공 대학생 3명으로 구성된 팀으로, 팀원 모두 프론트엔드 개발자를 꿈꾸고 있습니다.
저희 팀에 대한 더 자세한 정보는 이곳을 참고해주세요~😻
기획 및 아키텍처 디자인
먼저 정기 회의 요일을 정해, 매주 1회 이상 온라인 or 오프라인 모임을 통해 소통했어요.
오프라인은 서울 근교에서 만남을 가지고, 온라인은 디스코드 및 카카오톡을 활용했습니다.
또한 Trello를 활용해 모임과 개발 일정을 관리하여 효율적으로 이슈를 해결하기 위해 노력했습니다!
트렐로 보드 링크에서 더 자세히 확인해보세요!
플로우차트
와이어프레임
피그잼보드를 초기 구상 과정 스케치에 활용했습니다.
용량 이슈로 인해 아래 링크에서 보시는 것을 추천드립니다.
Figma
Created with FigJam
www.figma.com
프로토타입
디자인 시스템을 토대로 연습모드와 실전모드에 대한 프로토타입을 피그마로 구축했습니다.
용량 이슈로 인해 아래 링크에서 보시는 것을 추천드립니다.
Figma
Created with Figma
www.figma.com
기술 스택
React를 활용한 프론트엔드 개발에 집중하고, DB 및 서버가 반드시 필요한 부분은 Firebase로 구축했습니다.
주요 기능
- 연습모드
다양한 난이도(초급, 중급, 고급)에 맞춰 티켓팅 연습을 할 수 있는 모드입니다.
난이도에 따라 점진적으로 어려워지는 티켓팅 문제를 해결하며 예매 기술을 향상시킬 수 있습니다.
- 실전모드
실제 티켓팅과 유사한 화면을 구현하여, 제한 시간 내에 티켓팅에 성공하도록 유도하는 모드입니다.
실전 감각을 익힐 수 있도록 4개의 티켓팅 예매 사이트)를 참고하여 디자인했습니다. (구현을 위해 참고한 티켓팅 사이트 목록: 인터파크티켓, 티켓링크, YES24, 멜론티켓)
- 기록 보기
실전모드를 클리어한 기록(예매 성공 후 남은 시간)을 사이트 별로 필터링하여 볼 수 있습니다.
사용자가 자신의 기록 순위와 타 사용자의 기록을 비교해볼 수 있도록 하여 경쟁을 유도합니다.
프로젝트를 돌아보며..
이번 회고록은 4L 형식으로 작성하겠습니다🌸
🍀 1. Liked (좋았던 것)
✔️git-flow 전략을 지킨 첫번째 프로젝트!
프로젝트를 해오면서 항상 브랜치를 나눠서 작업하고 싶다는 생각을 종종해왔습니다.
협업 과정에서 동시에 같은 파일을 작업하다보면, main에 merge할 때 대부분 충돌이 발생했기 때문인데요.
사전에 이를 방지하기 위해 저희 팀은 우아한 형제들의 기술블로그를 참고하여 다음과 같은 git-flow 규칙을 세웠습니다.
main: 제품 출시 버전을 관리하는 메인 브랜치
develop: 다음 출시 버전을 위해 개발
feature/폴더이름-파일이름: 기능 개발
이런 식으로 feature, fix, refactor 등의 기능 명을 작성하고 뒤에는 폴더 및 파일 이름을 작성하도록 했습니다.
이를 적용함으로서 분업하기 수월했고, 충돌이 발생하더라도 브랜치 이름으로 구분되어있어서 resolve하기 편리했습니다.
✔️Trello를 통한 개발 생산성 증진
Jira는 요금 이슈로 인해 사용하지 않고, 좀 더 가벼운 느낌인 트렐로 툴로 개발 일정과 이슈를 관리하기로 했습니다.
트렐로를 통해 회의 일정을 관리하는 것 뿐만 아니라, 리마인더 기능을 통해 회의 시작 전 알림을 보낼 수 있습니다. 또 pc버전 뿐만 아니라 App도 제공되기 때문에 회의 장소와 시간을 언제 어디서나 기억하기 편리했습니다. (특히 저희는 오프라인 만남 시 매번 장소가 바뀌는 편이라 더욱 필요한 기능이었습니다...!)
또 카테고리별로 이미 한 일, 진행 중인 일, 해야할 일을 카드로 만들어서 사용자를 할당하여 협업하는데 아주 유용하게 사용했습니다. 처음 써본 툴인데 너무 좋아서 다른 친구들에게도 추천했던 기억이 있네요.
지라처럼 github 연동을 제공하여, 특정 PR을 연결하고 해당 부분에 코멘트를 달 수 있어 피드백도 할 수 있습니다. 이 기능 너무 좋았습니다ㅎㅎ
✔️백엔드 팀원의 부재로 인해.. DB 및 CI/CD 구축과 배포
사실 저희는 프론트엔드 스터디를 하다가 방학 때 시간이 남아서 갑자기 프로젝트를 하게 된 케이스입니다.
연습모드와 실전모드를 구현할때는 서버가 딱히 필요 없었지만, 기록을 저장할때는 반드시 서버와 DB가 필요했습니다. 팀원들 중 유일한 전공생이었기 때문에, 뭔가 책임감이 생겨서 이 부분은 제가 맡겠다고 했습니다. (일단 지르고 보기...)
이 과정에서 파이어베이스에서 DB를 만들고, 이 DB를 리액트 프로젝트와 연결하는 작업을 수행했습니다. 이후 Github Actions를 통해서 main에 배포하면 자동으로 다시 배포되도록 세팅도 진행했습니다. Vite랑 연결하느라 많은 시행착오가 있었지만 그래도 고치다보니 하나씩 문제가 해결되었습니다. 매우 뿌듯 >< API 연결이나 기타 세팅 에러 부분은 다른 팀원들도 함께 수정해주었습니다.
firebase-config.js
VITE의 환경변수를 저렇게 따로 import.meta.env로 처리해주어야 한다..!
import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getFirestore } from "firebase/firestore";
const firebaseConfig = {
apiKey: import.meta.env.VITE_API_KEY,
authDomain: import.meta.env.VITE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_PROJECT_ID,
storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
appId: import.meta.env.VITE_APP_ID,
measurementId: import.meta.env.VITE_MEASUREMENT_ID
};
// 파이어베이스 초기화
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
export const db = getFirestore(app);
✔️웹 최적화를 경험하다; 이미지 압축과 캐싱 설정
Firebase 무료 플랜의 한계로 인해 제한된 5GB 용량 내에서 운영하고 있었습니다. 배포 이후, 개발 과정에서 서버가 지속적으로 다운되는 문제를 겪었는데, 코드를 분석해보니 잘못된 의존성 배열 사용으로 useEffect가 무한 렌더링되고 있었던 것을 발견했습니다. 문제가 발생한 부분의 의존성 배열을 수정하여 이 문제를 해결했고 이후로는 서버가 다운되지 않고 현재까지도 안정적으로 유지되고 있습니다.
그러나 해당 사항 수정 이후에도 여전히 접속량 대비 스토리지 다운로드가 증가하는 현상이 있었습니다. 원인을 분석해본 결과 큰 용량의 이미지 파일들이 페이지 렌더링마다 서버에서 다운로드되고 있다고 추측, 왜냐하면 포스터가 많은 페이지에 들어갈때마다 사진이 이상하게 렌더링 시간이 오래 걸렸기 때문이었죠.
이 문제를 해결하기 위해 imagemin 라이브러리를 활용해 이미지 압축 유틸리티 함수를 만들어 서버에 적용했습니다. 또한, 이미 업로드된 이미지들은 동적인 변경사항이 없으면 1년간 캐싱되도록 설정하여 불필요한 서버 부하를 줄일 수 있도록 노력했습니다. 이러한 최적화를 통해 SVG 파일 로딩 속도를 30% 이상 개선하여 웹 브라우저 최적화에 대한 첫 걸음을 내딛었습니다. 팀원들도 이 문제를 해결한 것에 대하여 좋아해주어서 더욱 기뻤습니다!
해당 문제를 해결한 PR은 아래 링크에서 확인할 수 있습니다:)
Refactor: 렌더링 성능 및 용량 개선을 위한 포스터 이미지 파일 압축 by abyss-s · Pull Request #101 · Easy
📝작업 내용 💛 캐시 문제 해결 현재 Github Action으로 main과 develop에 병합이 될때 자동으로 배포되도록 설정해두었으나, 이전 캐시가 남아있어 새롭게 변경된 부분이 배포된 링크로 바로 반영되
github.com
🍀 2. Learned (배운 것)
✔️ 라우터를 객체로 다루는 법
react-router-dom은 리액트 라우팅 시 항상 사용하는 라이브러리입니다.
이때까지 리액트 프로젝트를 해왔을때는 BrowserRouter만 활용한 간단한 라우팅만 진행했었는데, 이번 프로젝트에서는 팀원 한희의 제안으로 createBrowserRouter를 도입하게 되었습니다.
Getting Started with createBrowserRouter in react-router-dom
Introduction
medium.com
위 문서에 따르면 라우터 객체를 중첩으로 사용하는 방식인데, 이를 위해 children 속성을 이용했습니다.
outlet를 사용해 전체적인 Layout을 적용하고, step별로 예매 과정이 진행되는데 이를 연습모드와 실전모드로 분리하여 다소 복잡함에도 불구하고 객체를 이용해 구조를 간결하게 유지할 수 있었습니다.
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ path: "/", element: <Main />, label: "메인화면-로그인" },
{ path: "select-mode", element: <SelectMode />, label: "모드 선택" },
{ path: "select-level", element: <SelectLevel />, label: "난이도 선택" },
{ path: "select-site", element: <SelectSite />, label: "사이트 선택" },
{ path: "record", element: <Record />, label: "기록 보기" },
{
path: "progress",
element: <ProgressContents />,
children: [
{
path: "step0",
element: <PrivateRoute element={<Intro />} />,
label: "인트로 화면"
},
{
path: "step1-1",
element: <PrivateRoute element={<SelectPerformance />} />,
label: "공연 선택"
},
{
path: "step1-2",
element: <PrivateRoute element={<SelectRound />} />,
label: "날짜 및 회차 선택"
},
// ...(이하 생략)
만약 createBrowserRouter를 사용하지 않았다면 정말 코드가 길어졌을 것 같네요..
✔️ 상태관리 라이브러리 도입을 통한 테마 전역 관리
예매 과정의 진행 단계를 개발하는 과정에서 다양한 데이터들이 여러 단계에서 계속 사용된다는 점을 알게 되었습니다. 특히 선택된 좌석의 위치나 가격과 같은 정보들을 2단계에서 선택했다면, 이후 이어지는 3단계와 4단계가 진행될 때마다 사용자가 선택한 데이터를 로컬스토리지에 저장하고 이를 받아와서 복사하는 것은 매우 비효율적인 과정이었습니다.
또한 이로 인해 여러 컴포넌트들의 렌더링도 동적으로 변경되어야 했는데, 기본적인 useHooks만을 사용해서 구현하기에는 무리가 있었고, 불필요한 코드가 너무 중복되는 문제도 있었습니다. 그래서 이에 대한 해결책으로 저희는 “전역으로 관리하는 상태를 정의하면 어떨까?”라는 생각을 하게 되었습니다.
상태관리 라이브러리를 Jotai로 선택한 이유?
- useState와 사용 방법이 유사
모든 팀원들이 상태 관리 라이브러리를 처음 사용한다는 점을 고려하여, Zustand, Recoil, Jotai 중에서 useHook과 사용 방식이 유사해 가장 익숙하다고 느껴진 Jotai로 선택했습니다. - atomWithStorage 지원
Jotai는 세션스토리지 값을 관리할 수 있는 atomWithStorage 유틸을 지원한다. 우리는 세션 스토리지 값을 atom으로 제어하여 진행 단계에 필요한 상태값이나 실전 모드 시간 기록을 저장하는 등에 활용했습니다.
import { atom } from "jotai";
import { atomWithStorage, createJSONStorage } from "jotai/utils";
import posters from "../components/poster/poster.json";
import getRandomInt from "../util/getRandomInt";
const storage = createJSONStorage(() => sessionStorage);
먼저 렌더링 시 바뀔 정보들인 테마를 전역 상태 atom으로 지정해서, 사용자가 선택한 테마 값을 세션스토리지에 저장합니다. 해당 세션스토리지 값에 따라 미리 지정해놓은 테마별 스타일링을 동적으로 진행합니다!
이 과정에서 styled-components와 css module을 사용한 컴포넌트 개별요소들을 전부 리팩토링해야했는데, 이때 제가 코로나에 걸려서 며칠동안 개발을 진행하지 못해서 다른 팀원 친구들이 수고를 많이 해주었습니다. 나중에 참여했을때는 이미 해당 부분 개발이 많이 진행된 상태였는데 PR도 너무 상세히 작성해주고, 모르는 부분이 생겨서 물어보았을때 정말 친절히 답변받았던 기억이 납니다. 친구들아 다시 한 번 정말 고마워..

✔️ vscode 내 Live Share 기능 활용
회의 도중 다같이 코드를 수정해야 할 때, 함께 코드를 보며 동시에 수정하고 싶을 때가 종종 있었습니다.
온라인으로 만나거나, 오프라인 만남이어도 각자 노트북만 볼 수 있는 환경이 대부분이었어서 이럴 때는 라이브 쉐어 기능을 활용해서 함께 작업했습니다.
사실 이런 기능이 vscode 내에 있는지도 몰랐는데요..!! 공유하려는 사람이 링크만 전달해주면, 이 링크로 다른 사람들이 들어와서 함께 작업할 수 있었습니다. 서로 커서가 어디있는지 눈에 보이고 동시에 작업할 수 있어서 아주 편리했습니다.
🍀 3. Longedfor (놀랐던 것)
✔️ 코드 포매팅을 위한 초기 설정의 어려움
프로젝트를 진행하면서 코드 포매팅을 위한 초기 설정, 특히 허스키(Husky), ESLint, Prettier 설정이 너무 힘들었습니다. 많은 블로그와 티스토리를 참고했지만, 대부분의 정보가 저희 쪽 개발환경에서는 전혀 도움이 되지 않았고, 결국 GPT조차도 해결해주지 못해서 그냥 셋이서 머리를 맞대고 여러 가지 시도를 하며 문제를 해결할 수밖에 없었습니다.
각 라이브러리별로 버전 차이가 크고, 설정 방법도 다 달라서 초기 설정하는 데 시간이 많이 소요되었습니다. 코드 포매팅과 관련된 도구들을 처음 설정할 때 한 번에 잘 설정하는 것이 얼마나 중요한지를 깨달았습니다.
✔️ 전역 상태관리의 편리한 정도
기존에는 useState만으로 상태를 관리했지만, 복잡한 상태를 처리할 때는 전역 상태 관리가 얼마나 유용한지 깨닫게 되었습니다.
특히 Jotai를 처음 사용해보면서 atom이라는 개념이 처음에는 좀 낯설었지만 상태를 전역적으로 관리하면서 각 컴포넌트에서 데이터를 공유하고 업데이트하는 과정이 생각보다 간단하고 직관적이라는 것을 알게되었습니다. useState와 비슷하게 동작하면서도 보다 복잡한 상태 관리에 적합하도록 설계되어 있다고 느꼈습니다. Jotai를 도입하길 잘했다고 생각하며 앞으로도 이런 종류의 라이브러리에 대한 정보를 얻어서 적극 활용해야겠다는 생각이 들었어요.
✔️ 사용자 친화적인 사고를 위한 팀원들의 아이디어
이번 프로젝트의 목적 중 하나는 어르신들이 쉽게 티켓팅을 할 수 있도록 돕는 것이었기 때문에 사용자 친화적인 디자인과 기능을 제공하는 것이 매우 중요했습니다. 여러 아이디어가 팀에서 나왔는데, 그 중 특히 인상 깊었던 것은 이름을 입력하지 않았을 때 애니메이션으로 버튼을 감싸면서 툴팁을 띄우는 아이디어였습니다. 이런 방식은 단순한 경고 메시지보다 훨씬 더 눈에 띄고 직관적으로 다가갈 수 있기 때문에 정말 좋은 아이디어라고 생각했습니다.
또한 좌석을 잘못 선택했을 때, 잘못된 선택이 무엇인지 정확히 알려주고 올바른 선택을 유도하는 방식으로 경고 문구를 컴포넌트화하기도 했습니다.
내가 생각하지 못한 부분들을 생각해낸 팀원들이 정말 대단하다고 느꼈고, 사용자 친화적인 UI/UX를 고안하기 위해 앞으로 많은 도움이 되지 않을까 생각합니다. 결국 사이트를 이용하는건 사용자들이니까!
🍀 4. Lacked (부족했던 것)
✔️ 기획 단계 사전 조사 미비
사실 프로젝트 기획 단계에서 전역 상태 관리 라이브러리를 미리 도입하기로 계획했어야 했습니다. 하지만 개발 중간에 도입을 결정하게 되었고, 이로 인해 기존 코드들을 리팩토링해야 했는데 이 과정에서 시간이 다소 소요되었습니다.
만약 기획 단계에서 전역 상태 관리의 필요성을 미리 고려하고 설계했다면 개발이 훨씬 더 수월하고 효율적으로 진행되었을 것입니다. 이런 경험을 통해 기획 초기 단계에서부터 기술적인 부분을 잘 계획하고, 만약 무언가 필요할 경우 미리 도입을 고려하는 것이 얼마나 중요한지를 깨달았습니다. 사실 개발자는 개발하는 능력도 중요하지만 어쨌든 일을 하려면 생산성을 위해서 시간과 노력을 절약하는 것도 중요한 작업이기 때문입니다. 프로젝트 설계를 현업에서 진행하는게 아니구나라는 것을 절실히 느끼는 시간이었습니다.
✔️ useMemo를 활용한 웹 최적화
초반에 우리는 useMemo를 활용한 웹 최적화를 진행하기로 했지만 결국 실행에 옮기지 못했습니다. useMemo를 활용하면 렌더링 성능을 개선할 수 있고, 불필요한 재계산을 방지할 수 있는 장점이 있습니다. 아무래도 목표가 2학기 개강 전에 프로젝트를 끝내는 것이었는데, 기능 구현과 다른 작업들에 몰두하다 보니 최적화 부분은 미루다가 결국 해내지 못했습니다. 성능 최적화 부분은 웹 애플리케이션에서 핵심적인 부분이기 때문에 언젠가는 꼭 다뤄보고 싶은 부분이었는데 아쉬운 부분...
✔️ 백엔드 팀원의 부재로 인한 구현의 미완성
프로젝트에서 티켓 예매 로직을 구현할 때, 백엔드 팀원이 부재한 상황에서 모든 로직을 자바스크립트만으로 처리해야 했습니다. 특히 예매한 좌석을 '이선좌(이미 선택된 좌석입니다!)'로 표시하려고 했는데, 간단히 말하면 실제 티켓팅처럼 시간이 지나면 다른 사람들이 예매한 것처럼 좌석이 사라지도록 구현하고 싶었습니다. 좌석 관련 로직은 팀원 다은이가 정말 많이 수고해준 부분입니다. (정말 고생 많았어..😊)
하지만 백엔드에서 관리하는 데이터베이스와 서버 로직이 없으면, 실시간으로 좌석 정보를 동기화하고 관리하는 데 어려움이 있었습니다. 예를 들어, 다른 사용자가 좌석을 선택하면 실시간으로 상태를 업데이트해야 했고, 이를 구현하기 위해서는 서버와의 연동이 필수적이었습니다. 만약 백엔드와의 연동이 가능했다면 진짜 티켓팅처럼 실시간 좌석 정보가 반영되었을 텐데 아쉬운 점입니다.
대신 random math를 활용하여 좌석 선택 시 예매 성공 여부를 랜덤 확률로 지정하는 방식으로 구현했습니다. 이 방식은 진짜처럼 동작하지는 않지만, 사용자가 좌석을 선택하는 연습에는 더 도움이 될 수 있도록 하여 실시간 예매처럼 동작하는 느낌을 조금이나마 구현해보았습니다.
프로젝트 관련 Link 모아보기
Github
GitHub - abyss-s/easy-ticket
Contribute to abyss-s/easy-ticket development by creating an account on GitHub.
github.com
Notion
쉽게🎫티켓 | Notion
PROJECTS
abyss-2.notion.site
Figma
Figma
Created with Figma
www.figma.com
쉽게티켓 프로젝트의 팀원들과 함께할 수 있어서 너무 좋았습니다.
팀원 모두들 너무 착하고 열심해주어서 나도 더 성실히 참여했던 것 같습니다😃
그리고 모두 같은 나이에 같은 학년이어서 더 편하기도 했어요.
이렇게 PR을 체계적으로 작성하는 것도 너무 좋았고,,
PR 코멘트에서 생산적인 피드백을 나누면서 함께 성장할 수 있었습니다!
PR 요청이 올라오면 한 명 이상 승인해야 merge하도록 했기 때문에 더 활발한 토론이 이루어지지 않았나 싶어요.
헤헤.. 다들 앞으로도 화이팅입니당❣️

'Activities > Project' 카테고리의 다른 글
[Open-Lawyer] 컴퓨터공학 종합설계 프로젝트 개발 회고록 (3) | 2025.01.19 |
---|