사용자의 하루에서
불편한 순간 하나를
없애는 일을 합니다.

일본 노래를 좋아해서 번역된 가사를 자주 찾아봤습니다. 문맥이 어색하거나 무슨 이야기인지 알기 어려운 번역이 많았습니다. 이럴 거면 내가 번역하자. 개발을 배웠으니 읽는 사람이 편한 환경까지 내가 만들자. 그 두 마음으로 2021년 가을 블로그를 열었습니다.

번역은 곡의 이야기를 이해한 뒤 자연스럽게 읽히게 옮겼습니다. 환경은 읽는 사람의 자리에 앉아 하나씩 만들었습니다. 가사를 읽는 동안 스크롤해도 따라오는 영상, 접고 펼치는 목차, 내한 일정을 한눈에 보는 캘린더, 노래방 번호 표. 꾸준히 고치고 보태는 사이 방문자가 늘어 지금은 하루 7천 명이 읽고, 크몽에서는 유료 번역 의뢰를 77건 받았습니다.

블로그를 운영하며 알게 된 것이 있습니다. 생각보다 많은 사람이 노래방 번호를 찾고 있었습니다. 이유는 공식 사이트에 있습니다. TJ·KY 사이트에는 곡마다 페이지가 없어서 검색창에 "곡 이름 노래방 번호"라고 쳐도 번호가 나오지 않았고, 일본 노래는 제목이 번역돼 있지 않아 일본어로만 검색해야 했습니다. 그래서 사람들은 제 블로그로 왔습니다. 이미 나와 있던 노래방 번호 앱들은 UI가 낡았고 쓰기 불편했습니다. 사람들이 이미 원하는 것을 제대로 찾을 수 있게 만들자. 2026년 2월, 곡마다 페이지를 두어 검색하면 바로 나오게 만든 첫 서비스가 노래방 가자!입니다.

그 뒤로 만든 것들도 출발은 같았습니다. 주소 검색 zooso는 대용량 데이터를 다뤄 보고 싶어서 시작했지만, 필요한 건 주소와 지도뿐이라는 생각으로 나머지를 덜어 내고 편한 검색 하나에 집중했습니다. 어바웃톡은 방치된 카톡 분석 앱들에 대한 반항심으로 만들었고, GB 단위 파일을 파싱하고 거르는 연습이 됐습니다. MyCon은 예매처 네 곳에 흩어진 공연을 한데 모았고, 기능이 완벽하면 디자인도 훌륭하다는 생각으로 다듬었습니다. 목돈은 주식은 쉽고 직관적이어야 한다는 생각으로 다시 만든 시세 앱입니다. 좁은 화면에 설명력 있는 정보를 넣고, 탭은 늘리지 않고, 시세가 멈춰 있으면 멈춰 있다고 표시하게 했습니다. 단어장 3종은 단어장의 본분만 남겼습니다. 무엇을 언제 외울지는 앱이 정하고 사람은 외우기만 하는데, 미니멀은 판단이 적다는 뜻이라 뺀 만큼 남은 것을 제대로 만들어야 했고, 그래서 기술을 가장 많이 쏟은 앱이 됐습니다.

제가 중요하게 여기는 건 하나입니다. 쓰는 사람의 자리에서 생각하고, 그 사람에게 좋은 경험을 주는 것. 그래서 새로 발명하기보다 이미 익숙한 것을 그 사람 쪽에서 다시 놓습니다. 기대하는 대로 움직이고, 설명이 필요 없고, 묻지 않아도 되는 화면. 그게 제가 만들고 싶은 것입니다.

저는 한 번에 멀리 가기보다 조금씩 꾸준히 나아가는 쪽입니다. 멈추면 물살에 밀리는 배처럼 배움도 그렇다고 믿어서, 블로그를 이어 오는 동안 해마다 시험을 하나씩 봤고 앱을 내던 올여름에도 둘을 치렀습니다. 크몽은 만 원으로 시작해 경험과 자격이 쌓인 뒤에야 만오천 원으로 올렸습니다. 실력은 먼저 검증하고 나서 말하고 싶었습니다. 만든 것 중 둘도 실력을 늘리려고 만든 디딤돌이었습니다.

쌓아 온 순서

  • 2017.03협성대학교 컴퓨터공학과 입학
  • 2020.03일본 워킹홀리데이, 도쿄에서 열 달
  • 2021.10블로그 시작
  • 2022.08JLPT N1
  • 2022.09정보처리기사
  • 2023.02컴퓨터공학 졸업 · 3.89 / 4.5
  • 2023.08크몽 가사 번역 시작
  • 2023.12컴퓨터활용능력 1급
  • 2024.04JPT 915
  • 2024.11TOEIC 815
  • 2025.05리눅스마스터 1급
  • 2025.05네트워크 엔지니어 근무 (~ 2025.12)
  • 2026.02노래방 가자! 출시
  • 2026.03zooso · 어바웃톡 출시
  • 2026.04MyCon 출시
  • 2026.06SQLD
  • 2026.07빅데이터분석기사 · 목돈 출시
  • 2026.08코츠코츠 · 티엔티엔 출시
  • 2026.09피터패터 심사 제출

기술 스택

프론트엔드
React Next.js TypeScript JavaScript Tailwind CSS HTML5 CSS3 jQuery
React Native Expo Zustand Reanimated iOS Android
백엔드 · 데이터
Supabase PostgreSQL SQLite Supabase Edge Functions Node.js Python Playwright
인프라 · 운영
Cloudflare Vercel Oracle Cloud Google Cloud Sentry Google Analytics Git
연동 · AI
YouTube Discord Gemini Claude Code

만든 것

쓰는 사람의 자리에서 시작한 것들입니다. 기획과 디자인, 앱과 웹, 서버와 데이터, 출시 뒤 운영까지 혼자 했고, 아래에서 풀어 가는 순서대로 놓았습니다. 카드를 누르면 무엇을 왜 어떻게 만들었는지가 이어집니다.

짱짱이의 짱짱한 일상 2021.10 ~ · 블로그

번역된 가사가 어색해서 직접 번역하기 시작했고, 읽는 사람이 편하도록 스킨을 직접 고쳤습니다. 가사와 발음과 번역, 노래방 번호, 내한 일정을 한곳에 둡니다.

MAU 19만 · 누적 1,800만 · 글 1,648

노래방 가자! 2026.02

사람들이 이미 찾고 있던 노래방 번호를 제대로 찾게 만들었습니다. 곡마다 페이지를 두어 검색하면 바로 나오고, 번호 옆에 가사와 유튜브와 보관함이 있습니다.

MAU 10만 · 곡 14만

목돈 2026.07

주식은 쉽고 직관적이어야 합니다. 좁은 화면에 설명력 있는 정보를 넣고, 탭은 늘리지 않고, 멈춘 시세는 멈췄다고 표시합니다.

종목 3,097 · ETF 1,166 · 라이브 2방송

MyCon 2026.04

예매처 네 곳에 흩어진 공연을 한 달력에 모으고, 다녀온 공연은 티켓 한 장으로 남깁니다. 기능이 완벽하면 디자인도 훌륭하다는 생각으로 만들었습니다.

공연 3,320 · MAU 6천

단어장 3종 2026.08 ~ · 코츠코츠 · 티엔티엔 · 피터패터

시중 단어장에 낀 군더더기를 빼고 본분만 남겼습니다. 무엇을 언제 외울지는 앱이 정하고, 사람은 외우기만 합니다. 일본어·중국어·영어 세 권입니다.

코츠코츠 티엔티엔 피터패터 · 9월

단어 18,578 · 11,092 · CEFR A1~C2

zooso 2026.03

대용량 데이터를 다뤄 보고 싶어 만들었고, 편한 검색이 무엇인지 고민했습니다. 필요한 것은 주소와 지도뿐이라, 한 칸에 아는 대로 치면 642만 건에서 1초 안에 찾습니다.

MAU 2.4만 · 건물 642만

어바웃톡 2026.03

방치된 카톡 분석 앱들에 대한 반항심으로 만들었고, GB 단위 대화 파일을 파싱하고 거르고 안전하게 푸는 연습이 됐습니다. 분석은 기기 안에서 하고, AI 요약은 원할 때만 보냅니다.

인사이트 35종 · 파서 포맷 12가지

짱짱이의 짱짱한 일상

일본 노래 가사 번역 블로그입니다. 가사와 발음과 번역, 노래방 번호, 내한 일정을 한곳에 두고 스킨을 직접 고쳐서, 다섯 해째 매달 19만 명이 읽습니다.

2021.10 ~ · 티스토리 스킨 직접 개발 j-pop-playlist.tistory.com 크몽 가사 번역
18,137,768
누적 방문
티스토리 · 2026-09-02
189,778
MAU · 일 활성 7,536
GA4 · 2026-08
1,648
글. 가사 번역 848 · 노래방 번호 553 · 내한 소식 191
티스토리 · 2026-09-02
77
크몽 번역 의뢰, 평점 5.0
크몽 · 2026-09-02
불편

번역된 가사는 문맥이 어색하거나 무슨 이야기인지 알기 어려웠습니다. 곡의 분위기를 모른 채 옮긴 가사는 노래를 해치는 한국어일 뿐이었습니다. 읽는 쪽도 불편했습니다. 가사를 읽다 스크롤하면 영상이 사라졌고, 광고가 늦게 실려 화면이 튀었고, 내한 공연 정보는 한곳에 없었습니다.

고친 것

곡의 분위기와 줄거리를 이해한 뒤 자연스럽게 읽히는 한국어로 옮겼습니다. 노래는 하나의 시이기 때문입니다. 2021년 10월 5일 첫 글부터 지금까지 848곡을 그렇게 번역했습니다. 원문과 발음과 번역을 세 줄로 나란히 놓고, 영상은 스크롤을 따라오게 하고, 광고가 늦게 실려도 화면이 튀지 않게 했습니다. 사람들이 찾아 들어오는 노래방 번호와 내한 공연은 따로 카테고리를 만들어 손으로 정리했습니다. 번역 요청과 평점은 구글 시트로 받았고, 월간 순위 글은 엑셀이 만듭니다.

가사 번역 글
가사 글. 곡 정보와 TJ·KY·JOYSOUND 번호 아래에 원문, 한국어 발음, 번역을 세 줄로 놓습니다. 스크롤을 내리면 영상이 화면 아래로 따라오고, 오른쪽에는 디스코드 채팅이 떠 있습니다. 글마다 같은 구조입니다.
내한 공연 캘린더
내한 캘린더. 크롤러가 모은 일정을 JSON으로 읽어 그리고, 콘서트·페스티벌·팬미팅을 색으로 나눕니다. 날짜를 누르면 장소와 예매 링크가 나옵니다. 이 구조가 그대로 MyCon이 됐습니다.
노래방 번호 모음
노래방 번호 모음. 아티스트마다 TJ·KY·JOYSOUND 번호를 표로 정리한 글 553편입니다. 생각보다 많은 사람이 찾았고, 노래방 가자!의 출발점이 됐습니다.

직접 만든 것

  • 플로팅 비디오스크롤 위치를 읽어 영상을 화면 아래에 고정합니다. 고정한 자리에는 같은 높이의 빈 공간을 넣어 본문이 튀지 않게 했고, 닫으면 원래 자리로 돌아가며 상태를 초기화합니다.
  • 페이지 로딩 컨트롤러화면을 덮는 대신 콘텐츠의 투명도를 0에서 1로 올립니다. 덮개 방식은 광고 로드를 막았기 때문입니다. 로딩 중에는 스크롤을 막고, 3초가 지나면 강제로 엽니다.
  • 내한 공연 캘린더크롤러가 수집한 일정을 JSON으로 두고 캘린더가 읽어 그립니다. 날짜를 누르면 장소와 예매 링크가 나오고, 달을 넘길 수 있고, 모바일에서도 같은 표가 나옵니다.
  • 접이식 목차카테고리마다 따로 접고 펼칩니다. 하나를 열어도 다른 것이 닫히지 않아서 여러 카테고리를 같이 볼 수 있습니다.
  • 일본어 단어 카드JSON의 단어를 Fisher-Yates로 섞어 한 장씩 보여 주고, 발음과 뜻은 눌러야 보입니다. 카드마다 그 단어가 나오는 실제 가사 글로 이어집니다. 코츠코츠가 나온 뒤에는 블로그에서 내렸습니다.
  • 채팅 위젯WidgetBot으로 디스코드 서버를 블로그에 붙였습니다. 마우스로 끌어 옮길 수 있고, 1040px을 기준으로 데스크톱과 모바일 화면을 나눴습니다.
  • 운영 자동화번역 요청과 평점은 구글 폼으로 받아 시트와 독스에 정리했고, 앱스 스크립트로 시트를 자동 갱신했습니다. 지금은 쓰지 않습니다. 월간 순위 글은 엑셀로 순위 변동을 계산해 HTML을 만들어 올립니다.

블로그가 알려 준 것

방문자가 늘면서 사람들이 무엇을 찾아 들어오는지 보였습니다. 생각보다 많은 사람이 노래방 번호를 찾았고, 내한 공연 일정을 찾았습니다. 손으로 정리하던 카테고리 둘이 앱 둘이 됐습니다. 노래방 번호 모음은 14만 곡을 크롤러로 채우는 노래방 가자!가 됐고, 내한 캘린더는 예매처 네 곳을 모으는 MyCon이 됐습니다. 번역은 크몽에서 의뢰 77건을 받았습니다.

기술

HTML5CSS3JavaScriptjQueryFetch APISVG
데이터크롤러 JSONGoogle Sheets · FormsApps ScriptExcel
인프라 · 운영티스토리 스킨WidgetBot(Discord)YouTube 임베드AdSense

노래방 가자!

TJ·KY 14만 곡의 노래방 번호를 찾는 앱과 웹입니다. 곡마다 페이지가 있어 검색창에서 바로 찾아지고, 가사와 유튜브, 즐겨찾기와 플레이리스트가 번호 옆에 있습니다.

143,662
DB · 2026-09-02
103,545
MAU(웹), 5개월 만에 12배
GA4 · 2026-08
3,958
일 활성(웹), 14일 평균
GA4 · 2026-08
4.78 · 4.8
Play(55명) · App Store(13개)
Play 콘솔 · App Store · 2026-09-02
88.6K
8월 검색
DB · 2026-08
불편

TJ·KY 공식 사이트는 곡을 검색 결과 화면 안에서만 그립니다. 곡마다 URL이 없으니 검색 엔진이 색인하지 못하고, 친구에게 링크로 보낼 수도 없습니다. 일본 노래는 제목이 일본어 그대로라 한글로는 찾을 수 없습니다. 먼저 나와 있던 번호 앱들은 화면이 낡았고 쓰기 불편했습니다. 블로그에서 번호를 손으로 553번 정리하며 알게 된 것들입니다.

고친 것

곡마다 URL이 있는 페이지 14만 개를 만들어 검색 엔진이 곡 하나하나를 색인하게 했습니다. 한글·일본어·로마자 어느 표기로 쳐도 같은 곡이 나오도록 발음과 별칭 데이터를 직접 만들고 검수했습니다. 번호를 찾으러 온 사람이 가사를 미리 보고 유튜브를 틀어 볼 수 있게, 곡 하나를 펼치면 유튜브와 가사와 댓글이 그 자리에서 나오고, 자주 부르는 곡은 즐겨찾기와 플레이리스트로 묶입니다. 가사 원문 11만 곡을 모아 그 위에 검색을 얹었습니다.

노래방 가자! 곡 페이지
곡 페이지. 곡마다 고유 URL이 있습니다. 한글 제목과 원제, 아티스트, TJ·KY 번호, 유튜브, 가사, 댓글이 한 페이지에 있고, 검색 엔진이 이 페이지를 색인합니다.
노래방 가자! 아티스트 페이지
아티스트 페이지. 한글 표기와 원어 표기, 소개, 관련 아티스트, 곡 목록을 TJ·KY와 인기·제목·번호순으로 봅니다. 별칭은 어느 표기로 검색해도 여기로 옵니다.
노래방 가자! 라운지
라운지. 두 곡 중 더 마음에 드는 곡을 끝없이 고르는 VS 게임과 주간 TOP 100입니다. 결과는 영구 URL로 공유합니다.
앱 검색 결과
검색. "요네즈 켄시"라고 한글로 치면 일본어 곡이 번호와 함께 나옵니다. 곡명·가수·번호·통합 네 가지로 찾고, TJ·KY와 장르로 거릅니다.
앱 곡 펼침
곡 펼침. 목록 안에서 바로 열립니다. 정보·유튜브·가사·댓글·관련 곡 탭이 그 자리에 있고, 사진은 관련 곡 탭입니다.
앱 차트
차트. TJ·KY 공식 인기 순위와 트렌딩, 인기 아티스트, 댓글 활발 차트를 장르와 기간으로 나눠 봅니다.
앱 보관함
보관함. 즐겨찾기·플레이리스트·좋아요를 로그인 전에는 기기에, 로그인하면 클라우드에 둡니다.

직접 만든 것

  • 웹은 곡마다 페이지, 앱은 목록에서 펼치기검색 엔진과 링크 공유가 필요한 곳은 웹이라서, 곡 페이지 14만 개와 아티스트 페이지 3만 개를 첫 요청 때 정적으로 만들어 CDN에 두고 신곡이 들어오면 그 경로만 다시 만듭니다. 사이트맵 3.8만 URL과 IndexNow로 새 곡을 바로 알립니다. 반대로 노래방에서 번호를 찾는 사람은 한 곡을 깊이 보지 않고 여러 곡을 훑기 때문에, 앱은 목록 안에서 곡을 펼치고 아코디언은 하나만 열립니다. 관련 곡이나 차트에서 파고들 때만 상세 창이 겹으로 쌓입니다.
  • 검색은 코드보다 데이터였습니다곡 제목의 한글 발음 44,500개와 로마자 11,600개, 아티스트 로마자 38,400개를 만들고 직접 검수했습니다. 영문 제목은 토큰마다 발음 사전을 곱해 최대 16가지 변형을 두어 "미세스"로 쳐도 Mrs.가 잡힙니다. 검색은 시작 일치, 발음 시작 일치, 포함 순서로 세 단계를 거치고, 사용자와 커뮤니티가 올린 번역 표기까지 합쳐서 봅니다.
  • TJ와 KY가 같은 가수를 다르게 씁니다TJ는 "가수(Feat.X)", KY는 "가수,X"로 쓰고, 같은 사람을 한글·영문·가나로 제각각 적습니다. 표기마다 페이지가 갈라지면 한 가수의 곡이 서너 곳으로 흩어지기 때문에 별칭 7,688개로 변형을 대표 표기 하나에 묶었습니다. 규칙으로 묶을 수 있는 것은 트리거가 묶되 같은 카테고리끼리만, 한 단계 연결만 허용합니다. 신곡의 feat. 표기는 별칭 표로 자동 분류하되 유일하게 맞거나 1위가 3표 이상이면서 2위의 두 배일 때만 채택합니다. 나머지는 다섯 개씩 손으로 봅니다. KY의 콤마는 대부분 합창이라 별칭이 아니었고, 보컬로이드 이름은 여러 프로듀서가 같이 써서 묶으면 안 되고, LISA와 LiSA처럼 카테고리가 다른 동명이인이 있습니다.
  • 가입부터 시키지 않습니다번호 하나 찾으러 온 사람에게 로그인을 요구하지 않습니다. 즐겨찾기와 플레이리스트는 기기에 먼저 저장하고, 로그인하면 클라우드로 올라갑니다. 플레이리스트는 공유하고 커버를 올릴 수 있고, 곡마다 난이도·키·메모를 남깁니다. 커버는 복사본에 승계하지 않습니다. 원본 주인이 커버를 바꾸면 복사본이 깨지기 때문입니다.
  • 가시성이 브랜드보다 앞섭니다테마는 배경 하나와 액센트 하나만 고르게 했습니다. TJ·KY 브랜드색은 공식 로고에서 채집한 뒤 WCAG 대비를 실측해, 다크용 실제 색과 라이트용 변형 두 벌로 고정했습니다. 글자 크기 4단계를 넣으면서 행간 토큰을 같이 만들어 21곳을 다시 연결했습니다. 행간이 21곳에서 제각각 비율로 박혀 있어 글자 배율을 따라가지 못했기 때문입니다.
  • 번호 너머의 것곡을 펼치면 유튜브와 가사 원문, 댓글, 관련 곡이 나옵니다. 가사는 커뮤니티가 번역을 올리고 투표하며, 댓글에는 좋아요와 신고가 있습니다. 차트는 트렌딩·인기 아티스트·댓글 활발을 기간별로 보고 주간 스냅샷을 23주째 쌓습니다. 라운지의 VS 게임은 두 곡 중 하나를 끝없이 고르고 결과를 영구 URL로 남깁니다.
  • 사람이 고르고 기계가 채웁니다신곡은 하루 다섯 번 TJ·KY에서 긁어 와 넣고, 카테고리는 두 사이트에서 다시 확인해 매깁니다. 앨범 커버는 Apple Music API로 맞춰 점수 0.75 이상이면 자동으로 붙입니다. 아티스트 이미지와 유튜브 ID, 일본어·한국어 가사는 크롤러가 후보를 모아 검토 화면에 올리고 제가 고른 것만 한 번에 적용하며, 영어 가사는 정확히 일치할 때만 자동으로 넣습니다. 가사 원문 11만 곡이 이렇게 쌓였고, 곡 소개 12,429편은 따로 씁니다. 쓰기는 전부 Supabase 함수 158개를 거쳐서 클라이언트가 테이블을 직접 만지지 않고, 크롤러는 Oracle Cloud의 cron이 돌립니다. 신고와 차단은 모더레이션 로그로 Discord에 갑니다.

운영하며 겪은 것

  • 통계 패널이 DB를 멈춘 것 · 2026.06곡 페이지에 통계 패널을 붙였습니다. 처음에는 페이지를 열 때마다 28일 조회 추이, 시간대 분포, 보관 수, 재방문 비율을 RPC 넷으로 그 자리에서 계산했습니다. 곡마다 조회 기록 전체를 다시 훑으니 디스크 IO가 한도를 넘어 DB가 멈췄습니다. 해결은 DB에서 했습니다. 계산을 전부 하루 한 번 도는 배치로 옮겨 곡별 통계 표에 미리 써 두고, 페이지는 RPC 하나로 그 행만 읽습니다. 같은 날 안에 네 함수를 하나로 접었습니다.
  • 리뷰 한 줄로 찾은 보관함 유실 · 2026.06"보관함에 저장해 둔 게 멋대로 날아감"이라는 리뷰가 왔습니다. 세션이 비면 무조건 로컬 보관함을 지우는 분기가 있었고, 사용자가 로그아웃하지 않아도 앱을 처음 켤 때와 토큰 갱신에 실패할 때 그 분기가 돌았습니다. 저장을 다 읽기 전에 지우는 경쟁까지 겹쳐 "멋대로"로 보였습니다. 직접 로그아웃할 때만 지우게 바꾸고, 읽기가 끝나기 전에는 쓰지 못하게 막았습니다.
  • 발음이 증발하던 트리거 · 2026.06일본 곡에 손으로 넣은 발음이 사라졌습니다. 제목이 갱신될 때마다 트리거가 한·일 제목의 발음을 NULL로 밀고 있었습니다. 기존 값을 보존하도록 고쳤고, 지워진 발음은 백업에 남아 있어 되돌릴 수 있게 해 두었습니다.
  • 조용히 어긋나던 곡 수 · 2026.07아티스트별 곡 수 캐시가 392건 틀려 있었습니다. 크롤러가 존재하지 않는 함수를 부르고 있었고, 실패가 조용히 폴백으로 넘어가 아무도 몰랐습니다. 곡이 늘고 줄 때마다 트리거가 다시 세게 하고, 모든 경로를 몇 번 겹쳐 돌아도 같은 결과가 나오게 만들었습니다.
  • DB가 한 시간 멈춘 날 · 2026.08.10낮 배치가 늘어지며 DB가 한 시간 멈췄습니다. work_mem 256MB가 노드당 설정이라 배치 하나가 1GB 인스턴스를 압박했고, 커넥션 60개가 차서 뒤따르던 작업이 시작도 못 했습니다. 배치의 count(DISTINCT) 하나가 665만 행을 디스크에서 정렬하고 있었는데, 원천이 이미 DISTINCT라 결과가 같다는 것을 증명한 뒤 바꿔 762초를 30~50초로 줄였습니다. 그 과정에서 정렬 타이브레이커가 없어 추천 순서가 갱신마다 뒤바뀌던 버그도 찾았습니다. 신고도 에러도 없던 버그였습니다.
  • 곡 페이지가 API의 절반을 먹던 것 · 2026.08.18곡 페이지가 관련 아티스트 여덟 명마다 별칭 해석 함수를 불러 렌더당 9회, 피크에는 시간당 15만 건으로 Supabase 호출의 절반이었습니다. 캐시에 든 값이 이미 정규화돼 있다는 것을 2,698건 전수로 확인하고 호출을 지워 1.64회로 줄였습니다.
  • 봇을 막았더니 앱 이미지가 깨진 것 · 2026.08.18Vercel 방화벽의 봇 방어를 켰더니 앱의 아티스트 이미지가 깨졌습니다. 앱은 브라우저가 아니라 자바스크립트 챌린지를 통과하지 못하기 때문입니다. 이미지 경로만 우회 규칙으로 빼고, 레이트 리밋 규칙을 그 위에 두어 봇이 이미지 경로로 빠져나가지 못하게 했습니다.
  • ISR이 걸려 있지 않았던 것 · 2026.08.30Vercel 청구가 보름에 36달러였고, 곡 페이지 하루 14.6만 호출이 CPU의 85%였습니다. 처음에는 재검증 주기가 짧다고 보고 배포까지 했지만 틀렸습니다. 응답 헤더가 no-store였고 빌드 라우트표에서 곡 페이지가 Dynamic이었습니다. generateStaticParams가 없어서 ISR이 걸리지 않고 매 요청 렌더하고 있었습니다. 빈 배열을 돌려주는 한 줄로 라우트가 SSG로 바뀌고 캐시가 맞기 시작했고, 하루 호출이 86% 줄었습니다. 선언값이 아니라 관측값으로 판정한다는 규칙이 여기서 생겼습니다.
  • 로그아웃해도 오던 알림 · 2026.06로그아웃한 기기에 알림이 계속 왔습니다. 푸시 토큰 삭제를 기다리지 않고 세션을 먼저 끊어서, 권한 정책이 0행을 지우고 있었습니다. 세션이 살아 있을 때 이 기기 토큰만 지우고 끝나기를 기다리게 바꿨습니다.

기술

Next.js 16React 19TypeScriptTailwind v4ISR
React Native 0.81Expo SDK 54React NavigationZustandReanimated 4expo-sqlite
데이터Supabase PostgreSQLPL/pgSQL 함수 158RLSpg_cronEdge FunctionsPython 크롤러
인프라 · 운영VercelOracle Cloud cronEAS BuildGA4IndexNowAdSense · AdMob인앱 구독Discord 웹훅

목돈

주식은 쉽고 직관적이어야 한다는 생각으로 만든 시세 앱입니다. 국내·미국 전종목 실시간 시세와 스크리너, 배당 캘린더를 좁은 화면에 설명과 판정을 붙여 넣었고, 24시간 유튜브 라이브로도 내보냅니다.

6 → 0
코스피 페이지 한 번에 드는 워커 요청
프로덕션 실측 · 2026-08-30
96.6%
브라우저 단건 시세 호출 제거
Cloudflare 로그 · 2026-09-01
$23 → $7.76
월 Cloudflare 비용
Cloudflare 청구 · 2026-08-31
31배
스파크라인 3.3MB → 104KB
커밋 9e4efb2 · 2026-09-01
2방송
24시간 유튜브 라이브, 국장·미장
유튜브 @mokdon_live · 2026-07 ~
불편

주식 앱은 쉽고 직관적이지 않았습니다. 그것이 진입장벽이었고, 사람들은 설명이 부족한 채로 투자에 들어갔습니다. 어쩔 수 없는 면도 있습니다. 과거에 머문 것을 함부로 바꿀 수 없고 정보는 덕지덕지 쌓이기만 하니 어지러운 앱이 됩니다. 가장 불편했던 것은 탭과 메뉴를 찾으려고 검색을 해야 했다는 점입니다. 플랫폼마다 용어가 다르고, 불필요한 탭이 많고, 설명이 충분하지 않은 곳이 있었습니다.

고친 것

좁은 화면 안에 설명력 있는 정보를 넣고 바로 확인할 수 있게 만들었습니다. 숫자마다 뜻을 여는 ⓘ 툴팁, 숫자 옆의 판정 한 마디, 24개 항목을 ✓⚠✕로 매기는 체크리스트, 배당 기준일까지 남은 날과 100주 보유 시 연 배당금입니다. 탭은 하나하나 신중히 만들었습니다. 만들 필요가 없는 탭은 서브탭으로 넣었고, 자주 가는 탭은 사용자가 하단 바에 직접 등록합니다. 개인 개발자라 정보의 신선도에 한계가 있지만, 그 안에서 최대한 신선하게 만들되, 멈춘 시세를 실시간인 척 보여 주지 않았습니다. 그 과정에서 비용을 어떻게 잡을지 고민이 많이 들어갔습니다.

목돈 종목 상세
종목 상세. 캔들 위에 볼린저 밴드와 이동평균을 겹치고, 오른쪽에 핵심 지표, 아래에 기술 지표를 판정 한 마디와 함께 둡니다. 기술 지표에는 판정 한 마디와 설명 툴팁이 붙고, 시세는 장중이면 라이브로 움직입니다.
목돈 S&P 500 스크리너
스크리너. S&P 500 503종목 전체가 한 표에 들어 있고 등락률로 실시간 정렬됩니다. 화면에 보이는 40행만 그리기 때문에 스로틀 없이 돌아갑니다.
목돈 대시보드
대시보드. 지수 여섯 개, 관심 종목, 공포·탐욕 지수, 오늘의 상승·하락 종목 수, 다가오는 배당을 한 화면에 둡니다. 지수는 각자 5~7초마다 따로 움직입니다.
앱 홈
홈. 지수 카드가 각자 살아 움직이고, 관심 종목과 다가오는 배당이 바로 아래 있습니다.
앱 종목 정보
종목 정보. 시세, 52주 위치, 업종과 비교하는 밸류에이션 막대, 수익성을 서브탭으로 나눴습니다. 화면을 늘리지 않고 탭 안에서 깊어집니다.
앱 스크리너
스크리너. 코스피 종목을 필터와 지표로 거릅니다. 웹과 같은 열, 같은 계산식을 씁니다.
앱 알림
알림. 목표가·등락률·거래량·배당 기준일 같은 조건을 종목마다 걸어 두면 서버가 평가해 푸시합니다. 앱이 켜져 있을 필요가 없습니다.

지금 이 값은 목돈의 스냅샷 CDN에서 옵니다

tick.mokdon.kr/idx.json 읽는 중

발행기가 2초마다 R2에 올리는 파일을 이 페이지가 10초마다 읽습니다. 파일의 발행 시각이 60초보다 오래되면 값을 실시간이라고 표시하지 않습니다. 목돈 웹과 앱이 쓰는 게이트를 그대로 축소한 것입니다.

직접 만든 것

  • 값은 모두에게 같은데 사람마다 계산하고 있었습니다시세 화면이 워커 API를 종목 단위로 불러서 코스피 한 페이지에 워커 요청이 6건이었고, 방문자 수만큼 늘었습니다. 발행기가 시세를 파일로 만들어 CDN에 올리고, 웹은 그 파일을 읽어 그리고 클라이언트가 폴링으로 갱신하게 바꿨습니다. 스키마, 하트비트, 파일 신선도, 분량 네 가지 게이트를 통과하지 못하면 워커로 돌아갑니다. 살아있음은 하트비트로, 신선함은 파일마다 따로 판정합니다. 합치면 장 마감 때 전 소비자가 폴백해서 CDN이 무의미해지기 때문입니다. 야간선물만은 마감 면제를 주지 않았습니다. 멈춘 값을 실시간처럼 보여 주는 것이 빈칸보다 나쁘기 때문입니다.
  • 발행 주기는 값의 무게가 아니라 쓰기 건수로 정했습니다R2는 바이트가 아니라 건수로 과금합니다. 재 보니 쓰기의 94%가 하트비트와 지수였고 사용자가 보는 시세는 6%였습니다. 하트비트를 5초에서 20초로, 지수를 5초에서 10초로 늦추고 시세를 30초에서 5초로 당겨서 월 쓰기를 1.03M에서 771K로 줄이면서 사용자가 보는 값은 6배 신선해졌습니다. 여러 스케줄러가 동시에 깨어나 같은 파일을 8번 받던 것은 완료값이 아니라 약속을 캐시해 2번으로 줄였습니다.
  • 스로틀을 지우고 렌더를 없앴습니다코스피·코스닥 2,500여 종목과 ETF 1,100여 개를 담은 스크리너가 틱마다 등락률로 재정렬되며 활발한 장에 렉이 났습니다. 먼저 써 본 2.5초 스로틀은 증상만 덮고 신선도를 잃었습니다. 표를 가상화해 DOM에 40행만 두자 재배치 비용이 사라져 스로틀을 뺐습니다. SSR로 40행을 먼저 내보내 검색 엔진과 첫 화면을 지켰고, 정렬은 정적인 시가총액으로 하고 표시값만 라이브로 해서 사용자가 잡은 행이 흔들리지 않게 했습니다.
  • 갱신 패턴을 셋으로 나누고 이유를 적었습니다지수는 지수마다 5~7초 독립 타이머라 제각각 살아 움직입니다. 관심 종목은 요청은 한 번이지만 적용을 0~1.2초 흩뿌려 종목마다 펄스가 보입니다. 급등락 카드는 일부러 동시에 바뀝니다. 같은 데이터를 다르게 갱신하는 이유를 각각 설명할 수 있어야 한다고 생각했습니다.
  • 비용은 추정하지 않고 재서 판정했습니다Cloudflare 청구를 항목 여덟 개로 계량하는 스크립트를 만들어 $23에서 $9로, 다시 $7.76으로 내렸습니다. 가장 큰 항목은 요청이 아니라 R2 쓰기였고, 워머가 매일 전 페이지를 훑는 것이 하루 쓰기의 45%였습니다. 페이지 안의 fetch 하나가 라우트 재검증 주기를 끌어내리는 것도 여기서 알았습니다. 회귀분석으로 방문자가 CPU를 설명하지 못한다는 것을 확인하고, 76~85%가 기계가 만드는 고정분이라 사용자를 겨냥한 최적화는 천장이 낮다고 판단해 멈췄습니다. 기본료가 청구서의 64%입니다.
  • 탭을 늘리지 않았습니다메뉴를 찾으려고 검색하는 것이 사용자에게 최악의 경험입니다. 하단 탭은 사용자가 고르고 최대 4개, 나머지는 서랍에 넣어 탭 목적지 8개와 스택 화면 6개로 압축했습니다. 지표마다 툴팁, 체크리스트는 판정 문장, 용어는 하나로 통일했습니다. RSI 과매도는 팔레트에 파랑이 없어 초록으로 했고 그 이유를 주석에 적었습니다.
  • 서버가 대신 지켜봅니다목표가와 등락률, 거래량 배수, 52주 고저, PER·PBR, 체크리스트 통과 개수, RSI 진입, 이동평균 교차 조건을 전부 서버가 평가해 푸시합니다. 빠른 조건은 5초마다, 느린 조건은 실행마다 한 번 봅니다. "오늘 봉" 판정은 시장의 현지 날짜로 해야 미국 장이 자정을 넘어도 틀리지 않습니다.
  • 24시간 유튜브 라이브 2방송국장과 미장 화면을 미니PC가 렌더링해 1080p60으로 송출합니다. 지수 카드, 차트 넷, 히트맵, 실시간 순위, 하단 티커가 웹과 같은 컴포넌트와 토큰으로 돌아갑니다. 채팅 봇은 시세를 답하고 급변과 거래량 급증을 자동으로 띄웁니다.

운영하며 겪은 것

  • 브라우저가 시세를 4시간 붙들고 있던 것 · 2026.07.30워커가 엣지 캐시용으로 내보낸 max-age 헤더를 Cloudflare 존 캐시가 브라우저 TTL 4시간으로 바꿔 쓰고 있었습니다. 11시 국장 방송 화면에 프리마켓 칩이 남아 있어 알아챘습니다. 엣지용 캐시 키를 따로 두고 브라우저에는 no-store를 보내도록 갈랐습니다.
  • 하한가가 +30%로 뒤집혀 보인 것 · 2026.07.10네이버 응답의 등락 부호 코드를 잘못 읽어 하한가였던 펩트론과 HLB가 +30%로 보였습니다. 이후로 시세 소스를 붙일 때마다 하락 종목을 실제로 골라 부호를 확인합니다. 시간외 시세를 붙일 때도 같은 확인을 거쳤습니다.
  • 방송 서버의 프레임 손실 · 2026.07.25전용 코어를 보장한다는 서버에서 하루 만에 프레임 손실이 38%까지 올랐습니다. 미장 방송 하나를 끄니 즉시 회복돼 누수가 아니라 용량 문제였고, 게스트가 놀고 있는데도 steal이 5~6%였습니다. 미니PC로 옮겨 부하가 15.6에서 4.5로 내려갔습니다.
  • ISR 쓰기를 줄이려다 늘린 것 · 2026.08.11 ~ 22R2 쓰기를 줄이려고 재검증 주기를 올려 배포했는데 하루 쓰기가 10만 건대에서 줄기는커녕 13만까지 늘었습니다. 워머가 매일 전 페이지를 훑어 만료된 페이지를 전부 다시 만들고 있었고, 페이지 안의 5초짜리 fetch 하나가 라우트 주기를 끌어내리고 있었습니다. 워머를 주 1회로 줄이고 52곳의 주기를 한 시간으로 맞춰 쓰기가 하루 1.3만으로 떨어졌고 R2 비용이 $8.97에서 $0이 됐습니다.
  • ETF 크롤러가 사흘 연속 죽어 있던 것 · 2026.08.058월 3일부터 사흘 연속 크래시해 ETF 750종이 닷새째 갱신되지 않았습니다. 행마다 키 집합이 달라 PostgREST가 배치 전체를 거부한 것이라 같은 키 집합끼리 묶어 보내게 했고, 이후 업서트는 키를 늘 채우고 값만 비우는 규칙을 세웠습니다.
  • 방송 화면이 두 시간 넘게 깨진 채 송출된 것 · 2026.08.26크롬 렌더러가 파일 디스크립터 1,024개 한도를 넘어 죽었는데 브라우저 프로세스는 살아 있어서 systemd가 잡지 못했습니다. 메모리가 아니었습니다. 시세 파일 발행을 늦추자 같은 데이터를 12번 받으며 공유메모리 핸들이 새는 것이 방아쇠였습니다. 한도를 올리고 장 열기 전 새로고침을 예약했습니다. 새로고침 한 번에 핸들의 97%가 돌아옵니다.
  • "스켈레톤이 안 풀린다" · 2026.09.01신고를 따라가니 스파크라인 하나가 한 달치 봉을 통째로 받아 6장에 3.3MB였습니다. 소비자는 마지막 96개만 씁니다. 지표 계산이 끝난 뒤에 개수를 자르는 인자를 두어 104KB로 줄였습니다. 먼저 자르면 앞쪽 봉의 이동평균이 비기 때문입니다.

기술

Next.js 16Tailwind v4react-virtuosoSSR + 클라이언트 폴링
React Native 0.86Expo SDK 57React NavigationFlashList
데이터Supabase(mokdon 스키마)업비트 · 바이낸스 WebSocketLLM 브리핑
인프라 · 운영Cloudflare Workers · R2 · CacheR2 스냅샷 CDN런타임 킬 스위치Oracle Cloud cron미니PC 발행기 · 방송 렌더러비용 계량 스크립트IndexNowGA4

MyCon

예매처 네 곳의 콘서트·내한·페스티벌·팬미팅을 한 달력에 모아 보여 주는 공연 캘린더입니다. 다녀온 공연은 티켓 한 장으로 남기는 티켓북까지 있습니다.

3,320
통합 공연
DB · 2026-09-02
4사 → 1
중복 병합 195건, 전건 자동
DB · 2026-09-02
227
가입, 8월에 90명
DB · 2026-09-02
262
관람 기록, 평점 205 · 리뷰 97
DB · 2026-09-02
2,995
X 봇 트윗
DB · 2026-09-02
불편

블로그에서 이어졌습니다. 일본 가수의 내한 공연을 찾는 사람이 많았는데, 우리나라는 NOL·YES24·멜론·티켓링크 네 곳에 티켓이 흩어져 있어서 정보를 얻으려면 사이트마다 돌아다녀야 했습니다. 같은 공연이 사이트마다 다른 이름으로 올라왔습니다. 블로그에 손으로 191개를 정리하던 일이었습니다.

고친 것

흩어진 정보를 한데 모으자는 것이었습니다. 네 곳을 자동으로 수집해 같은 공연을 하나로 합쳤습니다. 모은 것을 어떻게 보여 줄지 오래 고민했고, 캘린더는 Fantastical에서, 목록은 Letterboxd에서, 상세의 비주얼은 Spotify에서 배우되 구조는 티켓 사이트에서 가져왔습니다. 기능이 완벽하면 디자인도 훌륭하다는 생각으로 만들었고, 다녀온 공연을 남기는 티켓북까지 넓혔습니다.

MyCon 캘린더
캘린더. 왼쪽 작은 달력에서 언제를 고르고 오른쪽에서 무엇이 있는지 봅니다. 네 예매처의 공연이 한 달력에 들어오고, 콘서트·팬미팅·페스티벌·내한을 색으로 나누며, 일·주·월 뷰를 오갑니다.
MyCon 공연 상세
공연 상세. 첫 화면에서 날짜, 장소, 좌석별 가격, 예매처 넷의 판매 상태, 회차, 지도까지 결정에 필요한 것을 다 보여 줍니다. 오른쪽 레일의 달력은 이 공연 날짜에 맞춰 열립니다.
MyCon 티켓북
티켓북. 다녀온 공연이 티켓 스텁 한 장으로 남습니다. 별점, 한 줄 후기, 좌석, 가격, 사진 수, 일련번호가 티켓 위에 있습니다.
앱 캘린더 띠 모드
캘린더, 띠 모드. 좁은 화면의 기본입니다. 날짜 띠 하나와 그날의 목록만 보이고, 띠는 선택한 날이 가운데 오게 흐릅니다.
앱 캘린더 펼친 달
캘린더, 펼친 달. 달력 버튼을 누르면 달 전체가 내려옵니다. ‹ ›는 보이는 달만 넘기고 선택한 날은 그대로 둡니다.
앱 티켓북
티켓북. 다녀온 공연이 티켓 한 장으로 남습니다. 별점, 후기, 사진, 좌석, 가격을 적고 링크로 공유합니다.
앱 통계 포토 캘린더
통계, 포토 캘린더. 다녀온 날에 포스터가 박히고, 그달의 관람 횟수와 쓴 돈이 한 줄로 나옵니다.

직접 만든 것

  • 네 곳을 하나로 합칩니다NOL·멜론·예스24·티켓링크를 하루 27번쯤 HTTP 요청으로 긁어 오고, 브라우저는 티켓링크 토큰을 한 번 뽑을 때만 씁니다. 제목과 공연장을 정규화하고 날짜를 키로 삼아 같은 공연을 자동으로 묶어 195건을 병합했고, 손으로 고칠 때는 파일에 근거를 남기는 규칙을 두었습니다.
  • 첫 화면에서 결정이 끝나는 상세가격, 오픈일, 러닝타임, 등급, 판매 상태, 예매 버튼을 첫 화면에 다 둡니다. 예매처가 여럿이면 나란히 비교하고, 회차와 장소, 지도가 이어집니다. 데스크톱은 헤더가 따라오고 모바일은 예매 버튼이 아래에 고정됩니다.
  • 캘린더와 검색월 뷰에서 공연일과 티켓 오픈일을 토글로 오가고, 콘서트·내한·페스티벌·팬미팅을 네 색으로 나눕니다. 검색은 Cmd+K 팔레트로 어디서든 열리고, 250ms 디바운스에 키보드로만 고를 수 있습니다.
  • 좁은 화면에 달력을 어떻게 넣을지데스크톱은 Fantastical에서 배운 두 칸 구조입니다. 왼쪽 작은 달력에서 언제를 고르고 오른쪽에서 무엇이 있는지 봅니다. 일·주·월 뷰를 두고 마지막 뷰를 기억합니다. 모바일에는 그 셋을 그대로 옮기지 않았습니다. 날짜 띠 하나가 기본이고, 달력 버튼을 누르면 달 전체가 펼쳐지고, 그 아래가 그날의 목록입니다. 띠와 펼친 달과 필터의 높이를 함께 움직여 스크롤하면 상단이 접힙니다. 펼친 달에서 ‹ ›는 보이는 달만 넘기고 선택한 날은 그대로 둡니다. 웹의 모바일 화면도 같은 구조를 쓰고, 상단의 달력 버튼을 누르면 캘린더 페이지가 시트로 내려옵니다. 시트는 표준 이징으로는 펼쳐지는 맛이 없어서 느리게 시작해 중반에 급가속하는 420ms 곡선으로 열고, 닫힐 때는 240ms로 빠르게 빠집니다.
  • 상태를 세 등급으로 나눴습니다조작 상태를 다음 방문에도 남길 것, 이번 방문 동안만 남길 것, 열 때마다 초기화할 것으로 나눴습니다. 카테고리 필터는 며칠 뒤 들어왔을 때 "페스티벌"만 걸려 있으면 데이터가 사라진 것처럼 보여서 방문 단위로 내렸고, 저장하는 것은 기본 뷰뿐입니다. 전체와 내 일정 전환은 라우트가 아니라 상태로 바꿔서, 누를 때마다 보던 날짜와 달이 초기화되지 않게 했습니다.
  • 저장과 알림로그인 없이 저장하고, 로그인하면 저장한 것이 합쳐집니다. 티켓 오픈과 공연 전날을 푸시로 알리고, 직접 일정을 넣을 수도 있습니다.
  • 티켓북다녀온 공연이 티켓 스텁 한 장으로 남습니다. 노치는 mask와 radial-gradient로 파고, 노치를 따라 도는 1px 테두리는 4방향 drop-shadow로, 천공선은 SVG 점선으로 그렸습니다. 별점 0.5 단위, 사진 4장, 좌석, 가격을 적고 링크로 공유합니다. 공유 주소의 키는 닉네임은 바뀌고 uuid는 인증을 드러내고 시퀀스는 열거되기 때문에 셋 다 버리고, base62 8자를 암호학적 난수로 뽑았습니다.
  • 통계는 숫자를 지어내지 않습니다포토 캘린더, 차트, 필름, 포스터 네 가지 뷰와 연말 결산 카드가 있습니다. 필름은 컷의 폭이 티켓값에 비례해 줄 길이가 곧 지출이고, 포토 캘린더는 같은 날 두 장이면 부채꼴로 겹칩니다. 헤드라인 숫자는 관람 횟수와 지출 둘로 절제했고, 기록이 없어도 축과 열두 달과 0회를 그대로 보여 줍니다. 안내 문장 대신 구조를 지키는 쪽을 택했습니다.
  • 웹 채팅과 X 봇오픈 시각에 몰리는 1회성 사용자가 주 고객이라 채팅은 익명을 허용합니다. 메시지에 "그때 보던 공연" 칩이 붙고, 신고는 Discord로 가고, 킬 스위치가 있습니다. 익명 격리는 채팅보다 먼저 따로 커밋했습니다. 기본이 차단이고 채팅만 명시적으로 열리는 구조가 페이지마다 게이트를 고치는 것보다 안전하기 때문입니다. X 봇은 API 없이 Playwright 브라우저 자동화로 신규·오픈 공연을 2,995번 트윗했습니다.
  • 디자인에서 정한 것액센트는 검정입니다. 강렬한 브랜드 색은 포스터와 충돌하고, 무채색이 포스터를 돋보이게 합니다. 모바일 후기 폼은 바텀시트가 아니라 풀스크린입니다. 필드가 많아 키보드가 올라오면 바텀시트는 짓눌립니다. 필수는 공연과 관람일 둘뿐이고, 게시판은 만들지 않았습니다. 혼자서도 쓸 수 있어야 초기의 빈 서비스를 견딥니다.

만들며 겪은 것

  • 상세 페이지에서 네 번 틀린 것실패 네 가지를 적고 나서야 방향이 잡혔습니다. Spotify를 잘못 따라했습니다. 그것은 아티스트를 탐색하는 페이지고, 우리는 공연을 예매하는 페이지입니다. 목적이 다른데 구조를 베꼈습니다. 비주얼만 Spotify에서 배우고 구조는 티켓 사이트에서 가져오자 첫 화면만 보고 결정을 내릴 수 있게 됐습니다.
  • 포스터 그리드를 버린 것티켓북의 첫 시안은 포스터를 격자로 늘어놓은 것이었는데 수집하는 감성이 없었습니다. 티켓 스텁 한 장이 기능의 인상을 좌우한다고 보고 그것부터 제대로 만들었습니다.
  • 사라지지 않던 삭제 이벤트채팅에서 메시지를 소프트 삭제하면 Postgres Realtime이 새 행에 읽기 정책을 적용해 삭제 이벤트가 구독자에게 오지 않았습니다. 정책을 열고 본문을 비워서, 삭제된 행이 공개돼도 남는 것이 없게 했습니다.
  • ‹ ›가 선택한 날을 끌고 가던 것 · 2026.07앱에서 펼친 달을 다른 달로 넘기는 유일한 수단이 ‹ ›였는데, 그것이 선택한 날까지 같은 날짜로 점프시켰습니다. 6월 27일에서 ›를 누르면 7월 3일을 고르기도 전에 7월 27일이 잡혀 목록이 바뀌었습니다. 보이는 달과 선택한 날을 다른 상태로 분리했습니다.
  • 뒤로가기가 시트를 닫지 않고 페이지를 떠나던 것 · 2026.08모바일 캘린더 시트는 페이지처럼 보여서 사람들이 뒤로가기로 닫힐 것이라고 기대합니다. 열 때 히스토리에 표식을 하나 쌓고 popstate에서 닫게 해서 보던 페이지를 지키게 했습니다.
  • 주 달력 칩의 첫 줄이 비던 것 · 2026.08주간 칸은 폭이 112px쯤이라 12px 한글이 아홉 자 들어가는데, "전주얼티밋뮤직페스티벌"은 어절을 지키면 첫 줄에 들어갈 수 없어 브라우저가 통째로 다음 줄로 밀었습니다. 넓은 곳은 어절을 지키고 좁은 칩에서만 포기하게 나눴습니다.

기술

Next.js 16Supabase RealtimeCSS maskCmd+K 팔레트
Expo SDK 55Expo RouterFlashList푸시
데이터Supabase(함수 49)pgcryptobase62 공유 코드Node + Playwright 크롤러
인프라 · 운영IndexNowDiscord 신고X 봇

단어장 3종

일본어·중국어·영어 단어장 앱 세 권입니다. 군더더기를 빼고 본분만 남겨서 무엇을 언제 외울지는 앱이 정하고, 사람은 외우기만 합니다. 뺀 만큼 남은 것을 제대로 만들어야 해서, 제 기술을 가장 많이 쏟은 앱입니다.

코츠코츠 · 티엔티엔 · 피터패터 · 2026.08 ~ 코츠코츠 티엔티엔 피터패터 · 9월
18,578
코츠코츠 단어, 히라가나·가타카나 포함
앱 덱 DB · 2026-09-03
11,092
티엔티엔 단어, HSK 3.0 전체
HSK 3.0 목록 · 2026-08
13,488
피터패터 단어, A1~C2
앱 화면 · 2026-09-01
16,455 → 1
발음 파일 요청 횟수
발음 팩 매니페스트 · 2026-08
−14.6MB
생존 메모리, JS 멈춤 −1.9초
갤럭시 S10 계측 · 2026-08
불편

시중 단어장에는 쓸데없는 것이 너무 많이 들어가 있습니다. 쓰기를 시키고, 단어 끼워 맞추기를 넣고, 랭킹과 알림으로 사람을 재촉합니다. 그건 단어를 외우는 것이 아닙니다. 예문도 참고용이지, 외우는 시간을 잡아먹는 요소입니다. 단어장으로서의 본분을 지키지 않습니다.

고친 것

단어장이 앱이어야 하는 이유는 하나입니다. 책이 할 수 없는 학습 설계를 앱이 대신 해 줄 수 있기 때문입니다. 그래서 본분을 지키는 미니멀 단어장을 만들었습니다. 탭은 학습과 단어장 둘뿐이고, 무엇을 언제 얼마나 외울지는 앱이 정하고 사람은 외우기만 합니다. 미니멀은 기능이 적다는 뜻이 아니라 사람에게 남는 판단이 적다는 뜻이라서, 뺀 만큼 남은 것을 제대로 만들어야 했습니다. 일본어에서 시작해 같은 원칙으로 중국어와 영어를 만들었습니다.

코츠코츠 홈
홈. 오늘 할 것을 숫자 하나로 먼저 보여 줍니다. 다가오는 복습, 정답률, 하루 평균, 완주 예상일이 그 아래에 있습니다.
코츠코츠 암기 카드 뒷면
암기 카드 뒷면. 맞았는지가 아니라 얼마나 기억했는지를 묻습니다. 버튼마다 FSRS가 실제로 계산한 다음 간격이 적혀 있습니다.
코츠코츠 학습 히트맵
히트맵과 암기한 단어. 연속 학습일은 기록이지 벌이 아니라서 빈칸을 붉게 칠하지 않습니다. 자주 틀리는 단어는 따로 모입니다.
티엔티엔 단어 시트
티엔티엔 단어 시트. 성조 부호에만 색을 입혀 병음을 읽고, 암기율과 복습 이력을 함께 봅니다.
피터패터 단어 시트
피터패터 단어 시트. 미국식과 영국식 발음을 나란히 두고, 품사에 따라 발음이 갈리는 단어는 따로 적습니다.

직접 만든 것

  • 뺀 것마다 이유를 적었습니다새 기능은 질문 셋을 통과해야 들어옵니다. 이게 없으면 단어를 못 외우나, 사람에게 새 판단을 요구하나, 재촉하거나 남과 비교하나. 예문, 랭킹, 로그인, 알림은 그래서 없습니다. 덜어냄은 안 만든 것의 변명이 아니라서 이유를 못 쓰면 미완성으로 봤고, 대가도 같이 적었습니다. 알림이 없어서 앱을 잊는 사람이 생기고, 계정이 없어서 기기를 바꾸면 백업 파일로 옮겨야 합니다.
  • 요령은 앱이, 꾸준함은 사람이어느 단어를 언제 볼지는 FSRS-6이 계산합니다. 채점은 2버튼과 4버튼 중 고르고, 버튼마다 실제로 계산한 다음 간격을 보여 줍니다. 배우는 중인 카드는 20분 안이면 세션 안에서 다시 나오고, 되돌리기 20단계와 오늘은 넘기기, 전체 복습을 막지 않고 둡니다. 하루 신규 카드, 복습 상한, 목표 암기율, 하루가 바뀌는 시각까지 기본값은 좋되 전부 바꿀 수 있습니다.
  • 기억 상태는 숫자가 아니라 온도로수치는 판단을 요구하고 색은 요구하지 않습니다. FSRS의 기억 확률을 색으로 바꿔 단어장의 LED 미터 12칸, 목록의 도트, 오십음도의 셀, 진행바까지 한 언어로 관통시켰습니다.
  • 단순하지만 진보됨구조는 검증된 문법만 쓰고, 실험은 재질과 모션에서 합니다. 글자 크기는 여섯 단계로 잠그고 모션은 300ms 아래에서 transform과 opacity만 씁니다. 암기 카드는 끌기와 연동된 회전과 뒷카드 스케일이 실시간으로 따라오는 물리이고, 화면 전환은 우리 수준을 인정하고 미니멀한 애니메이션으로 갔습니다. 배경 블롭은 42~68초에 30px 움직이는데 60fps로 그릴 이유가 없어 초당 넷으로 갱신해 프레임 생산을 15~30분의 1로 줄였습니다.
  • iOS와 Android가 100% 같은 화면플랫폼 종속 네이티브를 빼고 글래스 재질을 틴트와 보더와 그림자만으로 다시 만들었습니다. 웹에서 설계하고 검증한 뒤 앱으로 옮기는데, 경로 별칭을 웹과 같게 맞춰 라이브러리 아홉 개와 덱 데이터를 손대지 않고 옮겼고 RN 어댑터 셋만 바꿨습니다. 테마 색은 웹의 oklch를 브라우저 캔버스 픽셀로 뽑아 sRGB로 옮겼습니다.
  • 데이터는 직접 만들고 검수했습니다코츠코츠는 가나 표제어를 발음만으로 맞추다 한자 뜻이 뒤섞인 오염을 발견해 정제, 메타, 뜻 확정 세 패스로 다듬었고, 한자가 있으면 한자를 표기로 삼는 표기 체계를 정해 판정 1,500건 넘게 근거와 함께 기록했습니다. 티엔티엔은 HSK 3.0 1~9급 전체를 담되 7~9급은 공식 배정이 없어 빈도순 3분할이라고 정직하게 썼고, 뜻 11,092건을 전건 검수했습니다. 피터패터는 CEFR-J 하나만 등급 정본으로 삼고, 미국식과 영국식을 나란히 두고, 불가산 명사에 잘못 붙은 복수형 66장을 걷어냈습니다.
  • 발음은 통째로 받아 오프라인에서Google Cloud TTS로 네 목소리를 미리 만들어 두고, 파일 16,455개를 색인이 든 팩 하나로 받습니다. R2 앞에 워커를 두어 캐시와 CORS 헤더를 직접 붙였고, 키가 내용 해시라 1년 불변 캐시입니다. 무료 한도를 실측하니 SSML 태그가 과금에 들어가 순수 글자의 15배였고, Standard 목소리로 바꿔 전량 무료로 만들었습니다.
  • 덱을 번들에서 빼고 DB 둘을 붙였습니다"왜 덱을 캐시에 넣어야 하지"라는 질문에서 전제 오류를 잡았습니다. 번들러가 JSON을 모듈로 굽고 이미 캐시하고 있어서, 할 일은 캐시가 아니라 번들에서 빼는 것이었습니다. 진도 DB와 덱 DB를 둘로 나눠 붙여 콘텐츠 갱신이 진도를 건드리지 않게 했고, 갤럭시 S10 실기기로 여덟 단계를 전후 대조했습니다.

만들며 겪은 것

  • 에러 없는 스케줄 버그 · 2026.08.15학습 단계 인덱스가 저장에서 빠져 매 세션 0단계로 리셋됐습니다. Good을 눌러도 다음 복습이 영원히 10분이었는데 화면은 정상이고 에러도 없었습니다. 저장, 복원, 채점을 노드에서 왕복시켜 재현했고, 상태를 저장할 때는 복원했을 때 같은 미래가 나오는지를 검증하게 됐습니다.
  • 가나 표제어 오염 · 2026.08.14단어 데이터를 만들 때 가나 표제어를 발음만으로 맞춰서 사탕과 비가 한 카드에 섞이는 식의 오염이 있었습니다. 충돌 66건을 걷어내고 JMdict 태그로 대조한 뒤 표기 체계를 확정했습니다.
  • 성조 부호만 색칠하기 · 2026.08.22음절 전체를 칠하면 알록달록해서 기각했고, 결합 부호를 스팬으로 쪼개면 ǚ의 점이 사라졌습니다. 부호만 색칠하는 표준이 없다는 것을 확인한 뒤 전략 세 벌을 만들어 비교했고, 11,092장을 NFC로 정규화하면 결합 부호가 남지 않는다는 것을 실측해 합성 글자 하나만 칠하는 쪽을 택했습니다.
  • 로드만 되고 안 쓰이던 한자 폰트공용 Text 래퍼가 굵기를 Pretendard로 강제해서 HarmonyOS Sans가 로드만 되고 쓰이지 않았습니다. 反은 고딕이고 应은 명조로 보이는 혼재로 발견했습니다.
  • 한 파일을 나눠 쓰던 발음 62쌍발음 파일 키를 표기만으로 해시해서 还처럼 읽기가 둘인 글자 62쌍이 파일 하나를 공유했고 한쪽은 틀린 소리가 났습니다. 키를 표기와 병음을 합친 해시로 바꿨습니다.
  • r2.dev 공개 주소는 프로덕션 부적격스로틀 503에 CORS 헤더가 없어 앱이 실패 이유조차 읽지 못했고, 캐시 없이 요청마다 0.6초가 걸렸습니다. 버킷 앞에 워커를 두고 헤더를 직접 붙였습니다.
  • 안드로이드 블러를 포기한 것진짜 블러를 쓰려고 라이브러리에 패치까지 넣었다가 자기 포함 크래시와 자글거림 때문에 걷어냈습니다. 틴트와 보더만으로 같은 인상을 내는 쪽이 두 플랫폼을 같게 만드는 길이었습니다.

기술

React Native 0.86Expo SDK 57Reanimatedexpo-sqlite(ATTACH 2 DB)ts-fsrs 5.4 (FSRS-6)Zustand persist
데이터JMdictHSK 3.0CEFR-J 1.6qc 판정 1,500+CC BY-SA 대조
인프라 · 운영Google Cloud TTSR2 + Worker CDN발음 팩 포맷sha1(표기|병음) 키

실력을 늘리려고 만든 것

이 둘은 철학의 산물이라기보다 디딤돌입니다. 블로그에서는 만날 수 없는 문제만 골라 겪었습니다. 642만 건의 데이터, GB 단위 파일, 깨진 인코딩, 관측 도구. 그 시험은 통과했고, 그래서 작게 놓습니다.

zooso

2026.03 ~ · 우편번호·도로명·영문주소 통합 검색
  • 대용량 데이터를 다뤄 보고 싶어서 만들었습니다. 전국 건물 642만 건을 직접 적재해 검색창 하나로 1초 안에 찾습니다.
  • 기존 주소 검색 웹은 주소 말고도 쓸데없는 정보가 너무 많았고, 검색하는 것 자체가 불편했습니다. 필요한 것은 주소와 지도뿐이라는 생각으로, 검색이 아주 단순하고 빠른 주소 플랫폼을 만들었습니다.
  • 영문주소·우편번호·도로명·건물명·국제우편을 한 칸에서 찾고, 자동완성을 7단계로 개선했습니다. "테헤란로152"처럼 붙여 써도 알아듣습니다.
  • 입력 전처리 6규칙을 앞단에 두고, 접두 검색은 text_pattern_ops, 부분 검색은 pg_trgm으로 인덱스를 나눴고, 검색 전체를 PL/pgSQL 함수 하나에 넣었습니다.
  • 매일 juso.go.kr 변경분을 동기화합니다. 전남광주 통합 때 하루 154만 건 변동을 밀도 검사로 잡아 72만 건을 다시 적재했습니다.
  • 토스 앱 안의 미니앱 「주소 어디지?」를 Granite와 TDS로 출시했습니다.
  • 월간 활성 23,673(웹) GA4 · 2026-08
zooso.app App Store Google Play 앱인토스 「주소 어디지?」

어바웃톡

2026.03 ~ · 카카오톡 대화 분석
  • 반항심으로 만들었습니다. 기존 카톡 대화 분석 앱들은 개선되지 않은 채 방치돼 있었고, 그마저도 분석이 빠지거나 부족했습니다.
  • 대화 파일은 AI 활용 능력을 시험해 볼 좋은 재료였습니다. 통계 11탭과 인사이트 35종은 기기 안에서 계산하고, AI 요약을 켤 때만 서버의 LLM 프록시 하나를 거칩니다.
  • 기술은 파일 쪽에 들어갔습니다. 대규모 파일을 철저히 파싱하고 거르고 안전하게 푸는 일입니다. 1.92GB ZIP에서 텍스트 10MB만 꺼내는 스트리밍 추출, UTF-16 직접 감지, 파서 포맷 12가지를 지원합니다.
  • 모르는 시스템 문구를 Sentry로 수집해 릴리즈 13시간 만에 새 패턴과 기존 버그를 같이 잡았습니다.
  • 웹과 앱의 순수 모듈 9개를 바이트 동일하게 유지하는 동기화 스크립트를 둡니다.
  • Play 5.0 · App Store 5.0 스토어 · 2026-09-02
zooso 검색 결과
zooso 검색. "서울특별시 강남구 테헤란로"로 찾은 뒤 결과 안에서 "152"로 좁힙니다. 한글·영문·우편번호를 한 번에 복사하고, 국제우편 형식까지 펼쳐 봅니다.
zooso 도로명 페이지
zooso 도로명 페이지. 테헤란로의 우편번호 범위와 건물 190개를 한 페이지에 두고, 지도와 주변 장소로 이어집니다.
어바웃톡 홈
어바웃톡 홈. 카카오톡 대화 파일을 올리면 통계와 패턴, AI 분석을 한눈에 봅니다. TXT·ZIP·EML을 받고, 처리는 기기 안에서 합니다.
어바웃톡 대시보드
대시보드. 기간을 고르면 메시지 수, 인원, 일평균, 참여자별 비율이 나옵니다.
어바웃톡 인사이트
인사이트. 가장 조용한 멤버, 폭풍톡 챔피언, 가장 많이 불린 사람처럼 대화에서 뽑은 것을 한 줄씩 보여 줍니다.
어바웃톡 AI 분석
AI 분석. 원할 때만 켭니다. 원문 대신 통계와 대화 샘플만 보내고, 분석이 끝나면 저장하지 않습니다.

저는 이런 사람이고, 이렇게 일합니다

정하기 전에 찾아보고, 믿기 전에 재고, 뺀 것과 사고는 그대로 적습니다. 여덟 서비스에서 같았고, 이 페이지를 만들 때도 같았습니다. 못 하는 것과 아직 안 해 본 것도 빼지 않고 적었습니다.

성향

사소한 불편을 그냥 넘기지 못합니다.

어색한 번역이 블로그가 됐고, 검색해도 나오지 않는 노래방 번호가 첫 서비스가 됐습니다. 메뉴를 찾으려고 검색해야 하는 주식 앱, 예매처 네 곳에 흩어진 공연, 방치된 카톡 분석 앱도 같은 출발이었습니다. 여덟 서비스 전부 그렇게 시작했습니다.

원인을 찾을 때까지 놓지 않습니다.

노래방 DB가 한 시간 멈췄을 때는 762초짜리 쿼리를 찾아 30~50초로 줄였습니다. 목돈 비용은 추정하지 않고 항목 여덟 개를 계량하는 스크립트를 짜서, 가장 큰 항목이 요청이 아니라 R2 쓰기라는 것을 확인한 뒤 월 23달러를 7.76달러로 내렸습니다. 네트워크 엔지니어로 일할 때도 새 현장의 AP가 되풀이해 꺼지는 원인을 전원 공급에서 찾아 복구했습니다.

설명이 충분하면 합칩니다. 탭을 나누는 것은 생각을 포기하는 일이라고 여깁니다.

코츠코츠는 학습과 단어장, 탭이 둘뿐입니다. 목돈은 하단 탭을 사용자가 고르게 하고 나머지는 서랍에 넣어 탭 목적지 8개로 압축했습니다. 노래방 가자!는 다섯 탭 안에서 곡을 펼치고, 상세 창은 파고들 때만 겹으로 엽니다.

만든 뒤가 진짜 일이라고 생각합니다.

만드는 것보다 운영하고 지켜 내고 최적화하는 일이 더 어렵고 더 값지다고 봅니다. 이 페이지에는 출시 뒤 겪은 사고와 사건 29건을 날짜와 함께 적었습니다. 사고를 숨기지 않는 이유는 그것을 고치는 일이 제 일이기 때문입니다.

새것을 미루지 않습니다. 써 보고, 재고, 남길 것만 남깁니다.

앱을 낼 때마다 Expo를 54, 55, 57로 올렸고, 단어장의 간격 반복은 그때 최신이던 FSRS-6로 짰습니다. 뒤이어 나온 FSRS-7은 옮길지 살핀 뒤 이득이 없어 두었습니다. 토스 앱 안의 미니앱 플랫폼에는 zooso를 「주소 어디지?」로 올렸습니다. 새것이라서 쓰는 것이 아니라 써 본 뒤에 남깁니다. 앱을 내던 올여름에 시험 둘을 더 치른 것도 같은 이유입니다.

혼자 여덟 서비스를 만들고 운영하는 방법은 AI 코딩 도구입니다.

Claude Code를 씁니다. 프로젝트마다 규칙 문서와 에이전트를 두고, 세션을 넘어 남는 메모리 130여 건으로 맥락을 설계합니다. 출력은 실측과 관례 조사, 승인 절차로 검증하고 근거 없는 것은 돌려보냅니다. 자동화된 검증과 두 번째 사람의 눈이 없다는 한계도 압니다.

혼자 파고들다 보면 말이 늦습니다.

문제를 붙들면 먼저 혼자 파고드는 편이라 중간에 알리고 묻는 일이 서툽니다. 여덟 서비스를 혼자 정하고 혼자 검증한 이유이기도 합니다. 리뷰어가 있었으면 ISR 오진은 배포 전에 잡혔을 것입니다. 아직 고치지 못한 약점이라 그대로 적습니다.

일하는 방식

정하기 전에 남들이 어떻게 하는지 찾아봅니다. 버린 안도 적어 둡니다.

티엔티엔의 성조 부호 색칠은 표준이 없다는 것까지 확인한 뒤에 절충안을 정했습니다. MyCon 프로필은 Letterboxd 페이지를 실제로 열어 보고 따라 하지 말 것 13가지를 적었습니다. 코츠코츠 홈 상단은 시안 넷을 만들어 전부 기각하고 "기능 없는 상단 요소는 전부 탈락"이라고 적어 두었습니다. 이 페이지도 개인 사이트 73곳을 찍어 비교한 뒤에 만들었습니다.

숫자를 읽기 전에 재는 도구부터 의심합니다.

목돈 트래픽을 조사할 때 처음 믿었던 것 중 틀린 것 24가지를 표로 만들어 문서 첫머리에 두었습니다. 시세를 쓰는 프로그램이 자기 요청 수를 직접 세게 해서 그것을 정답지로 삼고, 클라우드 분석이 맞게 세는지부터 채점했습니다. 노래방 웹의 캐시도 설정값이 아니라 응답 헤더로 판정했습니다. 이 페이지의 숫자마다 출처와 날짜를 옆에 적은 이유입니다.

화면의 말은 모르는 사람 쪽에서 고칩니다.

목돈은 플랫폼마다 다른 용어를 하나로 통일하고, 숫자마다 뜻을 여는 툴팁과 판정 한 마디를 붙였습니다. 체크리스트는 점수 대신 통과와 주의와 탈락으로 말합니다. 전문어와 비유는 아는 사람만 편한 것이라, 설명이 필요 없고 묻지 않아도 되는 화면을 기준으로 삼습니다.

근거목돈

한 번에 다 바꾸지 않습니다. 되돌리는 방법부터 만들고 하나씩 확인합니다.

목돈 시세를 새 구조로 옮길 때 성공 기준에 "실패해도 최악은 기존 구조로 복귀"를 넣었습니다. 앱은 스토어 롤백에 며칠이 걸려 코드 상수로는 끌 수 없어서, 서버 쪽 스위치 하나로 웹과 앱이 함께 옛 경로로 돌아가게 했습니다. 웹에서 먼저 검증하고 앱은 마지막에 옮겼습니다. 노래방 아티스트 발음 표기는 다섯 개씩 확인하고 다음으로 넘어갑니다.

에러 없이 틀리는 경우를 먼저 찾습니다.

목돈 시세 구조를 바꿀 때 잡은 오탐 7건은 전부 에러 대신 빈 값이 조용히 흘러가는 형태였습니다. 순위가 같을 때 갱신마다 순서가 바뀌던 관련 곡 목록, 예외를 삼키던 조회수 기록도 그랬습니다. 에러가 없다고 정상은 아니라서, 구조를 바꿀 때는 값이 빠질 수 있는 자리를 먼저 적어 두고 하나씩 확인합니다.

빼기로 한 것은 그 값을 같이 적습니다.

단어장에서 계정과 알림, 예문을 빼기로 한 문서에는 "이 철학이 치르는 값"이라는 절이 있습니다. 알림이 없어서 앱을 잊는 사람이 생기고, 계정이 없어서 기기를 바꾸면 손으로 옮겨야 합니다. 이유 칸을 먼저 채울 수 있을 때만 뺍니다. 이 페이지의 "아직 안 해 본 것"도 같은 이유로 있습니다.

웹으로 먼저 만들고 앱으로 옮깁니다.

웹은 스크롤에 맞고 앱은 아닙니다. 목돈 웹은 세로로 길게 두고 앱은 서브탭으로 다시 짰습니다. 옮길 때 복사하지 않고 크기와 동작을 앱 관례로 다시 정합니다. 코츠코츠는 웹을 공개하지 않고 디자인을 검증하는 작업대로만 썼습니다.

사고는 기록으로 남기고, 같은 자리를 다시 밟지 않게 적어 둡니다.

목돈 뉴스의 시각 파싱을 웹에서만 고치고 방송 쪽에 반영하지 않아 기사 50건이 버려진 뒤로, "날짜와 점수 로직은 두 파일을 같이 열 것"이라고 적어 두었습니다. 브리핑 생성기는 사본이 세 곳이라 고칠 때마다 세 파일의 해시를 맞춥니다. MyCon 크롤러는 예매처 원본을 그대로 두고, 교정할 때는 근거를 적은 뒤에만 덮어씁니다. 이 페이지의 사고 29건도 그 기록에서 왔습니다.

아직 안 해 본 것

  • 팀 개발과 코드 리뷰가장 큰 공백입니다. 리뷰를 주고받은 코드가 아직 없습니다.
  • 남이 짠 코드 이어받기여덟 서비스 전부 처음부터 제가 짰습니다. 모르는 코드베이스에 들어가 고쳐 본 것은 블로그 스킨이 전부입니다.
  • 디자이너의 시안 받아 만들기기획과 디자인을 제가 하고 제가 만들었습니다. 남의 시안을 그대로 옮겨 본 적이 없습니다.
  • 시세를 밀어 주는 서버실시간 채팅은 MyCon과 목돈에 Supabase Realtime으로 만들었고, 웹소켓 클라이언트는 업비트·바이낸스로 써 봤습니다. 시세를 밀어 주는 서버를 직접 만든 적은 없습니다.
  • 테스트 코드 · 스토리북부족합니다. 타입도 테스트도 못 잡은 오탐 7건이 그 자각이었고, 필드 집합 대조와 빌드 diff 같은 검증 스크립트를 대신 만들었습니다. 컴포넌트를 따로 문서화하는 도구는 안 써 봤습니다.
  • 테스트가 배포를 막는 파이프라인GitHub Actions로 빌드 확인과 크롤러 실행은 걸어 봤습니다. 테스트가 실패하면 배포가 멈추는 구조는 없습니다.
  • React Query · Recoil · Jotai · Redux상태는 Zustand만 썼습니다. 상태 종류별로 도구를 나누는 방식은 안 해 봤습니다.
  • Emotion · styled-components · vanilla-extractTailwind와 StyleSheet만 썼습니다. 대신 디자인 토큰만 쓰고 하드코딩을 금지하는 규칙을 세워 운영했습니다.
  • React 밖의 프레임워크Vue·Svelte·Angular는 안 써 봤습니다. 블로그의 jQuery 말고는 전부 React였습니다.
  • 모노레포 · 번들러 튜닝안 해 봤습니다. 지금까지는 프로젝트마다 레포 하나를 폴더로 나누는 것으로 충분했습니다.
  • API 서버 프레임워크Express나 NestJS로 서버를 짜 본 적이 없습니다. 백엔드는 Supabase의 DB 함수와 Edge Functions, Cloudflare Workers로 해결했습니다.
  • GraphQL안 써 봤습니다. PostgREST와 RPC 함수로 충분했습니다.
  • 네이티브 모듈Swift나 Kotlin으로 모듈을 쓴 적이 없습니다. Expo가 제공하는 범위 안에서만 만들었습니다.
  • 다국어앱은 전부 한국어 하나입니다. 다국어 프레임워크는 안 써 봤습니다.
  • 큰 트래픽동시 접속 수천 규모를 겪어 본 적이 없습니다. 가장 컸던 것은 노래방 웹의 일 활성 4천입니다.
  • 웹 결제PG 연동은 안 해 봤습니다. 만들어 본 결제는 노래방 앱의 인앱 구독뿐입니다.

앞으로

  • 팀에서 일하는 법부터 배우겠습니다코드 리뷰를 주고받고, 생각을 중간에 소리 내어 말하는 것입니다. 혼자서는 배울 수 없는 것이라 첫 번째로 둡니다.
  • 실시간을 밀어 주는 쪽에서도 풀고 싶습니다목돈의 시세는 스냅샷과 폴링으로 받습니다. 서버가 밀어 주는 구조는 혼자서는 겪어 볼 일이 없어서, 팀에서 배우고 싶습니다.
  • 검증을 테스트로 옮기겠습니다지금은 실측 스크립트가 테스트 노릇을 합니다. 사람이 돌리지 않아도 잡히게 만들고 싶습니다.

같은 문제를 큰 규모에서, 이번에는 팀에서 풀고 싶습니다

증권 앱을 쓰면서 가장 불편했던 것은 메뉴를 찾으려고 검색해야 한다는 점이었습니다. 주식은 쉽고 직관적이어야 한다는 생각으로 목돈을 만들었고, 여덟 서비스를 혼자 정하고 혼자 검증했습니다. 증권이 어렵다는 문제를 좋은 UX로 푸는 팀에서, 리뷰받고 배우면서 같은 문제를 훨씬 많은 사람이 쓰는 화면에서 풀어 보고 싶습니다.

이메일jjangjjangeyo@gmail.com
블로그j-pop-playlist.tistory.com 크몽가사 번역 77건 · 5.0 이 페이지PDF로 받기

이 페이지의 숫자는 전부 기록에서 왔고, 출처와 날짜를 옆에 적었습니다. DB 실측은 각 프로젝트 데이터베이스 직접 쿼리, GA4는 Data API, 스토어는 콘솔과 공개 페이지입니다. 마지막 갱신 2026-09-03. © 2026 김동현