Passing the Schema Does Not Mean It Is Valid
한국어 원문으로 표시합니다.
한 줄 요약
카탈로그는 스키마와 정책 두 층으로 거릅니다. 스키마만 통과한 엔티티는 형태만 맞을 뿐이라, 이름에 공백이 있어도 spec 을 오타로 써도 들어갑니다.
왜 두 층을 다 봐야 하나
앞 모듈에서 catalog-info.yaml 을 여러 개 썼습니다. 그런데 그 실습이 도는 곳에는 Backstage 백엔드가 없어서, 그 YAML 이 실제로 통과하는지 확인할 방법이 없었습니다.
이 실습은 Backstage 를 통째로 띄우지 않습니다 — 수백 MB 와 몇 분의 빌드가 걸립니다. 대신 카탈로그에 넣을 때 쓰는 바로 그 라이브러리를 직접 돌립니다.
스키마와 정책은 다른 것을 잡습니다
카탈로그는 두 층으로 거릅니다.
스키마 형태 — apiVersion·kind·필수 필드가 있는가
정책 규칙 — 이름 형식, 63자 제한, 모르는 루트 필드 거부
entitySchemaValidator 만 쓰면 스키마만 봅니다. 그래서 이름에 공백이 있어도, spec 을 spce 로 잘못 써도 통과합니다. 진짜 카탈로그는 그 위에 정책을 얹습니다.
모르는 루트 필드를 막는 이유가 특히 값집니다. spce 오타가 통과하면 그 서비스는 spec 없이 — owner 도 lifecycle 도 없이 — 카탈로그에 들어갑니다. CNPA 의 구조적 스키마 잘라내기와 같은 정신입니다.
참조가 관계를 만듭니다
owner: team-a 는 사실 group:default/team-a 를 가리키는 참조입니다. 이 참조들이 모여 그래프가 되고, Backstage 화면의 "누가 소유하고 무엇에 의존하는가" 가 전부 이 그래프를 걸어간 결과입니다. 참조가 깨지면 관계가 끊깁니다 — 그래서 이름 형식이 엄격합니다.
설정은 키 단위로 깊게 병합됩니다
마지막 파일이 전부를 덮어쓰는 게 아닙니다. 스칼라는 뒤가 이기고, 한쪽에만 있는 키는 살아남고, 배열은 통째로 교체됩니다. 이 배열 규칙이 함정입니다.
검증을 어디에서 하는가
카탈로그가 거절하는 것을 언제 알게 되느냐가 개발자 경험을 가른다. 같은 오류라도 알아채는 시점에 따라 비용이 완전히 다르다.
- 편집기에서 — 스키마를 IDE 에 연결해 두면 필드 이름을 잘못 쓴 순간 밑줄이 그인다. 가장 싸다.
- CI 에서 — 저장소에
catalog-info.yaml이 바뀌면 검증을 돌린다. 병합 전에 막히므로 잘못된 것이 아예 들어오지 않는다. - 카탈로그 수집 때 — 이미 병합된 뒤에 실패하고, 그 사실은 포털의 로그에만 남는다. 아무도 안 본다.
세 번째만 가지고 있는 조직이 대부분이고, 그것이 목록이 조용히 낡는 이유다. 검증을 CI 로 옮기는 데 드는 비용은 거의 없다. 이 실습에서 쓰는 것과 같은 라이브러리를 스크립트로 부르고, 실패하면 종료 코드로 알리면 끝난다. 그리고 그 스크립트는 새 저장소 템플릿에 함께 들어가야, 나중에 만들어지는 저장소에도 자동으로 따라간다.
카탈로그가 썩는 방식
개발자 포털이 실패하는 이유는 기능이 모자라서가 아니라 목록이 현실과 달라지기 때문이다. 아무도 믿지 않는 목록은 아무도 안 보게 되고, 안 보는 목록은 더 빨리 낡는다. 이 악순환이 시작되는 자리는 몇 군데로 정해져 있다.
소유자가 비어 있거나 없어진 팀을 가리킨다. 조직 개편이 있으면 그룹 이름이 바뀌는데 엔티티는 그대로 남는다. 참조가 깨진 엔티티는 화면에서 관계가 끊긴 채로 떠 있고, 장애가 났을 때 누구에게 연락해야 하는지 알 수 없다. 소유자 없는 엔티티를 정기적으로 세어 보고 그 수를 지표로 두는 것이 가장 값싼 방어다.
서비스가 사라져도 항목은 남는다. 저장소를 지워도 카탈로그에 마지막으로 읽은 내용이 남아 있으면, 존재하지 않는 서비스가 목록에 계속 뜬다. 원본이 사라지면 항목도 사라지도록 수집 방식을 잡아야 하고, 수동으로 등록한 항목은 특히 이 문제에서 자유롭지 않다.
등록만 하고 아무도 열지 않는다. 카탈로그가 값어치를 가지려면 그 항목에서 실제로 필요한 것으로 갈 수 있어야 한다. 대시보드, 온콜 담당, 최근 배포, 문서. 이 연결이 없으면 카탈로그는 이름과 소유자만 적힌 표에 그친다.
그래서 성숙한 팀은 카탈로그를 검사 대상으로 삼는다. 필수 필드가 채워져 있는지, 소유자가 실재하는 그룹인지, 수명 주기 값이 정해진 목록 안에 있는지를 주기적으로 확인하고, 새 저장소가 만들어질 때 템플릿이 올바른 catalog-info.yaml 을 함께 넣게 한다. 사람이 매번 손으로 쓰게 두면 형식은 저마다 달라지고, 그 차이가 쌓이면 목록으로 무엇을 집계하는 일 자체가 불가능해진다.
실무에서 진짜 중요한 것
모르는 루트 필드를 거부하는 정책을 반드시 켭니다. spec 을 spce 로 쓴 오타가 통과하면 그 서비스는 owner 도 lifecycle 도 없이 카탈로그에 들어갑니다. 등록은 성공했는데 소유자가 비어 있는 항목이 쌓이는 것이 목록이 썩는 가장 흔한 경로입니다.
참조 형식이 엄격한 이유는 그것이 관계 그래프이기 때문입니다. owner: team-a 는 group:default/team-a 를 가리키는 참조이고, 화면의 "누가 소유하고 무엇에 의존하는가" 는 전부 이 그래프를 걸어간 결과입니다. 참조가 깨지면 관계가 통째로 끊깁니다.
설정 병합에서 배열은 통째로 교체됩니다. 스칼라는 뒤가 이기고 한쪽에만 있는 키는 살아남는데, 배열만 규칙이 다릅니다. 환경별 파일에 배열을 반쯤 적어 두면 앞 파일의 항목이 전부 사라집니다.
다음 실습에서 이것들을 진짜 라이브러리로 직접 거절당해 가며 확인합니다.