배우자를 초대하면, 먼저 넣어둔 계좌가 사라진 것처럼 보일 뻔했습니다 🔍

돈독(제가 혼자 만드는 자산관리 앱, https://don-doc.com )을 기획할 때부터 정해둔 시나리오가 하나 있습니다. 처음엔 혼자 가입해서 계좌를 등록하고 엑셀을 올려봅니다. 마찰 없이 시작하는 게 먼저입니다. 그러다 쓸 만하다 싶으면 배우자를 초대합니다. 혼자 쓰던 게 부부가 함께 보는 화면으로 바뀌는 순간, 이게 이 앱의 진짜 전환점이라고 6월에 문서에 적어뒀습니다.
문제는 지난주 코드를 점검하던 AI 에이전트가 이 시나리오를 실제 코드로 한 줄씩 따라가 보다가 생겼습니다.
초대 수락 버튼, 코드는 뭘 하고 있었나
돈독 코드를 매일 점검하는 에이전트가 있습니다. 이번 라운드엔 코드가 한동안 멈춰 있어서(8월 26일 이후 새 커밋이 없었습니다), 그동안 미뤄뒀던 걸 하나 짚어보기로 했습니다. 기획 문서에 적어둔 “부부 전환 시나리오”와 실제 초대 코드를 나란히 대조해본 겁니다.
초대 코드를 수락하는 라우트(app/api/family/join/route.ts:69-77)를 열어보니, 하는 일이 딱 한 줄이었습니다. user.update({ familyId: invite.familyId }) — 내 사용자 레코드가 가리키는 가족 ID를 새 값으로 바꾸는 게 전부였습니다.

한 줄이 왜 문제였나 — 소유권은 사람이 아니라 가족에 붙어 있었다
바로 여기서 걸렸습니다. 계좌(prisma/schema.prisma:178-186)와 거래(:215-223) 테이블을 다시 열어보니, 둘 다 소유권을 붙잡고 있는 값이 userId가 아니라 familyId였습니다. 스키마 설계 자체가 “이 계좌는 이 가족 소속”이라는 전제로 되어 있던 겁니다.
그러니까 이런 일이 벌어집니다. 솔로로 가입하면 자동으로 내 이름의 가족 그룹을 하나 배정받고, 그 밑에 계좌를 등록하고 엑셀도 올립니다. 그다음 배우자 초대 코드를 눌러 수락하면, 방금 본 그 한 줄이 실행됩니다. 내 userId가 가리키는 familyId만 새 가족으로 바뀝니다. 넣어둔 계좌와 거래는 여전히 옛날 familyId에 달려 있고요. 화면은 새 가족 기준으로 데이터를 불러오니, 방금까지 있던 계좌가 안 보이게 됩니다.
사라진 게 아니라, 안 보이는 곳에 그대로 있는 겁니다. 쓰는 사람 입장에서는 그 둘이 구분이 안 갑니다.
초대 수락 화면(invite/[code]/page.tsx)도 확인해봤습니다. “합류하면 이전 데이터가 어떻게 되는지” 같은 안내는 한 줄도 없었습니다. 초대 코드 입력하고 확인 누르면 그걸로 끝입니다.
하필 여기서 걸렸다는 게 더 아픈 이유
이게 아무 화면에서나 생긴 버그였으면 그냥 버그입니다. 그런데 이 경로는 이 프로젝트가 처음부터 세운 성장 시나리오의 2단계, 그 자체입니다. 혼자 써보고, 배우자를 초대해서 진짜로 전환되는 순간. 사용자가 “이 앱 쓸 만하네” 하고 배우자를 부르는 바로 그때, 자기가 넣어둔 데이터가 없어진 것처럼 보이는 겁니다. 신뢰를 얻어야 할 순간에 정반대 경험을 주는 셈이었습니다.
아직 못 고친 이유 — 결정할 게 먼저 있다
발견은 됐지만 코드는 아직 그대로입니다. 고치기 전에 정책부터 정해야 하기 때문입니다. 지금 열려 있는 선택지는 세 가지입니다.
먼저, 초대를 수락하는 순간 예전 계좌·거래를 자동으로 새 가족으로 옮기는 방법이 있습니다. 다음으로, 자동으로 옮기지 않고 “합류하면 데이터가 이렇게 이동합니다”라고 미리 경고한 뒤 사용자가 선택하게 하는 방법이 있습니다. 마지막으로, 이미 계좌를 등록한 사용자는 초대를 아예 못 받게 막고 신규 계정만 초대 대상으로 제한하는 방법도 있습니다.
셋 다 일리가 있어서 아직 하나로 못 좁혔습니다. 코드를 먼저 고치고 정책은 나중에 정하지 않기로 한 건, 잘못 짠 이관 로직이 실제 계좌·거래 데이터를 건드리는 작업이라 되돌리기가 번거롭기 때문입니다. 결정이 먼저고, 코드는 그다음입니다.
따라 하기
화면에 보이는 버튼 하나가 실제로 뭘 하는지, 특히 “이 흐름 중요하다”고 문서에 적어둔 게 있다면 한 번씩 코드로 직접 따라가 보시길 권합니다.
1. 버튼이 누르는 API가 실제로 뭘 바꾸는지 찾기
grep -n "familyId\|userId" <초대-수락-라우트-경로>
- 화면 뒤에서 실제로 실행되는 코드가 몇 줄인지, 그 줄이 정확히 뭘 바꾸는지부터 확인합니다. 그럴듯한 이름의 함수 안에 생각보다 적은 일만 들어 있는 경우가 많습니다.
2. 그 값이 다른 테이블의 소유권 기준과 맞는지 대조하기
grep -n "model Account" -A 15 <스키마-파일-경로>
grep -n "model Transaction" -A 15 <스키마-파일-경로>
- 방금 바뀐 값(
familyId)이 다른 데이터의 소유권을 결정하는 값과 같은지 봅니다. 한쪽은userId기준, 다른 쪽은familyId기준처럼 서로 다르면 그 틈에서 데이터가 붕 뜹니다.
✅ 이렇게 되면 성공 — “버튼 하나가 바꾸는 값”과 “그 값을 소유권 기준으로 쓰는 다른 테이블”이 한눈에 대조됩니다.
⚠️ 막히면 — 스키마 파일이 커서 모델을 못 찾겠다면 grep -n "^model "로 전체 모델 목록부터 뽑고, 소유권 관련 필드(userId·familyId·ownerId 등)만 골라 다시 검색하세요.
3. AI에게 “문서와 코드가 맞는지” 대조시키기
📋 프롬프트 — 복사해서 AI에 붙여넣으세요
아래는 이 기능이 원래 의도한 시나리오다.
<문서에 적어둔 시나리오 요약>
이 기능을 실제로 구현한 코드는 아래와 같다.
<관련 라우트 파일 경로 + 줄 범위>
시나리오의 각 단계가 코드에서 실제로 처리되는지 하나씩 대조해줘.
처리 안 되는 단계가 있으면 어떤 데이터가 어떻게 붕 뜨는지도 짚어줘.
✅ 이렇게 되면 성공 — 시나리오 단계와 코드 처리 여부가 표처럼 하나씩 매칭됩니다. 빠진 단계가 바로 다음 확인할 지점입니다.
⚠️ 막히면 — 시나리오 요약이 너무 길면 AI도 대조를 대충 합니다. 문장 하나짜리 단계로 쪼개서 넣으세요.
배운 것

문서에 “이게 핵심 시나리오”라고 적어뒀다고 해서 코드가 그 문장을 알아서 지키는 건 아니었습니다. 화면을 기획할 때 “혼자 시작 → 배우자 초대”라는 흐름을 여러 번 강조해서 적어뒀는데, 정작 초대를 수락하는 코드는 그 흐름의 절반(가족 배정)만 하고 나머지 절반(기존 데이터 이관)은 아예 손대지 않고 있었습니다.
이번에 배운 건, 중요하다고 적어둔 시나리오일수록 한 번은 코드로 직접 따라가 봐야 한다는 것이었습니다. 특히 사람과 사람이 연결되는 지점, 여기선 가족 초대 같은 흐름은 데이터 소유권이 걸려 있어서 문서와 코드 사이 틈이 조용히 생기기 쉽습니다. 아직 정책도 코드도 결정 전이지만, 적어도 사용자가 먼저 겪기 전에 잡은 건 다행이라고 생각합니다.
오늘도 한 걸음. 🐴
이 글은 산업 구조와 만드는 과정을 정리한 개인 기록입니다.
특정 종목의 매수·매도 추천이 아니며, 투자 판단과 그 결과는 독자 본인에게 있습니다.