shipping_policies 의 값 하나를 고쳐야 했고 화면에서 고치는 방법과 UPDATE 로 직접 고치는 방법이 있었다. DB 가 빠르니 그것을 골랐고 값은 바뀌었는데, 화면 경로가 하는 일을 코드로 읽어 보니 값 저장 말고 셋이 더 있었다.
Table of contents
Open Table of contents
원인 — 화면 경로가 함께 하는 셋
화면에서 저장하면 histories 에 수정 이력을 남기고 그 정책에 연결된 계정의 needs_sync 를 켜고 외부 마켓으로 보내는 PropagatePolicy 작업을 큐에 넣는다. 셋 다 모델의 saved 이벤트에 걸려 있다.
UPDATE 로 직접 고치면 이 셋이 전부 안 일어난다. Laravel 공식 문서의 Eloquent 이벤트 항목도 대량 갱신·삭제 쿼리로는 모델을 실제로 읽지 않으므로 saved·updated·deleting·deleted 가 발행되지 않는다고 경고한다. 이력도 없고 계정 상태도 그대로이고 외부에는 옛 값이 남는다. 내부 값만 조용히 바뀐 상태가 되고 그 조용함이 나중에 다른 증상으로 돌아온다.
판단 기준 — 선택의 축
이걸 알고 나니 두 경로가 빠른 쪽과 느린 쪽의 차이가 아니었다. 외부에 반영돼야 하면 전파 작업이 생기는 화면 경로를 써야 하고, 내부 값만 조정하고 전파를 피해야 하면 DB 직접 경로가 맞다.
무엇을 고칠 것인가가 아니라 전파를 일으킬 것인가가 선택의 축이었다. 축을 잘못 잡으면 빠른 쪽을 고르는 것이 항상 합리적으로 보인다.
주석 처리된 가드
더 헷갈렸던 것이 있다. 그 화면 코드에 차단 조건이 둘 있었는데 둘 다 주석 처리돼 있었다.
// return '일주일 이내 재수정 할 수 없습니다';
// if ($manager->is_provider) abort(403);
코드만 훑으면 abort(403) 으로 화면 수정이 막혀 있으니 DB 로 해야 한다고 읽기 쉬운데 실제로는 열려 있다. 그리고 운영에는 정책을 수정하지 말고 새로 만들어 연동하라는 규칙이 있었다. 이것은 코드가 강제하는 제약이 아니라 그 전파를 피하려고 사람이 세운 가드였다. 코드가 막는 것과 사람이 안 하기로 한 것을 구분하지 않으면 어느 쪽도 정확히 읽히지 않는다.
반대 방향의 사례
삭제가 연쇄를 일으키는 경우도 봤다. 중복 반품을 정리하려고 DELETE 로 지우려 했는데 화면의 삭제 버튼은 단순 삭제가 아니었다. 하위 항목을 지우고 남은 것이 없으면 상위도 지우고 연관 상태를 이전 값으로 복원하고 상위 상태를 재계산하고 이력을 남기고 이벤트를 발행한다. DB로 지우면 그 여섯이 안 일어나고 데이터가 어긋난 채 남는다.
저장 함수가 삽입이 아닌 경우도 있었다. 작업을 하나 만든다고 생각한 함수가 실제로는 상위 레코드를 firstOrCreate() 로 먼저 만들고 그 안에서 기존 것을 찾으면 조용히 건너뛰었다. 기존 것을 안 지우고 부르면 오류 없이 아무 일도 일어나지 않는다.
조치 — 고치기 전에 보는 것
그 값을 바꾸는 정식 경로가 하는 일을 코드로 읽는다. 저장 말고 무엇이 더 일어나는지 보면 대개 이력과 연관 상태와 외부 전송 중 하나 이상이 나온다. 이번 건은 두 가지 길이 있었는데 화면에서 같은 값을 다시 저장해 셋을 내보내는 것과 DB 값을 그대로 두고 빠진 셋을 손으로 보충하는 것이다. 앞쪽을 골랐다. 이유는 손으로 보충하면 PropagatePolicy 가 무엇을 보내는지까지 다시 읽어야 해서다. 대신 histories 에 같은 값의 수정 기록이 두 번 남는 것을 감수했다.
부수효과가 필요한지 정하고 그 선택을 기록에 남긴다. 필요하면 정식 경로를 쓰고 필요 없으면 직접 경로를 쓰되, 직접 경로를 골랐으면 빠진 부수효과를 목록으로 만든다. 나중에 왜 이력이 없느냐는 물음이 나왔을 때 답할 수 있어야 한다.
정리
- 같은 값을 바꾸는 두 경로는 부수효과가 다르다
- 정식 경로는 대개 이력과 연관 상태와 외부 전송을 함께 처리한다
- 선택의 축은 무엇을 고치느냐가 아니라 전파를 일으킬 것인가다
- 축을 잘못 잡으면 빠른 쪽이 항상 합리적으로 보인다
- 주석 처리된 가드를 실행되는 제약으로 읽지 않는다
- 코드가 막는 것과 사람이 안 하기로 한 것은 다르다
- 직접 경로를 골랐으면 빠진 부수효과를 목록으로 남긴다