From f1a42ad1c4d6cea1a8885302d54581cdb32d079b Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:04:37 +0900 Subject: [PATCH 1/8] =?UTF-8?q?chore:=20Notion,=20Slack=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/properties/NotionProperties.kt | 13 +++++++++++++ .../global/config/properties/SlackProperties.kt | 11 +++++++++++ src/main/resources/application.yml | 8 ++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/main/kotlin/com/weeth/global/config/properties/NotionProperties.kt create mode 100644 src/main/kotlin/com/weeth/global/config/properties/SlackProperties.kt diff --git a/src/main/kotlin/com/weeth/global/config/properties/NotionProperties.kt b/src/main/kotlin/com/weeth/global/config/properties/NotionProperties.kt new file mode 100644 index 00000000..e0c10158 --- /dev/null +++ b/src/main/kotlin/com/weeth/global/config/properties/NotionProperties.kt @@ -0,0 +1,13 @@ +package com.weeth.global.config.properties + +import jakarta.validation.constraints.NotBlank +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.validation.annotation.Validated + +@Validated +@ConfigurationProperties(prefix = "notion") +data class NotionProperties( + @field:NotBlank val token: String, + @field:NotBlank val version: String, + @field:NotBlank val inquiryDatabaseId: String, +) diff --git a/src/main/kotlin/com/weeth/global/config/properties/SlackProperties.kt b/src/main/kotlin/com/weeth/global/config/properties/SlackProperties.kt new file mode 100644 index 00000000..a90636ac --- /dev/null +++ b/src/main/kotlin/com/weeth/global/config/properties/SlackProperties.kt @@ -0,0 +1,11 @@ +package com.weeth.global.config.properties + +import jakarta.validation.constraints.NotBlank +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.validation.annotation.Validated + +@Validated +@ConfigurationProperties(prefix = "slack") +data class SlackProperties( + @field:NotBlank val webhookUrl: String, +) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 535ad46d..d955eb5e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,3 +49,11 @@ app: career-net: key: ${CAREER_NET_API_KEY} base-url: https://www.career.go.kr/cnet/openapi/getOpenApi + +slack: + webhook-url: ${SLACK_WEBHOOK_URL} + +notion: + token: ${NOTION_TOKEN} + version: ${NOTION_VERSION} + inquiry-database-id: ${NOTION_INQUIRY_DATABASE_ID} From 6b25d11309e2b072a27ef149960b190883bf4856 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:05:04 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20port=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weeth/domain/user/domain/port/InquiryNotifyPort.kt | 8 ++++++++ .../com/weeth/domain/user/domain/port/InquirySavePort.kt | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/user/domain/port/InquiryNotifyPort.kt create mode 100644 src/main/kotlin/com/weeth/domain/user/domain/port/InquirySavePort.kt diff --git a/src/main/kotlin/com/weeth/domain/user/domain/port/InquiryNotifyPort.kt b/src/main/kotlin/com/weeth/domain/user/domain/port/InquiryNotifyPort.kt new file mode 100644 index 00000000..0acd48c4 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/domain/port/InquiryNotifyPort.kt @@ -0,0 +1,8 @@ +package com.weeth.domain.user.domain.port + +interface InquiryNotifyPort { + fun notify( + email: String, + message: String, + ) +} diff --git a/src/main/kotlin/com/weeth/domain/user/domain/port/InquirySavePort.kt b/src/main/kotlin/com/weeth/domain/user/domain/port/InquirySavePort.kt new file mode 100644 index 00000000..5d794af6 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/domain/port/InquirySavePort.kt @@ -0,0 +1,8 @@ +package com.weeth.domain.user.domain.port + +interface InquirySavePort { + fun save( + email: String, + message: String, + ) +} From 1fa51f5bdf272d9168c8df89a9a3cb604f0e178a Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:06:09 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feet:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateInquiryRequest.kt | 18 ++++++++++++++++++ .../exception/NotionApiException.kt | 11 +++++++++++ .../application/exception/SlackApiException.kt | 11 +++++++++++ .../application/exception/UserErrorCode.kt | 6 ++++++ .../usecase/command/CreateInquiryUseCase.kt | 17 +++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/user/application/dto/request/CreateInquiryRequest.kt create mode 100644 src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt create mode 100644 src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt create mode 100644 src/main/kotlin/com/weeth/domain/user/application/usecase/command/CreateInquiryUseCase.kt diff --git a/src/main/kotlin/com/weeth/domain/user/application/dto/request/CreateInquiryRequest.kt b/src/main/kotlin/com/weeth/domain/user/application/dto/request/CreateInquiryRequest.kt new file mode 100644 index 00000000..0a260414 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/application/dto/request/CreateInquiryRequest.kt @@ -0,0 +1,18 @@ +package com.weeth.domain.user.application.dto.request + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Email +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size + +data class CreateInquiryRequest( + @field:Schema(description = "이메일", example = "user@example.com") + @field:NotBlank + @field:Email + @field:Size(max = 255) + val email: String, + @field:Schema(description = "문의 내용", example = "서비스에 대해 문의드립니다.") + @field:NotBlank + @field:Size(max = 1000) + val message: String, +) diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt new file mode 100644 index 00000000..7c39b5a3 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt @@ -0,0 +1,11 @@ +package com.weeth.domain.user.application.exception + +import com.weeth.global.common.exception.BaseException + +class NotionApiException( + cause: Throwable? = null, +) : BaseException(UserErrorCode.NOTION_API_ERROR) { + init { + initCause(cause) + } +} diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt new file mode 100644 index 00000000..d75eb3ea --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt @@ -0,0 +1,11 @@ +package com.weeth.domain.user.application.exception + +import com.weeth.global.common.exception.BaseException + +class SlackApiException( + cause: Throwable? = null, +) : BaseException(UserErrorCode.SLACK_API_ERROR) { + init { + initCause(cause) + } +} diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt index 074ce475..de22009d 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt @@ -44,4 +44,10 @@ enum class UserErrorCode( @ExplainError("사용자 순서 지정 시 잘못된 값이 입력되었을 때 발생합니다.") INVALID_USER_ORDER(20911, HttpStatus.BAD_REQUEST, "잘못된 사용자 순서입니다."), + + @ExplainError("Notion API 호출에 실패했을 때 발생합니다.") + NOTION_API_ERROR(30900, HttpStatus.INTERNAL_SERVER_ERROR, "Notion API 호출에 실패했습니다."), + + @ExplainError("Slack API 호출에 실패했을 때 발생합니다.") + SLACK_API_ERROR(30901, HttpStatus.INTERNAL_SERVER_ERROR, "Slack 알림 전송에 실패했습니다."), } diff --git a/src/main/kotlin/com/weeth/domain/user/application/usecase/command/CreateInquiryUseCase.kt b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/CreateInquiryUseCase.kt new file mode 100644 index 00000000..bd1c3c41 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/application/usecase/command/CreateInquiryUseCase.kt @@ -0,0 +1,17 @@ +package com.weeth.domain.user.application.usecase.command + +import com.weeth.domain.user.application.dto.request.CreateInquiryRequest +import com.weeth.domain.user.domain.port.InquiryNotifyPort +import com.weeth.domain.user.domain.port.InquirySavePort +import org.springframework.stereotype.Service + +@Service +class CreateInquiryUseCase( + private val inquirySavePort: InquirySavePort, + private val inquiryNotifyPort: InquiryNotifyPort, +) { + fun execute(request: CreateInquiryRequest) { + inquirySavePort.save(request.email, request.message) + inquiryNotifyPort.notify(request.email, request.message) + } +} From 01d6bc800c672204c38075ad1b79ce31562d3624 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:06:44 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feet:=20Notion,=20Slack=20Adapter=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NotionInquirySaveAdapter.kt | 58 +++++++++++++++++++ .../SlackInquiryNotifyAdapter.kt | 32 ++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt create mode 100644 src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt diff --git a/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt b/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt new file mode 100644 index 00000000..748be44a --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt @@ -0,0 +1,58 @@ +package com.weeth.domain.user.infrastructure + +import com.weeth.domain.user.application.exception.NotionApiException +import com.weeth.domain.user.domain.port.InquirySavePort +import com.weeth.global.config.properties.NotionProperties +import org.springframework.http.HttpHeaders +import org.springframework.stereotype.Component +import org.springframework.web.client.RestClient +import java.time.LocalDate + +@Component +class NotionInquirySaveAdapter( + private val notionProperties: NotionProperties, + restClientBuilder: RestClient.Builder, +) : InquirySavePort { + private val restClient = restClientBuilder.baseUrl("https://api.notion.com").build() + + override fun save( + email: String, + message: String, + ) { + val body = + mapOf( + "parent" to + mapOf( + "type" to "database_id", + "database_id" to notionProperties.inquiryDatabaseId, + ), + "properties" to + mapOf( + "문의내용" to + mapOf( + "title" to listOf(mapOf("text" to mapOf("content" to message))), + ), + "이메일" to + mapOf( + "email" to email, + ), + "날짜" to + mapOf( + "date" to mapOf("start" to LocalDate.now().toString()), + ), + ), + ) + + runCatching { + restClient + .post() + .uri("/v1/pages") + .header(HttpHeaders.AUTHORIZATION, "Bearer ${notionProperties.token}") + .header("Notion-Version", notionProperties.version) + .header(HttpHeaders.CONTENT_TYPE, "application/json") + .body(body) + .retrieve() + .toBodilessEntity() + }.getOrElse { e -> throw NotionApiException(e) } + } +} diff --git a/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt b/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt new file mode 100644 index 00000000..ff917043 --- /dev/null +++ b/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt @@ -0,0 +1,32 @@ +package com.weeth.domain.user.infrastructure + +import com.weeth.domain.user.application.exception.SlackApiException +import com.weeth.domain.user.domain.port.InquiryNotifyPort +import com.weeth.global.config.properties.SlackProperties +import org.springframework.stereotype.Component +import org.springframework.web.client.RestClient + +@Component +class SlackInquiryNotifyAdapter( + private val slackProperties: SlackProperties, + restClientBuilder: RestClient.Builder, +) : InquiryNotifyPort { + private val restClient = restClientBuilder.build() + + override fun notify( + email: String, + message: String, + ) { + val text = "*[랜딩 문의하기]*\n*이메일:* $email\n*문의 내용:*\n```$message```" + + runCatching { + restClient + .post() + .uri(slackProperties.webhookUrl) + .header("Content-Type", "application/json") + .body(mapOf("text" to text)) + .retrieve() + .toBodilessEntity() + }.getOrElse { e -> throw SlackApiException(e) } + } +} From 978b9325292907b626a77ea78cecbf8ea58bad2e Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:07:01 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feet:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=97=94=ED=8A=B8=ED=8F=AC=EC=9D=B8=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/presentation/UserController.kt | 13 +++++++++++++ .../domain/user/presentation/UserResponseCode.kt | 1 + .../com/weeth/global/config/SecurityConfig.kt | 1 + 3 files changed, 15 insertions(+) diff --git a/src/main/kotlin/com/weeth/domain/user/presentation/UserController.kt b/src/main/kotlin/com/weeth/domain/user/presentation/UserController.kt index f46018e3..94f56fe6 100644 --- a/src/main/kotlin/com/weeth/domain/user/presentation/UserController.kt +++ b/src/main/kotlin/com/weeth/domain/user/presentation/UserController.kt @@ -1,12 +1,14 @@ package com.weeth.domain.user.presentation import com.weeth.domain.user.application.dto.request.AgreeTermsRequest +import com.weeth.domain.user.application.dto.request.CreateInquiryRequest import com.weeth.domain.user.application.dto.request.SocialLoginRequest import com.weeth.domain.user.application.dto.request.UpdateUserProfileRequest import com.weeth.domain.user.application.dto.response.SocialLoginResponse import com.weeth.domain.user.application.exception.UserErrorCode import com.weeth.domain.user.application.usecase.command.AgreeTermsUseCase import com.weeth.domain.user.application.usecase.command.AuthUserUseCase +import com.weeth.domain.user.application.usecase.command.CreateInquiryUseCase import com.weeth.domain.user.application.usecase.command.SocialLoginUseCase import com.weeth.domain.user.application.usecase.command.UpdateUserProfileUseCase import com.weeth.global.auth.annotation.CurrentUser @@ -38,6 +40,7 @@ class UserController( private val socialLoginUseCase: SocialLoginUseCase, private val updateUserProfileUseCase: UpdateUserProfileUseCase, private val agreeTermsUseCase: AgreeTermsUseCase, + private val createInquiryUseCase: CreateInquiryUseCase, private val tokenCookieProvider: TokenCookieProvider, ) { @PostMapping("/social/kakao") @@ -104,6 +107,16 @@ class UserController( return CommonResponse.success(UserResponseCode.USER_UPDATE_SUCCESS) } + @PostMapping("/inquiries") + @Operation(summary = "문의하기") + @SecurityRequirements + fun createInquiry( + @RequestBody @Valid request: CreateInquiryRequest, + ): CommonResponse { + createInquiryUseCase.execute(request) + return CommonResponse.success(UserResponseCode.INQUIRY_SEND_SUCCESS) + } + private fun buildTokenResponse( body: CommonResponse, accessToken: String, diff --git a/src/main/kotlin/com/weeth/domain/user/presentation/UserResponseCode.kt b/src/main/kotlin/com/weeth/domain/user/presentation/UserResponseCode.kt index c0a186ba..c1776a65 100644 --- a/src/main/kotlin/com/weeth/domain/user/presentation/UserResponseCode.kt +++ b/src/main/kotlin/com/weeth/domain/user/presentation/UserResponseCode.kt @@ -12,4 +12,5 @@ enum class UserResponseCode( JWT_REFRESH_SUCCESS(10902, HttpStatus.OK, "토큰 재발급에 성공했습니다."), SOCIAL_LOGIN_SUCCESS(10903, HttpStatus.OK, "소셜 로그인이 성공적으로 처리되었습니다."), USER_TERMS_AGREE_SUCCESS(10904, HttpStatus.OK, "약관 동의가 성공적으로 처리되었습니다."), + INQUIRY_SEND_SUCCESS(10905, HttpStatus.OK, "문의가 성공적으로 접수되었습니다."), } diff --git a/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt b/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt index d1c02a2b..33a27fef 100644 --- a/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt +++ b/src/main/kotlin/com/weeth/global/config/SecurityConfig.kt @@ -45,6 +45,7 @@ class SecurityConfig( "/api/v4/users/social/kakao", "/api/v4/users/social/apple", "/api/v4/users/social/refresh", + "/api/v4/users/inquiries", ).permitAll() .requestMatchers("/health-check") .permitAll() From 0e690f9454a24a87443d73d56cbb4d72a614dda4 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:20:42 +0900 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20=EC=9D=91=EB=8B=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weeth/domain/user/application/exception/UserErrorCode.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt index de22009d..a5a39953 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt @@ -46,8 +46,8 @@ enum class UserErrorCode( INVALID_USER_ORDER(20911, HttpStatus.BAD_REQUEST, "잘못된 사용자 순서입니다."), @ExplainError("Notion API 호출에 실패했을 때 발생합니다.") - NOTION_API_ERROR(30900, HttpStatus.INTERNAL_SERVER_ERROR, "Notion API 호출에 실패했습니다."), + NOTION_API_ERROR(20912, HttpStatus.INTERNAL_SERVER_ERROR, "Notion API 호출에 실패했습니다."), @ExplainError("Slack API 호출에 실패했을 때 발생합니다.") - SLACK_API_ERROR(30901, HttpStatus.INTERNAL_SERVER_ERROR, "Slack 알림 전송에 실패했습니다."), + SLACK_API_ERROR(20913, HttpStatus.INTERNAL_SERVER_ERROR, "Slack 알림 전송에 실패했습니다."), } From e484fec550ec028776479cea4fe44935d8d3ec62 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 14:31:47 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=B9=84=EB=8F=99=EA=B8=B0=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/com/weeth/WeethApplication.kt | 2 ++ .../user/application/exception/NotionApiException.kt | 11 ----------- .../user/application/exception/SlackApiException.kt | 11 ----------- .../user/application/exception/UserErrorCode.kt | 6 ------ .../user/infrastructure/NotionInquirySaveAdapter.kt | 7 +++++-- .../user/infrastructure/SlackInquiryNotifyAdapter.kt | 7 +++++-- 6 files changed, 12 insertions(+), 32 deletions(-) delete mode 100644 src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt delete mode 100644 src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt diff --git a/src/main/kotlin/com/weeth/WeethApplication.kt b/src/main/kotlin/com/weeth/WeethApplication.kt index 8664ed91..55d667ba 100644 --- a/src/main/kotlin/com/weeth/WeethApplication.kt +++ b/src/main/kotlin/com/weeth/WeethApplication.kt @@ -4,9 +4,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.ConfigurationPropertiesScan import org.springframework.boot.runApplication import org.springframework.data.jpa.repository.config.EnableJpaAuditing +import org.springframework.scheduling.annotation.EnableAsync import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity +@EnableAsync @EnableScheduling @EnableJpaAuditing @EnableWebSecurity diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt deleted file mode 100644 index 7c39b5a3..00000000 --- a/src/main/kotlin/com/weeth/domain/user/application/exception/NotionApiException.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.weeth.domain.user.application.exception - -import com.weeth.global.common.exception.BaseException - -class NotionApiException( - cause: Throwable? = null, -) : BaseException(UserErrorCode.NOTION_API_ERROR) { - init { - initCause(cause) - } -} diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt deleted file mode 100644 index d75eb3ea..00000000 --- a/src/main/kotlin/com/weeth/domain/user/application/exception/SlackApiException.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.weeth.domain.user.application.exception - -import com.weeth.global.common.exception.BaseException - -class SlackApiException( - cause: Throwable? = null, -) : BaseException(UserErrorCode.SLACK_API_ERROR) { - init { - initCause(cause) - } -} diff --git a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt index a5a39953..074ce475 100644 --- a/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt +++ b/src/main/kotlin/com/weeth/domain/user/application/exception/UserErrorCode.kt @@ -44,10 +44,4 @@ enum class UserErrorCode( @ExplainError("사용자 순서 지정 시 잘못된 값이 입력되었을 때 발생합니다.") INVALID_USER_ORDER(20911, HttpStatus.BAD_REQUEST, "잘못된 사용자 순서입니다."), - - @ExplainError("Notion API 호출에 실패했을 때 발생합니다.") - NOTION_API_ERROR(20912, HttpStatus.INTERNAL_SERVER_ERROR, "Notion API 호출에 실패했습니다."), - - @ExplainError("Slack API 호출에 실패했을 때 발생합니다.") - SLACK_API_ERROR(20913, HttpStatus.INTERNAL_SERVER_ERROR, "Slack 알림 전송에 실패했습니다."), } diff --git a/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt b/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt index 748be44a..c2ca6cb0 100644 --- a/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt +++ b/src/main/kotlin/com/weeth/domain/user/infrastructure/NotionInquirySaveAdapter.kt @@ -1,9 +1,10 @@ package com.weeth.domain.user.infrastructure -import com.weeth.domain.user.application.exception.NotionApiException import com.weeth.domain.user.domain.port.InquirySavePort import com.weeth.global.config.properties.NotionProperties +import org.slf4j.LoggerFactory import org.springframework.http.HttpHeaders +import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.web.client.RestClient import java.time.LocalDate @@ -14,7 +15,9 @@ class NotionInquirySaveAdapter( restClientBuilder: RestClient.Builder, ) : InquirySavePort { private val restClient = restClientBuilder.baseUrl("https://api.notion.com").build() + private val log = LoggerFactory.getLogger(javaClass) + @Async override fun save( email: String, message: String, @@ -53,6 +56,6 @@ class NotionInquirySaveAdapter( .body(body) .retrieve() .toBodilessEntity() - }.getOrElse { e -> throw NotionApiException(e) } + }.onFailure { e -> log.warn("Notion 저장 실패: {}", e.message) } } } diff --git a/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt b/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt index ff917043..25488bc1 100644 --- a/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt +++ b/src/main/kotlin/com/weeth/domain/user/infrastructure/SlackInquiryNotifyAdapter.kt @@ -1,8 +1,9 @@ package com.weeth.domain.user.infrastructure -import com.weeth.domain.user.application.exception.SlackApiException import com.weeth.domain.user.domain.port.InquiryNotifyPort import com.weeth.global.config.properties.SlackProperties +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import org.springframework.web.client.RestClient @@ -12,7 +13,9 @@ class SlackInquiryNotifyAdapter( restClientBuilder: RestClient.Builder, ) : InquiryNotifyPort { private val restClient = restClientBuilder.build() + private val log = LoggerFactory.getLogger(javaClass) + @Async override fun notify( email: String, message: String, @@ -27,6 +30,6 @@ class SlackInquiryNotifyAdapter( .body(mapOf("text" to text)) .retrieve() .toBodilessEntity() - }.getOrElse { e -> throw SlackApiException(e) } + }.onFailure { e -> log.warn("Slack 알림 전송 실패: {}", e.message) } } } From 4c44f8118b9351110344556b4888e54931e71633 Mon Sep 17 00:00:00 2001 From: soo0711 Date: Tue, 31 Mar 2026 22:41:32 +0900 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20AsyncConfig=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/weeth/global/config/AsyncConfig.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/kotlin/com/weeth/global/config/AsyncConfig.kt diff --git a/src/main/kotlin/com/weeth/global/config/AsyncConfig.kt b/src/main/kotlin/com/weeth/global/config/AsyncConfig.kt new file mode 100644 index 00000000..c9fffaa9 --- /dev/null +++ b/src/main/kotlin/com/weeth/global/config/AsyncConfig.kt @@ -0,0 +1,19 @@ +package com.weeth.global.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor +import java.util.concurrent.Executor + +@Configuration +class AsyncConfig { + @Bean(name = ["taskExecutor"]) + fun taskExecutor(): Executor = + ThreadPoolTaskExecutor().apply { + corePoolSize = 5 + maxPoolSize = 10 + queueCapacity = 50 + setThreadNamePrefix("async-") + initialize() + } +}