728x90

현재 Spring에서 아래와 같이 3가지 DI 방식을 사용하고 있다.

  • 가장 흔한 필드 주입방식: Field Injection 방식 (비 권장)
    @Controller
    public class BoardController {
    	@Autowired 
      private IBoardItemService boardItemService;
    }
  • 수정자 주입: Setter Injection
    private IBoardItemService boardItemService;
      @Autowired // 4.3 부터 생략 가능
      public void SetIBoardItemService (MessageSource messageSource){
        this.messageSource = messageSource;
      }
  • 생성자 주입: 가장 선호하는 방식 ( Class 상단에 @RequiredArgsConstructor 추가 , @AllArgsConstructor 는 지운다!)
    @RequiredArgsConstructor
    public class BoardController {
    	private final IBoardItemService boardItemService;
    }

    Lombok 없이 사용시:

    
    public class BoardController {
    	private final IBoardItemService boardItemService; // Immutability 이슈까지 해결하고 싶다면 private final 접근자 붙여주기
        
    		@Autowired
    		public BoardController(IBoardItemService boardItemService) {
        	this.boardItemService = boardItemService;
        }
    } 

내저장소 바로가기 luxury515

'Back-end > 기타' 카테고리의 다른 글

on duplicate key update  (0) 2023.04.16
AWS KMS 관련 세팅  (0) 2023.04.14
plantUml preview 갑자기 작동안될때.  (0) 2023.04.14
gradle bootrun 작동 에러  (0) 2023.04.14
radius(레이디스)서버  (0) 2023.04.14
728x90

들어가면서

Kafka는 대용량의 데이터를 처리하기 위한 분산 메시지 큐입니다. 이 문서에서는 Spring Boot 3.0에서 Kafka를 적용하는 방법과 기본적인 설정 코드를 제공합니다.

Kafka에 대한 기본적인 사항

Kafka는 Pub/Sub 모델을 사용하여 메시지를 처리합니다. 브로커(Broker)라고 불리는 Kafka 서버에서 메시지를 생성하고, 토픽(Topic)이라는 주제에 따라 메시지를 구분합니다. 프로듀서(Producer)는 메시지를 생성하고, 컨슈머(Consumer)는 메시지를 받아서 처리합니다.

Kafka 설정 코드

Kafka를 Spring Boot 3.0에서 적용하려면, spring-kafka 라이브러리를 추가해야 합니다. 예제 코드는 다음과 같습니다.

@Configuration
@EnableKafka
public class KafkaConfiguration {

    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    @Bean
    public Map<String, Object> consumerConfigs() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        return props;
    }

}

위 코드에서 @Value 어노테이션을 사용하여 bootstrap-servers 값과 같은 Kafka 설정 정보를 application.yml 파일에서 가져올 수 있습니다.

RabbitMQ와의 비교

RabbitMQ는 메시지 큐를 위한 오픈소스 브로커입니다. Kafka와 비교하면 RabbitMQ는 메시지 브로커의 역할만 수행합니다. 반면에 Kafka는 메시지 브로커 역할과 분산 처리 역할을 수행합니다. RabbitMQ는 AMQP(Advanced Message Queuing Protocol) 프로토콜을 사용하며, Kafka는 Apache ZooKeeper를 사용합니다.

다른 메시지 큐와의 비교

다음 표는 Kafka와 유사한 메시지 큐와의 성능, 기능, 사용자 인기 등을 비교한 것입니다.

메시지 큐성능기능어떤 프로젝트에 더 어울리는지사용자 인기
ActiveMQ높음다양함중간높음
Redis높음캐시 용도작은 규모높음
RabbitMQ중간다양함중간높음

MySQL, JPA와 함께 사용하기

Spring Boot 3.0에서 Kafka를 사용하면서 MySQL과 JPA를 함께 사용할 수 있습니다. 아래는 application.yml 파일에서 MySQL과 Kafka 설정 정보를 가져오는 예제 코드입니다.

spring:
  datasource:
    url: {MySQL DB URL}
    username: {MySQL DB username}
    password: {MySQL DB password}
  kafka:
    bootstrap-servers: {Kafka bootstrap servers}

JUnit 6로 테스트하기

Spring Boot 3.0에서 Kafka를 사용하는 애플리케이션을 테스트하려면, spring-kafka-test 라이브러리를 추가해야 합니다. 아래는 예제 코드입니다.

@SpringBootTest
@EmbeddedKafka
class KafkaTest {

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;

    @Test
    void testKafka() throws InterruptedException {
        String topic = "test-topic";
        kafkaTemplate.send(topic, "test-message");
        Thread.sleep(1000);
        assertThat(kafkaListenerEndpointRegistry.getListenerContainer("test-group").getRecordLatches().get(topic).getCount())
                .isEqualTo(1);
    }

}

위 코드에서 @EmbeddedKafka 어노테이션을 사용하여 내장된 Kafka 서버를 사용하여 테스트를 수행할 수 있습니다.

결론

Spring Boot 3.0에서 Kafka를 적용하는 방법과 기본적인 설정, RabbitMQ와의 비교, 다른 메시지 큐와의 비교, MySQL과 JPA와 함께 사용하는 방법, JUnit 6로 테스트하는 방법 등을 살펴보았습니다. 이를 통해 Kafka 사용 설정에 대한 내용을 이해하고, 적용할 수 있도록 도움이 되었기를 바랍니다.


내저장소 바로가기 luxury515

728x90

들어가면서

spring Boot 프로젝트에서 Ehcache 대신 Caffeine을 사용하면 성능을 향상시킬 수 있습니다.

Coffine이란 무엇인가?

Caffeine은 인기있는 오픈 소스 자바 캐시 라이브러리이며, Google이 개발하고 있습니다. Caffeine은 Java 8 이상을 지원하며, 최신 JVM의 기능을 활용하여 캐시 퍼포먼스를 높이고 메모리 사용을 최적화합니다.

Coffine은 Guava 캐시 라이브러리의 대체 제품입니다. Coffine은 Guava 캐시의 기능을 대부분 포함하고 있으며, 더 나은 쓰기 성능과 더 적은 메모리 사용량을 제공합니다.

어떤특징들이 있는가?

Caffeine은 다음과 같은 특징을 가지고 있습니다.

  1. 빠른 접근 및 응답 속도: Caffeine은 인-메모리 캐시를 사용하며, ConcurrentHashMap 및 ConcurrentLinkedHashMap과 같은 최신 자바 컬렉션을 활용하여 데이터에 대한 빠른 액세스 및 응답 속도를 제공합니다.
  1. 최신 JVM 최적화: Caffeine은 최신 JVM의 기능을 활용하여 캐시 퍼포먼스를 최적화합니다.
  1. 캐시 유효 기간 및 최대 크기 제한: Caffeine은 캐시 항목의 유효 기간 및 최대 크기 제한을 설정할 수 있습니다. 이러한 설정을 통해 메모리 사용을 최적화하고 캐시 데이터의 최신 상태를 유지할 수 있습니다.
  1. 스레드 안전성: Caffeine은 스레드 안전성을 보장하기 위해 synchronized 블록을 사용하지 않고 최신 자바 동시성 라이브러리를 활용합니다.

Caffeine 외 기타 library들에 대한 간단한 비교자료

라이브러리기능성능기타
Ehcache분산 캐싱, 대규모 데이터 처리높음Spring Boot에서 기본적으로 제공됨
Caffeine쓰기 성능이 뛰어나며, 메모리 사용량이 적음매우 높음Guava 캐시의 대체제
Redis분산 캐싱, Pub/Sub 메시징, Set 처리높음NoSQL 데이터베이스
Hazelcast분산 캐싱, 대규모 데이터 처리높음장애 복구 및 확장성이 뛰어남
Infinispan분산 캐싱, 대규모 데이터 처리높음JBoss에서 개발됨

Coffine의 장점

  • Guava 캐시의 대체제로서 Guava 캐시의 대부분의 기능을 포함하고 있음
  • 쓰기 성능이 더 뛰어나며, 메모리 사용량이 더 적음

Coffine의 단점

  • Guava 캐시와 달리, 캐시 다시 로드를 지원하지 않음

Coffine을 Spring Boot 3.0에 적용하는 코드 설정

1. Gradle 파일에 의존성 추가

dependencies {
    implementation("com.github.ben-manes.caffeine:caffeine:3.0.0")
    implementation("com.github.ben-manes.caffeine:caffeine-jcache:3.0.0")
}

2. Coffine 캐시 매니저 설정

@Configuration
@EnableCaching
public class CachingConfig extends CachingConfigurerSupport {

    @Bean
    @Override
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeineCacheBuilder());
        return cacheManager;
    }

    Caffeine<Object, Object> caffeineCacheBuilder() {
        return Caffeine.newBuilder()
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .maximumSize(100)
                .weakKeys()
                .recordStats();
    }
}

Coffine을 적용한 CRUD 작성하기

1. application.yml 파일에 MySQL 정보 기재

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/{database_name}?serverTimezone=UTC
    username: {username}
    password: {password}
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true

2. JPA 적용

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "email")
    private String email;

    // getters and setters
}

3. Coffine을 적용한 Repository 구현

@Repository
public class UserRepositoryImpl implements UserRepository {

    @Autowired
    private EntityManager entityManager;

    @Cacheable(value = "users")
    public List<User> findAll() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> cq = cb.createQuery(User.class);
        Root<User> root = cq.from(User.class);
        cq.select(root);
        TypedQuery<User> query = entityManager.createQuery(cq);
        return query.getResultList();
    }

    @Cacheable(value = "users", key = "#id")
    public User findById(Long id) {
        return entityManager.find(User.class, id);
    }

    @CachePut(value = "users", key = "#user.id")
    public User save(User user) {
        entityManager.persist(user);
        return user;
    }

    @CacheEvict(value = "users", key = "#id")
    public void deleteById(Long id) {
        User user = entityManager.find(User.class, id);
        entityManager.remove(user);
    }
}

JUnit6를 사용하여 테스트 코드 작성

@SpringBootTest
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testFindAll() {
        List<User> userList1 = userRepository.findAll();
        List<User> userList2 = userRepository.findAll();
        assertEquals(userList1, userList2);
    }

    @Test
    public void testFindById() {
        User user1 = userRepository.save(new User("John", "john@example.com"));
        User user2 = userRepository.findById(user1.getId());
        assertEquals(user1.getName(), user2.getName());
        assertEquals(user1.getEmail(), user2.getEmail());
    }

    @Test
    public void testSave() {
        User user1 = new User("John", "john@example.com");
        userRepository.save(user1);
        User user2 = userRepository.findById(user1.getId());
        assertEquals(user1.getName(), user2.getName());
        assertEquals(user1.getEmail(), user2.getEmail());
    }

    @Test
    public void testDeleteById() {
        User user1 = userRepository.save(new User("John", "john@example.com"));
        userRepository.deleteById(user1.getId());
        assertNull(userRepository.findById(user1.getId()));
    }
}


내저장소 바로가기 luxury515

+ Recent posts