같은 서비스의 데스크톱, WebGL, 모바일 클라이언트가 공지를 조금씩 다르게 보여주고 있었다. 한쪽에서는 매번 팝업이 열리고 다른 쪽에서는 한 번 읽은 공지가 다시 나오지 않았다. API 응답은 같았지만 각 클라이언트가 표시 대상과 읽음 상태를 별도 규칙으로 해석했다.
화면 모양을 똑같이 만드는 것보다 공지가 언제, 누구에게, 몇 번 보이는지 계약을 맞추는 게 먼저였다.
공지 상태의 명시
공지 데이터에 게시 기간, 대상 플랫폼, 중요도, 읽음 처리 기준을 포함하고 클라이언트가 임의로 날짜와 플래그를 조합하지 않게 했다.
{
"id": "notice-example",
"revision": 3,
"display": "once-per-revision",
"platforms": ["desktop", "web", "mobile"]
}
문구와 대상 정보는 예시이며 실제 내부 값은 글에 남기지 않았다.
읽음 상태의 소유자
기기별로만 읽음 상태를 저장하면 다른 클라이언트에서 다시 보인다. 계정 전체에 공유할 읽음은 서버가 관리하고, 로그인 전 공지처럼 기기 범위인 값은 로컬에 두었다.
팝업을 화면에 띄운 순간과 사용자가 확인한 순간도 구분했다. 네트워크 오류로 확인 저장이 실패하면 제한적으로 다시 시도하고, 매 프레임 팝업을 다시 열지 않았다.
여러 공지가 동시에 유효할 때는 중요도와 게시 시각으로 표시 순서를 정하고, 한 번에 팝업을 겹쳐 열지 않았다. 긴급 공지와 일반 공지가 서로 다른 닫기 정책을 갖는다면 API 상태에도 그 차이가 드러나야 했다.
공통 시나리오 기반 클라이언트 비교
클라이언트 구현 코드가 달라도 같은 입력에서 기대 행동을 표로 맞췄다.
새 공지 첫 로그인 -> 한 번 표시
같은 revision 재로그인 -> 표시 안 함
revision 증가 -> 다시 표시
게시 기간 종료 -> 표시 안 함
오프라인 후 연결 복구 -> 현재 유효 공지만 처리
각 클라이언트에서 이 시나리오를 실행해 결과 차이를 찾았다.
계정 전환, 언어 변경, 앱 업데이트 뒤에도 같은 revision 규칙이 유지되는지 확인했다. 클라이언트 로컬 캐시만 지웠다고 서버 읽음 상태까지 사라지면 안 됐고 반대도 마찬가지였다.
공유 기능의 클라이언트 계약
한 클라이언트의 코드를 다른 쪽에 복사할 수 없는 경우가 많았다. 대신 상태와 전이, API 의미, 테스트 시나리오를 공유하면 서로 다른 기술 스택에서도 같은 제품 동작을 만들 수 있었다.
이 작업 뒤 여러 클라이언트가 쓰는 기능은 화면 캡처만 비교하지 않는다. 데이터 계약과 상태 소유자, 공통 시나리오를 먼저 맞춘다. 일관성은 같은 코드보다 같은 규칙에서 나왔다.
공식 문서 기반의 추가 정리
작업 당시에는 눈앞의 오류를 먼저 고쳤지만, 이후 기술 문서와 맞춰 보며 확인 순서를 조금 더 구체화했다.
Unity 실행 순서 문서는 Awake, OnEnable, Start, Update 같은 엔진 이벤트가 어느 구간에서 호출되는지 설명한다. 하지만 로딩·성공·빈 결과·실패 같은 업무 상태의 순서까지 대신 정해 주지는 않는다. 두 종류의 순서를 섞어 생각하면 콜백이 한 프레임 늦게 왔다는 이유만으로 이전 화면 상태가 다시 살아날 수 있다.
그래서 수명주기 메서드는 상태 전이를 직접 흩어 놓는 장소보다, 한 곳에 모인 전이 함수를 호출하는 경계로 쓰는 편이 추적하기 쉬웠다.
다음에 다중 클라이언트 공지 동기화 같은 문제를 보면 아래 순서부터 확인하려고 한다.
- 입력 조건과 현재 상태를 한 줄로 기록
- 성공·빈 값·실패·취소를 구분
- 늦은 응답과 중복 이벤트 처리
- 실제 대상 환경에서 회귀 확인
새 응답을 적용하기 전에 요청 식별자와 현재 선택 조건이 여전히 같은지 확인해야 오래된 응답을 버릴 수 있다.
공식 참고 자료
- Unity 2021.3 이벤트 함수 실행 순서 — MonoBehaviour 이벤트와 프레임 단계의 실행 순서
- ECMA-334 C# 언어 사양 6판 — decimal·enum·예외를 포함한 C# 언어 규칙