WebFlux + Kafka + R2DBC 기반으로 설계된 대용량 게시판 백엔드 아키텍처

백엔드&인프라 추종자·2025년 4월 22일

좋습니다.
아래는 WebFlux + Kafka + R2DBC 기반으로 설계된 대용량 게시판 백엔드 아키텍처 예시입니다.


🧩 1. 아키텍처 구성도

[클라이언트 요청]
       ↓
 ┌────────────────────────┐
 │   Spring WebFlux App   │
 └────────────────────────┘
      │         │
      │         └────▶ Redis (조회 캐시)
      │
      └────▶ Kafka Producer (INSERT 요청 발행)
                ↓
         Kafka Topic (게시글)
                ↓
         Kafka Consumer (Spring or Worker App)
                ↓
     ┌────────────────────┐
     │ R2DBC + PostgreSQL │
     └────────────────────┘

⚙️ 2. 주요 기술 스택

계층기술
클라이언트React, Android 등
서버Spring WebFlux (@RestController, Flux, Mono)
메시징Kafka (spring-kafka)
캐시Redis (lettuce + ReactiveRedisTemplate)
DBPostgreSQL + R2DBC (spring-data-r2dbc)

🧪 3. 흐름 예시: 게시글 등록

✅ 클라이언트

POST /api/posts
Content-Type: application/json

{
  "title": "WebFlux 게시판",
  "content": "이건 WebFlux + Kafka + R2DBC 예시입니다.",
  "authorId": 123
}

✅ WebFlux Controller

@PostMapping("/posts")
public Mono<Void> createPost(@RequestBody Mono<PostRequest> postMono) {
    return postMono
        .flatMap(post -> kafkaTemplate.send("post-topic", post))
        .then(); // 즉시 응답
}

💡 WebFlux는 요청을 받아 Kafka에 넣고 바로 응답합니다 → 빠른 반응성


✅ Kafka Consumer (Spring App or Worker)

@KafkaListener(topics = "post-topic", groupId = "post-group")
public void consume(PostRequest post) {
    PostEntity entity = new PostEntity(post.getTitle(), post.getContent(), post.getAuthorId());
    postRepository.save(entity).subscribe();
}

💡 Kafka Consumer는 R2DBC를 이용해 비동기로 DB에 저장합니다.


🗂️ 4. 조회 최적화

✅ Redis 캐시 구조

  • Key: post:{postId}
  • TTL: 1시간 또는 자주 조회되는 게시글만 유지
  • 없으면 DB에서 조회 후 Redis에 저장
public Mono<Post> getPost(Long id) {
    return redisTemplate.opsForValue()
        .get("post:" + id)
        .switchIfEmpty(
            postRepository.findById(id)
                .flatMap(post -> redisTemplate.opsForValue()
                    .set("post:" + id, post)
                    .thenReturn(post)
                )
        );
}

📊 5. R2DBC Repository 예시

@Table("posts")
public class PostEntity {
    @Id private Long id;
    private String title;
    private String content;
    private Long authorId;
    private LocalDateTime createdAt;
}

public interface PostRepository extends ReactiveCrudRepository<PostEntity, Long> {
    Flux<PostEntity> findAllByAuthorId(Long authorId);
}

📌 6. 장점 요약

항목설명
✅ 빠른 응답클라이언트는 Kafka 전송 직후 응답 받음
✅ 병렬 처리Kafka Consumer는 병렬로 DB Insert 가능
✅ 논블로킹 전체 구조WebFlux + R2DBC + Kafka 모두 논블로킹
✅ 확장성 우수Kafka + WebFlux 구조는 수평 확장 최적

⚠️ 주의할 점

  • Kafka 메시지 유실 방지 설정 필요 (acks=all, retries, DLQ)
  • Kafka Consumer에서 에러 처리 (onErrorResume 등)
  • 트랜잭션 보장 불완전 (INSERT 후 다른 작업과 일관성 유지 어려움)
  • 게시글 작성 실패 시 클라이언트는 이미 성공 응답을 받은 상태

✨ 부가 확장 아이디어

기능설명
🔄 Kafka에 댓글도 발행댓글도 async 저장
🧵 WebSocket 알림게시글 작성 시 알림 push
📈 게시글 조회수도 Kafka로 적재조회수 버퍼 처리 후 R2DBC 저장
🧭 ElasticSearch 연동검색용 인덱싱 용이

필요하시면 이 구조로 실제 폴더 구조 + 코드 예제도 함께 구성해드릴 수 있습니다.
계속 진행해볼까요? (예: 게시글 등록 전체 코드 + config 등)

profile
AI 답변 글을 주로 올립니다.

0개의 댓글