From 0388e6bb18c134fb12ed91a37a302e35d0c41f9b Mon Sep 17 00:00:00 2001 From: kimdohyung Date: Sun, 19 Nov 2023 21:44:56 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20=EB=AC=B8=EC=9E=90=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=A0=84=EC=86=A1=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 7 +++++++ settings.gradle | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9e73624..2a33d8d 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,13 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' //s3 implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + //sms + implementation 'net.nurigo:sdk:4.3.0' + + //RateLimiter + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation group: 'io.github.resilience4j', name: 'resilience4j-spring-boot3', version: '2.1.0' } tasks.named('test') { diff --git a/settings.gradle b/settings.gradle index ab6c67b..0f5036d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -rootProject.name = 'Backend' +rootProject.name = 'backend' From 710e95dbab93fe2e03ac6d8edbb43119eb339038 Mon Sep 17 00:00:00 2001 From: kimdohyung Date: Sun, 19 Nov 2023 21:45:08 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9E=90=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=9A=94=EC=B2=AD=EC=9D=84=20?= =?UTF-8?q?1=EB=B6=84=EC=97=90=20=ED=95=9C=20=EB=B2=88=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/backend/config/SecurityConfig.java | 1 + .../mail/controller/MailController.java | 21 +++++++-- .../domain/mail/dto/request/SmsRequest.java | 6 +++ .../domain/mail/service/MailService.java | 47 ++++++++++++++++++- .../java/com/backend/error/ErrorCode.java | 3 +- 5 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/backend/domain/mail/dto/request/SmsRequest.java diff --git a/src/main/java/com/backend/config/SecurityConfig.java b/src/main/java/com/backend/config/SecurityConfig.java index b1f925e..d249ed9 100644 --- a/src/main/java/com/backend/config/SecurityConfig.java +++ b/src/main/java/com/backend/config/SecurityConfig.java @@ -62,6 +62,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(mvcMatcherBuilder.pattern("/api/**")).permitAll() .requestMatchers(mvcMatcherBuilder.pattern("/mail/**")).permitAll() .requestMatchers(mvcMatcherBuilder.pattern("/s3/create")).permitAll() + .requestMatchers(mvcMatcherBuilder.pattern("/sms")).permitAll() .anyRequest().authenticated()) .exceptionHandling() .authenticationEntryPoint(entryPoint); diff --git a/src/main/java/com/backend/domain/mail/controller/MailController.java b/src/main/java/com/backend/domain/mail/controller/MailController.java index dbd7dae..5ec9015 100644 --- a/src/main/java/com/backend/domain/mail/controller/MailController.java +++ b/src/main/java/com/backend/domain/mail/controller/MailController.java @@ -1,14 +1,16 @@ package com.backend.domain.mail.controller; import com.backend.domain.mail.dto.request.MailRequest; +import com.backend.domain.mail.dto.request.SmsRequest; import com.backend.domain.mail.service.MailService; +import com.backend.error.ErrorCode; +import com.backend.error.exception.custom.BusinessException; +import io.github.resilience4j.ratelimiter.annotation.RateLimiter; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @@ -21,4 +23,17 @@ public class MailController { public ResponseEntity sendCode(@Valid @RequestBody MailRequest emailRequest) { return ResponseEntity.ok(mailService.sendAuthenticationCode(emailRequest)); } + + @RateLimiter(name = "jedero", fallbackMethod = "rateLimiterFallback") + @GetMapping("/sms") + public ResponseEntity sendSMS(@Valid @RequestBody SmsRequest smsRequest) { + int result = mailService.sendSMS(smsRequest); + return ResponseEntity.ok(result); + } + + public ResponseEntity rateLimiterFallback(Throwable t) { +// HttpHeaders responseHeaders = new HttpHeaders(); +// responseHeaders.set("Retry-After", "10s"); + throw new BusinessException(ErrorCode.TOO_MANY_SMS); + } } \ No newline at end of file diff --git a/src/main/java/com/backend/domain/mail/dto/request/SmsRequest.java b/src/main/java/com/backend/domain/mail/dto/request/SmsRequest.java new file mode 100644 index 0000000..09e3768 --- /dev/null +++ b/src/main/java/com/backend/domain/mail/dto/request/SmsRequest.java @@ -0,0 +1,6 @@ +package com.backend.domain.mail.dto.request; + +import jakarta.validation.constraints.NotEmpty; + +public record SmsRequest(@NotEmpty String toNumber) { +} \ No newline at end of file diff --git a/src/main/java/com/backend/domain/mail/service/MailService.java b/src/main/java/com/backend/domain/mail/service/MailService.java index c178b3c..8a52347 100644 --- a/src/main/java/com/backend/domain/mail/service/MailService.java +++ b/src/main/java/com/backend/domain/mail/service/MailService.java @@ -1,21 +1,45 @@ package com.backend.domain.mail.service; import com.backend.domain.mail.dto.request.MailRequest; +import com.backend.domain.mail.dto.request.SmsRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.nurigo.sdk.NurigoApp; +import net.nurigo.sdk.message.model.Message; +import net.nurigo.sdk.message.request.SingleMessageSendingRequest; +import net.nurigo.sdk.message.response.SingleMessageSentResponse; +import net.nurigo.sdk.message.service.DefaultMessageService; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Service; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + @Service @RequiredArgsConstructor @Slf4j public class MailService { + private final JavaMailSender javaMailSender; - @Value("{mail.mail.username}") + + private DefaultMessageService messageService; + + @Value("${spring.mail.username}") private String username; + @Value("${coolsms.api.key}") + private String apiKey; + + @Value("${coolsms.api.secret}") + private String apiSecret; + + @Value("${coolsms.api.fromNumber}") + private String fromNumber; + + public int sendAuthenticationCode(MailRequest mailRequest) { int code = (int) (Math.random() * 900000) + 100000; @@ -31,4 +55,23 @@ public int sendAuthenticationCode(MailRequest mailRequest) { return code; } -} + + public int sendSMS(SmsRequest smsRequest) { + Message message = new Message(); + this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecret, "https://api.coolsms.co.kr"); + + Random rand = new Random(); + String code = IntStream.range(0, 4) + .mapToObj(i -> Integer.toString(rand.nextInt(10))) + .collect(Collectors.joining()); + + message.setFrom(fromNumber); + message.setTo(smsRequest.toNumber()); + message.setText("[제대로] 인증번호를 입력해주세요.\n인증번호: " + code); + + SingleMessageSentResponse response = messageService.sendOne(new SingleMessageSendingRequest(message)); + log.info("SMS 메세지 결과 = {}", response); + + return Integer.parseInt(code); + } +} \ No newline at end of file diff --git a/src/main/java/com/backend/error/ErrorCode.java b/src/main/java/com/backend/error/ErrorCode.java index 5ee15b5..eaf3642 100644 --- a/src/main/java/com/backend/error/ErrorCode.java +++ b/src/main/java/com/backend/error/ErrorCode.java @@ -17,7 +17,8 @@ public enum ErrorCode { INVALID_GROUP_TYPE(BAD_REQUEST, "잘못된 그룹 종류입니다."), INVALID_PASSWORD(BAD_REQUEST, "잘못된 비밀번호입니다."), IMAGE_UPLOAD_FAIL(BAD_REQUEST, "이미지 업로드에 실패했습니다."), - INVALID_FILE(BAD_REQUEST, "잘못된 파일 형식입니다."); + INVALID_FILE(BAD_REQUEST, "잘못된 파일 형식입니다."), + TOO_MANY_SMS(TOO_MANY_REQUESTS, "짧은 시간에 너무 많은 요청을 보냈습니다. 1분 후에 재시도하세요."); private final int code; private final String message; From 7c6e9e6413ff04185b10bbb333875695588893d2 Mon Sep 17 00:00:00 2001 From: kimdohyung Date: Sun, 19 Nov 2023 21:54:56 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=BC,=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=20=EC=9D=B8=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20api=20swagger=20=EB=AC=B8=EC=84=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/mail/controller/MailController.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/com/backend/domain/mail/controller/MailController.java b/src/main/java/com/backend/domain/mail/controller/MailController.java index 5ec9015..dde2743 100644 --- a/src/main/java/com/backend/domain/mail/controller/MailController.java +++ b/src/main/java/com/backend/domain/mail/controller/MailController.java @@ -4,8 +4,13 @@ import com.backend.domain.mail.dto.request.SmsRequest; import com.backend.domain.mail.service.MailService; import com.backend.error.ErrorCode; +import com.backend.error.dto.ErrorResponse; import com.backend.error.exception.custom.BusinessException; import io.github.resilience4j.ratelimiter.annotation.RateLimiter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,11 +24,21 @@ public class MailController { private final MailService mailService; + @Operation(summary = "메일 인증 코드 전송", description = "입력한 메일에 인증 코드를 전송합니다. 형식: test@test.com", + responses = { + @ApiResponse(responseCode = "200", description = "인증 코드 전송 성공, 보낸 인증 코드를 반환합니다.", + content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) @PostMapping("/mail") public ResponseEntity sendCode(@Valid @RequestBody MailRequest emailRequest) { return ResponseEntity.ok(mailService.sendAuthenticationCode(emailRequest)); } + @Operation(summary = "문자 인증 코드 전송", description = "입력한 번호에 인증 코드를 전송합니다. 형식: 01012345678", + responses = { + @ApiResponse(responseCode = "200", description = "인증 코드 전송 성공, 보낸 인증 코드를 반환합니다.", + content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) @RateLimiter(name = "jedero", fallbackMethod = "rateLimiterFallback") @GetMapping("/sms") public ResponseEntity sendSMS(@Valid @RequestBody SmsRequest smsRequest) { From 820fe95d70740cb5fed3dfbf13ad3dd31aa3b448 Mon Sep 17 00:00:00 2001 From: kimdohyung Date: Sun, 19 Nov 2023 22:00:08 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=EB=A9=94=EC=9D=BC=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=A0=84=EC=86=A1=20method=20?= =?UTF-8?q?get=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/backend/domain/mail/controller/MailController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/backend/domain/mail/controller/MailController.java b/src/main/java/com/backend/domain/mail/controller/MailController.java index dde2743..1c9e54e 100644 --- a/src/main/java/com/backend/domain/mail/controller/MailController.java +++ b/src/main/java/com/backend/domain/mail/controller/MailController.java @@ -29,7 +29,7 @@ public class MailController { @ApiResponse(responseCode = "200", description = "인증 코드 전송 성공, 보낸 인증 코드를 반환합니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) }) - @PostMapping("/mail") + @GetMapping("/mail") public ResponseEntity sendCode(@Valid @RequestBody MailRequest emailRequest) { return ResponseEntity.ok(mailService.sendAuthenticationCode(emailRequest)); }