같은 서비스의 데스크톱, WebGL, 모바일 클라이언트가 공지를 조금씩 다르게 보여주고 있었다. 한쪽에서는 매번 팝업이 열리고 다른 쪽에서는 한 번 읽은 공지가 다시 나오지 않았다. API 응답은 같았지만 각 클라이언트가 표시 대상과 읽음 상태를 별도 규칙으로 해석했다.
화면 모양을 똑같이 만드는 것보다 공지가 언제, 누구에게, 몇 번 보이는지 계약을 맞추는 게 먼저였다.
공지의 상태를 명시했다
공지 데이터에 게시 기간, 대상 플랫폼, 중요도, 읽음 처리 기준을 포함하고 클라이언트가 임의로 날짜와 플래그를 조합하지 않게 했다.
{
"id": "notice-example",
"revision": 3,
"display": "once-per-revision",
"platforms": ["desktop", "web", "mobile"]
}
문구와 대상 정보는 예시이며 실제 내부 값은 글에 남기지 않았다.
읽음 상태의 주인을 정했다
기기별로만 읽음 상태를 저장하면 다른 클라이언트에서 다시 보인다. 계정 전체에 공유할 읽음은 서버가 관리하고, 로그인 전 공지처럼 기기 범위인 값은 로컬에 두었다.
팝업을 화면에 띄운 순간과 사용자가 확인한 순간도 구분했다. 네트워크 오류로 확인 저장이 실패하면 제한적으로 다시 시도하고, 매 프레임 팝업을 다시 열지 않았다.
여러 공지가 동시에 유효할 때는 중요도와 게시 시각으로 표시 순서를 정하고, 한 번에 팝업을 겹쳐 열지 않았다. 긴급 공지와 일반 공지가 서로 다른 닫기 정책을 갖는다면 API 상태에도 그 차이가 드러나야 했다.
공통 시나리오로 비교했다
클라이언트 구현 코드가 달라도 같은 입력에서 기대 행동을 표로 맞췄다.
새 공지 첫 로그인 -> 한 번 표시
같은 revision 재로그인 -> 표시 안 함
revision 증가 -> 다시 표시
게시 기간 종료 -> 표시 안 함
오프라인 후 연결 복구 -> 현재 유효 공지만 처리
각 클라이언트에서 이 시나리오를 실행해 결과 차이를 찾았다.
계정 전환, 언어 변경, 앱 업데이트 뒤에도 같은 revision 규칙이 유지되는지 확인했다. 클라이언트 로컬 캐시만 지웠다고 서버 읽음 상태까지 사라지면 안 됐고 반대도 마찬가지였다.
공유 기능은 계약으로 맞춰야 했다
한 클라이언트의 코드를 다른 쪽에 복사할 수 없는 경우가 많았다. 대신 상태와 전이, API 의미, 테스트 시나리오를 공유하면 서로 다른 기술 스택에서도 같은 제품 동작을 만들 수 있었다.
이 작업 뒤 여러 클라이언트가 쓰는 기능은 화면 캡처만 비교하지 않는다. 데이터 계약과 상태 소유자, 공통 시나리오를 먼저 맞춘다. 일관성은 같은 코드보다 같은 규칙에서 나왔다.