← ClaudeAtlas

sqlite-expertlisted

SQLite 파일을 직접 읽고 쓰거나, 읽기 전용 조회·WAL·잠금·마이그레이션·동적 테이블명 주입 같은 SQLite 고유 문제를 다룰 때 사용한다.
LeeYudok/doksam-skills · ★ 10 · API & Backend · score 77
Install: claude install-skill LeeYudok/doksam-skills
# sqlite-expert SQLite **엔진 고유의 문제**가 대상이다. 스키마 설계 이론·PostgreSQL 운영은 `db-expert`, Go 코드 관용구는 `go-expert` 가 맡는다. SQLite 는 "작은 RDB"가 아니라 **파일 하나가 데이터베이스인 라이브러리**다. 서버가 없다는 사실에서 이 문서의 거의 모든 항목이 파생된다. ## 1. 남의 파일을 읽을 때 — 원본을 바꾸지 않는다 앱이 쓰고 있는 캐시·데이터 파일을 조회하는 작업이 흔하다. **원본을 건드리면 그 앱의 데이터가 깨진다.** 기본은 읽기 전용이다. ```go dsn := "file:" + path + "?mode=ro&_pragma=busy_timeout(5000)" ``` - **`mode=ro`** — 쓰기를 엔진 수준에서 막는다. 애플리케이션 규율에 기대지 않는다. - **`busy_timeout`** — 다른 프로세스가 쓰는 중이면 즉시 실패하지 않고 기다린다. 없으면 산발적인 `database is locked` 로 나타난다. - **URI 파일명에서 `?`·`#` 은 구분자다.** 경로에 들어 있으면 퍼센트 인코딩한다. 경로 문자열을 그냥 이어붙이면 파일을 못 찾는다. ### 곁 파일까지 확인한다 읽기만 해도 `-wal`·`-shm`·`-journal` 이 생기면 **원본 폴더를 오염시킨 것**이다. WAL 모드 DB 를 열면 실제로 발생할 수 있다. 정말 건드리면 안 되는 파일은 `immutable=1` 을 고려하되, 이건 "파일이 변하지 않는다"는 약속이므로 앱이 쓰는 중이면 쓰지 않는다. **가장 안전한 순서**: 사본을 떠서 사본을 연다. 그럴 수 없으면 `mode=ro` + 곁 파일 검사. ### 이건 테스트로 고정한다 문서에만 적힌 "읽기 전용"은 다음 리팩터링에서 사라진다. 회귀 테스트로 못 박는다. ```go // 조회란 조회를 다 돌린 뒤 파일 해시가 같은지, 곁 파일이 안 생겼는지 before := sha256sum(path) // ... Rooms / Messages / Count / Search ... if after := sha256sum(path); after != before { t.Error("원본이 바뀌었다") } for _, s := range []string{"-wal", "-shm", "-journal"} { if _, err := os.Stat(path + s); !os.IsNotExist(err) { t.Error("곁 파일이 생겼다") } } ``` 쓰기가 실제로 막히는지도 확인한다 — `mode=ro` 로 연 뒤 `DELETE` 가 실패해야 한다. ## 2. 동적 테이블·컬럼명 — 유일한 방어선 테이블명은 **플레이스홀더로 넘길 수 없다.** 스키마가 `Chat_<방ID>` 처럼 데이터에 따라 갈리는 구조면 문자열 조립이 불가피하다. 그러면 검증이 유일한 방어선이 된다. ```go var roomIDRe = regexp.MustCompile(`^[0-9