LabHub
배우기 러닝패스 코스

필드 번호를 바꿨더니 옛 클라이언트가 조용히 틀린 값을 읽었다 · 계약 검사는 리뷰어가 아니라 스크립트가 한다 · 실습

계약 검사기를 만든다

LabHub 에서 이어서 보기

목표

.proto 를 비교해 와이어 호환성 위반을 한 줄씩 출력하는 protocheck.py
만듭니다. 규칙을 하나씩 더해 가며 픽스처 8쌍과 채점기가 숨겨 둔 쌍을 통과시키고,
마지막에는 디렉터리 단위로 돌리는 check-all.sh 를 만들어 CI 에 넣을 수 있는
형태로 마무리합니다.

왜 중요한가

앞 실습에서 번호 하나를 바꾸면 옛 클라이언트가 오류 없이 틀린 값을 읽는 것을
봤습니다. 그 변경은 코드 리뷰에서 눈에 잘 띄지 않습니다 — diff 에는 숫자 두 개가
바뀐 것뿐이고, 컴파일도 테스트도 통과합니다. 사람이 매번 잡아낼 수 있는 종류의
실수가 아닙니다. 그래서 계약 검사는 리뷰어가 아니라 스크립트가 해야 합니다.
검사기의 규칙은 전부 공식 문서의 "안전한 변경·안전하지 않은 변경" 목록에서
나오며, 여러분이 만드는 것은 그 목록을 기계가 읽게 옮긴 것입니다.

단계

1. /root/grpc/compat/protocheck.py 를 만들고 python3 protocheck.py --dump <file.proto> 가 메시지별 필드를 <메시지> <번호> <타입> <이름> 으로, 예약을 <메시지> reserved <번호><메시지> reserved "<이름>" 으로 한 줄씩 출력하게 하세요. 주석(//, /* */)·공백·옵션([deprecated=true])은 무시하고, 9 to 11 은 세 번호로 펴고, 중첩 메시지는 Outer.Inner 로 부릅니다.
2. python3 protocheck.py <old.proto> <new.proto> 에 첫 규칙을 넣으세요 — 옛 번호가 새 파일에 없고 reserved 도 아니면 REMOVED_NOT_RESERVED <메시지>.<이름>=<번호>. 위반이 있으면 exit 1, 없으면 마지막 줄에 OK 를 찍고 exit 0.
3. 같은 이름이 다른 번호로 옮겨 갔으면 RENUMBERED <메시지>.<이름> <옛번호>-><새번호>.
4. 같은 번호·같은 이름에 타입이 다르면 TYPE_CHANGED <메시지>.<이름>=<번호> <옛타입>-><새타입>, 같은 번호에 이름도 타입도 다르면 NUMBER_REUSED <메시지>#<번호> <옛이름>:<옛타입>-><새이름>:<새타입>.
5. 옛 파일이 reserved 한 번호(범위 포함)를 새 파일이 필드로 쓰면 REUSED_RESERVED <메시지>#<번호> <이름>.
6. 같은 번호·같은 타입에 이름만 다르면 WARN RENAMED <메시지>#<번호> <옛이름>-><새이름> 을 찍되 exit 코드에는 영향을 주지 마세요(경고만 있으면 OK · exit 0).
7. /opt/app/grpc/compat/ 의 8쌍을 모두 돌려 /root/grpc/compat/07-report.txt<case> OK 또는 <case> FAIL 을 한 줄씩 적으세요.
8. /root/grpc/compat/check-all.sh <디렉터리> 를 쓰세요 — 그 아래 old.protonew.proto 가 있는 하위 디렉터리마다 검사기를 돌려 <case>: OK / <case>: FAIL 을 찍고, FAIL 이 하나라도 있으면 exit 1.

참고

단계 8개

  1. .proto 를 읽는 파서를 만든다
  2. 예약 없이 지운 번호를 잡는다
  3. 번호가 옮겨 간 것을 잡는다
  4. 타입 변경과 번호 재사용을 잡는다
  5. 예약된 번호를 꺼내 쓴 것을 잡는다
  6. 이름 변경은 경고만
  7. 픽스처 8쌍을 전부 돌린다
  8. 디렉터리 단위 검사 스크립트