22
33[ Singcode - 당신의 노래방 메모장] ( https://www.singcode.kr )
44
5-
65노래방만 가면 뭘 부르려고 했었지 하면서 부를 곡들을 잊어버린다면. <br />
76매번 인터넷에서 노래방 번호를 검색해야 했었다면. <br />
87내가 어떤 노래를 가장 많이 불렀는지 궁금하다면. <br />
@@ -12,14 +11,12 @@ Supabase를 활용한 자체 DB를 통해 금영, TJ 노래방의 번호를 한
1211
1312<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
1413
15-
1614</div >
1715
18-
1916---
2017
21-
2218## 📦 배포
19+
2320[ Singcode - 당신의 노래방 메모장] ( https://www.singcode.kr )
2421
2522## 📚 기술 스택
@@ -54,109 +51,94 @@ sing-code/
5451├── package.json # 루트 패키지 관리 파일
5552└── 기타 설정 파일들 # .gitignore, .prettierrc.json, LICENSE 등
5653```
54+
5755### Supabase DB 구조
5856
5957<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
6058
61-
62-
6359<img width =" 700 " height =" 725 " alt =" image " src =" https://github.com/user-attachments/assets/17080dc1-1b63-4cfb-a325-429d207c52d6 " />
6460
6561</div >
6662
67-
6863## ✨ 주요 기능
6964
7065### 검색 페이지
7166
72- * 제목, 가수 이름으로 곡을 검색할 수 있습니다.
67+ - 제목, 가수 이름으로 곡을 검색할 수 있습니다.
7368
7469<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
75-
70+
7671
7772</div >
7873
79- ### 검색 페이지 - 재생목록으로 저장
80-
81- * 기존 재생목록이나 새로운 재생목록에 곡을 저장할 수 있습니다.
74+ ### 검색 페이지 - 재생목록으로 저장
8275
83- < div style = " display : flex ; justify-content : center ; gap : 10 px ; flex-wrap : wrap ; " >
76+ - 기존 재생목록이나 새로운 재생목록에 곡을 저장할 수 있습니다.
8477
78+ <div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
8579
8680</div >
8781
8882### 부를 곡 페이지
8983
90- * 자신이 저장한 부를 곡을 조회합니다.
91- * 부를 곡의 순서를 바꿀 수 있습니다.
92- * 곡을 부르거나, 부르지 않고 삭제할 수 있습니다.
93-
94- <div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
84+ - 자신이 저장한 부를 곡을 조회합니다.
85+ - 부를 곡의 순서를 바꿀 수 있습니다.
86+ - 곡을 부르거나, 부르지 않고 삭제할 수 있습니다.
9587
88+ <div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
9689
9790</div >
9891
99- * 좋아요 표시한 곡이나 재생목록에 저장한 곡에서 빠르게 부를곡을 추가할 수 있습니다.
100-
92+ - 즐겨찾기 표시한 곡이나 재생목록에 저장한 곡에서 빠르게 부를곡을 추가할 수 있습니다.
10193
10294<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
10395
104-
10596</div >
10697
107-
10898### 인기곡 페이지
10999
110- * 곡의 추천 순위를 집계해서 보여줍니다.
100+ - 곡의 추천 순위를 집계해서 보여줍니다.
111101
112102<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
113103
114-
115104</div >
116105
117-
118106### 라이브러리 페이지
119107
120- * 자신의 활동 기록을 조회 및 관리할 수 있습니다.
121- * 좋아요 한 곡들을 조회하고 일괄 삭제할 수 있습니다.
122- * 자신이 가장 많이 부른 곡의 순위를 확인할 수 있습니다.
108+ - 자신의 활동 기록을 조회 및 관리할 수 있습니다.
109+ - 즐겨찾기 한 곡들을 조회하고 일괄 삭제할 수 있습니다.
110+ - 자신이 가장 많이 부른 곡의 순위를 확인할 수 있습니다.
123111
124112<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
125-
113+
126114
127115</div >
128116
129117### 출석 체크 기능
130118
131- * 회원일 경우 하루에 한 번 출석 체크를 통해 포인트를 획득할 수 있습니다. 매일 12시 마다 초기화됩니다.
119+ - 회원일 경우 하루에 한 번 출석 체크를 통해 포인트를 획득할 수 있습니다. 매일 12시 마다 초기화됩니다.
132120
133121<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
134122
135-
136123</div >
137124
138125### 곡 추천 기능
139126
140- * 출석 체크로 획득한 포인트를 사용해서 곡을 추천할 수 있습니다. 1 포인트 당 1 추천입니다.
127+ - 출석 체크로 획득한 포인트를 사용해서 곡을 추천할 수 있습니다. 1 포인트 당 1 추천입니다.
141128
142129<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
143130
144-
145131</div >
146132
147-
148-
149-
150133### 로그인 & 회원가입 지원
151134
152- * 몇몇 추가적인 기능을 사용하려면 회원가입을 진행해야 합니다.
153- * 이메일 인증 회원가입과 카카오 회원가입을 지원합니다.
154-
135+ - 몇몇 추가적인 기능을 사용하려면 회원가입을 진행해야 합니다.
136+ - 이메일 인증 회원가입과 카카오 회원가입을 지원합니다.
137+
155138<div style =" display : flex ; justify-content : center ; gap : 10px ; flex-wrap : wrap ;" >
156139
157140</div >
158141
159-
160142## 📖 프로젝트 기록
161143
162144- 2025.03.12 : 프로젝트 시작
@@ -181,29 +163,29 @@ sing-code/
181163- 2026.2.8 : 버전 2.1.0 배포. 비회원 상태로 곡 부를곡 추가가 가능, Footer 애니메이션 추가.
182164- 2026.2.20 : 버전 2.2.0 배포. 검색어 자동 완성 기능. es-hangul로 초성 검색 지원.
183165- 2026.3.2 : 버전 2.3.0 배포. 곡 추천 페이지에서 UI 조정, 곡 추천 기능 추가. 글자 자동 스크롤 기능 추가.
184-
166+
185167## 📝 회고
186168
187169### 해결하고자 했던 문제
188170
189171- 기존 유사한 서비스에서는 하나의 곡을 검색할 때 TJ, 금영 API로 따로따로 곡 데이터를 요청하기에 기존 방식으로는 데이터를 관리하기가 어려웠습니다.
190- - Supabase로 자체 DB를 구축하고, OPEN API와 puppeteer, cheerio를 활용한 웹 크롤링을 통해 데이터를 파싱하여 DB에 넣어주었습니다.
191- - OPEN API에 의존하지 않고, 목적에 맞게 활용할 수 있는 자체 API를 구성할 수 있었습니다.
172+ - Supabase로 자체 DB를 구축하고, OPEN API와 puppeteer, cheerio를 활용한 웹 크롤링을 통해 데이터를 파싱하여 DB에 넣어주었습니다.
173+ - OPEN API에 의존하지 않고, 목적에 맞게 활용할 수 있는 자체 API를 구성할 수 있었습니다.
192174- 52000개가 넘는 DB 테이블에서 모든 검색 결과를 한번에 제공하고 있어서 검색 응답 속도가 굉장히 길었습니다.
193- - 검색 기능에 Tanstack Query의 useInfiniteQuery를 활용한 무한 스크롤을 도입하여, 페이지 단위의 데이터를 점진적으로 불러오도록 구현했습니다.
194- - 초기 로딩 속도를 크게 감소시켰고, 사용자 경험을 크게 향상시켰습니다.
175+ - 검색 기능에 Tanstack Query의 useInfiniteQuery를 활용한 무한 스크롤을 도입하여, 페이지 단위의 데이터를 점진적으로 불러오도록 구현했습니다.
176+ - 초기 로딩 속도를 크게 감소시켰고, 사용자 경험을 크게 향상시켰습니다.
195177- 검색 결과로 렌더링된 곡 컴포넌트에서 이벤트에 따라 UI를 동적으로 변경해야 했으나, 단 하나의 속성값 변경을 위해 매번 queryKey를 무효화하고 전체 데이터를 다시 요청하는 방식은 기술적으로도, 사용자 경험 측면에서도 불필요했습니다.
196- - ` Tanstack Query ` 의 낙관적 업데이트를 도입하여, UI가 변경해야 하는 이벤트 발생 시 즉각적으로 UI가 변경되도록 최적화하였습니다.
197- - 로딩 없이 즉시 변경되는 UI를 사용자에게 보여주며 사용자 경험을 크게 개선했습니다.
178+ - ` Tanstack Query ` 의 낙관적 업데이트를 도입하여, UI가 변경해야 하는 이벤트 발생 시 즉각적으로 UI가 변경되도록 최적화하였습니다.
179+ - 로딩 없이 즉시 변경되는 UI를 사용자에게 보여주며 사용자 경험을 크게 개선했습니다.
198180- 하나의 mutation 요청에 의존하는 side effect(노래를 불렀을 때 의존하는 여러 queryKey 무효화, log 증가 mutation 호출)가 많았기에, 빠르게 여러 mutation을 호출할수록 요청 처리가 늦어지고 timeout이 나오기도 하였습니다.
199- - mutation 함수에 debounce를 도입하여 짧은 시간 내 많은 요청 시 queryKey를 한 번만 무효화시키게 처리하였습니다.
200- - Supabase에서 제공하는 Database Functions과 Trigger를 적절하게 활용하여 클라이언트의 요청 응답 속도를 줄였습니다.
181+ - mutation 함수에 debounce를 도입하여 짧은 시간 내 많은 요청 시 queryKey를 한 번만 무효화시키게 처리하였습니다.
182+ - Supabase에서 제공하는 Database Functions과 Trigger를 적절하게 활용하여 클라이언트의 요청 응답 속도를 줄였습니다.
201183- POST, DELETE, PATCH 요청 때마다 DB와 클라이언트 간의 데이터를 동기화해줘야 했습니다.
202- - Zustand의 store action으로 제어해보려고 하였으나, 한계를 느끼고 Tanstack Query를 도입하였습니다.
203- - DB의 변동이 생길 시 queryKey를 무효화시켜 최신 DB 데이터를 가져올 수 있게끔 하였습니다.
184+ - Zustand의 store action으로 제어해보려고 하였으나, 한계를 느끼고 Tanstack Query를 도입하였습니다.
185+ - DB의 변동이 생길 시 queryKey를 무효화시켜 최신 DB 데이터를 가져올 수 있게끔 하였습니다.
204186- API 요청 시 Supabase DB의 정보가 노출될 위험이 있었습니다.
205- - Next.js의 API Route를 활용하여 서버리스 API를 구축하고 외부 API의 URL을 감췄습니다.
206-
187+ - Next.js의 API Route를 활용하여 서버리스 API를 구축하고 외부 API의 URL을 감췄습니다.
188+
207189### 무엇을 얻었는지
208190
209191- Turborepo를 활용하면서 모노레포의 이해를 높이고 프로젝트에 어떻게 적용하고 확장해야 하는지에 대해 익혔습니다.
0 commit comments