diff --git a/build.gradle b/build.gradle index 4895ebc..d0d5ddd 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' + // Rabbit MQ + implementation 'org.springframework.boot:spring-boot-starter-amqp' } tasks.named('test') { diff --git a/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqConfig.java b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqConfig.java new file mode 100644 index 0000000..87d9690 --- /dev/null +++ b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqConfig.java @@ -0,0 +1,75 @@ +package gdsc.cau.puangbe.common.config.RabbitMq; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.amqp.core.Queue; + +@RequiredArgsConstructor +@Configuration +public class RabbitMqConfig { + private final RabbitMqProperties rabbitMqProperties; + private final RabbitMqInfo rabbitMqInfo; + + @Bean + public Queue queue() { + return new Queue(rabbitMqInfo.getQueueName()); + } + + /** + * 지정된 Exchange 이름으로 Direct Exchange Bean 생성 + */ + @Bean + public DirectExchange directExchange() { + return new DirectExchange(rabbitMqInfo.getExchangeName()); + } + + /** + * 주어진 Queue와 Exchange Binding + * Routing Key 을 이용하여 Binding Bean 생성 + **/ + @Bean + public Binding binding(Queue queue, DirectExchange exchange) { + return BindingBuilder.bind(queue).to(exchange).with(rabbitMqInfo.getQueueName()); + } + + /** + * RabbitMQ 연동을 위한 ConnectionFactory 빈을 생성하여 반환 + **/ + @Bean + public CachingConnectionFactory connectionFactory() { + CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); + connectionFactory.setHost(rabbitMqProperties.getHost()); + connectionFactory.setPort(rabbitMqProperties.getPort()); + connectionFactory.setUsername(rabbitMqProperties.getUsername()); + connectionFactory.setPassword(rabbitMqProperties.getPassword()); + return connectionFactory; + } + + /** + * RabbitTemplate + * ConnectionFactory 로 연결 후 실제 작업을 위한 Template + */ + @Bean + public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { + RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter()); + return rabbitTemplate; + } + + /** + * 직렬화 (메세지를 JSON 으로 변환하는 Message Converter) + */ + @Bean + public MessageConverter jackson2JsonMessageConverter() { + return new Jackson2JsonMessageConverter(); + } +} \ No newline at end of file diff --git a/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqInfo.java b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqInfo.java new file mode 100644 index 0000000..0045c77 --- /dev/null +++ b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqInfo.java @@ -0,0 +1,30 @@ +package gdsc.cau.puangbe.common.config.RabbitMq; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Getter +@Configuration +@ConfigurationProperties(prefix = "rabbitmq") +public class RabbitMqInfo { + + /** + * Exchange : Producer로부터 전달받은 메시지를 어떤 메시지 큐로 전송할 지 결정 + */ + @Value("${rabbitmq.exchange.name}") + private String exchangeName; + + /** + * Routing : Exchange에서 해당하는 key에 맞게 Queue에 분배 + */ + @Value("${rabbitmq.routing.key}") + private String routingKey; + + /** + * Queue : Consumer가 소비하기 전까지 메시지 보관 + */ + @Value("${rabbitmq.queue.name}") + private String queueName; +} diff --git a/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqProperties.java b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqProperties.java new file mode 100644 index 0000000..2dd2dc5 --- /dev/null +++ b/src/main/java/gdsc/cau/puangbe/common/config/RabbitMq/RabbitMqProperties.java @@ -0,0 +1,20 @@ +package gdsc.cau.puangbe.common.config.RabbitMq; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * spring.rabbitmq 의 prefix 을 가지는 값들을 + * RabbitMqProperties 클래스 필드로 바인딩 한 후 사용 + */ +@ConfigurationProperties(prefix = "spring.rabbitmq") +@AllArgsConstructor +@Getter +public class RabbitMqProperties { + private String host; + private int port; + private String username; + private String password; +} diff --git a/src/main/java/gdsc/cau/puangbe/photorequest/service/PhotoRequestServiceImpl.java b/src/main/java/gdsc/cau/puangbe/photorequest/service/PhotoRequestServiceImpl.java index 07f93c3..de3823a 100644 --- a/src/main/java/gdsc/cau/puangbe/photorequest/service/PhotoRequestServiceImpl.java +++ b/src/main/java/gdsc/cau/puangbe/photorequest/service/PhotoRequestServiceImpl.java @@ -36,6 +36,7 @@ public class PhotoRequestServiceImpl implements PhotoRequestService { private final UserRepository userRepository; private final RedisTemplate redisTemplate; private final ObjectMapper mapper; + private final RabbitMqService rabbitMqService; //이미지 처리 요청 생성 (RabbitMQ호출) @Override @@ -67,8 +68,7 @@ public Long createImage(CreateImageDto dto, Long userId){ .build(); String message = mapper.writeValueAsString(imageInfo); - // TODO: RabbitMQ 호출 - // 1. RabbitMQ를 호출해서 message를 큐에 함께 넣어서 파이썬에서 접근할 수 있도록 한다. + rabbitMqService.sendMessage(message); // 1. RabbitMQ를 호출해서 message를 큐에 함께 넣어서 파이썬에서 접근할 수 있도록 한다. // 2. Redis에 형식으로 진행되고 있는 request 정보를 저장한다. // 3. 추후 사진이 완성된다면 requestId를 통해 request를 찾아서 상태를 바꾸고 1:1 관계인 result에 접근해서 imageUrl를 수정한다. // 4. 즉, 파이썬에서 스프링으로 향하는 POST API는 {requestId, imageUrl}이 필수적으로 존재해야 한다. diff --git a/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqService.java b/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqService.java new file mode 100644 index 0000000..1c3c147 --- /dev/null +++ b/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqService.java @@ -0,0 +1,7 @@ +package gdsc.cau.puangbe.photorequest.service; + +import gdsc.cau.puangbe.photorequest.dto.ImageInfo; + +public interface RabbitMqService { + public void sendMessage(String message); +} diff --git a/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqServiceImpl.java b/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqServiceImpl.java new file mode 100644 index 0000000..148dd1d --- /dev/null +++ b/src/main/java/gdsc/cau/puangbe/photorequest/service/RabbitMqServiceImpl.java @@ -0,0 +1,28 @@ +package gdsc.cau.puangbe.photorequest.service; + +import gdsc.cau.puangbe.common.config.RabbitMq.RabbitMqInfo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class RabbitMqServiceImpl implements RabbitMqService{ + + private final RabbitTemplate rabbitTemplate; + private final RabbitMqInfo rabbitMqInfo; + + /** + * 1. Queue 로 메세지를 발행 + * 2. Producer 역할 -> Direct Exchange (메시지의 routing key와 정확히 일치하는 binding된 Queue로 routing) + **/ + public void sendMessage(String message) { + this.rabbitTemplate.convertAndSend(rabbitMqInfo.getExchangeName(), rabbitMqInfo.getRoutingKey(), message); + log.info("**Message Send**: {}", message); + log.info("messagge queue: {}", rabbitMqInfo.getQueueName()); + log.info("messagge exchange: {}", rabbitMqInfo.getExchangeName()); + log.info("messagge routingKey: {}", rabbitMqInfo.getRoutingKey()); + } +}