FastAPI — 타입이 곧 계약이다 · ETag로 덮어쓰기 충돌을 막는 API · 실습
ETag로 덮어쓰기 충돌을 막는 API
목표
두 사용자가 같은 초안을 수정할 때 SQL 조건부 갱신과 HTTP 응답으로 유실을 막습니다.
왜 중요한가
정상 요청 한 번의 성공은 경계값과 장애 복구를 보장하지 않습니다. 이 실습은 각 함수의 계약을 작게 구현하거나 테스트한 뒤 실제 실행으로 연결합니다. 코드가 존재하는지나 보고서 문구만 보지 않고 결과·예외·저장 상태를 검사합니다. 앞 단계의 코드를 유지하며 다음 단계로 진행하세요.
단계
1. /root/work/fa-conditional-lab/service.py에서 init_db(path)는 SQLite 파일에 notes(id TEXT PRIMARY KEY, title TEXT NOT NULL, version INTEGER NOT NULL)를 생성하세요. 반복 호출해도 기존 데이터를 지우지 않습니다. 첫 준비는 다음 명령으로 합니다.
mkdir -p /root/work/fa-conditional-labcp /opt/fixtures/practice_depth/fa-conditional-lab/* /root/work/fa-conditional-lab/cd /root/work/fa-conditional-lab2. /root/work/fa-conditional-lab/service.py에서 create_note(path, note_id, title)는 버전 1로 메모를 INSERT하고 커밋하세요. 같은 id는 sqlite3.IntegrityError로 거절하며 기존 제목을 덮어쓰지 않습니다.
3. /root/work/fa-conditional-lab/service.py에서 read_note(path, note_id)는 {id, title, version} 딕셔너리 또는 None을 반환하세요. SQL에 id를 문자열 연결하지 말고 바인딩합니다.
4. /root/work/fa-conditional-lab/service.py에서 etag(version)는 정수 버전을 큰따옴표까지 포함한 문자열로 반환하세요. 예: 버전 3은 문자열 '"v3"'입니다.
5. /root/work/fa-conditional-lab/service.py에서 parse_match(header)는 큰따옴표 안 v와 양의 십진 정수 하나인 태그만 받아 버전을 반환하세요. None, weak 태그, 와일드카드, 목록, v0 등은 ValueError입니다. 예: '"v12"' → 12.
6. /root/work/fa-conditional-lab/service.py에서 update_note(path, note_id, title, version)는 id와 version이 모두 일치할 때만 제목을 갱신하고 버전을 1 올리세요. 변경 행이 하나면 True, 없으면 False입니다. 동시 요청에도 승자는 하나여야 합니다.
7. /root/work/fa-conditional-lab/service.py에서 put_note(path, note_id, title, header)는 없는 id 404, 조건 누락 428, 지원하지 않는 조건·버전 불일치 412, 성공 204를 반환하세요. 거절 시 DB를 바꾸지 않습니다.
8. /root/work/fa-conditional-lab/service.py에서 create_app(path)는 GET /notes/{note_id}와 PUT /notes/{note_id}를 제공하세요. GET은 메모 JSON과 ETag, PUT은 {title: 1~100자 문자열}·If-Match를 받아 위 결과 코드로 빈 응답을 반환합니다. 빈 제목은 422, 없는 GET은 404입니다.
참고
- 패키지는 이미지에 설치돼 있습니다. 인터넷 연결이나 pip install은 필요하지 않습니다.
- 준비 복사는 최초 한 번만 하세요. 다시 복사하면 작업 파일이 초기화됩니다.
- 채점은 여러분의 service.py를 불러와 다른 id와 임시 저장소로 실행합니다. 결과를 상수로 박지 마세요.
- 각 단계 채점은 45초 안에서 실행됩니다. 무한 루프나 실제 대기를 만들지 마세요.
8단계
- 버전을 가진 저장소를 만든다
- 첫 저장의 버전을 고정한다
- 없는 메모와 있는 메모를 구분한다
- 강한 ETag를 만든다
- 지원하는 조건 문법만 받는다
- 비교와 쓰기를 하나의 SQL로 묶는다
- 실패를 HTTP 결과로 분류한다
- 오래된 화면 저장을 HTTP로 재현한다