club313
운영중초대받은 사람만 들어오는 모임 운영 앱. 공동 자산·회비·일정·사진·투표를 한 곳에서.
- 연도
- 2026
- 스택
- Next.jsSupabaseTailwindCloudflare R2VercelWeb Push
- 코드
- 비공개
친구 모임 하나를 운영하는 데 필요한 게 생각보다 많았습니다. 회비 장부는 엑셀에, 일정은 카톡에, 사진은 각자 폰에, 경조사는 누군가의 기억에 있었습니다. 그걸 한 곳으로 모은 앱입니다.
앱스토어에 올리지 않았습니다. 주소로 들어와 휴대폰 홈 화면에 추가하면 앱처럼 뜨는 웹앱(PWA)입니다. 검색엔진에도 걸리지 않게 해뒀습니다.
회원 정보와 회비·자산 내역이 오가는 앱이라 코드와 주소는 공개하지 않습니다. 어떻게 만들었는지는 기능별로 만들면서 쓴 기록에 모아뒀습니다. 비슷한 기능을 만들어 보려는 분은 그쪽이 더 쓸모 있을 겁니다.
무엇을 하나
아래 탭 다섯 개와 옆 메뉴로 되어 있습니다.
| 탭 | 무엇을 하나 |
|---|---|
| 🏠 홈 | 상황에 맞춰 바뀌는 인사말, 날씨 카드, 고정 공지, 다가오는 일정 D-day, 진행 중인 투표, 경조사 요약 |
| 💰 자산 | 모임이 같이 투자한 자산의 총액·종목별 손익·현금, 추이 그래프, 보유 종목 목록 ↔ 마켓맵. 관리자가 거래를 한 줄씩 적으면 나머지는 알아서 계산됩니다 |
| 🧾 회비 | 월별 납부 현황과 공동 가계부. 미납자에게 카톡 독촉, 회원은 「냈어요」 요청. 아이폰 단축어로 앱을 안 열고도 처리합니다 |
| 🖼 갤러리 | 사진·영상을 게시물로 묶어 좌우로 넘겨 보고, 좋아요·댓글. 유튜브 링크는 용량 없이 붙습니다. 아이폰 공유 버튼에서 바로 올립니다 |
| 📅 캘린더 | 모임 일정·생일·경조사에 대한민국 공휴일(대체공휴일 포함) 을 겹쳐 보여줍니다 |
옆 메뉴에는 공지, 투표(텍스트·날짜, 복수선택·익명), 경조사함, 맛집·여행지 추천, 알림 설정, 내 프로필이 있고 관리자에게만 회원 관리와 초대 링크가 보입니다.
월드컵 기간에는 조별리그·토너먼트 대진표와 승부예측 이벤트 탭이 따로 있었습니다. 대회가 끝나고 옆 메뉴로 보관했습니다.
가입은 초대로만
아무나 가입할 수 없습니다. 관리자가 만든 24시간·1인 전용 초대 링크로만 들어옵니다. 카카오톡으로 보낼 문구까지 함께 생성돼서, 링크를 복사해 붙여넣기만 하면 됩니다.
닫힌 모임에 열린 가입 폼을 두면 언젠가 사고가 납니다. 초대 링크를 1회용으로 만든 건 그래서입니다.
권한은 두 번 막습니다
자산·회비·공지를 고치는 버튼은 관리자에게만 보입니다. 그런데 버튼을 숨기는 것만으로는 막은 게 아닙니다. 화면을 거치지 않고 데이터베이스에 직접 요청을 보낼 수도 있으니까요.
그래서 데이터베이스 쪽에도 같은 규칙을 한 번 더 걸었습니다. 「이 표는 관리자만 쓸 수 있다」를 데이터베이스가 직접 판단하게 하는 기능(RLS)입니다. 화면이 뚫려도 데이터는 안 바뀝니다.
관리자가 회원에게 앱이 어떻게 보이는지 미리 볼 수 있는 모드도 있습니다. 이 모드는 화면만 회원처럼 바꾸고 권한은 건드리지 않습니다. 회원 화면을 보는 동안에도 데이터 규칙은 관리자 그대로입니다.
무엇으로 만들었나
| 하는 일 | 쓴 것 | 한 줄 설명 |
|---|---|---|
| 화면 | Next.js 16 · React 19 · TypeScript | 화면을 부품으로 나눠 만들고, 대부분을 서버에서 그려 보냅니다 |
| 꾸미기 | Tailwind CSS v4 · lucide 아이콘 | 색·여백을 화면 만드는 자리에 바로 적습니다 |
| 데이터·로그인·권한 | Supabase | 데이터베이스(PostgreSQL)와 로그인, 위의 RLS를 한 번에 줍니다 |
| 사진 창고 | Cloudflare R2 | 비공개로 두고, 볼 때만 잠깐 유효한 서명 주소를 발급합니다 |
| 알림 | Web Push | 앱스토어 앱이 아니어도 휴대폰에 알림을 보냅니다 |
| 띄우기 | Vercel | 서버를 빌려 띄워줍니다. 이 앱은 GitHub를 거치지 않고 제 컴퓨터에서 바로 올립니다 |
| 정해진 시각에 돌리기 | Vercel Cron | 매일 오후 4시에 자산을 기록하고 알림을 보냅니다. 무료로는 두 개까지라 하나로 합쳤습니다 |
데이터베이스 구조를 바꿀 때마다 그 변경을 번호 붙인 파일로 남겼습니다(20개). 나중에 새로 세워도 1번부터 순서대로 돌리면 지금과 같은 구조가 됩니다.
돈 안 드는 데이터 가져오기
바깥에서 받아오는 데이터는 전부 무료이고, 대부분 한 곳이 안 되면 다른 곳으로 넘어가게 했습니다. 따라 만드신다면 이 표가 제일 쓸모 있을 겁니다.
| 무엇 | 먼저 쓰는 곳 | 안 되면 | 비고 |
|---|---|---|---|
| 주식 시세 | 네이버 증권 실시간 | 야후 파이낸스 | 30초 캐시. 야후만 쓸 땐 15분 늦은 값이었습니다 |
| 날씨 | 기상청 공공데이터 | Open-Meteo | 기상청은 키가 필요하고 호출 횟수 제한이 있습니다 |
| 공휴일 | date.nager.at | 2026년 공휴일을 앱에 직접 적어둠 | |
| 월드컵 경기 | 네이버 스포츠 | — | 공식 API가 아니라 언제든 바뀔 수 있습니다. 90초 캐시 |
| 유튜브 재생목록 | YouTube Data API | — | 읽기 전용 키를 서버에만 둡니다 |
| 동네 이름 | BigDataCloud | — | 좌표 → 「○○동」. 키가 필요 없습니다 |
캐시는 한 번 받아온 값을 잠깐 들고 있다가 다시 쓰는 것입니다. 회원 열 명이 동시에 열어도 바깥에는 한 번만 묻게 되어, 무료 한도에 안 걸립니다.
규모
2026년 5월 말에 시작해 지금도 고치고 있습니다. 커밋 135개, 릴리스 마흔 번 남짓(v1.40), 데이터베이스 변경 20번. 혼자 만들어 실제로 굴리고 있습니다.