DoltHub에 데이터를 저장하는 애플리케이션 개발

습관 추적기의 데이터를 서버가 아니라 사용자의 DoltHub 저장소에 두는 구성입니다. 서버에 토큰을 저장하지 않고 기본 DB도 두지 않는 설계, htmx와 Cloudflare Pages 조합, DoltHub API로 스키마를 자동 생성한 방법과 그 과정에서 확인한 제약을 정리합니다.

"don’t break the chain" 방식의 습관 추적기를 만들었습니다. 웹 페이지의 달력에서 하루 칸을 클릭하면 사슬이 이어지고, 하루를 건너뛰면 사슬이 끊어지는 단순한 기능의 앱입니다. 이런 도구는 이미 많지만, 데이터를 서비스 운영자의 DB가 아니라 사용자가 소유하고 통째로 가져갈 수 있는 곳에 저장하면서 무료로 운영하고 싶었습니다. 그래서 사용자가 직접 DB를 생성할 수 있는 DoltHub를 데이터 저장소로 사용했습니다. 이 글에서는 그 구성의 선택지와 제약, 구현하면서 확인한 사실을 정리합니다.

소스는 benelog/habit-chain에 있습니다.

1. 습관 추적기의 화면과 연결 절차

화면은 습관마다 카드 하나입니다. 카드 안에는 최근 5주를 요일별로 늘어놓은 격자가 있고, 체크한 날끼리는 서로 이어져 하나의 사슬로 보입니다.

습관 카드 하나에 최근 5주 격자가 있고 7월 22일부터 25일까지 네 칸이 이어져 있다

사용자는 DoltHub에 가입하고 DB를 생성해야 합니다. 공개 DB는 이름만으로 읽을 수 있고, 비공개 DB는 읽기에도 토큰이 필요합니다. 앱은 설정된 토큰을 읽기 요청에도 전달하므로 둘 다 지원합니다.

  1. DoltHub에서 빈 데이터베이스를 하나 만듭니다. 테이블은 만들지 않아도 됩니다.

  2. DoltHub의 Settings → Tokens에서 API 토큰을 발급합니다. 토큰은 가능한 한 이 DB와 필요한 권한으로만 제한합니다.

  3. 앱의 설정 화면에 데이터베이스 이름(owner/name)과 토큰을 넣고 저장합니다. 저장하는 시점에 앱이 DoltHub에 토큰이 유효한지 물어보므로, 잘못 붙여 넣은 토큰은 그 자리에서 드러납니다.

  4. 목록 자리에 나타나는 DB 준비하기 버튼을 누릅니다. 앱이 사용자의 토큰으로 테이블을 만듭니다.

4단계가 끝나면 바로 습관을 추가할 수 있습니다. 스키마 SQL을 손으로 옮겨 붙이는 단계는 없습니다.

2. 데이터 저장소 후보 비교와 DoltHub 선택

데이터 저장소는 아래의 조건을 고려해서 선택했습니다.

  • 제가 서버 비용과 백업 책임을 지지 않아야 함

  • 사용자가 자기 데이터를 언제든 통째로 가져갈 수 있어야 함

  • SQL로 데이터 조회/입력 가능

선택지 장점 걸린 점

Cloudflare D1

Worker와 같은 계정에서 바로 쓰고 지연이 짧음

데이터가 제 계정에 쌓입니다. 사용자별로 격리하려면 인증과 테넌트 구분을 직접 만들어야 합니다.

브라우저 localStorage

서버가 아예 필요 없음

기기를 바꾸면 기록이 사라집니다. 브라우저 데이터를 지워도 사라집니다.

GitHub 저장소에 JSON 커밋

사용자 소유이고 이력이 남음

체크 한 번에 파일 전체를 다시 쓰게 됩니다. 조건 검색도 직접 구현해야 합니다.

DoltHub

사용자 소유, SQL 질의, 커밋 이력, 웹 관리 화면

쓰기 한 번에 1.5초에서 2초가 걸립니다. 무료 요금제의 데이터베이스는 공개됩니다.

DoltHub는 Git처럼 커밋과 브랜치를 갖는 SQL 데이터베이스인 Dolt의 호스팅 서비스입니다. 사용자가 자기 계정에 데이터베이스를 만들고, 앱은 사용자가 발급한 토큰으로 그 데이터베이스에 접근합니다. 체크를 한 번 할 때마다 커밋이 하나 쌓이므로, 언제 무엇을 고쳤는지가 DoltHub의 커밋 로그에 그대로 남습니다.

DoltHub의 Commit Log 화면. 체크 추가·삭제와 습관 수정이 SQL 구문 단위의 커밋으로 나열되어 있다

이력은 Git처럼 쌓이지만 조회는 SQL로 합니다. Dolt가 커밋 로그와 행 단위 변경 이력을 시스템 테이블로 노출하므로, 별도 명령을 배우지 않아도 익숙한 SQL로 이력을 들여다볼 수 있습니다.

-- 커밋 이력 조회
SELECT commit_hash, committer, date, message FROM dolt_log LIMIT 3;

-- 3개 커밋 전 시점의 체크 기록 조회
SELECT * FROM checks AS OF 'HEAD~3';

-- checks 테이블의 행 단위 변경 이력 조회
SELECT check_date, commit_date FROM dolt_history_checks ORDER BY commit_date DESC;

웹 화면의 데이터 관리 기능도 풍부합니다. DoltHub에서 테이블을 직접 편집하고, 커밋별 diff를 확인하고, SQL 콘솔에서 질의를 실행할 수 있습니다. 데이터베이스를 fork하고 pull request로 변경을 제안하는 흐름도 Git 호스팅과 같습니다. 덕분에 앱에 관리 화면을 따로 만들지 않아도 DoltHub 웹 화면이 그 역할을 합니다.

DoltHub의 Database 화면. 왼쪽에 테이블 목록과 스키마가 있고

2.1. 데이터 반출과 백업의 경계

앱의 설정 화면에는 현재 앱이 읽는 상태를 JSON으로 내려받는 기능이 있습니다. 이 내려받기는 일반 링크여서 URL에 비밀을 싣지 않으려고 토큰을 전달하지 않으므로, 공개 DB에서만 동작합니다. 스키마와 커밋 이력까지 포함한 DB 전체를 가져오려면 Dolt CLI로 저장소를 clone할 수 있습니다.

dolt clone owner/name

따라서 앱 운영자인 제가 사용자 데이터의 백업과 반출 기능을 별도로 만들 필요는 없습니다. 다만 DoltHub의 커밋 이력은 DoltHub 계정 삭제나 서비스 장애까지 막아 주는 별도의 백업은 아닙니다. 기록이 중요하다면 사용자가 주기적으로 clone해서 다른 곳에 사본을 두어야 합니다.

3. 토큰을 저장하지 않고 기본 DB도 두지 않는 애플리케이션 서버 구성

이 구성에서 서버가 하는 일은 화면을 그리고 DoltHub API를 대신 호출하는 것뿐입니다. 서버는 다음 두 가지를 갖지 않습니다.

  • 영속적으로 저장한 DB 접근 토큰: 사용자가 설정에 넣은 값이 요청마다 X-Dolt-Token 헤더로 옵니다. Worker는 요청을 처리하는 동안 토큰을 취급하지만 DB나 환경 변수에 저장하지 않습니다.

  • 기본 데이터베이스: 설정이 비어 있는 사용자에게 보여 줄 데이터베이스가 없습니다.

공개 DB의 읽기는 토큰 없이 누구나 할 수 있고, 비공개 DB의 읽기와 모든 쓰기는 DoltHub가 토큰의 권한으로 통제합니다. Worker는 사용자 계정이나 세션을 구현하지 않고 권한 검사를 DoltHub에 맡깁니다. 사용 편의성을 위해 DB 이름과 토큰은 브라우저의 localStorage에 저장합니다. 공용 PC에는 저장하지 말 것을 안내하고, 저장한 값을 지우는 버튼도 두었습니다.

첫 화면의 shell은 고정된 HTML이지만, 습관 목록은 DB의 현재 내용과 브라우저가 보낸 오늘 날짜로 Worker가 동적으로 렌더링합니다. 서버에 세션이나 사용자별 저장 공간이 없다는 뜻에서 상태가 없을 뿐, 정적 페이지는 아닙니다.

DB 이름과 날짜, 토큰을 매 요청에 싣는 구현은 htmx에서 했습니다. htmx의 hx-headers는 상속되는 속성이라, body에 한 번만 선언하면 모든 요청에 추가됩니다.

worker/src/render.ts가 만드는 shell
<body hx-headers='js:{"X-Local-Date": habitChain.today(), "X-Dolt-DB": habitChain.db(), "X-Dolt-Token": habitChain.token()}'>

js: 접두사를 붙이면 요청 시점마다 표현식을 평가합니다. 그래서 설정을 바꾸면 다음 요청부터 새 값이 실립니다. 이 구조에서는 읽기 요청에도 토큰이 실리고, Worker는 설정된 토큰을 DoltHub 읽기 호출에 전달합니다. 비공개 DB를 읽는 경로가 이것입니다. 부수 효과도 하나 있습니다. 2026년 8월에 확인했을 때 DoltHub의 읽기 엔드포인트는 공개 DB의 조회라도 잘못된 토큰이 붙어 있으면 400으로 거부했습니다. 그래서 저장한 뒤에 폐기된 토큰도 다음 읽기에서 드러나며, 앱은 이 오류 메시지를 알아보고 설정 화면을 가리키는 안내를 띄웁니다.

토큰이 앱의 여러 경로를 흐르므로 Worker 코드와 배포 경로, 같은 출처에서 실행되는 JavaScript를 신뢰해야 합니다. 특히 localStorage를 읽을 수 있는 XSS가 생기면 토큰이 앱 밖으로 유출될 수 있으므로, 토큰의 대상 DB와 권한을 최소화해야 합니다.

3.1. 저장 시점의 토큰 검증

잘못 붙여 넣은 토큰은 검증이 없으면 첫 쓰기에서야 오류로 드러납니다. 그래서 설정의 저장 버튼을 누르는 시점에 Worker가 DoltHub의 v2 API인 GET /api/v2/user로 토큰이 유효한지 확인합니다. `v1alpha1`에는 DB와 무관하게 토큰만 확인하는 문서화된 엔드포인트가 없어서, 이 요청 하나만 v2를 씁니다.

판정은 유효와 무효의 두 가지가 아니라 세 가지로 나눴습니다. DoltHub가 명확히 거부(401, 403)한 토큰만 저장을 막습니다. DoltHub 장애나 네트워크 오류처럼 판정할 수 없는 경우에는 확인하지 못했다는 안내와 함께 저장을 진행합니다. 저장소 쪽 장애를 "토큰이 틀렸다"로 보여 주면 사용자가 멀쩡한 토큰을 의심하게 되기 때문입니다.

4. htmx 서버 렌더링과 Cloudflare Pages 배포

화면 렌더링은 전부 서버에서 합니다. 브라우저는 htmx가 HTML 조각을 갈아 끼우는 일만 합니다. 브라우저에서 DoltHub를 직접 호출하지 않으므로 CORS 설정에 기댈 일이 없습니다. 2026년 8월에 확인했을 때 DoltHub의 write 엔드포인트는 브라우저의 preflight 요청에 쓰기를 허용하지 않았으므로, Worker의 API 중계 역할은 서버 렌더링과 별개로도 필요했습니다.

이름과 설명을 고치는 폼은 카드마다 접힌 채로 함께 내려보냅니다. 수정 버튼을 누르면 클래스 하나만 바꿔서 펼치므로 서버를 다시 부르지 않습니다. 읽기 한 번에 1초 가까이 걸리는 앱이라, 폼을 그때 받아 오면 수정 버튼과 취소 버튼이 매번 멈춥니다.

카드 안에서 이름과 설명 입력란이 펼쳐지고 아래에는 사슬 격자가 그대로 남아 있다

취소는 form.reset()으로 처리합니다. 서버가 내려보낸 값이 곧 폼의 기본값이라서 따로 되돌릴 값을 들고 있지 않아도 됩니다. 저장에 성공하면 서버가 카드를 통째로 새로 내려주므로 펼침 상태도 함께 사라집니다.

서버 실행 환경으로 Cloudflare를 고른 이유는 Workers입니다. 관리할 서버 없이 요청 단위로 코드가 실행되고, 무료 요금제로 하루 10만 요청까지 처리할 수 있습니다. 개인용 습관 추적기의 트래픽은 그 안에 넉넉히 들어갑니다.

배포는 Cloudflare Pages의 advanced mode를 씁니다. 빌드 산출물의 _worker.js 하나가 모든 요청을 먼저 받고, 자기가 모르는 경로만 정적 자산으로 넘깁니다. 이 _worker.js도 같은 Workers 런타임에서 실행되므로, Pages를 써도 실행 모델은 그대로입니다.

그런데도 Workers 직접 배포가 아니라 Pages를 고른 이유는 도메인입니다. 이 앱의 도메인 DNS는 Netlify에 있는데, Workers의 커스텀 도메인은 Cloudflare 존을 요구합니다. Pages는 서브도메인이면 외부 DNS에 CNAME 한 줄로 붙습니다.

4.1. X-Local-Date 헤더로 받는 오늘 날짜

서버에는 사용자의 시간대가 없습니다. UTC를 기준으로 삼으면 한국 사용자는 오전 9시 전에 체크한 기록이 어제 칸에 들어갑니다. 습관 추적기에서 그것은 사슬이 끊어진 것으로 보입니다.

그래서 브라우저가 로컬 날짜를 X-Local-Date 헤더로 보내고, 서버는 그 값을 씁니다. 헤더가 없을 때만 UTC로 계산합니다. 클라이언트가 보낸 값이므로 형식은 검사합니다.

체크 시각을 UTC로 저장하고 조회할 때 사용자의 시간대로 변환하는 방법도 검토했지만, 이 앱에는 맞지 않다고 판단했습니다. check_date는 시각이 아니라 "그 날 했다"는 날짜이기 때문입니다.

  • checks 테이블의 PK인 (habit_id, check_date)가 하루에 체크 하나라는 규칙을 스키마에 담고 있습니다. 현재 스키마에서 UTC 시각만 저장하면 하루의 경계가 조회하는 시간대에 따라 달라지므로, 이 규칙을 같은 방식으로 표현할 수 없습니다. 체크 해제도 지금은 날짜 단위 DELETE 구문 하나인데, 시각 저장이면 범위 질의가 됩니다.

  • 시간대를 옮겨서 조회하면 과거 기록이 다른 날로 재해석됩니다. 밤 11시의 체크가 다음 날로 넘어가면, 이미 이어졌던 사슬이 나중에 끊어져 보입니다.

  • 렌더링이 서버에서 일어나므로, 조회할 때 변환하려면 결국 브라우저가 시간대를 헤더로 보내야 합니다. X-Local-Date와 같은 클라이언트 의존이 그대로 남고 변환 지점만 늘어납니다.

  • DoltHub 웹 화면에서 2026-07-25라는 날짜는 그대로 읽히지만, UTC 시각은 시간대를 계산해야 맞는 날이 보입니다. 관리 화면을 DoltHub에 맡긴 구성과 어긋납니다.

시각인 데이터는 UTC로 저장합니다. 습관의 생성 시점인 created_at 칼럼이 그렇습니다. 남는 트레이드오프는 브라우저가 보낸 날짜를 형식만 검사하고 신뢰한다는 점입니다. 조작된 날짜를 막는 인증 경계가 아니라, 사용자가 자기 DB에 기록하는 개인 도구이므로 받아들였습니다.

5. DoltHub API를 사용한 스키마 자동 생성

이 앱은 SQL 읽기와 쓰기에 DoltHub의 v1alpha1 API를 사용합니다. 2026년 7월에 명시적인 OpenAPI 계약과 일관된 오류 형식을 갖춘 v2 API가 출시되었고, 공식 문서도 새 연동에는 v2를 권장합니다. 그래서 전체 이전을 검토했지만, 2026년 8월에 확인한 아래 사실 때문에 보류했습니다.

  • v2의 /api/v2/databases/{owner}/{db} 계열 엔드포인트가, `v1alpha1`로는 정상으로 읽히는 살아 있는 저장소 다수에 404(no such repository)를 반환했습니다. 제가 실제로 쓰는 DB와 v2 출시 이후에 만든 DB도 마찬가지였고, v2 문서의 예시 응답에 등장하는 dolthub/us-jails조차 라이브 v2에서는 404였습니다. 이 격차는 마이그레이션 가이드에 언급이 없습니다.

  • v1alpha1`은 폐기 예정이 아닙니다. v2 발표 글이 지원 지속을 명시하고, SQL API의 본 문서도 여전히 `v1alpha1 기준입니다. /api/v1 같은 정식 v1은 존재하지 않아서, 이름과 달리 `v1alpha1`이 현행 주력 API입니다.

  • v2 전용 토큰은 따로 없습니다. 같은 API 토큰을 `v1alpha1`은 authorization: token 스킴으로, v2는 Authorization: Bearer 스킴으로 받습니다.

커버리지 문제가 풀린 뒤에 v2로 옮기더라도 확인할 것이 남습니다. 응답 형식이 다르고(행이 칼럼명을 가진 객체가 아니라 값의 배열입니다), 아래에서 설명할 문서에 없는 write 동작이 v2의 sql-writes 엔드포인트에서도 유지되는지 다시 확인해야 합니다. 따라서 아래 내용은 DoltHub API 일반의 보장이 아니라 2026년 8월에 확인한 `v1alpha1`의 동작입니다.

DoltHub의 DDL 실행 경로는 아래 3가지입니다.

경로 DDL 확인한 내용

웹의 SQL 조회 콘솔

거부

ALTER TABLE을 보내면 Unsupported SQL statement로 거부합니다.

웹의 테이블 편집 화면

허용

테이블 목록에서 연필 아이콘을 눌러 들어가는 SQL Query 화면입니다. 임시 workspace에 반영한 뒤 커밋합니다.

REST write 엔드포인트

허용

앱이 쓰기에 쓰는 POST /api/v1alpha1/{owner}/{db}/write/{branch}/{branch}입니다.

REST write 엔드포인트가 DDL을 받는다는 내용은 공식 문서에 없습니다. 2026년 8월에 빈 데이터베이스로 직접 확인한 동작이므로, 보장된 계약으로 보지 않고 실패 경로를 남겨 두었습니다.

그래서 스키마 준비를 다음과 같이 구현했습니다.

  1. 목록 읽기가 실패하면 그때만 SHOW TABLESSHOW COLUMNS로 데이터베이스의 상태를 확인합니다.

  2. 스키마 상태가 충분하지 않으면 화면에서 DB 준비하기 버튼을 보여 줍니다.

  3. 버튼을 누르면 서버가 상태를 다시 확인하고, 앱이 알고 있는 마이그레이션의 사후 조건을 만족시키는 DDL을 실행합니다.

테이블이 없는 데이터베이스를 연결하면 DB 준비하기 버튼과 스키마 SQL 보기 링크가 나온다

3단계에서 DB의 스키마 상태를 다시 확인하는 이유는 앞선 시도가 일부만 적용되었거나 완료 응답을 받지 못한 경우에 ALTER TABLE …​ ADD COLUMN 등을 무조건 다시 실행하지 않기 위해서입니다.

5.1. 순번이 있는 마이그레이션 목록

실행할 DDL은 순번이 있는 목록으로 관리합니다. 목록은 뒤에 붙기만 하고, 순번이 의존 순서를 정합니다. 실행 시점에는 적용되지 않은 SQL 구문만 순서대로 골라냅니다.

worker/src/dolt.ts
export const MIGRATIONS: Migration[] = [
  { id: 1, name: "create-habits", statement: CREATE_HABITS, applied: (s) => s.habits },
  { id: 2, name: "create-checks", statement: CREATE_CHECKS, applied: (s) => s.checks },
  {
    id: 3,
    name: "habits-description",
    statement: ADD_DESCRIPTION,
    // Covered by create-habits on a fresh DB: the snapshot already has it.
    applied: (s) => !s.habits || s.description,
  },
];

export function migrations(shape: Shape): string[] {
  return MIGRATIONS.filter((m) => !m.applied(shape)).map((m) => m.statement);
}

이 형태는 Flyway나 golang-migrate 같은 마이그레이션 도구에서 가져왔습니다. 다만 도구를 그대로 붙일 수는 없었습니다. 이런 도구는 JDBC 같은 DB 연결을 전제하는데 DoltHub는 REST API만 노출합니다. 그리고 배포자가 자격증명을 갖고 배포 시점에 실행하는 모델인데, 이 앱에서는 사용자의 DB가 배포 이후에 생기고 마이그레이션은 그 사용자의 토큰으로 요청 시점에 돕니다. 그래서 순번 목록이라는 형태만 가져오고 두 가지를 바꿨습니다.

  • 버전 테이블을 두지 않습니다. 마이그레이션 도구는 schema_migrations 같은 테이블에 적용된 버전을 기록합니다. 이 구성에서는 사용자가 DoltHub 웹 화면에서 스키마를 직접 만질 수 있고, 여러 write 요청을 하나로 묶는 원자적 트랜잭션도 없습니다. DDL은 성공하고 버전 기록은 실패하면 둘이 어긋날 수 있습니다. 그래서 적용 여부는 마이그레이션마다 실제 스키마를 조사한 결과(applied)로 판정합니다. 실제 스키마가 유일한 기록입니다. 다만 현재 조사는 테이블과 일부 칼럼의 존재처럼 각 마이그레이션이 정한 조건만 확인합니다. 칼럼 타입, 기본값, PK까지 전체 스키마가 정확한지 검증하는 도구는 아닙니다.

  • CREATE 구문은 현재 스키마의 스냅숏입니다. 도구는 새 DB에도 과거 ALTER를 순서대로 재생하지만, 여기서는 SQL 구문 하나가 커밋 하나이고 1.5초에서 2초가 걸려서 재생할수록 첫 준비가 느려집니다. 새 테이블은 최신 모양으로 바로 만들고, 그 마이그레이션이 뒤의 ALTER까지 덮은 것으로 판정합니다. 3번의 applied가 "habits 테이블이 없으면 적용된 것"으로 읽히는 이유입니다.

자동으로 실행하는 내림(down) 마이그레이션도 만들지 않았습니다. 문제가 생기면 Dolt의 커밋 이력을 보고 수동으로 복구합니다. 스키마와 데이터가 함께 변한 커밋을 되돌리는 일은 별도의 호환성 판단이 필요하므로, 커밋 이력이 down 마이그레이션과 같은 것은 아닙니다.

5.2. 새 데이터베이스의 branch not found 오류

새 데이터베이스를 연결한 사용자가 query error: branch not found를 만났습니다.

원인은 DDL 실패가 아니었습니다. DoltHub에서 방금 만든 데이터베이스는 커밋이 하나도 없어서 main 브랜치 자체가 없습니다. 목록을 읽는 첫 질의가 거기서 실패한 것이었습니다.

curl "https://www.dolthub.com/api/v1alpha1/benelog/chain2/main?q=SHOW%20TABLES"
# {"query_execution_status":"Error","query_execution_message":"query error: branch not found"}

이 응답을 오류 화면에 그대로 띄우면 사용자가 할 수 있는 일이 없습니다. 지금은 이 메시지를 알아보고 준비 안내로 보냅니다. 다른 오류는 그대로 오류 화면으로 갑니다. 존재하지 않는 데이터베이스 이름까지 준비 안내로 덮으면 진짜 고장을 감추게 됩니다.

브랜치가 없는 데이터베이스에 write 엔드포인트로 CREATE TABLE을 보내면 테이블과 함께 브랜치도 생깁니다. 이것도 문서에 없는 동작입니다.

6. DoltHub 저장 구성의 제약

이 방식을 그대로 따라 하실 분을 위해 걸리는 점을 적습니다.

  • 공개 데이터베이스는 이름을 아는 누구나 읽을 수 있습니다. 토큰 없이 API로 현재 테이블과 커밋 이력을 읽을 수 있으므로, 습관 이름과 날짜뿐 아니라 설명, 생성 시각, note 칼럼과 수정·삭제 전 값도 공개됩니다. 민감한 값을 나중에 지우거나 고쳐도 과거 커밋에서는 사라지지 않습니다. 기록을 감추려면 비공개 DB를 써야 합니다. DoltHub Pro가 비공개 DB를 지원하며 2026년 8월 기준으로 비공개 데이터 100MB까지 무료이고, 초과하면 월 5달러입니다. 앱은 읽기 요청에도 토큰을 전달하므로 비공개 DB도 지원하지만, JSON 내려받기는 토큰을 싣지 않는 일반 링크라 공개 DB에서만 동작합니다.

  • 쓰기가 1.5초에서 2초 걸립니다. write 엔드포인트가 비동기라서 작업을 걸고 완료까지 폴링합니다. 체크 버튼을 누르고 바로 반응하는 화면을 원한다면 맞지 않습니다.

  • SQL 구문 하나에 커밋 하나가 쌓입니다. 영향받은 행이 0건이어도 커밋이 생깁니다. 체크를 자주 하면 커밋 로그가 길어집니다.

  • write 엔드포인트는 한 요청에 SQL 구문 하나만 받습니다. 세미콜론으로 이어 보내면 Error parsing SQL로 거부합니다. 습관 삭제는 `checks`와 `habits`를 차례로 지우는 두 요청입니다. 첫 요청 뒤에 두 번째 요청이 실패하면 습관은 남고 체크 기록만 지워진 상태가 되며, 앱은 이를 자동으로 보상하지 않습니다. 이런 부분 실패는 DoltHub의 커밋 이력을 보고 수동으로 복구해야 합니다.

  • ALTER TABLEAFTER절을 Dolt 파서가 거부합니다. 칼럼 위치는 지정하지 않았습니다.

7. 참고 자료


이 포스트는 Claude Code, Codex와 정상혁이 함께 작성했습니다.

부동소수점 오차와 돈 계산, BigDecimal Cloud Run과 Turso로 개인 앱 배포하기