From f74d73a7504e049d81df64c8b1e2bd62f2125354 Mon Sep 17 00:00:00 2001 From: yang Date: Tue, 20 Feb 2024 00:42:22 +0900 Subject: [PATCH 1/3] =?UTF-8?q?RAC-67=20docs=20:=20gitignore=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b1ef8e7d..af902cdf 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ out/ .vscode/ ### yml ### -application.yml \ No newline at end of file +application.yml +application-*.yml \ No newline at end of file From 13d33b630d89339f6e935e6f8be1f359f56b006b Mon Sep 17 00:00:00 2001 From: yang Date: Tue, 20 Feb 2024 00:42:47 +0900 Subject: [PATCH 2/3] =?UTF-8?q?RAC-67=20deploy=20:=20=EC=9A=B4=EC=98=81?= =?UTF-8?q?=EC=9A=A9=20cicd=20workflow=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/CD-prod.yml | 77 +++++++++++++++++++++++++++++++++++ .github/workflows/CI-prod.yml | 52 +++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 .github/workflows/CD-prod.yml create mode 100644 .github/workflows/CI-prod.yml diff --git a/.github/workflows/CD-prod.yml b/.github/workflows/CD-prod.yml new file mode 100644 index 00000000..10220910 --- /dev/null +++ b/.github/workflows/CD-prod.yml @@ -0,0 +1,77 @@ +name: Java CD with Gradle + +on: + push: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + env : + working-directory: ./ + APPLICATION: ${{ secrets.APPLICATION_PROD }} + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'adopt' + + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Create application.yml + run: | + echo "${{env.APPLICATION}}" > ./src/main/resources/application.yml + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + working-directory: ${{ env.working-directory }} + + - name: Build with Gradle + run: ./gradlew build + working-directory: ${{ env.working-directory }} + + - name: Cleanup Gradle Cache + if: ${{ always() }} + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and Push Docker image + run: | + docker build -t ywj9811/kimseonbae:latest . + docker push ywj9811/kimseonbae:latest + + - name: Deploy + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_SERVER_HOST_PROD }} + username: ${{ secrets.EC2_SERVER_USERNAME }} + key: ${{ secrets.PRIVATE_KEY_PROD }} + envs: GITHUB_SHA + script: | + chmod +x /home/ec2-user/config/deploy.sh + /home/ec2-user/config/deploy.sh + debug: true + + diff --git a/.github/workflows/CI-prod.yml b/.github/workflows/CI-prod.yml new file mode 100644 index 00000000..04a9f6d5 --- /dev/null +++ b/.github/workflows/CI-prod.yml @@ -0,0 +1,52 @@ +name: Java CI with Gradle + +on: + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + env : + working-directory: ./ + APPLICATION: ${{ secrets.APPLICATION_PROD }} + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'adopt' + + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Create application.yml + run: | + echo "${{env.APPLICATION}}" > ./src/main/resources/application.yml + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + working-directory: ${{ env.working-directory }} + + - name: Build with Gradle + run: ./gradlew build + working-directory: ${{ env.working-directory }} + + - name: Cleanup Gradle Cache + if: ${{ always() }} + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties \ No newline at end of file From 3bb1d5a167ebd1518bbe0c7419ea9e69e7e3796d Mon Sep 17 00:00:00 2001 From: yang Date: Tue, 20 Feb 2024 05:43:58 +0900 Subject: [PATCH 3/3] =?UTF-8?q?RAC-67=20fix=20:=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20webClient=20=EC=82=AC=EC=9A=A9=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwt/filter/CustomAccessDeniedHandler.java | 6 +- .../CustomAuthenticationEntryPoint.java | 6 +- .../config/security/jwt/util/JwtUtils.java | 5 +- .../exception/GlobalExceptionHandler.java | 6 +- .../global/logging/aop/LogAspect.java | 12 +- .../global/logging/dto/LogRequest.java | 14 +-- .../global/logging/service/LogService.java | 39 +++++-- .../global/mq/config/RabbitMQConfig.java | 109 ------------------ .../global/mq/producer/MessageProducer.java | 34 ------ 9 files changed, 64 insertions(+), 167 deletions(-) delete mode 100644 src/main/java/com/postgraduate/global/mq/config/RabbitMQConfig.java delete mode 100644 src/main/java/com/postgraduate/global/mq/producer/MessageProducer.java diff --git a/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAccessDeniedHandler.java b/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAccessDeniedHandler.java index 4d14eead..e6a4c585 100644 --- a/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAccessDeniedHandler.java +++ b/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAccessDeniedHandler.java @@ -7,6 +7,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.AccessDeniedException; @@ -21,6 +22,9 @@ @Component @RequiredArgsConstructor public class CustomAccessDeniedHandler implements AccessDeniedHandler { + @Value("${log.Type}") + private String env; + private final ObjectMapper objectMapper; private final LogService logService; @@ -29,7 +33,7 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Acc response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); - logService.save(new LogRequest(CustomAccessDeniedHandler.class.getSimpleName(), PERMISSION_DENIED.getMessage())); + logService.save(new LogRequest(env, CustomAccessDeniedHandler.class.getSimpleName(), PERMISSION_DENIED.getMessage())); objectMapper.writeValue( response.getOutputStream(), new ErrorResponse(AUTH_DENIED.getCode(), PERMISSION_DENIED.getMessage()) diff --git a/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAuthenticationEntryPoint.java b/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAuthenticationEntryPoint.java index 61059851..d104778f 100644 --- a/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAuthenticationEntryPoint.java +++ b/src/main/java/com/postgraduate/global/config/security/jwt/filter/CustomAuthenticationEntryPoint.java @@ -8,6 +8,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.core.AuthenticationException; @@ -22,6 +23,9 @@ @Component @RequiredArgsConstructor public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint { + @Value("${log.Type}") + private String env; + private final ObjectMapper objectMapper; private final LogService logService; @@ -30,7 +34,7 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A response.setStatus(HttpStatus.OK.value()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); - logService.save(new LogRequest(CustomAuthenticationEntryPoint.class.getSimpleName(), FAILED_AUTH.getMessage())); + logService.save(new LogRequest(env, CustomAuthenticationEntryPoint.class.getSimpleName(), FAILED_AUTH.getMessage())); objectMapper.writeValue( response.getOutputStream(), new ErrorResponse(AUTH_FAILED.getCode(), FAILED_AUTH.getMessage()) diff --git a/src/main/java/com/postgraduate/global/config/security/jwt/util/JwtUtils.java b/src/main/java/com/postgraduate/global/config/security/jwt/util/JwtUtils.java index ae0ba361..9ac88267 100644 --- a/src/main/java/com/postgraduate/global/config/security/jwt/util/JwtUtils.java +++ b/src/main/java/com/postgraduate/global/config/security/jwt/util/JwtUtils.java @@ -59,6 +59,9 @@ public class JwtUtils { private int refreshExpiration; @Value("${jwt.accessExpiration}") private int accessExpiration; + @Value("${log.Type}") + private String env; + private static final String ROLE = "role"; private static final String TYPE = "type"; private static final String AUTHORIZATION = "Authorization"; @@ -153,7 +156,7 @@ private void jwtExceptionHandler(HttpStatus status, HttpServletResponse response response.setContentType(CONTENT_TYPE); response.setCharacterEncoding(CHARACTER_ENCODING); try { - logService.save(new LogRequest(JwtFilter.class.getSimpleName(), ex.getMessage())); + logService.save(new LogRequest(env, JwtFilter.class.getSimpleName(), ex.getMessage())); String json = new ObjectMapper().writeValueAsString(ResponseDto.create(ex.getErrorCode(), ex.getMessage())); response.getWriter().write(json); } catch (Exception e) { diff --git a/src/main/java/com/postgraduate/global/exception/GlobalExceptionHandler.java b/src/main/java/com/postgraduate/global/exception/GlobalExceptionHandler.java index d15f9469..dcf1066c 100644 --- a/src/main/java/com/postgraduate/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/postgraduate/global/exception/GlobalExceptionHandler.java @@ -7,6 +7,7 @@ import com.postgraduate.global.logging.service.LogService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -18,6 +19,9 @@ @RequiredArgsConstructor public class GlobalExceptionHandler { private final LogService logService; + + @Value("${log.Type}") + private String env; private static final String LOG_FORMAT = "Code : {}, Message : {}"; @ExceptionHandler(ApplicationException.class) @@ -27,7 +31,7 @@ public ResponseDto handleApplicationException(ApplicationExceptio @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseDto handleArgumentValidException(MethodArgumentNotValidException ex) throws IOException { - logService.save(new LogRequest("@Valid", ex.getBindingResult().getAllErrors().get(0).getDefaultMessage())); + logService.save(new LogRequest(env, "@Valid", ex.getBindingResult().getAllErrors().get(0).getDefaultMessage())); log.error(LOG_FORMAT, "@Valid Error", "MethodArgumentNotValidException"); return ResponseDto.create(ErrorCode.VALID_BLANK.getCode(), ex.getBindingResult().getAllErrors().get(0).getDefaultMessage()); } diff --git a/src/main/java/com/postgraduate/global/logging/aop/LogAspect.java b/src/main/java/com/postgraduate/global/logging/aop/LogAspect.java index 1d00991c..d4ff236c 100644 --- a/src/main/java/com/postgraduate/global/logging/aop/LogAspect.java +++ b/src/main/java/com/postgraduate/global/logging/aop/LogAspect.java @@ -8,15 +8,19 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import static com.postgraduate.global.logging.aop.LogUtils.*; +import static com.postgraduate.global.logging.aop.LogUtils.clearLogId; +import static com.postgraduate.global.logging.aop.LogUtils.setLogId; @Aspect @Slf4j @Component @RequiredArgsConstructor public class LogAspect { + @Value("${log.Type}") + private String env; private final LogTrace logTrace; private final LogService logService; @@ -42,18 +46,18 @@ private Object getObject(ProceedingJoinPoint joinPoint) throws Throwable { Object result = joinPoint.proceed(); Integer executionTime = logTrace.end(traceStatus); log.info("ExecutionTime : {}", executionTime); - logService.save(new LogRequest(traceStatus.threadId(), executionTime, traceStatus.methodName())); + logService.save(new LogRequest(env, traceStatus.threadId(), executionTime, traceStatus.methodName())); return result; }catch (ApplicationException e) { if (traceStatus != null) { logTrace.exception(e, traceStatus); - logService.save(new LogRequest(traceStatus.threadId(), traceStatus.methodName(), e.getMessage())); + logService.save(new LogRequest(env, traceStatus.threadId(), traceStatus.methodName(), e.getMessage())); } throw e; }catch (Exception e) { if (traceStatus != null) { logTrace.exception(e, traceStatus); - logService.save(new LogRequest(traceStatus.threadId(), traceStatus.methodName(), e.getMessage())); + logService.save(new LogRequest(env, traceStatus.threadId(), traceStatus.methodName(), e.getMessage())); } throw e; } diff --git a/src/main/java/com/postgraduate/global/logging/dto/LogRequest.java b/src/main/java/com/postgraduate/global/logging/dto/LogRequest.java index bd89ecba..bae2ebbb 100644 --- a/src/main/java/com/postgraduate/global/logging/dto/LogRequest.java +++ b/src/main/java/com/postgraduate/global/logging/dto/LogRequest.java @@ -1,15 +1,15 @@ package com.postgraduate.global.logging.dto; -public record LogRequest(String logId, Integer executeTime, String methodName, String exceptionMessage) { - public LogRequest(String logId, Integer executeTime, String methodName) { - this(logId, executeTime, methodName, null); +public record LogRequest(String env, String logId, Integer executeTime, String methodName, String exceptionMessage) { + public LogRequest(String env, String logId, Integer executeTime, String methodName) { + this(env, logId, executeTime, methodName, null); } - public LogRequest(String logId, String methodName, String exceptionMessage) { - this(logId, null, methodName, exceptionMessage); + public LogRequest(String env, String logId, String methodName, String exceptionMessage) { + this(env, logId, null, methodName, exceptionMessage); } - public LogRequest(String methodName, String exceptionMessage) { - this(null, null, methodName, exceptionMessage); + public LogRequest(String env, String methodName, String exceptionMessage) { + this(env, null, null, methodName, exceptionMessage); } } diff --git a/src/main/java/com/postgraduate/global/logging/service/LogService.java b/src/main/java/com/postgraduate/global/logging/service/LogService.java index f884e6ec..d7b67faf 100644 --- a/src/main/java/com/postgraduate/global/logging/service/LogService.java +++ b/src/main/java/com/postgraduate/global/logging/service/LogService.java @@ -1,11 +1,15 @@ package com.postgraduate.global.logging.service; import com.postgraduate.global.logging.dto.LogRequest; -import com.postgraduate.global.mq.producer.MessageProducer; import com.postgraduate.global.slack.SlackLogErrorMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; import java.io.IOException; @@ -13,16 +17,33 @@ @Slf4j @RequiredArgsConstructor public class LogService { - private final MessageProducer messageProducer; + private final WebClient webClient; private final SlackLogErrorMessage slackLogErrorMessage; + @Value("${log.uri}") + private String logUri; + public void save(LogRequest logRequest) throws IOException { - try { - log.info("log save"); - messageProducer.sendMessage(logRequest); - } catch (Exception ex) { - log.error("로그 서버 연결 실패"); - slackLogErrorMessage.sendSlackLog(ex); - } + log.info("log save"); + webClient.post() + .uri(logUri) + .accept(MediaType.APPLICATION_JSON) + .bodyValue(logRequest) + .retrieve() + .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> { + log.error("클라이언트 에러 발생: " + clientResponse.statusCode()); + return Mono.error(new RuntimeException("클라이언트 에러")); + }) + .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> { + log.error("서버 에러 발생: " + clientResponse.statusCode()); + return Mono.error(new RuntimeException("서버 에러")); + }) + .bodyToMono(Void.class) + .doOnError(ex -> { + log.error("예상치 못한 에러 발생: " + ex.getMessage()); + slackLogErrorMessage.sendSlackLog(new IllegalArgumentException("로그 서버 예외 발생")); + }) + .subscribe(); + } } diff --git a/src/main/java/com/postgraduate/global/mq/config/RabbitMQConfig.java b/src/main/java/com/postgraduate/global/mq/config/RabbitMQConfig.java deleted file mode 100644 index a66a0ebd..00000000 --- a/src/main/java/com/postgraduate/global/mq/config/RabbitMQConfig.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.postgraduate.global.mq.config; - -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.DirectExchange; -import org.springframework.amqp.core.Queue; -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.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class RabbitMQConfig { - @Value("${spring.rabbitmq.host}") - private String rabbitmqHost; - - @Value("${spring.rabbitmq.port}") - private int rabbitmqPort; - - @Value("${spring.rabbitmq.username}") - private String rabbitmqUsername; - - @Value("${spring.rabbitmq.password}") - private String rabbitmqPassword; - - @Value("${spring.rabbitmq.queue.name}") - private String queueName; - - @Value("${spring.rabbitmq.exchange.name}") - private String exchangeName; - - @Value("${spring.rabbitmq.routing.key}") - private String routingKey; - /** - * 지정된 큐 이름으로 Queue 빈을 생성 - * - * @return Queue 빈 객체 - */ - @Bean - public Queue queue() { - return new Queue(queueName); - } - - /** - * 지정된 익스체인지 이름으로 DirectExchange 빈을 생성 - * Direct 방식으로 - * @return TopicExchange 빈 객체 - */ - @Bean - public DirectExchange exchange() { - return new DirectExchange(exchangeName); - } - - /** - * 주어진 큐와 익스체인지를 바인딩하고 라우팅 키를 사용하여 Binding 빈을 생성 - * - * @param queue 바인딩할 Queue - * @param exchange 바인딩할 TopicExchange - * @return Binding 빈 객체 - */ - @Bean - public Binding binding(Queue queue, DirectExchange exchange) { - return BindingBuilder.bind(queue).to(exchange).with(routingKey); - } - - /** - * RabbitMQ 연결을 위한 ConnectionFactory 빈을 생성하여 반환 - * - * @return ConnectionFactory 객체 - */ - @Bean - public ConnectionFactory connectionFactory() { - CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.setHost(rabbitmqHost); - connectionFactory.setPort(rabbitmqPort); - connectionFactory.setConnectionTimeout(300); - connectionFactory.setUsername(rabbitmqUsername); - connectionFactory.setPassword(rabbitmqPassword); - return connectionFactory; - } - - /** - * RabbitTemplate을 생성하여 반환 - * - * @param connectionFactory RabbitMQ와의 연결을 위한 ConnectionFactory 객체 - * @return RabbitTemplate 객체 - */ - @Bean - public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) { - RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); - // JSON 형식의 메시지를 직렬화하고 역직렬할 수 있도록 설정 - rabbitTemplate.setMessageConverter(messageConverter()); - return rabbitTemplate; - } - - /** - * Jackson 라이브러리를 사용하여 메시지를 JSON 형식으로 변환하는 MessageConverter 빈을 생성 - * - * @return MessageConverter 객체 - */ - @Bean - public MessageConverter messageConverter() { - return new Jackson2JsonMessageConverter(); - } -} diff --git a/src/main/java/com/postgraduate/global/mq/producer/MessageProducer.java b/src/main/java/com/postgraduate/global/mq/producer/MessageProducer.java deleted file mode 100644 index b8766707..00000000 --- a/src/main/java/com/postgraduate/global/mq/producer/MessageProducer.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.postgraduate.global.mq.producer; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.postgraduate.global.logging.dto.LogRequest; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -@Slf4j -@RequiredArgsConstructor -@Service -public class MessageProducer { - - @Value("${spring.rabbitmq.exchange.name}") - private String exchangeName; - - @Value("${spring.rabbitmq.routing.key}") - private String routingKey; - - private final RabbitTemplate rabbitTemplate; - private final ObjectMapper objectMapper; - - /** - * Queue로 메시지를 발행 - * - * @param logRequest 발행할 메시지의 DTO 객체 - */ - public void sendMessage(LogRequest logRequest) throws JsonProcessingException { - rabbitTemplate.convertAndSend(exchangeName, routingKey, objectMapper.writeValueAsString(logRequest)); - } -}