Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,9 @@ KAKAO_CLIENT_SECRET=your_kakao_client_secret
JWT_SECRET=your_very_long_and_secret_random_string_for_signing_tokens_at_least_64_characters

# 6. Frontend Redirect URL
OAUTH2_REDIRECT_URL=http://localhost:3000/oauth2/redirect
OAUTH2_REDIRECT_URL=http://localhost:3000/oauth2/redirect

# 7. SMS
SMS_API_KEY=your_sms_api_key
SMS_SECRET_KEY=your_sms_secret_key
SMS_SENDER_NUMBER=01012345678
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@ jobs:
REDIS_PORT: 6379
MAIL_USERNAME: test@example.com
MAIL_PASSWORD: test_password
JWT_SECRET: dGhpcy1pcy1hLXRlc3Qtc2VjcmV0LWtleS1mb3ItY2ktdGVzdC1wdXJwb3Nlcw==
JWT_SECRET: dGhpcy1pcy1hLXRlc3Qtc2VjcmV0LWtleS1mb3ItY2ktdGVzdC1wdXJwb3Nlcw==
SMS_API_KEY: test_api_key
SMS_SECRET_KEY: test_secret_key
SMS_SENDER_NUMBER: 01000000000
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ dependencies {

//Email
implementation 'org.springframework.boot:spring-boot-starter-mail'

// SMS
implementation 'net.nurigo:sdk:4.3.0'

// Dotenv
implementation 'me.paulschwarz:spring-dotenv:4.0.0'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

public class SmsRequest {
public record SmsSendRequest(
@NotBlank(message = "전화번호는 필수입니다.")
@Pattern(regexp = "^\\d{10,11}$", message = "전화번호는 하이픈 없이 10~11자리 숫자만 입력해주세요.") String phoneNumber) {
}

public record SmsVerifyRequest(
@NotBlank(message = "전화번호는 필수입니다.") String phoneNumber,
@NotBlank(message = "인증코드는 필수입니다.") String verificationCode) {
}
Comment on lines +7 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

검증 요청에도 전화번호/인증코드 형식 검증을 맞춰주세요

발송 요청은 10~11자리만 허용하지만 검증 요청은 제약이 없어 '010-1234-5678' 같은 값이 통과할 수 있습니다. 이렇게 되면 Redis 키가 달라져 항상 실패할 수 있어요. 검증 요청에도 동일한 패턴과 6자리 코드 제약을 넣는 걸 권장합니다.

✅ 수정 제안
     public record SmsVerifyRequest(
-                    `@NotBlank`(message = "전화번호는 필수입니다.") String phoneNumber,
-                    `@NotBlank`(message = "인증코드는 필수입니다.") String verificationCode) {
+                    `@NotBlank`(message = "전화번호는 필수입니다.")
+                    `@Pattern`(regexp = "^\\d{10,11}$", message = "전화번호는 하이픈 없이 10~11자리 숫자만 입력해주세요.") String phoneNumber,
+                    `@NotBlank`(message = "인증코드는 필수입니다.")
+                    `@Pattern`(regexp = "^\\d{6}$", message = "인증코드는 6자리 숫자만 입력해주세요.") String verificationCode) {
     }
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/user/application/dto/request/SmsRequest.java`
around lines 7 - 15, SmsVerifyRequest currently lacks the same format
constraints as SmsSendRequest, allowing phone strings like "010-1234-5678" and
variable-length codes which breaks Redis key matching; update the
SmsVerifyRequest record to apply the same `@Pattern`(regexp = "^\\d{10,11}$",
message = "...") annotation to phoneNumber as used in SmsSendRequest and add a
`@Pattern`(regexp = "^\\d{6}$", message = "인증코드는 6자리 숫자만 입력해주세요.") (and keep
`@NotBlank`) on verificationCode so both request DTOs use identical phone
validation and enforce a 6-digit code.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.response;

public class SmsResponse {

public record SmsSentResponse(
String message,
String phoneNumber, // 전송한 휴대폰 번호
long expireIn) {
}

public record SmsVerifiedResponse(
boolean isVerified,
String verificationMessage,
String email) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.whereyouad.WhereYouAd.domains.user.domain.service;

import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode;
import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler;
import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User;
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository;
import net.nurigo.sdk.NurigoApp;
import net.nurigo.sdk.message.model.Message;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.global.utils.RedisUtil;
import net.nurigo.sdk.message.request.SingleMessageSendingRequest;
import net.nurigo.sdk.message.service.DefaultMessageService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Random;

@Service
@Transactional
public class SmsService {
private final RedisUtil redisUtil;
private final UserRepository userRepository;
private final DefaultMessageService defaultMessageService;
private final String senderNumber;

public SmsService(RedisUtil redisUtil, UserRepository userRepository,
@Value("${coolSms.apiKey}") String smsApiKey,
@Value("${coolSms.secretKey}") String smsSecretKey,
@Value("${coolSms.senderNumber}") String senderNumber) {
this.redisUtil = redisUtil;
this.userRepository = userRepository;
this.senderNumber = senderNumber;
this.defaultMessageService = NurigoApp.INSTANCE.initialize(smsApiKey, smsSecretKey,
"https://api.coolsms.co.kr");
}

// SMS 전송
public SmsResponse.SmsSentResponse sendSms(String phoneNumber) {
// 이메일 찾기 기능: 가입된 유저인지 확인
if (!userRepository.existsByPhoneNumber(phoneNumber)) {
throw new UserHandler(UserErrorCode.USER_NOT_FOUND_BY_PHONE);
}

Message message = new Message();
String verificationCode = generateCode();

message.setFrom(senderNumber); // 발신 번호
message.setTo(phoneNumber);
message.setText("[Where You Ad] 이메일 찾기\n 휴대폰 인증 번호는 [" + verificationCode + "] 입니다.");

// redis 저장
redisUtil.setDataExpire(phoneNumber, verificationCode, 180); // 유효 기간 3분

try {
this.defaultMessageService.sendOne(new SingleMessageSendingRequest(message));
} catch (Exception e) {
e.printStackTrace();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4: log.error("SMS 발송 실패: {}", e.getMessage()) 이런식으로 에러 로그 찍는건 어떤가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵! Slf4j 쓰는 게 낫겠네요

throw new UserHandler(UserErrorCode.SMS_SEND_FAILED);
}
Comment on lines +54 to +62
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

SMS 전송 실패 시 Redis 데이터가 남아있는 문제가 있습니다.

현재 코드는 Redis에 인증 코드를 저장한 후 SMS를 전송합니다. 만약 SMS 전송이 실패하면 Redis에 저장된 데이터는 그대로 남아있게 되어, 사용자가 받지 못한 인증 코드가 3분간 유효한 상태로 남습니다.

예를 들어:

  1. 사용자 A가 인증 요청 → Redis에 코드 저장 → SMS 전송 실패
  2. 공격자가 해당 번호로 무작위 코드 대입 시도 가능
🛡️ 수정 제안: SMS 전송 성공 후 Redis 저장
     public SmsResponse.SmsSentResponse sendSms(String phoneNumber) {
         Message message = new Message();
         String verificationCode = generateCode();

         message.setFrom(senderNumber); // 발신 번호
         message.setTo(phoneNumber);
         message.setText("[Where You Ad] 이메일 찾기\n 휴대폰 인증 번호는 [" + verificationCode + "] 입니다.");

-        // redis 저장
-        redisUtil.setDataExpire(phoneNumber, verificationCode, 180); // 유효 기간 3분
-
         try {
             this.defaultMessageService.sendOne(new SingleMessageSendingRequest(message));
+            // SMS 전송 성공 후 redis 저장
+            redisUtil.setDataExpire(phoneNumber, verificationCode, 180); // 유효 기간 3분
         } catch (Exception e) {
-            e.printStackTrace();
+            log.error("SMS 전송 실패 - phoneNumber: {}", phoneNumber, e);
             throw new UserHandler(UserErrorCode.SMS_SEND_FAILED);
         }

         return new SmsResponse.SmsSentResponse("인증번호가 전송되었습니다.", phoneNumber, 180L);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// redis 저장
redisUtil.setDataExpire(phoneNumber, verificationCode, 180); // 유효 기간 3분
try {
this.defaultMessageService.sendOne(new SingleMessageSendingRequest(message));
} catch (Exception e) {
e.printStackTrace();
throw new UserHandler(UserErrorCode.SMS_SEND_FAILED);
}
try {
this.defaultMessageService.sendOne(new SingleMessageSendingRequest(message));
// SMS 전송 성공 후 redis 저장
redisUtil.setDataExpire(phoneNumber, verificationCode, 180); // 유효 기간 3분
} catch (Exception e) {
log.error("SMS 전송 실패 - phoneNumber: {}", phoneNumber, e);
throw new UserHandler(UserErrorCode.SMS_SEND_FAILED);
}
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/service/SmsService.java`
around lines 47 - 55, The Redis-stored verification code is set before sending
the SMS (redisUtil.setDataExpire), leaving a valid code if send fails; change
the flow in SmsService so you call defaultMessageService.sendOne(new
SingleMessageSendingRequest(message)) first and only on success persist the code
with redisUtil.setDataExpire(phoneNumber, verificationCode, 180); alternatively,
if you must keep the current order, catch exceptions from
defaultMessageService.sendOne and remove the stored key (use redisUtil.delete or
equivalent) inside the catch before throwing
UserHandler(UserErrorCode.SMS_SEND_FAILED) to ensure no orphaned codes remain.


return new SmsResponse.SmsSentResponse("인증번호가 전송되었습니다.", phoneNumber, 180L);
}

// 랜덤 인증 번호 생성
public String generateCode() {
Random random = new Random();
int code = 100000 + random.nextInt(900000); // 6자리 인증 번호
return Integer.toString(code);
}

// 인증번호 확인
public boolean verifyCode(String phoneNumber, String insertedNum) {
String savedCode = redisUtil.getData(phoneNumber);

if (savedCode != null && savedCode.equals(insertedNum)) {
redisUtil.deleteData(phoneNumber);
// 인증 완료
return true;
}
return false;
}

// 인증번호 확인 후 유저 이메일 반환
public String isPhoneVerified(String phoneNumber, String insertedNum) {
if (verifyCode(phoneNumber, insertedNum)) {
User user = userRepository.findUserByPhoneNumber(phoneNumber)
.orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND));
return user.getEmail();
} else
throw new UserHandler(UserErrorCode.USER_SMS_NOT_VERIFIED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@
@Getter
@AllArgsConstructor
public enum UserErrorCode implements BaseErrorCode {
//400
// 400
USER_EMAIL_DUPLICATE(HttpStatus.BAD_REQUEST, "USER_400_1", "이미 사용중인 이메일 입니다."),
USER_EMAIL_NOT_VALID(HttpStatus.BAD_REQUEST, "USER_400_2", "해당 이메일로 메일 전송에 실패했습니다."),
USER_EMAIL_AUTH_INVALID(HttpStatus.BAD_REQUEST, "USER_400_3", "인증 코드가 올바르지 않습니다."),
NOT_PROVIDE_SOCIAL(HttpStatus.BAD_REQUEST, "USER_400_4", "지원하지 않는 소셜 로그인 방식입니다."),

// 401
USER_EMAIL_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "USER_401_1", "이메일 인증이 진행되지 않았습니다."),
USER_SMS_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "USER_401_2", "휴대폰 인증이 진행되지 않았습니다."),

// 404
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_404_1", "이메일에 해당하는 사용자를 찾을 수 없습니다.")
;
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_404_1", "이메일에 해당하는 사용자를 찾을 수 없습니다."),
USER_NOT_FOUND_BY_PHONE(HttpStatus.NOT_FOUND, "USER_404_2", "해당 전화번호를 가진 사용자를 찾을 수 없습니다."),

// 500
SMS_SEND_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "USER_500_1", "문자 전송에 실패했습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.whereyouad.WhereYouAd.domains.user.exception.handler;

import com.whereyouad.WhereYouAd.global.exception.AppException;
import com.whereyouad.WhereYouAd.global.exception.BaseErrorCode;

public class UserHandler extends AppException {
public UserHandler(BaseErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
@Query("select u from User u where u.email = :email")
Optional<User> findUserByEmail(@Param("email") String email);

//이메일 중복 예외 처리를 위한 이메일로 조회 메서드
@Query("select u from User u where u.phoneNumber = :phoneNumber")
Optional<User> findUserByPhoneNumber(@Param("phoneNumber") String phoneNumber);

boolean existsByPhoneNumber(String phoneNumber);

// 이메일 중복 예외 처리를 위한 이메일로 조회 메서드
boolean existsByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.whereyouad.WhereYouAd.domains.user.presentation;

import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SmsRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService;
import com.whereyouad.WhereYouAd.domains.user.domain.service.SmsService;
import com.whereyouad.WhereYouAd.domains.user.domain.service.UserService;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SignUpRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
Expand All @@ -23,29 +26,41 @@ public class UserController implements UserControllerDocs {

private final UserService userService;
private final EmailService emailService;
private final SmsService smsService;

@PostMapping("/signup")
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request) {
SignUpResponse signUpResponse = userService.signUpUser(request);
return ResponseEntity.ok(
DataResponse.created(signUpResponse)
);
DataResponse.created(signUpResponse));
}

@PostMapping("/email-send")
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmail(@RequestBody @Valid EmailRequest.Send request) {
EmailSentResponse emailSentResponse = emailService.sendEmail(request.email());
return ResponseEntity.ok(
DataResponse.from(emailSentResponse)
);
DataResponse.from(emailSentResponse));
}

@PostMapping("/email-verify")
public ResponseEntity<DataResponse<String>> verifyEmail(@RequestBody @Valid EmailRequest.Verify request) {
emailService.verifyEmailCode(request.email(), request.authCode());

return ResponseEntity.ok(
DataResponse.from("이메일 인증이 성공적으로 완료되었습니다.")
);
DataResponse.from("이메일 인증이 성공적으로 완료되었습니다."));
}

@PostMapping("/sms-send")
public ResponseEntity<DataResponse<SmsResponse.SmsSentResponse>> sendSms(
@RequestBody @Valid SmsRequest.SmsSendRequest request) {
SmsResponse.SmsSentResponse smsSentResponse = smsService.sendSms(request.phoneNumber());
return ResponseEntity.ok(DataResponse.from(smsSentResponse));
}

@PostMapping("/sms-verify")
public ResponseEntity<DataResponse<SmsResponse.SmsVerifiedResponse>> verifySms(
@RequestBody @Valid SmsRequest.SmsVerifyRequest request) {
String email = smsService.isPhoneVerified(request.phoneNumber(), request.verificationCode());
return ResponseEntity.ok(DataResponse.from(new SmsResponse.SmsVerifiedResponse(true, "이메일 찾기 성공", email)));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.whereyouad.WhereYouAd.domains.user.presentation.docs;

import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SmsRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SignUpRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
import com.whereyouad.WhereYouAd.global.response.DataResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -13,34 +15,41 @@
import org.springframework.web.bind.annotation.RequestBody;

public interface UserControllerDocs {
@Operation(
summary = "단순 회원가입 API",
description = "이메일, 비밀번호, 이름, 전화번호를 받아 회원가입을 진행합니다(먼저 이메일 인증이 진행되어야 회원가입 가능)"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_2", description = "이메일 중복 회원 존재")
})
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request);
@Operation(summary = "단순 회원가입 API", description = "이메일, 비밀번호, 이름, 전화번호를 받아 회원가입을 진행합니다(먼저 이메일 인증이 진행되어야 회원가입 가능)")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4: 저 혹시 들여쓰기 8칸으로 수정하신 이유가 있을까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 딱히 이유는 없습니다!! 수정하다보니 들여쓰기가 고쳐진 것 같습니다(?)...

@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_2", description = "이메일 중복 회원 존재")
})
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request);

@Operation(
summary = "이메일 인증코드 전송 API",
description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" +
"테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_3", description = "이메일 전송실패(이메일 오타 등)")
})
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmail(@RequestBody @Valid EmailRequest.Send request);
@Operation(summary = "이메일 인증코드 전송 API", description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" +
"테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_3", description = "이메일 전송실패(이메일 오타 등)")
})
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmail(@RequestBody @Valid EmailRequest.Send request);

@Operation(
summary = "이메일 인증코드 인증 API",
description = "이메일과 인증코드를 받아 인증코드가 맞는지 검증합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_4", description = "실패(인증코드 불일치)")
})
public ResponseEntity<DataResponse<String>> verifyEmail(@RequestBody @Valid EmailRequest.Verify request);
@Operation(summary = "이메일 인증코드 인증 API", description = "이메일과 인증코드를 받아 인증코드가 맞는지 검증합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_4", description = "실패(인증코드 불일치)")
})
public ResponseEntity<DataResponse<String>> verifyEmail(@RequestBody @Valid EmailRequest.Verify request);

@Operation(summary = "이메일 찾기 - SMS 인증 번호 전송 API", description = "입력받은 전화번호로 인증 번호를 전송합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "실패")
})
public ResponseEntity<DataResponse<SmsResponse.SmsSentResponse>> sendSms(
@RequestBody @Valid SmsRequest.SmsSendRequest request);

@Operation(summary = "이메일 찾기 - SMS 인증 번호 확인 API", description = "전화번호와 인증 코드를 받아 맞는지 검증합니다.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "실패")
})
public ResponseEntity<DataResponse<SmsResponse.SmsVerifiedResponse>> verifySms(
@RequestBody @Valid SmsRequest.SmsVerifyRequest request);
}
8 changes: 7 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,10 @@ jwt:

# 7. OAuth2 로그인 성공 후 프론트엔드 리다이렉트 URL
oauth2:
redirect-url: ${OAUTH2_REDIRECT_URL:http://localhost:3000/oauth2/redirect}
redirect-url: ${OAUTH2_REDIRECT_URL:http://localhost:3000/oauth2/redirect}

# 8. SMS 인증
coolSms:
apiKey: ${SMS_API_KEY}
secretKey: ${SMS_SECRET_KEY}
senderNumber: ${SMS_SENDER_NUMBER}