지운 구글 캘린더 일정에 PATCH를 보냈더니, 오류 대신 200이 돌아왔습니다

· 약 3분

#구글캘린더 #API #업무자동화

결론부터 말씀드리면, 지운 구글 캘린더 이벤트 위에 보낸 PATCH는 제 실험에서 거절되지 않았습니다. 요청은 200으로 성공했고, 제목과 시각은 최신 값으로 바뀌었는데, 상태만 cancelled로 그대로 남았습니다. 오류가 나지 않았으니, 오류를 기다리며 만들어 둔 안전장치는 한 번도 돌지 않았습니다.

이 글에서 다룰 것은 두 가지입니다. 1) 무엇을 전제로 안전장치를 만들었고 그 전제가 어디서 어긋났는지, 2) 어떻게 고쳤고 무엇이 아직 확인되지 않았는지.

무엇을 만들고 있었나

할 일 앱(Super Productivity)에 잡아 둔 작업 시간 블록을 구글 캘린더 일정으로 옮기는 플러그인을 쓰고 있습니다. 이 플러그인은 캘린더 이벤트의 id를 할 일의 id에서 정해진 규칙으로 만듭니다. 기본 경로에서는 같은 할 일이 같은 이벤트 id를 갖습니다. 덕분에 “이 할 일의 일정이 이미 있는가"를 따로 찾지 않고 바로 고칠 수 있습니다.

같은 id가 돌아오지 못했습니다

문제는 지운 일정이었습니다. 구글 캘린더는 이벤트를 지워도 그 id를 완전히 비우지 않고, status: cancelled인 기록을 남깁니다. 저는 이것을 “무덤"이라고 불렀습니다.

id를 할 일에서 만드는 구조라, 일정을 지웠다가 다시 잡거나 할 일을 프로젝트 A에서 B로 옮겼다가 다시 A로 돌린 경우, 그 블록은 같은 id로는 캘린더에 돌아오지 못했습니다.

그래서 안전장치를 붙였습니다. 이벤트에 할 일 id를 속성으로 함께 적어 두고, id로 쓰기가 실패하면 그 속성으로 살아 있는 이벤트를 찾아 고치거나, 없으면 id 없이 새로 만들게 했습니다(이때 새 id는 구글이 정합니다). 다만 인증 오류, 요청 한도 초과, ‘없음’(404) 응답은 이 처리에서 뺐습니다. 404는 새로 만들라는 신호로 따로 다룹니다. 이 안전장치는 “무덤 위의 쓰기는 거절된다"는 전제 위에 서 있었습니다.

실제로 돌려 보니 거절이 오지 않았습니다

실물로 확인하려고 할 일을 프로젝트 A → B → A로 왕복시켰습니다. 결과는 이랬습니다.

옛 캘린더의 무덤    제목·시각은 최신 값으로 갱신됨
                   status는 cancelled 그대로
PATCH 응답         200 (성공)
안전장치           발동하지 않음

무덤 위의 PATCH는 실패하지 않았습니다. 조용히 성공했고, 일정은 여전히 지워진 상태였습니다. 안전장치는 “실패하면” 도는 구조였기 때문에, 실패가 오지 않자 아무 일도 하지 않았습니다.

이것은 제 캘린더에서 프로젝트 왕복이라는 한 가지 경우를 돌려 본 결과입니다. 다른 조작이나 다른 계정에서도 똑같이 응답한다고 넓혀 말하지는 않겠습니다.

어떻게 고쳤나

일정을 만들 때 보내는 내용에 status: 'confirmed'를 넣고, 고칠 때 보내는 PATCH에도 그 값이 실리게 했습니다. 무덤이 된 일정의 상태를 같은 PATCH로 되돌리려는 것입니다. 당시 기록에서는 살아 있는 일정에는 해가 없다고 판단했지만, 이것도 실제 캘린더에서 확인한 것은 아닙니다. 기존 안전장치는 지우지 않고, 진짜로 거절이 오는 경우를 위해 남겨 두었습니다.

당시 커밋 기록에 남긴 확인 범위는 이렇습니다.

자동 테스트        38건 통과 (새로 넣은 2건은 고치기 전에 실패하는 것부터 확인)
타입 검사          통과
배포 파일          status: "confirmed"가 실리는 것 확인
실제 캘린더 부활    아직 확인하지 못함

실제 구글 캘린더에서 무덤이 되살아나는지는 이 기록을 남긴 시점에 확인하지 못했습니다. 위의 확인은 코드 쪽에서 값이 실리는 것까지이고, 구글이 그 값을 받아 실제로 일정을 되살리는지는 따로 확인해야 하는 일로 남아 있습니다.

예상되는 부작용도 하나 남깁니다. 이 변경이 의도대로 동작한다면, 캘린더 화면에서 블록을 직접 지워도 다음 동기화 때 다시 살아날 것입니다. 실제로 그렇게 되는지는 위와 같은 이유로 아직 확인하지 못했습니다. 이 일정들은 할 일 앱이 주인인 복사본이라, 그렇게 되더라도 의도된 동작으로 보기로 했습니다.

배운 것

오류를 전제로 만든 안전장치는 오류가 나지 않으면 돌지 않습니다. 이번에는 성공 응답이 돌아왔기 때문에, 실패를 조건으로 한 안전장치가 문제를 잡지 못했습니다.

이번에 제가 바꾼 습관은 하나입니다. “이 요청은 거절될 것이다"도 확인하기 전까지는 가설로 둡니다. 거절을 기다리는 코드를 짜기 전에, 그 거절이 실제로 오는지부터 한 번 돌려 봅니다.