Skip to content

build(foundation): 빌드 기반과 품질 임계 - #6

Merged
UHeeJoon merged 9 commits into
developfrom
feature/CY-18-foundation
Aug 18, 2026
Merged

UHeeJoon merged 9 commits into
developfrom
feature/CY-18-foundation

Conversation

@UHeeJoon

Copy link
Copy Markdown
Member

Phase 1 의 1.2 빌드 기반 절이다. 코드를 쓰기 전에 갖춰야 할 것만 세운다.

무엇

태스크 산출물
1.2.1 Gradle 프로젝트 — Java 21 툴체인 + foojay resolver
1.2.2 기동 스모크 (RED → GREEN)
1.2.3 JaCoCo · PIT 품질 임계
1.2.4 testFixtures 소스셋

판단이 필요했던 것

툴체인 자동 provisioning 을 켰다. toolchain 만 선언하면 JDK 21 이 없는 기기에서
No matching toolchain 으로 죽는다. 러너는 setup-java 가 깔아주지만 개발 기기는
아무도 안 깔아준다 — 실제로 이 작업 기기는 24 다.

패키지 루트를 com.kafkick.waiting 으로 잡았다. cy-be 가 com.kafkick 아래를
쓰고 있고 두 저장소를 합칠 가능성이 있다. 합칠 때 패키지를 전부 옮기는 것이 가장 비싸다.

의존성은 셋만 넣었다 — webflux, gateway-server-webflux, actuator.
Redis·resilience4j 는 해당 페이즈에 도달할 때 넣는다. 미리 넣으면 어느 페이즈가
무엇을 요구하는지가 흐려진다.

커버리지 임계를 계층별로 나눴다. 도메인은 분기 100%, 나머지는 80%. 도메인은
순수 계층이라 채울 수 있고, 못 채운다는 것은 도달 불가 분기가 있다는 뜻이며
그건 설계 문제다. 뮤테이션은 domain 한정이다 — 어댑터까지 돌리면 시간이
폭발하고 정작 지켜야 할 곳의 신호가 묻힌다.

픽스처를 커버리지 대상에서 뺐다. 테스트를 돕는 코드까지 포함시키면 본 코드의
미달을 픽스처가 덮는다.

검사가 실제로 무는지 확인했다

위반을 잡지 못하는 검사는 모든 코드를 통과시킨다. 그래서 둘 다 일부러 깨뜨려 봤다.

  • 커버되지 않은 분기를 넣자 jacocoTestCoverageVerification 이 빌드를 실패시켰다
  • 픽스처를 src/main 으로 옮기자 verifyFixturesExcluded 가 잡았다

남은 것

1.3(CI 파이프라인)은 이미 main 에 있고, 1.4·1.5(공개 저장소 표면)는
이 브랜치에서 이어간다.

Refs: CY-18

Java 21 툴체인에 foojay resolver 를 붙인다. toolchain 만 선언하면 JDK 21 이
없는 기기에서 "No matching toolchain" 으로 죽는다 — 러너는 setup-java 가
깔아주지만 개발 기기는 아무도 안 깔아준다. 이 기기는 24 다.

패키지 루트는 com.kafkick.waiting. cy-be 가 com.kafkick 아래를 쓰고 있고
두 저장소를 합칠 가능성이 있어 지금부터 맞춘다. 합칠 때 패키지를 전부
옮기는 것이 가장 비싸다.

의존성은 webflux · gateway-server-webflux · actuator 만 넣는다. Redis 와
resilience4j 는 해당 페이즈에 도달할 때 넣는다 — 미리 넣으면 어느 페이즈가
무엇을 요구하는지가 흐려진다.

Refs: CY-18
[red]

컨텍스트 로딩 자체가 단언이다. 빈 배선이 깨지면 여기서 먼저 걸리고,
그러지 않으면 부하 시험 전까지 아무도 모른다.

Refs: CY-18
게이트웨이는 입장만 소유한다. 발급과 재고 차감은 쿠폰 서비스가 한다는
경계를 진입점 주석에 남긴다 — 이 경계가 흐려지면 게이트웨이가 재고를
판단하려 든다.

Refs: CY-18
임계 미달이면 build 가 실패한다. "확인했다" 는 통과가 아니다.

도메인 분기 100% 를 요구한다. 순수 계층이라 채울 수 있고, 못 채운다는 것은
도달 불가 분기가 있다는 뜻이며 그건 설계 문제다. 나머지는 80%.

뮤테이션은 domain 패키지 한정이다. 어댑터까지 돌리면 시간이 폭발하고
정작 지켜야 할 곳의 신호가 묻힌다.

픽스처는 커버리지 대상에서 뺀다. 테스트를 돕는 코드까지 포함시키면
본 코드의 미달을 픽스처가 덮는다.

커버되지 않은 분기를 넣어 build 가 실제로 실패하는 것을 확인했다 —
위반을 잡지 못하는 검사는 모든 코드를 통과시킨다.

Refs: CY-18
픽스처를 src/test 에 두면 다른 소스셋에서 못 쓰고, 프로덕션에 두면
도달 불가 상태를 만드는 생성자가 운영 코드에 노출된다.

배선을 사람 눈으로 확인하지 않는다. 테스트가 픽스처를 볼 수 있는지와
프로덕션 JAR 에는 없는지를 빌드가 검사한다. 픽스처를 main 으로 옮겨
검사가 실제로 실패하는 것까지 확인했다.

Refs: CY-18
CI 는 만든 시점이 아니라 처음 돌린 시점에 검증된다. 첫 푸시에서 나온
워크플로 결함 넷을 다음 사람에게 남긴다 — 주석 안의 표현식, 재사용
워크플로 권한, 형제 저장소 상대 링크, 액션 태그 접두사.

Refs: CY-18
CI 가 integrationTest·contextTest·chaosTest 를 부르는데 정의가 없었다.
main 에서는 src/ 가 없어 잡이 스킵돼 드러나지 않았고, 첫 PR 에서 나왔다.

한 태스크에 다 넣으면 어느 계층이 왜 느린지·왜 깨졌는지가 안 보이고
페이즈별로 켜고 끌 수도 없다. 태그로 가르되 태그 없는 테스트는 unit 으로
본다 — 태그를 잊어도 어딘가에서는 돈다.

빈 계층을 실패로 두지 않는다. 계층은 페이즈가 진행되며 채워지고, 아직
비어 있다는 이유로 파이프라인이 막히면 안 된다. 같은 이유로 pitest 도
대상이 0건일 때 통과시킨다 — domain 은 Phase 2 에서 들어온다.

Refs: CY-18
도구 훅만 두면 `git commit` 을 손으로 치는 순간 검사가 통째로 우회된다.
실제로 초기 커밋 전부가 그렇게 빠져나갔다. 한쪽만 막으면 막지 않은 것과 같다.

규칙은 .githooks/lib/ 하나에만 둔다. 두 훅이 그것을 부른다 — 복사하면
한쪽만 고쳐지고 그때부터 어느 쪽이 맞는지 알 수 없다. Claude 훅에서
같은 규칙 28줄을 걷어냈다.

훅을 복사하지 않고 core.hooksPath 를 쓴다. 복사하면 훅을 고칠 때마다
각자 다시 깔아야 하고, 누가 안 깔았는지 알 수 없다.

자기검증에 git 훅 케이스 8건을 넣었다. 그 과정에서 판정 함수가 exit 2 만
차단으로 세는 것을 발견했다 — git 훅은 1 을 쓴다. 그대로 뒀으면 차단을
전부 통과로 읽어 검사가 장식이 됐다.

Refs: CY-18
태그는 옮겨질 수 있다. 같은 v4 가 어제와 오늘 다른 코드를 가리켜도
알 방법이 없고, 그게 공급망 공격의 표준 경로다.

서드파티 8종을 커밋 SHA 로 고정하고 어느 버전인지 주석에 남긴다.
최초 고정은 손으로 해야 한다 — dependabot 은 이미 SHA 인 것을 올려줄 뿐
태그를 SHA 로 바꿔주지 않는다.

cy-ci-actions 는 우리 조직 저장소라 태그로 둔다. 공급망 위험 대상이
아니고 v1 을 움직여 배포하는 구조다.

Refs: CY-18
@UHeeJoon
UHeeJoon merged commit a3c5838 into develop Aug 18, 2026
16 checks passed
@UHeeJoon
UHeeJoon deleted the feature/CY-18-foundation branch August 18, 2026 23:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant