Spring Book를 이용해서 Kafka 관련 테스트 코드를 실행하는데 아래와 같은 에러가 발생했다.
Caused by: org.apache.kafka.common.KafkaException: Socket server failed to bind to localhost:9092: Address already in use.
Caused by: java.util.concurrent.CompletionException: java.lang.RuntimeException: Unable to start acceptor for ListenerName(PLAINTEXT)
docker-compose로 kafka 실행 중
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
container_name: hhplus-zookeeper
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
ports:
- "2181:2181"
networks:
- test-network
restart: unless-stopped
kafka:
image: confluentinc/cp-kafka:7.5.0
container_name: hhplus-kafka
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
networks:
- test-network
restart: unless-stopped
networks:
test-network:
driver: bridge
```kotlin
@DataJpaTest
@ComponentScan(basePackages = ["com.hhplus.ecommerce"])
@EmbeddedKafka(
partitions = 1,
topics = ["order-created", "payment-completed", "coupon-issued"],
brokerProperties = ["listeners=PLAINTEXT://localhost:9092", "port=9092"]
)
@TestPropertySource(
properties = [
"spring.kafka.enabled=true",
"spring.kafka.bootstrap-servers=localhost:9092",
"spring.kafka.consumer.group-id=test-group"
]
)
class KafkaTest {
// ...
}
port가 충돌이 돼서 두 개의 kafka 인스턴스가 동일한 9092 port를 사용하려고 했음
localhost:9092 port 사용localhost:9092 port 사용하려고 시도처음에는 "테스트를 위해서 Kafka가 필요해서 Docker로 Kafka를 띄워놓자"라는 생각으로 docker-compose로 kafka, zookeeper 를 실행했다.
그런데 @EmbeddedKafka도 함께 사용하면서 동일한 port를 두번 바인딩하려는 문제가 발생했다.
Docker Compose로 이미 Kafka를 띄웠다면, @EmbeddedKafka를 제거하고 Docker Kafka를 테스트에서도 사용하는 것이 좋습니다.
@DataJpaTest
@ComponentScan(basePackages = ["com.hhplus.ecommerce"])
// @EmbeddedKafka 제거!
@TestPropertySource(
properties = [
"spring.jpa.hibernate.ddl-auto=create-drop",
"spring.datasource.url=jdbc:h2:mem:testdb",
"spring.datasource.driver-class-name=org.h2.Driver",
"spring.jpa.database-platform=org.hibernate.dialect.H2Dialect",
"spring.kafka.enabled=true",
"spring.kafka.bootstrap-servers=localhost:9093", // Docker의 호스트 포트 사용
"spring.kafka.consumer.group-id=test-group"
]
)
class KafkaTest {
// Docker Kafka 사용
}
주의사항: Docker Compose의 PLAINTEXT_HOST://localhost:9093 리스너를 사용한다.
장점
단점
완전히 격리된 테스트 환경을 원한다면 Docker를 내리고 @EmbeddedKafka만 사용한다.
# 테스트 전
docker-compose down
# 테스트 실행
./gradlew test
# 로컬 개발 시 다시 시작
docker-compose up -d
장점
단점
Docker Compose로 Kafka를 띄워놨다면, @EmbeddedKafka를 제거하고 Docker Kafka를 테스트에서 사용한다.
핵심은 한 가지 Kafka 인스턴스만 사용하는 것이다. 두 개를 동시에 사용하려고 하면 포트 충돌이 발생하기 때문이다.
| 상황 | 추천 방법 |
|---|---|
| Docker 환경 구축되어 있음 | Docker Kafka 사용 |
| 완전히 격리된 단위 테스트 | @EmbeddedKafka 사용 |
| CI/CD 환경에서만 테스트 | @EmbeddedKafka 사용 |
| 로컬 개발 + 통합 테스트 | Docker Kafka 사용 |
Docker Compose에서 두 개의 포트를 노출한 이유:
ports:
- "9092:9092" # 컨테이너 간 통신용 (PLAINTEXT://kafka:9092)
- "9093:9093" # 호스트 접근용 (PLAINTEXT_HOST://localhost:9093)