기술소개

[스타트업/기술] Sleuth 프로토타입에서 프로덕션까지 | 전체 포스팅 모음

Sleuth는 플레이어가 AI 용의자와 직접 대화하며 단서를 모으고 범인을 지목하는 모바일 추리 게임이다. 6명이 함께 만들고 운영하며, 나는 공동창업자로서 백엔드와 LLM 인프라, 결제, 콘텐츠 제작 도구를 주로 맡았다. 이 글은 Sleuth를 만들며 쓴 기술 글 17편을 한 곳에 모은 목차다. 1절에는 구성도로 전체 모양을 보고, 2절에 개발 과정에서 주로 고민했던 대표 작업 세 가지를 기록해두었다. 3절은 17편 전부를 서비스 개발 …

hyeon_B•

추리 게임 시나리오, 외주에서 자체 파이프라인으로

문제: 추리 시나리오를 한 편 늘리려면 매번 외부 제작사와 새로 계약해야 했다. 팀에는 전담 작가도, 완성된 원고를 검수할 사람도 없었다. 결정: 용의자 한 명을 이루는 필드와 개수를 스키마로 고정하고, Claude 세션 하나가 설계 역할, 작성 역할, 검수 역할을 차례로 맡는 10단계 저작 파이프라인을 만들었다. 다음 단계로 넘어가도 되는지는 Python 스크립트(게이트)가 판정한다. 게이트는 산출물 존재와 승인 상태를 검사하고, 체크…

hyeon_B•

[스타트업/기술] 인앱결제 Part 3 | 검증에 성공한 뒤에 시작되는 문제들

문제: 베타테스터가 스토어 프로모션 코드로 결제했는데 S코인을 받지 못했고, 영구히 실패하는 영수증은 앱을 켤 때마다 서버로 재전송됐다. 결정: 서버가 "다시 보내도 소용없다"는 신호( terminal )를 응답에 싣고 실패한 영수증을 거부 기록으로 남겨 재전송을 끊었다. 막힌 정당한 결제는 예외 규칙과 수동 적립으로 구제했다. 결과: 영구 실패 영수증을 재전송하면 그 다음부터 terminal 응답을 받고 앱이 더 보내…

hyeon_B•

[스타트업/기술] 알림함 아키텍처: Fan-out on Write vs Fan-out on Read | 비용을 '구독자 수'가 아닌 '활성 유저 수'에 비례시키기

문제 푸시 알림은 놓치면 끝이라 사용자별 알림함이 필요했다. 그런데 모든 발송을 서버가 알림함에 펼쳐 쓰면 쓰기 건수가 수신자 수만큼 늘고, 언어별 토픽처럼 서버가 수신자를 모르는 발송은 펼칠 수조차 없다. 결정 서버가 수신자를 uid로 풀 수 있는 발송(uid 지정, 세그먼트, 동의 기반 토픽)은 발송 시점에 서버가 알림함에 쓰고, 풀 수 없는 언어 토픽( lang-* )과 가입 환영 공지만 앱이 열릴 때 클라이언트가 채운다. 대가…

hyeon_B•

[스타트업/기술] FastAPI와 클라우드 네이티브 | 부록: FastAPI의 비동기 구조 (동기/비동기, 블로킹, 이벤트 루프)

이 글에서 다루는 것 · 동기/비동기는 결과를 받는 방식, 블로킹/논블로킹은 실행 흐름이 묶이는지를 묻는 서로 다른 축이다. · FastAPI(uvicorn)와 Node.js는 모두 단일 스레드 이벤트 루프로, I/O를 기다리는 동안 다른 요청을 처리한다. · async def 는 이벤트 루프에서, def 는 스레드풀에서 실행된다. await 없는 동기 호출은 어디서 실행되느냐가 중요하다. · 비동기는 응답 시간이 아니라 동시 처…

hyeon_B•

[스타트업/기술] WebP 변환을 통한 이미지 로딩 최적화 및 GCS 업로드 자동화 | PNG 대비 용량 89%, 지연 시간 81% 개선

문제 GCS 직접 호출로 한 번 줄였는데도 이미지 한 장에 1.39s가 걸렸다. 원인은 파일 자체였다. 에셋이 전부 무손실 PNG였고, 측정한 이미지는 477KB였다. 결정 PNG를 품질 82의 WebP로 바꾸고, 변환과 GCS 업로드(캐시 헤더, URL 맵 포함)를 셸 스크립트 두 개로 묶었다. 대가 손실 압축이라 화질이 조금 깎이는데, 얼마나 깎였는지는 재지 않았다. WebP를 못 읽는 환경을 위한 폴백도 두지 않았다. 결과 …

hyeon_B•

[스타트업/기술] Node.js & App Store 인앱결제 | 결제 시스템 아키텍처 (Part 2)

문제 안드로이드와 영수증 형식이 전혀 다른 iOS(StoreKit 2 JWS)를 같은 원칙, 같은 지급 로직 위에 올려야 했다. 결정 JWS는 로컬에서 풀어 값싼 사전 거절에만 쓰고, 진위는 App Store Server API 조회로 판단한다. 플랫폼별 검증은 따로 두고 재화 지급은 공통 함수 하나로 모았다. 대가 결제마다 애플 서버 왕복이 필요하고, 플랫폼별 검증 코드라는 유지보수 대상이 하나 늘었다. 결과 두 플랫폼이 같은…

hyeon_B•

[스타트업/기술] GitHub Actions CI 파이프라인 | 프로덕션 배포 전 런타임 에러를 차단하는 테스트 자동화

문제 2026년 3월 20일, main에 직접 푸시한 코드가 Cloud Run에 자동 배포된 뒤 채팅 요청이 500을 냈다. 원인은 Gemini 호출의 400 INVALID_ARGUMENT 였고 로컬에서는 한 번도 보지 못한 에러였다. 결정 외부 의존성을 mock으로 바꾼 pytest를 만들고, PR마다 포매터·린터·테스트·Docker 빌드를 돌리는 GitHub Actions CI를 붙였다. main에는 PR로, CI가 통과한 것만 들…

hyeon_B•

[스타트업/기술] GCP Vertex AI | 프로덕션 환경을 위한 GeminiAPI 마이그레이션

문제 출시 전 로컬에서 여러 유저의 동시 플레이를 흉내 내려고 1분 안에 요청을 10번가량 보내자, 평소 2~3초이던 응답이 27.5초까지 늘었다. 플레이어가 용의자에게 질문하고 27초를 기다리는 상황이다. 결정 LLM 호출 창구를 AI Studio(개발자, 실험용)에서 GCP Vertex AI(엔터프라이즈용)로 옮겼다. SDK는 그대로 두고 클라이언트 생성 부분만 바꿨으며, 인증은 API 키 대신 Cloud Run 서비스 계정의 IAM …

hyeon_B•

[스타트업/기술] FastAPI와 클라우드 네이티브 | Cloud Run 도입과 백엔드 전환기 (Part 2)

문제 트래픽이 없어 인스턴스가 0개일 때 첫 메시지를 보내면 컨테이너 기동과 Firestore 적재가 겹쳐, 응답까지 15.3초가 걸렸다(Cloud Run 요청 로그 1건). 결정 최소 인스턴스를 두지 않고, 서버 기동 시 스토리 데이터를 메모리에 적재하고, 프롤로그 진입 시 앱이 예열 요청( /health )을 보내도록 했다. 대가 예열한 인스턴스가 실제 채팅 요청을 받는다는 보장은 없다. 인스턴스마다 캐시를 따로 들고 있어서, …

hyeon_B•
게시물 더보기
검색결과 없음