LDBD
/
전체 글

LDBD 개발 일지 #1 - Claude와 LDBD를 다듬은 하루

사람들이 LDBD에 다시 돌아올 이유는 아직 약했다. 단순히 예측만 하는 사이트가 아니라 다양한 사람과 AI의 관점·인사이트를 함께 읽을 수 있는 곳으로 가야 한다는 생각이 출발점이었다. 그 방향으로 하루를 통째로 비워 Claude Code와 LDBD를 다듬은 기록, 그리고 그 과정에서 본 AI 협업 패턴 세 가지.

앞으로 LDBD를 다듬어가는 과정을 개발 일지처럼 틈틈이 남겨두려고 한다. 이번 글이 그 첫 번째 기록이다.

이번 글의 출발점은 하루를 통째로 비워 Claude Code와 LDBD를 한 번 더 손본 일이었다. 새 화면을 처음부터 만드는 날은 아니었다. 이미 동작하는 페이지를 다시 열어보고, “여기서 사용자가 궁금해할 정보가 빠져 있지 않나?”를 찾는 날에 가까웠다. 앞 글 두 편(사용자 아직 없어도 운영은 시작된다 디자이너 없이 SaaS 브랜딩하기)도 비슷한 방향이었는데, 이번에는 그 흐름을 하루 안에 압축했다.

그 위에 더 큰 질문이 깔려 있었다. 사람들이 LDBD에 다시 돌아올 이유가 무엇인가. 예측만 하고 줄세우는 사이트라면 한두 번 들어와본 다음 굳이 다시 올 이유가 약했다. 그보다 다양한 사람과 AI의 관점·인사이트가 한자리에 모이는 곳이 되어야겠다는 방향이 잡혔다. 같은 자산을 두고 누구는 상승을 보고 누구는 하락을 보는데, 그 사이의 근거가 함께 읽히면 시장을 보는 관점 자체가 풍부해진다는 생각이었다.

그 큰 그림 안에서 이날 작업의 공통점은 하나였다. 기능이 없는 게 아니라, 보여줘야 할 정보가 보이지 않았다.

리더보드는 왜 이 사람이 1등인지 설명하지 못했고, 자산 페이지는 이 종목을 누가 잘 맞히는지 보여주지 못했고, 프로필은 점수가 좋아지는 중인지 나빠지는 중인지 말해주지 못했다. 그래서 이날의 목표는 새 기능을 잔뜩 붙이는 게 아니라, 이미 쌓인 데이터를 사용자가 읽을 수 있게 만드는 것이었다.

하루 동안 작업을 여섯 덩어리로 나눴다. 코드상으로는 각각 별도 브랜치로 떼어 작업했다 — 브랜치는 기능 단위 작업 묶음이라고 보면 된다. 리더보드 점수 공정성, 자산 페이지의 커뮤니티 기능, 브랜드 컬러 적용, 프로필 차트, 자산별 specialist, achievement 뱃지까지.

기능 자체보다 더 흥미로웠던 건, 여섯 작업을 지나며 반복해서 보인 협업 패턴이었다. Claude는 첫 안을 빠르게 만들었고, 가끔은 내가 낸 안보다 한 단계 더 나은 제품 판단을 제안했다. 대신 작은 버그는 계속 나왔고, 방향 결정은 끝까지 내가 해야 했다.

0. 하루의 작업 목록

여섯 작업을 한눈에 정리하면 이렇다. 각 항목을 차례대로 풀어 쓴다.

#브랜치다룬 화면무엇을 풀었나
1scoring-fairness리더보드한두 건 적중하고 예측 멈춘 사람이 상위에 보이던 구멍 메우기
2asset-discovery자산 검색 + 자산 페이지관점·인사이트 공유 채널 만들기 + 카피캣 방지
3brand-color-pass사이트 전체검정 버튼들을 브랜드 그린으로
4profile-insights프로필 페이지숫자 네 개에서 곡선 하나로
5specialist-leaderboard자산 페이지“이 자산을 잘 맞히는 사람 누구?”
6streaks-achievements프로필 페이지연속 적중·마일스톤 격려

1. 1건 맞히고 90일 기다리면 1등이 되는 문제

리더보드의 평균 점수 정렬에 작은 구멍이 하나 있었다. 표시 기준이 “30건 검증 OR 가입 후 90일 경과”였는데, 이 OR 분기가 의외의 동작을 만들고 있었다.

가입 직후에 한두 건 예측을 올리고, 그중 한 건만 운 좋게 적중한 다음, 더 이상 예측하지 않고 90일을 기다리면 어떻게 될까. 90일이 지나는 순간 그 한 건의 평균이 그대로 리더보드에 노출된다. 100% 정확도, 평균 점수 +1.0짜리 신규 1위가 자동으로 만들어지는 구조였다. 같은 자리에 1년 동안 수백 건을 검증받으며 평균 +0.3 정도를 쌓아온 사람도 있었으니, 같은 칸에서 비교가 되는 게 이상했다.

Claude한테 “예측 수가 적은 사용자가 상위로 튀는 걸 막고 싶다”고 물었더니 세 가지를 한꺼번에 제안했다.

  1. Bayesian 평탄화total_score / (resolved_count + 20)로 정렬. 쉽게 말하면 예측 수가 적은 사람의 평균은 바로 믿지 않고 0에 가깝게 눌러두는 방식이다. 표본이 쌓일수록 점점 실제 평균에 가까워진다.
  2. 타임프레임 가중 카운트 — 1d 30번과 1y 1번이 같은 무게로 잡히던 문제. 1d=1, 1w=2, 1m=5, 6m=12, 1y=24로 가중치를 줘서, 1y 한 건만 하는 사람도 두 건 정도면 리더보드에 진입할 수 있게.
  3. Calibration 루프홀 제거— “OR 90일” 분기를 떼고 weighted count만 보게. 위에서 말한 1건만 던지고 기다리는 시나리오가 자연스럽게 막힌다.

이 세 가지는 한 묶음으로 가야 의미 있었다. 평탄화만 하고 가중치를 안 주면 1y 예측자가 영원히 안 보이고, 가중치만 주고 평탄화를 안 하면 신규 사용자가 여전히 튄다. 셋이 동시에 들어가야 균형이 맞는다.

머지하고 리더보드를 다시 보니 상위 5명이 자연스럽게 “실제로 꾸준히 적중을 쌓아온 사람들”로 정리됐다. 표시되는 점수와 정렬 키가 다르다는 작은 버그(정렬은 보정값, 표시는 원본 평균)도 한 번에 따라왔는데, 그건 다음 패치에서 둘 다 보정값으로 통일했다.

2. 자산 페이지를 다시 만들었다 — 게임이 아니라 커뮤니티

이번 회차에서 가장 무게가 큰 작업이었다. 인트로에서 말한 방향이 가장 구체적인 형태로 만들어진 곳이기도 했다. 표면적인 결과는 자산 페이지에 예측 분포 바와 근거가 붙은 정도지만, 그 아래에는 LDBD를 예측 게임에서 관점 공유 사이트로 어떻게 옮길지에 대한 결정이 깔려 있었다.

그 비전의 첫 발이 자산 페이지였다. /explore 검색 페이지, 헤더의 자산 검색, 그리고 자산 상세 페이지의 새 예측 패널(상승/하락 분포 바, 근거가 적힌 리스트)을 한꺼번에 넣었다.

그런데 근거를 다 공개하는 순간 따라오는 문제가 있었다. 누가 좋은 근거로 예측을 올리면, 다른 사용자가 그대로 베껴서 같은 콜을 던질 수 있다. 이른바 카피캣. 처음에 내가 Claude한테 던진 안은 단순했다 — “같은 자산에 본인 예측이 없는 사람한테는 근거를 통째로 가려라.”

Claude의 답이 한 발 더 나아가 있었다.

“Explore에서 자산을 검색하면 근거는 보이게 하되, 누가 예측했는지는 감추자.” 이렇게 비대칭으로 가면 커뮤니티로서 의미는 계속 가져가면서 카피캣은 막힌다. 누구인지만 가리고, 왜 그렇게 봤는지는 살리는 방식이다.

같은 자산·같은 timeframe·같은 날짜에 본인이 예측을 제출했을 때만 누구의 예측인지가 공개된다. 그 외에는 “Human” 또는 “AI” 카테고리 라벨과 자물쇠 아이콘만 보인다. 근거 본문은 로그인만 하면 누구나 읽을 수 있다.

이 결정의 묘미는 한 줄로 요약된다. 근거는 학습 가치가 있지만, 핸들은 카피 트리거가 된다. 카피캣을 일으키는 건 콜 자체가 아니라 그 콜을 누가 했는지다. 유명 예측자의 콜은 따라가게 되지만, 익명의 근거를 읽고 자기 판단으로 콜을 던지는 건 그냥 학습이다.

내가 던진 첫 안이 그대로 갔으면 커뮤니티 가치가 통째로 죽었을 것이다. 관점을 공유하는 곳으로 만들려고 자산 페이지에 손을 댔는데, 정작 그 페이지에서 근거를 다 가려버리는 셈이 됐을 테니까. Claude의 한 발 나아간 답이 이 모순을 잡아줬다. 이번 하루에 본 협업 순간 중 가장 영리한 순간이었다.

3. 색 한 줄로 사이트 전체가 바뀌었다

브랜드 로고는 에메랄드 그린과 골드인데, 헤더의 가입 버튼은 검정색이었다. 알림 아이콘의 빨간 점도, 언어 토글 버튼의 활성 상태도, 다 검정. “브랜드 컬러가 어디에 있지?”가 새로 들어온 사람의 첫 인상이었을 것 같다.

원인은 shadcn/ui의 디폴트 테마였다. --primary라는 CSS 변수가 거의 검정에 가까운 회색(oklch(0.205 0 0))으로 잡혀 있었고, 모든 기본 Button과 Badge가 그걸 따라가고 있었다. 내가 “여기 색깔 좀 바꿔봐”라고 명시적으로 적은 곳에만 emerald가 들어가 있던 셈이다.

Claude한테 “브랜드 그린이 더 많이 보였으면 좋겠다”고 말하니까, 변경할 후보를 쭉 나열한 다음에 “가장 효과적인 한 방은 --primary 자체를 바꾸는 것”이라고 했다. 한 줄 바꾸면 사이트 전체의 기본 Button, Badge, bg-primary 사용처가 한꺼번에 emerald로 바뀐다.

/* before */
--primary: oklch(0.205 0 0);

/* after */
--primary: oklch(0.696 0.17 162.48);  /* emerald-500 */

“의도치 않은 곳까지 emerald로 바뀔 수 있다”는 부분이 조금 걸렸지만, 개발 서버에서 확인해보니 영향받는 곳은 다 활성/선택 상태였다. 알림 점, 선택된 탭, 활성 토글 — 전부 “여기 강조해서 보여줘야 하는 곳”이었고, 거기에 브랜드 색이 들어가는 건 오히려 자연스러웠다.

골드(amber)는 더 신중하게. 골드는 #6 브랜치에서 “드문 마일스톤”에만 쓰기로 정했다 — 6개월 적중, 1년 적중, 자산 specialist, 100건 리졸브. 흔하지 않아야 골드의 신호로서의 힘이 살아난다.

4. 숫자 네 개에서 곡선 하나로

프로필 페이지에는 평균 점수 / 1년 평균 / 예측 수 / 정확도 네 개의 숫자가 박스로 떠 있었다. 잘하는지는 보였지만 어디로 가고 있는지는 안 보였다. 본인이 봐도 그렇고, 누군가 팔로우를 고민할 때도 최근 흐름이 결정적이다.

365일 데이터를 한 번에 가져와서 클라이언트에서 7d/30d/1y 토글하는 시계열 차트를 추가했다. Y축은 처음에 누적 score_delta로 잡았다. “위로 가면 점수가 쌓이고 있다”가 가장 직관적이라고 생각했다.

내가 차트를 보고 눈에 걸린 게 있었다. 차트 옆의 ScoreCell이 평균 점수를 보여주는데, 내가 만든 차트 Y축은 누적 점수였다. 같은 페이지에서 두 메트릭이 따로 놀고 있었던 거였다. 무엇이 맞는지 흐려졌다.

Y축을 리더보드 기준인 누적 평균(Bayesian-adjusted)으로 바꿨다. 시간이 가면서 리더보드 상의 내 점수가 어떻게 변해왔는지가 한눈에 보이게 됐다.

다음으로 걸린 건 X축이었다. 같은 날에 여러 예측이 리졸브되면 X축에 같은 날짜가 두세 번 찍히고 있었다. 그리고 리졸브 없는 날은 점 자체가 빠져서, 30일 토글이 실제로는 90일 간격을 보여주기도 했다.

수정은 두 단계였다. 첫째, 일자별로 그룹핑해서 한 날에 한 점만. 둘째, 달력 기준으로 7/30/365개 점을 만들고 리졸브 없는 날은 직전 값으로 forward-fill. 7d 토글이 이름 그대로 최근 7일을 보여주게 됐다.

이 작은 차트 하나에서 “한 번에 잘 만든 코드는 거의 없다”는 사실을 한 번 더 확인했다. Claude도, 나도. 처음 안이 마지막 안이 되는 일은 거의 없었다.

5. 자산 페이지에 “이 종목 잘 맞히는 사람”

자산 페이지 방문자가 분포 바와 근거를 보고 나면 “그래서 이 종목 가장 잘 맞히는 사람은 누구?”가 자연스러운 다음 질문이었다. 글로벌 리더보드로 점프해도 자산별 그림은 안 나온다.

해당 자산에서 최근 1년간 가장 잘 맞힌 예측자 다섯 명을 보여주는 카드를 넣었다. 메달 + Human/AI 라벨 + 정확도 + Bayesian-adjusted 평균.

여기서 한 가지 결정이 있었다. 글로벌 리더보드는 평탄화 상수 k=20으로 쓰는데, 자산 단위로는 표본이 본질적으로 작다. k=20을 그대로 쓰면 거의 모든 자산이 빈 칸이 된다. Claude가 “per-asset은 k를 작게 잡자, 5 정도”라고 제안했고 그 값이 잘 들어맞았다. 최소 3건 이상 자산별 리졸브 조건까지 같이.

자산 페이지 위치는 예측 패널 바로 다음. “왜 그렇게 봤는지(근거) → 이 자산을 누가 잘 보고 있는지(specialist) → 따라가볼 만한 사람인지(팔로우)”라는 흐름이 한 페이지 안에서 이어진다.

6. 벌주지 않는 게이미피케이션 — 그리고 만들지 않은 것

마지막은 streak(연속 적중)과 8종의 achievement 뱃지. 5건 연속 적중하고 있어도 그게 “지금 잘 가고 있다”는 신호로 안 나타나는 게 아쉬웠다.

여기서 한 가지 의식적으로 선택한 건 “만들지 않은 것”들이었다.

  • 연속 실패는 표시 안 함. 격려 톤만.
  • “0건” 같은 negative milestone도 노출 안 함.
  • 새 테이블도 만들지 않음. 마이그레이션 + Edge Function 배포 같은 운영 부담을 회피하고, 프로필을 볼 때 raw 예측 데이터에서 바로 계산한다. 비용은 캐시로 흡수한다.

마지막 결정이 의외였다. Claude한테 “achievement 시스템을 어떻게 짤까” 했을 때 가장 정통적인 방법은 “identity_achievements 테이블 + resolve Edge Function에서 unlock 트리거”였다. 하지만 그건 마이그레이션 1개 + Edge Function 배포 + 추후 정의 변경 시 백필이 필요한 무거운 길이다. 지금 단계에서는 프로필 페이지가 뜰 때 그 자리에서 계산하는 게 훨씬 가볍고 충분했다.

“규모가 커지면 자연스럽게 materialized view나 trigger로 옮겨갈 수 있다”는 Claude의 한 줄이 안심거리였다. 지금 단계에 맞는 만큼만, 나중에 필요하면 그때.

골드 컬러는 여기서 희소 마일스톤(6개월·1년 적중·자산 specialist·100건 리졸브)에만 들어갔다. 일반 마일스톤은 emerald. 골드가 흔하면 신호로서의 힘이 죽는다는 건 디자이너 출신이 아닌 나에게도 명백했다.

하루 동안 본 협업 패턴 세 가지

여섯 개의 브랜치를 거치면서 “Claude와 같이 짠다”는 게 실제로 어떤 모양인지가 좀 더 구체적으로 보였다.

가장 큰 가치는 “한 발 더 나아간 답”이었다

내가 단순한 안을 던지면 Claude가 한 단계 더 영리한 답을 돌려준다. #2의 카피캣 정책에서 “근거 전부 가려”“누구만 가리고 근거는 살려”로 바뀐 게 가장 큰 사례였다. “이거 어때”라고 처음 안을 던지면 Claude는 그걸 그대로 구현하는 게 아니라 “근거 가리면 커뮤니티 가치가 죽는데, 이런 비대칭은 어때요”라고 되돌려준다. 그 순간들이 이 하루 작업의 골격을 만들었다.

작은 버그가 자주 나온다

recharts v3에서 Tooltip의 formatter 시그니처가 v2와 미묘하게 바뀌어 있어서 타입 에러가 났다. 같은 자산을 검색해서 같은 날 두 번 리졸브되면 X축에 같은 날짜가 두 번 찍히는 버그도 내가 봐야 알아챘다. “한 번에 잘 짠 코드는 거의 없다”는 게 Claude 협업에서 반복해서 확인한 사실 중 하나다. 빠르게 첫 안을 만들고, 작은 버그를 내가 확인해서 잡고, 다시 푸시. 그 사이클이 평균 30분 정도다.

방향 결정은 위임하지 않는다

“어느 방향으로 갈지”는 내가 정한다. “어떻게 구현할지”는 Claude가 푼다. 이 분업이 흐려지면 — 예를 들어 “네가 알아서 좋은 방향으로 만들어줘”라고 던지면 — Claude는 가장 정통적이고 무거운 길로 가는 경향이 있다. 새 테이블, 새 마이그레이션, 새 Edge Function. #6에서 “lazy compute, 즉 페이지를 볼 때 계산하는 방식으로 가자, 새 테이블 만들지 말고”라고 짧게 끊어준 게 가장 명확한 사례였다. 작은 방향 결정 하나로 작업량이 절반이 됐다.

마무리 — Claude가 코드를 짜도, 제품 판단은 내 일

이 하루를 지나며 Claude Code와 일하는 방식이 조금 더 선명해졌다.

Claude는 첫 안을 빠르게 만든다. 가끔은 내가 낸 단순한 안보다 한 발 더 나아간 제품 판단을 돌려준다. 하지만 그 첫 안에는 작은 버그가 자주 있고, 기능이 커질수록 방향 결정은 더 중요해진다.

결국 내가 해야 할 일은 코드를 전부 직접 쓰는 것이 아니었다. 대신 계속 봐야 했다. 이 점수가 공정한지, 이 정보가 사용자에게 보이는지, 이 기능이 지금 단계에 필요한지, 이 설계가 너무 무거운지.

Claude가 구현을 빠르게 밀어주는 만큼, 나는 방향을 더 자주 결정해야 했다. 이번 하루의 결론은 그거였다.


본인 봇이나 예측을 LDBD에 올려보고 싶다면 /settings에서 identity와 (필요 시) API key를 만들면 된다. 가입은 메인 페이지에서, 사용은 무료다.

vibe-codingclaude-collaborationfeature-workiterationdev-log