동기 : 지인용에서 누구나 쓸 수 있는 서비스로
- Day1에서 기본 기능은 다 만들었다. 투표, 사진, 가격, 그룹까지.
- 그런데 이걸 지인이 아닌 다른 사람들도 쓸 수 있게 하려면 문제가 있었다.
- 홈에 들어가면 다른 사람이 만든 투표방이 다 보인다. 내 방만 보고 싶은데?
- 누군가 만든 방에 아무나 들어가서 장난칠 수도 있고, 오래된 데이터가 계속 쌓인다.
- 사진을 원본 그대로 올리면 용량이 어마어마하다. 누가 5MB짜리 사진 10장 올리면...
- 그래서 오늘의 목표는 "모르는 사람도 편하게 쓸 수 있는 구조"로 바꾸는 것이었다.
큰 흐름
1. 이미지 자동 압축
- 사용자가 사진을 올리면 업로드 전에 자동으로 압축된다.
- Canvas API를 이용해서 최대 1920×1920px로 리사이즈하고, JPEG 품질 70%로 변환한다.
- 500KB 이하의 작은 파일은 굳이 압축하지 않고 그대로 올린다.
- 3~5MB짜리 사진이 300~500KB 수준으로 줄어드니까 Storage 용량이 확 줄었다.
- 검색 키워드 : Canvas API, toBlob, image resize, JPEG compression
2. 공개 서비스 모드 전환
- 기존에는 홈에 들어가면 Firestore에서 전체 그룹 목록을 가져와서 보여줬다.
- 이제는 그걸 없애고, 두 가지만 보여준다.
- "🗳️ 새 투표 만들기" 버튼 → 방 이름 입력하면 바로 생성
- "📋 내 투표방" 목록 → 내가 만들거나 방문한 방만 표시
- "내 투표방"은 Firestore가 아니라 localStorage에 저장한다. 최대 20개까지.
- 방을 만들면 자동 저장되고, 공유 링크로 방문해도 자동 저장된다.
- 검색 키워드 : localStorage, JSON.parse/stringify
3. 링크 공유
- 투표 페이지 헤더에 🔗 공유 버튼을 추가했다.
- 누르면 현재 방 URL이 클립보드에 복사된다.
- 친구한테 카톡으로 링크 보내면 같은 방에서 바로 투표 가능.
- 이게 핵심이다. 방을 만들고 → 링크를 공유하고 → 각자 투표하고 → 끝.
- 검색 키워드 : navigator.clipboard.writeText, window.location.href
4. 자동 만료 (24시간)
- 방을 만들면
lastUsedAt 타임스탬프가 찍힌다.
- 투표하거나, 사진 올리거나, 방에 접속할 때마다 이 시각이 갱신된다.
- 누군가 방에 들어왔을 때 마지막 활동으로부터 24시간이 지났으면?
- 투표 데이터, 사진, 그룹 문서를 전부 자동 삭제하고 홈으로 보낸다.
- 서버(Cloud Functions) 없이 클라이언트 코드만으로 구현했다.
- 검색 키워드 : Firestore serverTimestamp, Timestamp.toMillis, deleteDoc, deleteObject
조금 헤맸던 부분들
1. localStorage와 Firestore의 싱크
- "내 투표방" 목록은 localStorage에 저장하는데, 방이 만료되어 Firestore에서 삭제되면 localStorage에는 남아있다.
- 그래서 방에 접속했을 때 만료 확인 후 localStorage에서도 같이 지워주는 처리를 추가했다.
- 클라이언트 저장소와 서버 데이터의 정합성을 맞추는 게 은근히 신경 쓸 부분이 많다.
2. 만료 체크 타이밍
- 자동 만료를 서버 없이 구현하려면 "누군가 방에 접속하는 시점"에서만 체크할 수 있다.
- 아무도 안 들어오면 데이터는 24시간이 지나도 Firestore에 남아있긴 하다.
- 하지만 다음에 누군가 접속하면 그때 삭제되니까, 실질적으로는 문제없다.
- 완벽하게 하려면 Cloud Functions의 스케줄러를 써야 하는데, 무료 플랜에서는 못 쓰니까 이 정도면 충분하다고 판단했다.
3. 이미지 압축과 원본 보존 사이
- 처음에는 모든 이미지를 무조건 압축했는데, 이미 작은 파일까지 압축하면 오히려 용량이 커지는 경우가 있었다.
- 그래서 500KB 이하는 압축을 건너뛰도록 조건을 추가했다.
- Canvas API로 JPEG 변환하면 PNG의 투명 배경이 사라지는 점도 알아두면 좋다. 메뉴판 사진은 대부분 JPG라 상관없었지만.
오늘 추가된 기능 정리
| 기능 | 설명 |
|---|
| 이미지 자동 압축 | 업로드 전 Canvas API로 리사이즈 + JPEG 70% 압축 |
| 홈페이지 리뉴얼 | "새 투표 만들기" + "내 투표방" (localStorage 기반) |
| 링크 공유 | 🔗 버튼으로 방 URL 클립보드 복사 |
| 자동 만료 | 24시간 미사용 시 방 자동 삭제 |
현재까지의 전체 기능
| 기능 | 설명 | Day |
|---|
| 실시간 투표 | 이름 + 메뉴 입력 → 모든 사용자 화면에 즉시 반영 | 1 |
| 빠른 참여 | 기존 메뉴에 "+ 참여" 버튼으로 바로 합류 | 1 |
| 메뉴 사진 | 다중 업로드, 캐러셀, 전체화면 모달, 개별 삭제 | 1 |
| 가격 & 총 금액 | 메뉴별 가격 입력 → 소계, 총 예상 금액 자동 계산 | 1 |
| 그룹 | 여러 팀이 독립 그룹으로 동시 사용 가능 | 1 |
| 전체 초기화 | 투표 + 사진 한 번에 리셋 | 1 |
| 이미지 자동 압축 | 큰 사진 자동 리사이즈 + JPEG 압축 | 2 |
| 공개 서비스 모드 | 방 만들기 + 내 투표방 + 링크 공유 | 2 |
| 자동 만료 | 24시간 미사용 방 자동 삭제 | 2 |
기술 스택 (변경 없음)
| 분류 | 기술 |
|---|
| 프론트엔드 | React 19, CSS (순수) |
| 라우팅 | react-router-dom |
| 백엔드/DB | Firebase Firestore (실시간 NoSQL) |
| 파일 저장 | Firebase Storage |
| 배포 | Firebase Hosting |
| 새로 사용 | Canvas API (이미지 압축), localStorage (방 목록) |
결과
👉 https://vote-eat.web.app
- 이제 링크를 공유하면 누구나 방을 만들고 투표할 수 있다.
- 방은 24시간 후 자동으로 사라지니까 데이터가 쌓이는 걱정도 없다.
- Day1에서는 "일단 돌아가게" 만들었고, Day2에서는 "남한테 보여줄 수 있게" 다듬었다.
- 다음에 하고 싶은 것들 : 여러 그룹 결과 합산 뷰, 투표 마감 기능, 결과 차트 시각화, 1인 1투표 제한.
텍스트