동기화가 실패하면 목록 조회, 로컬 검사, 서버 저장, 결과 기록 중 어디에서 멈췄는지 나눠 확인해야 한다. 연결 오류라는 짧은 안내만으로 인증을 다시 만들거나 같은 작업을 반복하면 실제로 막힌 지점을 놓칠 수 있다.
메모장 상태 동기화에서도 목록은 읽히는데 저장은 계속 실패했다. 루틴을 멈추고 조사한 수리 기록에 따르면, 서버 요청을 보내기 전에 관리 클라이언트의 정책 검사가 저장을 막고 있었다. 원래 메모·계정·사이트 정보는 제외하고 이 호출 경로와 수리 방법을 살펴본다.
같은 실패 안내 안에서 멈춘 단계를 찾았다
당시 기록에서는 대화 목록을 가져오는 단계와 메모장 상태를 저장하는 단계를 구별했다. 읽기 경로는 동작했지만 상태 인덱스 갱신에서 LocalOnlyError가 나왔다. 확인한 코드에서 이 오류는 로컬 정책에 의한 거절을 나타낸다. 클라이언트가 허용하지 않는 작업을 요청 전에 막는 것이다.
보관된 수리 전 동기화 코드는 하위 프로세스가 실패하면 일반적인 동기화 실패 안내와 종료 코드만 남겼다. 오류 원인이 그 사이에 빠지면 상위 로그만 읽는 사람은 인증, 네트워크, 애플리케이션 정책 중 무엇을 확인해야 할지 알기 어렵다.
따라서 조사 순서는 실패한 단계와 실제 호출 경로를 맞추는 것이었다. 목록을 이미 읽었는지, 서버 호출 전에 예외가 났는지, 응답을 받았는지, 마지막 성공 기록을 갱신했는지를 따로 읽는다. 이번 집필에서는 과거 기록과 현재 소스를 대조했으며 실제 동기화를 다시 실행하지 않았다.
인증을 가져온 경로에 업무 정책도 붙어 있었다
수리 전 메모장 서버는 프로젝트의 관리 래퍼를 불러왔다. 집필 때 확인한 래퍼도 다른 홈페이지의 Cloudflare 관리 클라이언트를 가져오고 있었다. 수리 기록은 이 인증 재사용 경로를 통해 그 홈페이지의 로컬 집필 정책까지 적용된 것을 원인으로 설명한다.
해당 홈페이지는 당시 원고를 로컬에 저장하고 서버 쓰기를 막는 정책을 쓰고 있었다. 관리 클라이언트는 HTTP 요청 전에 이를 검사한다. 홈페이지 집필에 필요한 제한이지만, 별도로 승인된 메모장 상태 저장까지 같은 작업으로 취급하면 저장이 막힌다.
이 경로에서는 HTTP 방식만으로 읽기와 쓰기를 나누기도 어렵다. D1 조회는 POST로 전달되지만 읽기 SELECT와 데이터 갱신의 의미는 다르다. 기존 정책은 좁은 읽기 조회를 허용하고 갱신을 거절한다. 읽기가 된다는 사실만으로 저장 권한이나 저장 경로까지 정상이라고 결론 내릴 수 없는 이유다.
인증은 재사용하고 데이터 연결의 대상을 좁혔다
수리 후 메모장 서버는 관리·배포 기능이 붙은 클라이언트 대신 목적을 제한한 데이터 연결을 사용한다. 중앙 인증은 원래 위치에서 읽고 복사하지 않는다. 연결 설정과 리소스 장부의 계정·데이터베이스·소유자 항목이 일치하는지 확인하고, 요청한 DB가 정해 둔 대상과 다르면 거절한다.
이 연결은 허용한 운영 데이터 작업과 대상 테이블을 제한한다. 스키마 변경이나 여러 구문을 한꺼번에 넣는 요청, 이전 대화·메시지를 삭제하는 작업을 받아들이지 않는 조건도 있다. 다른 홈페이지의 정책 파일을 풀어 전체 프로그램의 권한을 넓힌 방식이 아니다.
다만 목적지 제한과 소유자별 접근 검사는 같은 기능이 아니다. 어떤 사람의 어떤 메모를 읽고 바꿀 수 있는지는 각 도구가 자신의 SQL과 소유자 조건으로 지켜야 한다. 이 작은 연결을 모든 SQL 입력을 안전하게 판정하는 범용 보안 장치라고 설명할 수는 없다.
- 공유할 부분: 중앙 인증의 보관과 제자리 사용.
- 연결이 확인할 부분: 등록 계정·DB·소유자 정보의 일치와 정해진 DB 대상.
- 도구가 확인할 부분: 현재 사용자와 자료의 소유 관계, 허용한 업무 작업.
- 별도로 유지할 부분: 다른 프로젝트의 로컬 전용·발행·배포 정책.
고정 오류 코드를 남기고 마지막 성공 기록을 지킨다
수리 후 코드는 전송 실패, 인증 거절, DB 대상 불일치 같은 상황을 정해진 오류 코드로 전달한다. 상위 동기화 프로세스는 하위 출력 전체를 로그에 복사하지 않고 허용한 형태의 고정 코드만 추린다. 문제를 구별할 정보는 남기되, 메모 원문이나 자격증명까지 함께 기록하지 않으려는 선택이다.
상태 저장에도 대기 사본과 마지막 성공 사본이 나뉜다. 하위 저장이 성공한 뒤에만 대기 사본으로 성공 사본을 교체한다. 저장이 실패했다면 실패 원인은 남기되 이전에 확인한 상태까지 새 성공처럼 덮지 않는다. 보관된 시험 정의에도 실패 뒤 성공 사본의 바이트가 유지되는지 확인하는 항목이 있다.
내용이 같을 때도 결과를 구별한다. 현재 코드는 같은 목록이면 서버 갱신과 저장 시각 변경을 생략하고, 같은 작업 상태면 하위 저장 호출도 생략한다. 새 저장 성공, 변경 없음, 실패를 같은 성공 문구로 합치지 않아야 로그의 시각과 화면의 상태를 해석할 수 있다.
수리의 완료 기록과 이번 확인을 나눠 남겼다
과거 수리 기록에는 회귀 검사 13개와 실제 동기화 뒤 재조회, 같은 목록의 중복 쓰기 생략이 남아 있다. 이번에는 그 기록과 시험 정의를 읽었고 원제품 시험이나 운영 D1 호출을 재실행하지 않았다. 수리 전 코드는 보관 ZIP에서 필요한 부분만 읽고 현재 코드와 비교했다.
당시 집필에서 대조한 홈페이지의 정책·클라이언트 파일 2개는 수리 기록의 해시와 같았다. 원래 메모장 루틴은 일시정지 상태로 두었다. 같은 인증을 쓰는 프로그램을 고치더라도 다른 프로젝트의 제한을 해제하거나 보류된 작업을 함께 시작할 권한이 생기는 것은 아니다.
비슷한 문제를 조사한다면 실제로 불러온 파일, 그 파일이 적용하는 정책, 원격 요청 전후의 실패 위치를 함께 기록한다. 수정 후에는 새 연결의 대상 제한과 기존 프로젝트 정책의 보존을 따로 확인한다. 오류 안내나 화면 상태 하나만 보고 전체 연결을 정상이라고 보고하지 않는다.