describug
← 만든 것

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번. 혼자 만들어 실제로 굴리고 있습니다.

만들면서 쓴 기록