본문 바로가기
IT & Tech 정보

Pact · Spring Cloud Contract · Kafka Contract 테스트 · GitHub Actions 통합

by 지식과 지혜의 나무 2025. 5. 27.
반응형

자동 계약 테스트 끝판왕 가이드:

서론: 왜 계약 테스트인가
• 서비스 간 불일치 방지: REST·gRPC·메시지 큐로 연결된 마이크로서비스 간 스펙 충돌 없이 안전 보장
• 통합 테스트 비용 절감: 실제 환경 프로비저닝 없이 소비자·제공자 계약으로 상호 검증
• CI 단계 전면 통합: 계약 위반 시 PR 차단 → 배포 파이프라인 중단
• Event-Driven 환경 지원: Kafka, RabbitMQ 등 메시지 버스 계약도 자동화

실리콘밸리·심천 IT기업 톱티어 팀들은 Pact(HTTP & 메시지), Spring Cloud Contract, Pact-Kafka를 결합해, 계약 정의→게시→검증→배포 전 자동화된 Contract-First 워크플로우를 설계합니다.



아키텍처 개관

Consumer Repo          Pact Broker           Provider Repo         CI/CD Pipeline         Message Broker
┌───────────────┐     ┌───────────────┐     ┌───────────────┐       ┌───────────────┐      ┌───────────┐
│ Pact Consumer │──▶  │ Pact Broker   │◀───▶│ Pact Provider │────▶  │ GitHub Actions│      │ Kafka Cluster│
│ 테스트 생성   │     │ 저장소        │     │ 검증 모듈     │       │ - 계약 게시    │      └───────────┘
└───────────────┘     └───────────────┘     └───────────────┘       │ - 계약 다운로드│
                                                                    │ - Provider 검증│
                                                                    │ - 메시지 계약 검증│
                                                                    └───────────────┘

1. Consumer: Pact DSL로 예상 요청·응답 계약 정의
2. Pact Broker: 중앙 계약 레지스트리
3. Provider: Spring Cloud Contract 및 Pact 검증
4. Pact-Kafka: 메시지 기반 계약 테스트
5. CI/CD: GitHub Actions로 계약 게시·다운로드·검증 자동화



Pact HTTP 계약 테스트

Consumer 측 정의 (Java JUnit 예시)

@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(provider="user-service", port="8081")
class UserClientPactTest {
  @Pact(consumer="order-service")
  public RequestResponsePact createPact(PactDslWithProvider p) {
    return p
      .given("user exists", Map.of("userId", 123))
      .uponReceiving("get user by id")
        .path("/users/123")
        .method("GET")
      .willRespondWith()
        .status(200)
        .body(new PactDslJsonBody()
          .integerType("id", 123)
          .stringType("name", "Alice"))
      .toPact();
  }

  @Test
  void testGetUser(MockServer mockServer) {
    UserClient client = new UserClient(mockServer.getUrl());
    User user = client.getUser(123);
    assertEquals("Alice", user.getName());
  }
}

Pact Broker에 계약 게시

- name: Publish Pact to Broker
  run: |
    ./gradlew pactPublish \
      -DpactBrokerUrl=${{ secrets.PACT_BROKER_URL }} \
      -DpactBrokerToken=${{ secrets.PACT_BROKER_TOKEN }}




Spring Cloud Contract Provider 검증

Gradle 설정

dependencies {
  testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}
contracts {
  baseClassForTests = 'com.myorg.ContractBase'
  packageWithBaseClasses = 'com.myorg'
  testMode = 'JUNIT5'
}

검증 실행

- name: Verify Provider Contracts
  run: ./gradlew clean contractTest

• Pact Broker에서 최신 계약을 다운로드해 Spring 서비스에 대한 검증 수행



Kafka 메시지 계약 테스트 (Pact-Kafka)

Consumer 메시지 계약 예시 (Java)

@Pact(consumer="inventory-service", provider="order-service")
public MessagePact createKafkaPact(MessagePactBuilder builder) {
  return builder
    .expectsToReceive("order.created event")
    .withContent(new PactDslJsonBody()
      .integerType("orderId", 456)
      .stringType("status", "CREATED"))
    .toPact();
}

@Test
void testOrderCreatedPact(MockMessagePact mockPact) {
  // Pact-JVM Kafka 모듈로 메시지 디코딩 후 컨슈머 로직 검증
}

Provider 메시지 발행 검증

- name: Verify Kafka Provider
  run: |
    ./gradlew pactVerify \
      -DpactBrokerUrl=${{ secrets.PACT_BROKER_URL }} \
      -DpactBrokerToken=${{ secrets.PACT_BROKER_TOKEN }}




GitHub Actions 통합 워크플로우

name: Contract Testing

on:
  pull_request:
    paths:
      - 'consumer/**'
      - 'provider/**'
      - 'contracts/**'

jobs:
  publish:
    if: startsWith(github.ref, 'refs/heads/consumer-')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Publish Consumer Pact
        run: ./gradlew pactPublish \
          -DpactBrokerUrl=${{ secrets.PACT_BROKER_URL }} \
          -DpactBrokerToken=${{ secrets.PACT_BROKER_TOKEN }}

  verify-http:
    needs: publish
    if: github.event.pull_request.base.ref == 'main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Verify HTTP Provider
        run: ./gradlew clean contractTest

  verify-kafka:
    needs: publish
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Verify Kafka Provider
        run: ./gradlew pactVerify \
          -DpactBrokerUrl=${{ secrets.PACT_BROKER_URL }} \
          -DpactBrokerToken=${{ secrets.PACT_BROKER_TOKEN }}

• publish: Consumer 브랜치에서만 계약 게시
• verify-http/verify-kafka: main 브랜치 PR에 대해 실행



고급 팁 & “아, 이런 방법도”
1. Branch Tag Versioning: Pact 브로커에 브랜치 태그별로 격리된 계약 관리
2. Contract Rollforward: 위반 시 자동 PR 생성 → Consumer 수정 유도
3. Continuous Replay: Pact Broker 웹훅 → CI 재검증 파이프라인 트리거
4. Consumer-Driven Mock Server: WireMock + Pact Stub Runner로 독립 테스트
5. Security Contracts: 계약에 authentication 조건 포함 → 보안 테스트 자동화



결론
• Pact로 HTTP·메시지 계약을 정의·게시
• Spring Cloud Contract로 스프링 서비스 검증
• Pact-Kafka로 이벤트 기반 메시지 계약 자동화
• GitHub Actions로 전 과정 CI/CD 통합

“교과서론 결코 알려주지 않는, 실리콘밸리·심천 IT기업 최정예 엔지니어급 자동 계약 테스트”를 지금 바로 구현해 보세요. 서비스 간 계약 위반 제로, 배포 안전성 100% 그날까지!

반응형