CBA — Backstage 인증 어소시에이트 · 소프트웨어 카탈로그 · 이론
엔티티와 관계 — 소유권이 카탈로그의 심장인 이유
한 줄 요약
카탈로그는 서비스 목록이 아니라 관계 그래프 입니다. 엔티티 종류를 외우는 것보다, 각 종류가 어떤 질문에 답하기 위해 존재하는지를 이해하는 편이 시험과 실무 모두에 유용합니다.
왜 이게 필요했나
"서비스 목록"만 있으면 이런 질문에 답할 수 없습니다.
- 이 API 를 없애면 뭐가 깨지나? → 의존 관계 가 필요
- 이 팀이 해체되면 누가 인수하나? → 소유권 이 필요
- 결제 도메인 전체의 상태는? → 묶음(System/Domain) 이 필요
- 이 데이터베이스는 누구 것인가? → Resource 라는 종류가 필요
그래서 Backstage 는 엔티티를 여러 종류로 나누고 그 사이의 관계를 저장합니다.
어떻게 동작하나
엔티티 여덟 종류
| kind | 답하는 질문 | 대표 필드 |
| --- | --- | --- |
| Component | 우리가 만들고 배포하는 소프트웨어 조각 | spec.type(service/website/library), spec.lifecycle, spec.owner, spec.system |
| API | 컴포넌트가 노출하거나 소비하는 인터페이스 | spec.type(openapi/asyncapi/graphql/grpc), spec.definition |
| Resource | 컴포넌트가 필요로 하는 인프라 | spec.type(database/s3-bucket/queue) |
| System | 함께 동작하는 엔티티들의 묶음 | spec.owner, spec.domain |
| Domain | 여러 시스템을 아우르는 사업 영역 | spec.owner |
| Group | 팀·조직 단위 | spec.type(team), spec.children, spec.profile |
| User | 사람 | spec.memberOf |
| Location | 다른 엔티티가 있는 위치를 가리키는 표지판 | spec.type, spec.targets |
여기에 Template(스캐폴더용)이 더해집니다. 그리고 spec.lifecycle 은 자유 문자열이지만 관례적으로 experimental / production / deprecated 를 씁니다 — 폐기 예정 서비스를 목록에서 걸러 내는 데 쓰입니다.
관계(relations)는 계산되는 것이다
중요한 포인트입니다. catalog-info.yaml 에 쓰는 것은 spec.owner, spec.system, spec.providesApis 같은 선언 이고, 카탈로그가 이를 읽어 양방향 관계 를 계산해 저장합니다.
spec.owner: group:team-checkout → ownedBy / ownerOfspec.system: commerce → partOf / hasPartspec.providesApis: [checkout-api] → providesApi / apiProvidedByspec.consumesApis: [payments-api] → consumesApi / apiConsumedByspec.dependsOn: [resource:checkout-db] → dependsOn / dependencyOf그래서 providesApis 를 한쪽에만 써도 API 엔티티 페이지에서 "이 API 를 제공하는 컴포넌트"가 보입니다. 반대 방향을 손으로 쓸 필요가 없습니다. 시험에서 "관계를 양쪽 파일에 모두 적어야 하는가?" 를 물으면 답은 아니오입니다.
엔티티 참조 형식
관계를 쓸 때 다른 엔티티를 가리키는 문자열 형식이 정해져 있습니다.
[<kind>:][<namespace>/]<name>세 부분 중 kind 와 namespace 는 생략 가능하고, 생략하면 문맥에 따른 기본값이 적용됩니다. namespace 의 기본값은 default 입니다.
| 쓴 것 | 해석 |
| --- | --- |
| team-checkout | 문맥의 기본 kind + default 네임스페이스 |
| group:team-checkout | group:default/team-checkout |
| group:payments/team-checkout | 네임스페이스까지 명시 |
spec.owner 같은 필드는 기본 kind 가 정해져 있어 team-checkout 만 써도 동작하지만, 명시적으로 group: 을 붙이는 편이 리뷰에서 훨씬 안전합니다. 사람이 읽을 때 그것이 팀인지 개인인지 바로 보이기 때문입니다.
왜 catalog-info.yaml 은 코드 옆에 사는가
카탈로그를 채우는 방식은 세 가지입니다.
1. 정적 등록 — Backstage 설정 파일에 URL 목록을 적는다. 소규모에는 되지만 늘어나면 관리 불가.
2. Location 엔티티 — 표지판 엔티티가 다른 파일들을 가리킨다. 계층적으로 묶을 때 유용.
3. 디스커버리(discovery) — 조직의 저장소를 훑어 catalog-info.yaml 을 찾아 자동 등록. 실무의 정답.
3 번이 성립하려면 파일이 코드와 같은 저장소 에 있어야 합니다. 이 배치가 만드는 효과가 큽니다.
- 서비스를 만든 사람이 소유자를 적습니다. 나중에 다른 사람이 추측하지 않습니다.
- 코드 리뷰를 함께 거칩니다. 소유권 변경이 PR 로 기록됩니다.
- 저장소를 아카이브하면 엔티티도 함께 사라집니다. 유령 항목이 남지 않습니다.
- 서비스 구조가 바뀌면 같은 커밋에서 카탈로그도 바뀝니다.
반대로 카탈로그를 중앙 저장소 한 곳에 몰아 두면, 그 파일을 고칠 동기가 있는 사람이 아무도 없어집니다. 카탈로그가 썩는 가장 흔한 경로입니다.
소유권이 심장인 이유
카탈로그에서 딱 하나만 정확해야 한다면 그것은 소유자 입니다.
- 장애가 났을 때 누구를 부를지 결정합니다.
- 취약점이 발견됐을 때 누구에게 티켓을 보낼지 결정합니다.
- 비용을 누구에게 귀속할지 결정합니다.
- 폐기 결정을 누가 내릴지 결정합니다.
그래서 소유자는 사람(User)이 아니라 팀(Group) 이어야 합니다. 사람은 퇴사하고 팀은 인수됩니다. 시험에서 자주 나오는 지점이고, 실무에서 카탈로그가 무너지는 첫 번째 원인이기도 합니다.
현장에서 만나는 모습
저자의 홈랩에서 쿠버네티스 워크로드에 app.kubernetes.io/name, app.kubernetes.io/part-of 같은 표준 라벨을 붙이는 습관이 이 카탈로그 사고와 정확히 같은 것입니다. Helm 차트 모범 사례에서도 app.kubernetes.io/name, instance, version, component, part-of, managed-by 여섯 개를 표준으로 붙이라고 권합니다.
app.kubernetes.io/name→ 카탈로그의 Component 이름app.kubernetes.io/part-of→ 카탈로그의 Systemapp.kubernetes.io/component→ Component 의spec.type에 해당하는 역할
즉 같은 소유권·소속 정보를 클러스터 라벨과 카탈로그 엔티티 두 곳에 일관되게 표현 하는 것이 실무의 모습입니다. 그리고 이 둘을 연결해 주는 것이 Backstage 의 Kubernetes 플러그인입니다 — 엔티티에 애너테이션 backstage.io/kubernetes-id 를 달아 두면, 그 값과 같은 라벨을 가진 워크로드를 클러스터에서 찾아 엔티티 페이지에 보여 줍니다.
홈랩 GPU 노드에 붙인 gpu.homelab/tier=xlarge 같은 의미 기반 라벨도 같은 정신입니다. 물리적 사실을 사람이 결정에 쓸 수 있는 어휘로 번역하는 것 — 카탈로그가 시스템·도메인으로 서비스를 묶는 이유와 다르지 않습니다.
다음 실습에서 할 것
/root/cba-catalog/ 에 Component, API, Resource, System, Domain, Group, User, Location 엔티티 파일을 직접 작성합니다. 그다음 같은 소유권 정보를 실제 클러스터의 라벨로 표현해 kubectl 로 확인하고, 마지막에 모든 엔티티의 참조 문자열을 정규화된 형식으로 뽑아 봅니다.