728x90
인프라 메시징 모듈 설계
도입
대규모 서비스에서 Kafka는 단순한 메시지 브로커 이상의 역할을 합니다.
이벤트 기반 아키텍처에서 비동기 통신을 안정적으로 처리하기 위한 핵심 구성 요소로 자리잡고 있죠.
이번에는 서비스 간 이벤트 흐름의 기반이 되는 인프라 메시징 모듈을 설계하고,
Kafka 전송을 추상화한 프로듀서 구조와 구성 전략, 그리고 테스트 방법까지 정리합니다.
설계 목표
- Kafka 연동을 각 서비스 모듈에 강제하지 않고, 인프라 수준에서 추상화
- 전송 포맷 통일 (JSON 직렬화)
- 이벤트 타입 명시 및 시점 기록 (
occurredAt) 포함 - 모듈 분리를 통한 테스트 용이성 확보
- 향후 Consumer 및 Outbox 확장 고려
설계 고려사항
🔹 추상화 레이어 설계
서비스는 CustomEvent만 구현하면 되도록 간결한 인터페이스 제공
→ 내부 직렬화, KafkaTemplate 사용, 토픽 분기 등은 모두 내부에서 처리
🔹 Kafka 설정 외부화
KafkaProperties는 Spring Boot의 자동 구성 대상이므로 그대로 사용KafkaTopicProperties는 프로젝트 도메인에 맞게 별도 구성
🔹 퍼블릭 모듈 테스트
@SpringBootApplication이 없는 퍼블릭 모듈에서도 테스트 가능하도록@ContextConfiguration 기반 테스트 환경 구성
구현 구조
패키지 구조
infra-messaging
├── config
│ └── KafkaProducerConfig.java
├── producer
│ └── KafkaEventProducer.java
├── properties
│ └── KafkaTopicProperties.java
└── resources
└── application.yamlKafkaEventProducer
@Component
public class KafkaEventProducer {
private final KafkaTemplate<String, String> kafkaTemplate;
private final KafkaTopicProperties topicProperties;
public KafkaEventProducer(KafkaTemplate<String, String> kafkaTemplate,
KafkaTopicProperties topicProperties) {
this.kafkaTemplate = kafkaTemplate;
this.topicProperties = topicProperties;
}
public void send(CustomEvent event) {
KafkaEventPayload payload = KafkaEventSerializer.serialize(event);
String topic = resolveTopic(event.name());
DataSerializer.serialize(payload)
.ifPresent(json -> kafkaTemplate.send(topic, json));
}
private String resolveTopic(String eventName) {
return switch (eventName) {
case "user.registered" -> topicProperties.getUserRegistered();
case "order.created" -> topicProperties.getOrderCreated();
default -> throw new IllegalArgumentException("Unknown event: " + eventName);
};
}
}
KafkaProducerConfig
@Configuration
@EnableConfigurationProperties({KafkaProperties.class, KafkaTopicProperties.class})
public class KafkaProducerConfig {
private final KafkaProperties kafkaProperties;
public KafkaProducerConfig(KafkaProperties kafkaProperties) {
this.kafkaProperties = kafkaProperties;
}
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>(kafkaProperties.buildProducerProperties());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(props);
}
@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
테스트 전략
퍼블릭 모듈 특성상 Spring Boot의 전체 Context를 사용할 수 없으므로,@SpringBootTest 대신 @ContextConfiguration 기반 테스트 구성:
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {
KafkaEventProducerTest.TestConfig.class
})
@TestPropertySource("classpath:application.yaml")
class KafkaEventProducerTest {
@Autowired
private KafkaEventProducer kafkaEventProducer;
static class TestUserRegisteredEvent implements CustomEvent {
@Override public String name() { return "user.registered"; }
}
@Test
void 전송_테스트() {
kafkaEventProducer.send(new TestUserRegisteredEvent());
}
@Configuration
@ComponentScan(basePackages = "inframessaging.producer")
@Import(KafkaProducerConfig.class)
@EnableConfigurationProperties(KafkaTopicProperties.class)
static class TestConfig {}
}
회고 및 확장 가능성
이 모듈을 통해 서비스는 Kafka 전송에 대한 책임을 지지 않고도 손쉽게 이벤트를 발행할 수 있게 되었습니다.
하지만 여전히 Consumer 책임 분리, Outbox 패턴 적용, 에러 핸들링 보완,
그리고 Tracing 및 로그 수집 구조와의 통합까지 고려할 점이 많습니다.
이번 설계를 통해 한 단계 더 나아간 이벤트 기반 아키텍처를 향한 초석을 다졌다고 생각합니다.
향후에는 이 구조를 기반으로 Outbox 모듈, Retry-Topic 처리, Kafka Tracing까지도 도전할 예정입니다.
728x90
'BindProject' 카테고리의 다른 글
| [중간 회고] 여기까지 구현 하면서... (1) | 2025.06.17 |
|---|---|
| [Backend] Outbox 패턴 기반 메시지 전송 시스템 설계 (0) | 2025.06.17 |
| [Backend] Event(이벤트 역/직렬화)모듈 구현 (0) | 2025.06.17 |
| [Backend] exception(2) ( 예외 계층화 구현) (1) | 2025.06.17 |
| [Backend] exception (CustomErrorCode 구현) (1) | 2025.06.17 |