목차
프로젝트 개발에서 요구되는 요구사항을 효과적으로 관리하고, 빠르게 응답하면서도 견고한 애플리케이션을 구축하는 것은 언제나 도전적인 과제입니다. 이번 포스팅에서는 AxonFramework와 Kafka를 활용하여 간단한 게시판 서비스를 Spring Boot로 구현하는 방법을 소개하려 합니다.

1. AxonFramework와 Kafka 개발환경 설정
- Java JDK 8이상이 설치 되어있는지 확인
- Eclipse or IntelliJ 와 같은 통합개발환경 (IDE)을 설치
- 종속성 관리를 위한 Apache Maven 설치
2. Spring Boot 프로젝트 생성
- Spring Start Project를 생성
- pom.xml에 Web, Kafka, AxonFramework 및 JPA(또는 원하는 다른 데이터베이스)를 사용하기 위한 의존성을 추가
이 pom.xml 파일은 게시판 응용 프로그램에 필요한 종속성을 사용하여 Spring Boot 프로젝트를 설정합니다.
여기에는 다음과 같은 주요 종속성이 포함됩니다.
- spring-boot-starter-web: REST API를 지원하는 웹 애플리케이션 생성용.
- spring-boot-starter-data-jpa: JPA 및 Hibernate를 사용하여 데이터 액세스 관리용.
- axon-spring-boot-starter: AxonFramework와 Spring Boot 통합용.
- spring-kafka: Kafka를 Spring Boot와 통합하기 위한 것입니다.
- h2: 개발용 인메모리 데이터베이스.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>axonProject</artifactId> <version>0.0.1-SNAPSHOT</version> <name>axonProject</name> <description>Demo project for Spring Boot</description> <properties> <java.version>19</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.axonframework</groupId> <artifactId>axon-spring-boot-starter</artifactId> <version>4.7.3</version> </dependency> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
- application.properties(프로젝트의 src/main/resources 폴더에 application.properties 있음)
# Server configuration server.port=8080 # Kafka configuration spring.kafka.consumer.bootstrap-servers=localhost:9092 spring.kafka.consumer.group-id=bulletin-board-group spring.kafka.consumer.auto-offset-reset=earliest spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.producer.bootstrap-servers=localhost:9092 spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer # Axon configuration axon.axonserver.servers=localhost:8124
3. 게시판 설계
- 집계, 명령, 이벤트 및 쿼리 모델과 같은 애플리케이션의 주요 구성 요소를 정의합니다.
- AxonFramework를 사용하여 명령 및 이벤트 핸들러를 구현합니다.
- Spring Kafka를 사용하여 메시지를 생성하고 소비합니다.
4. 게시판 구현
- 게시판의 게시물을 나타내는 새로운 집계 클래스(예: PostAggregate)를 만듭니다.
- 게시물을 만들고 업데이트하기 위한 명령 및 이벤트 클래스(예: CreatePostCommand, PostCreatedEvent)를 구현합니다.
- 집계 내에서 명령 및 이벤트 처리기를 구현합니다(예: @CommandHandler, @EventHandler).
- 프로젝션 클래스(예: PostProjection)를 구현하여 이벤트를 기반으로 쿼리 모델을 업데이트합니다.
- 쿼리 모델을 유지하기 위한 저장소(예: PostRepository)를 만듭니다.
- REST API(예: PostController)를 구현하여 애플리케이션의 기능을 노출합니다.
// PostAggregate.java @Aggregate public class PostAggregate { @AggregateIdentifier private String id; private String title; private String content; @CommandHandler public PostAggregate(CreatePostCommand command) { apply(new PostCreatedEvent(command.getId(), command.getTitle(), command.getContent())); } @EventHandler public void on(PostCreatedEvent event) { this.id = event.getId(); this.title = event.getTitle(); this.content = event.getContent(); } protected PostAggregate() { // For Axon Framework } }
// CreatePostCommand.java public class CreatePostCommand { private final String id; private final String title; private final String content; public CreatePostCommand(String id, String title, String content) { this.id = id; this.title = title; this.content = content; } // Getters ... }
// PostProjection.java @Component public class PostProjection { private final PostRepository postRepository; public PostProjection(PostRepository postRepository) { this.postRepository = postRepository; } @EventHandler public void on(PostCreatedEvent event) { Post post = new Post(event.getId(), event.getTitle(), event.getContent()); postRepository.save(post); } }
// Post.java (Entity) @Entity public class Post { @Id private String id; private String title; private String content; public Post(String id, String title, String content) { this.id = id; this.title = title; this.content = content; } // Getters and setters ... }
// PostRepository.java public interface PostRepository extends JpaRepository<Post, String> { }
// PostController.java @RestController @RequestMapping("/api/posts") public class PostController { private final CommandGateway commandGateway; private final PostRepository postRepository; public PostController(CommandGateway commandGateway, PostRepository postRepository) { this.commandGateway = commandGateway; this.postRepository = postRepository; } @PostMapping public CompletableFuture<String> createPost(@RequestBody PostRequest postRequest) { String postId = UUID.randomUUID().toString(); return commandGateway.send(new CreatePostCommand(postId, postRequest.getTitle(), postRequest.getContent())); } @GetMapping public List<Post> getAllPosts() { return postRepository.findAll(); } }
// PostRequest.java public class PostRequest { private String title; private String content; // Getters and setters ... }
5. 게시판 프로젝트 실행
6. AxonFramework와 Kafka 실행
- kafka 시작
- kafka를 설치 하지 않았다면(https://kafka.apache.org/)에서 kafka를 다운로드 하고 압축해제
- 터미널 실행하여 kafka 설치 폴더로 이동
- maxOS 기준(bin/zookeeper-server-start.sh config/zookeeper.properties)
- 터미널 실행 2
- maxOS 기준(bin/kafka-server-start.sh config/server.properties)
- Spring Boot 애플리케이션을 시작합니다(예: 기본 클래스를 실행하거나 Maven의 경우 ./mvnw spring-boot:run 또는 Gradle의 경우 ./gradlew bootRun 사용).
- Postman 또는 CURL과 같은 REST 클라이언트를 사용하여 포스트 생성 및 가져오기를 위한 /api/posts 엔드포인트를 테스트합니다.
- AxonServer 다운로드: 공식 웹 사이트에서 AxonServer의 최신 릴리스를 다운로드합니다: https://axoniq.io/product-overview/axon-server. “Axon Server Standard Edition”을 선택하고 .jar 파일을 다운로드합니다.
- AxonServer 실행: 터미널(또는 Windows의 경우 명령 프롬프트)을 열고 AxonServer .jar 파일을 다운로드한 디렉터리로 이동합니다. 다음 명령을 실행하여 AxonServer를 시작합니다.
java -jar axonserver.jar
기본적으로 AxonServer는 포트 8124에서 수신 대기합니다. 게시판 애플리케이션이 작동하려면 AxonServer가 백그라운드에서 실행 중이어야 하므로 터미널이 계속 실행되도록 허용하십시오.
애플리케이션 속성 업데이트: application.properties 파일의 axon.axonserver.servers 속성이 올바른 AxonServer 주소 및 포트(기본적으로 localhost:8124)를 가리키는지 확인하십시오. 아직 추가하지 않은 경우 application.properties 파일에 다음 행을 추가하십시오.(이미 추가하였음, 혹시 지웠다면 추가)
이렇게 AxonFramework와 Kafka를 활용한 Spring Boot 게시판 서비스 구현하기를 진행해 보았습니다. 다음 포스에서는 AxonFramework와 Kafka에 대해 더 많은 정보를 다루어 볼 예정이니 많은 관심 부탁드립니다.