My Role & Technical Decisions
단독 개발 — 버전·매니페스트 도메인, 레지스트리 연동(이미지 존재 검증·크기 산정), skopeo 기반 아카이브 수집, Microsoft Graph 업로드·링크 발급, Job 오케스트레이션(진행률·재시도·정리 배치), 오류 코드 노출 등급 체계, Flyway 스키마와 Testcontainers 통합 테스트까지 설계·구현했다.
Troubleshooting & Evidence
원인: 아카이브에 기록되는 이미지 참조를 저장소명만으로 적었는데, 컨테이너 런타임은 조회 시 레지스트리 호스트를 붙여 정규화한다 — 기록된 이름과 조회 이름이 영영 달라져 적재는 성공하는데 실행·조회·삭제가 전부 실패하고 이미지 목록에 같은 항목이 두 번 표시됐다
해결: 목적지 참조를 호스트까지 붙인 완전 수식 참조로 고정. 네임스페이스가 없는 저장소는 정규형이 한 단계 더 들어가는 것을 7MB 테스트 이미지로 분리 확인해 별도 처리했다. 반입물에 사내 레지스트리 주소가 실리면 안 되므로 호스트는 공개 기본값을 명시해 정규화 결과와 일치시켰다
배운 점: 컨테이너 이미지 이름은 저장 시점과 조회 시점의 정규화 규칙이 같아야 한다 — '적재 성공'은 사용 가능을 뜻하지 않는다. 그리고 증상이 겹쳐 보일 때는 변수를 하나만 바꾼 최소 이미지로 원인을 분리해야 엉뚱한 것을 고치지 않는다
원인: 매니페스트 조회 Accept 헤더에 단일 매니페스트 형식만 넣고 멀티아키텍처 인덱스 형식을 빠뜨렸다. 레지스트리는 사유를 명시한 404를 주는데, 클라이언트가 404를 '없음'으로 뭉뚱그려 처리해 존재하는 이미지가 조용히 누락됐다
해결: Accept에 인덱스 2종을 추가하고, 404의 사유를 구분해 '확인 불가'와 '확실히 없음'을 분리했다. 실측 저장소 12개 중 3개가 인덱스였다
배운 점: 외부 API의 404를 전부 '없음'으로 접으면, 협상 실패처럼 다른 이유로 온 404가 도메인 사실로 둔갑한다 — 응답 사유를 읽고 분기해야 한다
원인: 아카이브 포맷이 레이어를 다시 풀어 담아 압축이 사라졌고, 멀티아키텍처 인덱스를 플랫폼 하나로 평탄화해 담으면서 아카이브 digest가 레지스트리 digest와 달라졌다
해결: 압축과 digest를 그대로 보존하는 OCI 아카이브 포맷으로 바꾸고, 원본 형식을 유지하는 옵션과 인덱스를 통째로 보존하는 옵션을 함께 적용. 포맷을 특정 버전으로 강제하면 빌드 어테스테이션에서 죽는 것도 확인해 강제를 걷어냈다
배운 점: '같은 이미지'의 기준은 실행 가능 여부가 아니라 digest 동일성이다 — 포맷을 손대는 순간 그 기준이 깨지므로, 전송 계층은 원본을 변형하지 않는 선택지를 먼저 찾아야 한다
원인: 요청 본문을 불변 Map 리터럴로 만들었는데, 이 자료구조는 순회 순서가 JVM 기동마다 무작위로 바뀐다. 외부 API가 특정 키를 다른 키보다 먼저 요구해서, 순서가 어긋난 기동에서만 400이 났다 — 재기동 뽑기가 된 것이다
해결: 순서가 보장되는 본문 생성기로 교체하고, 직렬화된 본문 바이트 자체를 테스트로 고정했다
배운 점: 순서를 보장하지 않는 자료구조는 '보통 순서대로 나온다'가 아니라 '언제든 바뀔 수 있다'로 읽어야 한다. 재현이 안 되는 간헐 실패일수록 실행마다 달라지는 값이 어디 있는지부터 찾는다
원인: 마이그레이션 도구가 파일 내용으로 체크섬을 내므로 주석 한 줄도 불일치가 된다. 통합 테스트는 매번 빈 DB로 시작해 이 부류를 구조적으로 잡지 못했다
해결: 마이그레이션을 한 파일로 통합하고 기존 DB는 재기준선으로 넘겼다. 그리고 마이그레이션 파일의 CRC32를 고정하는 테스트를 추가해, 적용된 파일이 수정되면 CI에서 먼저 깨지게 했다
배운 점: '빈 DB에서 잘 된다'가 검증하지 못하는 영역이 있다 — 기존 DB의 상태에 의존하는 결함은 테스트 전략 자체를 바꿔야 잡힌다
원인: 항목별 실패 메시지를 자유 문자열로 저장했는데, 외부 API 예외 메시지에 업스트림 응답 본문이 통째로 들어 있었다. 이 값이 무인증 조회 응답에 노출됐다
해결: 오류를 enum으로만 정의하고 각 코드에 노출 등급(응답 노출 / 로그 전용)을 부여. 실패 기록 API가 문자열을 아예 받지 못하게 막고, 응답에 나가는 문구에 경로·호스트 패턴이 없는지 검사하는 테스트를 세워 3중으로 강제했다
배운 점: 민감 정보 유출은 규칙 문서로 막히지 않는다 — 위반이 컴파일 또는 테스트에서 실패하도록 타입과 검사로 강제해야 다음 사람도 지키게 된다