Steadywag: 기록되지 않은 정보가 데이터인 반려견 케어 앱을 구현하다
요약
Steadywag은 만성 질환을 앓는 반려견의 케어 기록을 관리하는 앱으로, 기존 문서나 서류에 명시되지 않은 구두 지침이나 부재 자체가 중요한 정보를 구조화하여 제공합니다. 이 앱은 Sanity Context를 활용해 에이전트 기능을 구현했으며, 실제 사례 기반으로 데이터 출처와 맥락을 중요하게 다룹니다.
핵심 포인트
- 기록되지 않은 정보(부재)를 데이터로 활용하는 개념 제시
- Sanity Context를 이용한 구조화된 기록 및 에이전트 기능 구현
- 실제 만성 질환 케어 사례 기반의 높은 실용성 확보
- 데이터 로딩 시 승인 절차와 검증 과정을 강조
이 글은 Sanity Challenge, Path Two: Vibe-Code Something Strange에 제출하는 내용입니다.
제가 만든 것
Steadywag는 만성 질환을 앓는 반려견을 위한 케어 동반자 앱이며, 이 앱의 특이점은 바로 그 기록물들이 말해주지 않는 정보들에 관한 것입니다.
저희 집 강아지 Theo는 구리 저장 간질환(copper storage liver disease), 재발성 췌장염(recurrent pancreatitis), 고중성지방혈증(high triglycerides)을 앓고 있는 8살 시츄 믹스입니다. 그의 케어 기록은 약 180페이지 분량의 PDF, 이메일, 그리고 진료실에서 구두로 들었던 내용들 속에 존재합니다. 한 가지 약물은 여전히 서면 목록에 있지만, 전문의가 직접 저희에게 중단하라고 했음에도 불구하고 어떤 문서에도 그 내용은 기록되어 있지 않습니다. 돌보는 사람(sitter), 응급 동물병원 수의사, 또는 이 목록을 읽는 챗봇조차도 이를 잘못 이해할 수 있습니다.
그래서 저는 부재 자체가 데이터가 되는 앱을 만들었습니다. Sanity 레코드는 특정 약물이
앱은 데모를 실행하기 위해 일일 질문 횟수를 제한합니다. 다른 페이지들은 어시스턴트 없이도 작동합니다.
코드
GitHub logo narlynars07 / steadywag
만성 질환을 앓는 개의 케어 동반자. 기록에 담기지 않은 것을 알려주는 Sanity Context의 에이전트. DEV Sanity Challenge, Path One.
Steadywag
만성 질환을 앓는 개의 케어 동반자입니다. 이 앱은 개의 기록을 읽고 서류가 말해주지 않는 내용을 알려줍니다.
실제 사례 하나를 기반으로 구축되었습니다: 구리 저장 간병증, 재발성 췌장염 및 고중성지방혈증을 앓는 8세의 시츄믹스견 테오(Theo)입니다. 그의 케어 기록은 약 180페이지 분량의 보고서, 이메일, 그리고 진료실에서 구두로 언급된 내용에 담겨 있었습니다. Steadywag은 이를 Sanity 내 하나의 구조화되고 출처가 명시된 기록으로 변환한 다음, 그 위에 에이전트, 일일 계획, 체크인 및 예약을 추가합니다. DEV Sanity Challenge의 Path One을 위해 구축되었으며, 이는 실제 콘텐츠를 쿼리하는 에이전트입니다.
하나의 예시로 아이디어를 설명합니다. 약물은 서면 목록에 있었지만, 개에게 투여되지 않았습니다. 전문의가 한 간병인에게 구두로 건너뛰라고 지시했고, 아무도 서류를 업데이트하지 않았습니다. 구조화된 데이터는 이를 알고 있습니다 (`medication.status =
- 계획하고, 기다리세요. 실제로 결정된 모든 것(스택, 저장소, 공개 여부 등)에 대해 Claude Code는 선택지를 제시하고 제 답변을 기다려야 했습니다. 저는 그것이 결론처럼 보이는 방식으로 결정을 내리는 것을 절대 허용하지 않았습니다.
- 승인 없이는 아무것도 로드되지 않습니다. 데이터셋은 스크립트로 구축되었고, 드라이런(dry-run) 과정을 거친 후 제가 승인했을 때만 로드되었습니다. 가족의 관찰 기록 또한 플래그 뒤에 숨겨져 있었으며, 제가 승인할 때까지 꺼진 상태였습니다.
- 주장하기 전에 확인하세요. 그것이 무언가가 고정되었다고 말하기 전에, 실제 화면 너비에서의 스크린샷, 쿼리 결과, 테스트를 보여줘야 했습니다.
최종 레포지토리(repo)는 약 이틀 동안 40개의 커밋으로 이루어져 있으며, 앱 코드는 약 8,900줄, Sanity 스키마는 1,300줄입니다.
효과적이었던 프롬프트
짧고 구체적이며, 보통 제가 볼 수 있는 무언가에 대한 반응이었습니다. 이들은 세션에서 남겨진 오타와 함께 실제 메시지들입니다:
-
"에이전트를 어떻게 더 유용하게 만들 수 있을까요… 지식 기반을 분리할까요? 웹 검색? 가드레일(guardrails)?" 이것 하나가 에이전트의 구조를 재편했습니다. 그것은 판정(verdict)-우선 음식 답변(
-
"개인 정보(personal information), API 키 또는 토큰을 노출한 부분이 없는지 보안 점검을 할 수 있을까요?" 이 질문에 대해 시스템은 내 개인 파일에서 34개의 실제 이메일, 전화번호 및 ID 번호를 스캔했습니다. 아무것도 발견하지 못했습니다. 또한 내가 요청하지 않았던 두 가지 실제 문제점도 발견했습니다: 속도 제한기(rate limiter)의 원시 방문자 IP 주소와 누락된 브라우저 보안 헤더입니다. 둘 다 수정되었습니다.
작동하지 않은 프롬프트 및 접근 방식
- "이 간식 라벨을 검색해 줄 수 있나요?" 에이전트는 마치 할 수 있는 것처럼 말했습니다. 도움이 되는 것처럼 들리는 모델은 빈틈을 추측으로 채우는 경향이 있으며, 강아지 사료에 대한 추측은 잘못된 종류의 도움입니다.
- 페이지에 데이터 하드코딩(Hard-coding data in the page). 초기에 진단 전의 식단 정보가 Sanity에 저장되지 않고 페이지 자체에 존재했기 때문에 에이전트가 이를 볼 수 없었습니다. 그래서 평범한 질문에도 잘못 대답했습니다. 이는 모델의 문제가 아니라 내가 구축하는 과정의 결함이었습니다.
npm audit fix --force. GitHub의 Dependabot은 41개의 경고를 발생시켰습니다. 제안된 수정 사항은 두 개의 핵심 패키지를 오래된 메이저 버전으로 다운그레이드했습니다. Claude Code는 감사(audit) 내용을 읽고, 어떤 경고가 실제 사이트에 도달할 수 있는지 추적했습니다 (없었습니다. 모두 Sanity의 커맨드라인 툴링과 linter에 있었습니다). 그리고 별도의 브랜치에서 오버라이드를 테스트한 후 적용했습니다. 한 오버라이드는 설치를 깨진 상태로 남겼기 때문에, 그 부분은 되돌렸습니다.- 백그라운드 테스트 실행(A background test run). 백그라운드에서 시작한 긴 감사 작업은 모델이 활발하게 작동하는 동안에만 진행되었습니다. 저는 그것을 멈추고 나머지 작업을 포그라운드에서 실행했습니다.
모델이 막혔던 부분과 해결 방법
1. 공백 답변(Blank answers). "오늘 무엇이 필요할까요?"라는 질문과 간병인 요약(sitter brief)은 내용 없이 돌아왔습니다. 에이전트는 도구 단계 8단계 전체를 조회에 사용했지만, 결코 답변을 작성하지 않았습니다. 해결책은 프롬프트가 아닌 구조적인 것이었습니다: 상한선을 12로 올리고 마지막 단계를 텍스트 전용으로 만들어 항상 답변이 작성되도록 했습니다.
2. 데스크톱 채팅 창에서 답변이 잘림 문제. 이 문제를 해결하기 위해 세 번의 수정 작업이 필요했습니다. 처음에는 페이지가 채팅 패널 대신 스크롤되었습니다. 그다음에는 스레드가 열릴 때 카드가 더 이상 flex container 역할을 하지 못하는 문제가 발생했습니다. 마지막으로 그리드 행(grid row)에 제약 조건이 걸리지 않는 문제가 있었습니다. 각 수정 과정마다 다음 버그가 드러났습니다. 유일한 해결책은 측정하는 것이었습니다. 정확한 데스크톱 너비의 프레임 안에 긴 실제 답변을 넣으면서, 아무것도 잘리지 않을 때까지 테스트했습니다. 같은 버그가 나중에 다른 형태로 재발했습니다 (Theo의 사진이 짧은 창 상단에서 잘림). 그때는 근본 원인이 내용보다 작은 박스 중앙 정렬(centering content in a box shorter than the content)에 있었습니다.
3. 필터 트랩, 두 번. Sanity Context의 차트 엔드포인트(chart endpoint)는 필터에 있는 문서 유형만 인식하며, 호스팅된 Studio에서 스키마를 읽어옵니다. 제가 새로운 유형을 추가했을 때, 에이전트는 그것들을 볼 수 없었습니다. 에이전트는 솔직하게 "그의 가족 루틴을 검색할 수 없습니다(I couldn't retrieve his family routine)."라고 말했습니다. 이 정직한 실패가 두 번 모두 우리가 문제를 발견한 방식이었습니다.
4. 지식 기반(Knowledge Base)이 숫자를 만들어냄. 출처에서 제공하지 않는 '간-구리 경계선(400-600 borderline)' 행을 간-구리표에 추가했고, 에이전트가 이를 반복했습니다. 해결책은 지식 기반에 명시적인 지침을 설정한 것입니다: 인용된 출처가 언급하지 않은 참고 범위는 절대 추가하지 말 것. 같은 지식 기반에서 "3/10"으로 작성된 날짜를 3월 1일이 아닌 3월 3일로 읽어들이기도 했습니다. 이제 저는 월(month)을 풀어서 설명합니다.
5. 비어 보이는 환경 변수들. Vercel은 기본적으로 프로덕션 변수를 "민감한(sensitive)" 것으로 표시하며, 이 경우 빈 값만 가져옵니다. Claude Code는 이를 "설정되지 않음(not set)"으로 해석하여 존재하지 않는 문제에 상당한 시간을 할애했습니다. 해결책은 해당 변수들을 비민감(non-sensitive)하게 설정하는 것이었습니다.
6. 나 자신의 보안 헤더가 내 테스트를 망가뜨림. 보안 패스를 거치면서 앱을 다른 사이트에서 임베딩하는 것을 막는 헤더를 추가한 후, Claude Code가 의존해 왔던 레이아웃 테스트(휴대폰 너비의 프레임에 앱 로드)가 작동하지 않게 되었습니다. 이는 해당 헤더가 제 역할을 하고 있다는 뜻입니다. 저는 패널 자체를 직접 크기 조정하는 방식으로 전환했습니다.
7. 첫 번째 감사(Audit). 에이전트를 통해 12개의 질문을 실행했습니다. 그중 4개가 실패했는데, 블루베리, 사전 진단 식단, 구리 참고 자료, 그리고 Atopica 날짜에 관한 것이었습니다. 모든 수정 사항은 프롬프트 트릭이 아니라 데이터나 지침의 변경이었습니다. 이제 감사는 리포지토리(repo) 내 evals/ 경로에 존재합니다. 여기에는 24개의 질문 각각이 존재하는 이유와 실행기가 포함되어 있습니다. 이들은 실시간 사이트와 비교되며, README 파일에는 통과했다는 것이 "알려진 함정을 피했다"는 의미이지 "좋다"는 의미가 아님을 명확히 명시하고 있습니다.
나의 작업이었던 부분과 Claude Code의 작업이었던 부분
Claude Code가 코드, 스키마(schema), 스크립트, 그리고 기록물의 대부분 초안 작성을 담당했습니다. 저는 문서에 담겨 있지 않은 것을 제공했습니다. 즉, 약물에 대한 구두 지침, 가족의 실제 일상 루틴, 각 발작 전에 우리가 알아차린 것들입니다. 또한 AI가 할 수 없는 모든 단계는 Sanity 대시보드에서 제가 직접 수행했습니다. Context 엔드포인트와 Knowledge Base를 생성하는 것은 대시보드 작업이므로, Claude Code가 클릭별 설정 시트(docs/context-setup.md)를 작성해 주었고 저는 그것을 따랐습니다.
App SDK와 워크플로우(Workflows)
저는 App SDK나 Sanity Workflows를 사용하지 않았으며, 그렇게 말하는 것이 더 정확하다고 생각합니다. 가장 가까운 것은 데이터로 저장된 변경 기록(recordUpdate)입니다. 수의사가 작성한 목록을 변경할 때, 저는 스튜디오(Studio)에서 해당 약물을 중단 처리하고, 그 공백을 해결하며, 누가 그렇게 했는지와 함께 로그 항목을 추가합니다. 이 과정은 약 1분 만에 홈 페이지, 오늘 정보, 간병인 요약본, 그리고 에이전트 모두가 업데이트됩니다. 왜냐하면 이들이 동일한 기록을 읽기 때문입니다. 이것은 Sanity Workflow가 아닌, 문서 내에 인코딩된 검토 단계입니다.
다르게 할 수 있는 것들
- 첫 실패를 경험한 후에가 아니라, 첫날부터 감사 질문들을 작성할 것입니다.
- 소스(Source) 모델링을 먼저 할 것입니다. 수의사의 말과 가족의 루틴이 별도의 문서로 분리되자, 에이전트가 이 둘을 혼합하는 것을 멈췄습니다.
- 보안 검사(security scan)는 마지막에 하는 것이 아니라 처음부터 실행할 것입니다.
Sanity 프로젝트 상세 정보
• Project ID: yahsq70q
• Dataset: production (public read)
• Dataset query URL: https://yahsq70q.api.sanity.io/v2026-10-01/data/query/production?query=count(*)
• Studio: https://steadywag.sanity.studio
스키마(Schema): 22개의 문서 유형과 하나의 공유 객체로 구성되어 있으며, 총 516개의 문서를 포함합니다 (검사 결과 288개, 방문 기록 26개, 체중 측정 26개, 투약 기록 18개). 검토자에게 강조하고 싶은 설계 선택 사항은 다음과 같습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기