[런칭스프린트] 도장봇 개발기 - 1주차

소개

시도하고자 했던 것과 그 이유를 알려주세요.

도배·장판 시공을 구하려면 보통 여러 업체에 일일이 연락해 견적을 받고 비교해야 합니다. 저는 이 과정을 카카오톡/텔레그램 챗봇 하나로 압축해서, 아파트 주소 몇 마디만 입력하면 3턴 안에 예상 견적이 나오고, 그대로 도배사 매칭·예약·결제까지 이어지는 서비스("도장봇")를 Claude Code로 만들어보고 있습니다.

경쟁 서비스인 "미소"의 카카오 견적 흐름을 스크린샷으로 직접 캡처해 턴 수를 세어보니 약 14턴(본인인증, 인터넷 설치 여부 등 견적과 무관한 질문 포함)이 걸리는 것을 확인했고, 이를 벤치마크 삼아 "3턴 이내 견적"을 명확한 목표로 잡았습니다.

이번에 정리한 세션에서는 이미 만들어져 있던 MVP 위에서

  1. 미구현 상태였던 사진 업로드 기능을 실제로 동작하게 만들고,

  2. 실서비스에 배포했을 때 터질 수 있는 P0~P3급 구조적 빈틈(상태 비영속, 도배사 수락 트리거 부재, 웹훅 인증 우회 가능, PII 평문 저장 등)을 표로 정리해 우선순위대로 고쳐나갔습니다.


진행 방법

어떤 도구를 사용했고, 어떻게 활용하셨나요?

사용 도구: Claude Code (Sonnet 5) + FastAPI(Python) 백엔드 + GitHub

작업 방식은 "기능/버그를 자연어로 지시 → Claude Code가 코드 탐색·구현·테스트·커밋까지 수행 → 결과 보고받고 다음 지시" 형태로 반복했습니다. 특히 이번 세션은 다른 세션(혹은 제가 직접)이 같은 저장소를 동시에 수정하고 있는 상태에서 진행돼서, Claude Code가 "파일이 디스크에서 외부적으로 변경됨"을 스스로 감지하고 재확인 후 병합하는 과정이 여러 번 있었던 것도 특징이었습니다.

실제 사용한 프롬프트 중 대표적인 것들입니다.

현재 기능이 좀 바뀌었어 테스트 시나리오를 다시 작성해서 가이드해줘 찍은 사진을 올릴수 있는 기능이 미구현상태야 확인하고 빠진 기능이 없는지 확인하고 추가구현해줘

→ 카카오 스킬 웹훅은 이미지 바이너리를 직접 받을 수 없다는 제약을 새로 발견해서, 위치공유 때 썼던 "서명된 토큰 + 웹뷰 업로드 페이지" 패턴을 사진 업로드에도 그대로 적용해 구현했습니다.

미소 라는 서비스의 대화턴이야 이거보다 간단하게 3번턴 안에 견적을 받을 수 있게 대화안을 구성해줘

→ 경쟁사 스크린샷 6장을 보고 턴을 재구성해 비교했더니, 이미 구현되어 있던 흐름이 미소보다 훨씬 짧다는 걸 확인하는 분석 작업이었습니다.

가장 큰 작업은 아래처럼 P0~P3 우선순위 표를 통째로 던지고 순서대로 처리를 맡긴 것이었습니다.

P0 — 지금 바로 깨질 수 있는 것
상태 비영속 | Conversation이 메모리 dict. 재시작·다중 인스턴스·슬립 복구 시 대화 유실
Render 슬립 vs 카카오 5초 | 15분 미사용 시 cold start가 수십 초 → 스킬 타임아웃
견적·예약금 하드코딩 | ...
도배사 더미 3명 | ...
시크릿 비어 있을 때 인증 스킵 | ...

P1 — UX·채널 빈틈
주소 파싱 취약 / 전화번호·이름 오인식 / 위치 공유 후 수동 복귀 / ...

P2 — 제품·운영 품질
LLM 미연결 / 알림톡·친구톡 없음 / 사진 저장 위치·보관 정책 / 관측성 / 개인정보 평문 저장 ...

P3 — 코드 위생
conversation_service 전역 싱글톤 / _route 함수 분리 / 카카오 응답 방어 로직 없음

이부분 보강해줘 코드 개선하고 커밋도 진행해줘

이후 "바로 다음 진행해줘", "모두 진행해줘", "순서대로 진행해보자" 같은 짧은 지시만으로 계속 다음 항목을 이어서 처리하게 했고, 진행 중 Claude가 스스로 코드를 다시 읽다가 "도배사가 수락했다는 신호를 보낼 방법이 실제로는 어디에도 없어서, 예약을 진행하면 사용자가 영원히 멈춰버리는" 숨은 치명적 버그를 발견해 보고했습니다. 이런 "외부 키/계약이 필요해서 임의로 못 만드는 부분"(LLM 연동, 알림톡 계약, S3 자격증명, Render 유료 전환 등)은 매번 먼저 확인받고, 코드로 가능한 부분만 진행하도록 역할을 나눴습니다.

실제로 만든 코드 중 하나(도배사 수락 데드락을 푼 관리자용 엔드포인트) 예시입니다.

@router.post("/reservations/contractor-accepted")
async def contractor_accepted(
    payload: ContractorAcceptedRequest, x_admin_sync_token: str | None = Header(default=None)
) -> dict:
    """Advance a reservation past 'reservation_waiting' once a contractor has
    confirmed availability by phone.

    There is no contractor-facing app yet, so an operator triggers this
    manually after contacting the contractor. Without it, a user who
    reaches "reservation_waiting" has no way to ever reach payment.
    """
    _require_admin_token(x_admin_sync_token)
    try:
        reply = conversation_service.mark_contractor_accepted(payload.user_id)
    except ValueError as exc:
        raise HTTPException(status_code=409, detail=str(exc)) from exc

    channel = conversation_service.get_channel(payload.user_id)
    if channel == Channel.TELEGRAM.value:
        settings = get_settings()
        if settings.telegram_bot_token:
            try:
                await TelegramClient().send_message(payload.user_id, reply.text, reply.buttons)
            except Exception:
                logger.exception("Failed to push contractor-accepted notice")
    else:
        conversation_service.queue_notice(payload.user_id, reply.text)

    return {"success": True, "data": {"user_id": payload.user_id, "stage": "payment_ready"}}

한국어로 메시지를 표시하는 모바일 앱

결과와 배운 점

배운 점과 나만의 꿀팁을 알려주세요.

  • "켜지기 전까지는 아무것도 안 바뀐다" 패턴이 안전하다. Redis 영속화, PII 암호화 모두 환경변수(REDIS_URL, PII_ENCRYPTION_KEY)가 설정되지 않으면 기존 동작(인메모리·평문)과 100% 동일하게 유지되도록 설계했습니다. 실제 Redis 인스턴스가 아직 없는 상태에서도 "코드는 준비되어 있고, 나중에 키만 넣으면 켜진다"는 안전한 결과물을 받을 수 있었습니다.

  • 표로 우선순위를 던지면 순서대로 처리하되, 매번 "이건 제가 판단할 수 없는 부분입니다"라고 멈춰서 물어보는 게 오히려 신뢰가 갔습니다. LLM 키, 알림톡 계약, S3 자격증명, Render 결제처럼 실제 돈이나 외부 계정이 필요한 항목은 절대 임의로 진행하지 않고 매번 확인받았습니다.

  • 코드를 읽다가 원래 요청하지 않은 치명적 버그(도배사 수락 트리거 부재, 주소 정정 안내문이 실제로는 동작 안 하던 버그)를 스스로 찾아냈습니다.

과정 중에 어떤 시행착오를 겪었나요?

  • 카카오 스킬 웹훅이 이미지 바이너리를 못 받는다는 걸 처음엔 몰랐다가, 위치 공유 때 썼던 "서명 토큰 + 웹뷰" 우회 패턴을 재사용해야 한다는 걸 알게 됐습니다.

  • 정규식으로 이름을 추출하는 로직에서 "실크"처럼 2글자 한글 단어가 이름으로 오인식될 뻔한 함정이 있었는데, 테스트를 먼저 짜서 잡아냈습니다.

  • 같은 저장소를 다른 프로세스가 동시에 수정하고 있어서, 커밋 경계를 어디서 끊을지 매번 판단해야 했습니다.

도움이 필요한 부분이 있나요?

  • Gemini API 키(자유문장 인식 고도화), 카카오 알림톡/친구톡 비즈니스 계약(능동 알림), 오브젝트 스토리지 자격증명(S3/GCS, 사진 저장), 도배사 실제 계정·매칭 알고리즘 설계 — 전부 외부 키·계약·설계 결정이 필요해서 아직 손을 못 대고 있습니다.

앞으로의 계획이 있다면 들려주세요.

  • PII 암호화 범위 확장: 지금은 대화 상태(이름·전화·주소)만 암호화했는데, 결제 주문의 시공 주소(Order.apartment)도 같은 방식으로 포함

  • 관측성 로깅 확장: 챗봇 대화 턴만 로깅되는데, 결제 완료·사진 업로드·도배사 수락 같은 웹훅/관리자 이벤트도 동일하게 로깅

  • 사진 저장소를 Redis 임시 저장(base64)에서 S3/GCS 같은 실제 오브젝트 스토리지로 전환

  • LLM 연동: Gemini API 키 확보 후 자유문장 추출 정확도 고도화

  • 카카오 알림톡/친구톡 비즈니스 계약 체결 후 능동 알림 연결 — 계약 전까지는 로컬 폴링 방식 우회도 병행 검토 중

  • 도배사 계정·매칭 API 본격 설계·구현: 인증 방식, 매칭 알고리즘, 도배사용 UX부터 결정 필요

  • Render 유료 플랜 전환 검토: 현재는 무료 플랜 특성상 슬립·콜드스타트 위험이 있어 외부 무료 핑으로 임시 완화만 해둔 상태

  • 상태가 안정되면 conversation_service 전역 싱글톤 구조도 재검토

    한국인


도움 받은 글 (옵션)

  • 조코딩 창업 유튜브

  • 스터디장님 강의내용 등


2
1개의 답글
지피터스 뉴스레터

이번 주 AI, 딱 쓸 것만

매주 월요일 아침,
읽고 바로 써먹을 AI 소식만