Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-mail'

// Actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@

public record SendLoginIdVerificationCodeResponse(
String email,
String maskedEmail,
String verificationCode
String maskedEmail
) {

public static SendLoginIdVerificationCodeResponse of(
String email,
String maskedEmail,
String verificationCode
String maskedEmail
) {
return new SendLoginIdVerificationCodeResponse(
email,
maskedEmail,
verificationCode
maskedEmail
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ public SendLoginIdVerificationCodeResponse execute(FindLoginIdRequest request) {
Member member = memberService.findByEmail(request.email())
.orElseThrow(() -> new MemberException(MemberErrorCode.MEMBER_NOT_FOUND));

String code = emailVerificationService.createAndSendVerificationCode(member.getEmail());
emailVerificationService.createAndSendVerificationCode(member.getEmail());
String maskedEmail = emailVerificationService.maskEmail(member.getEmail());

return SendLoginIdVerificationCodeResponse.of(
member.getEmail(),
maskedEmail,
code
maskedEmail
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import plango.global.common.exception.BusinessException;
Expand All @@ -20,15 +23,20 @@ public class EmailVerificationService {

private final Map<String, VerificationInfo> store = new ConcurrentHashMap<>();

public String createAndSendVerificationCode(String email) {
private final JavaMailSender mailSender;

@Value("${spring.mail.username:no-reply@plango.com}")
private String fromAddress;

public void createAndSendVerificationCode(String email) {
String code = generateCode();

VerificationInfo info = new VerificationInfo(code, Instant.now());
store.put(email, info);

log.info("[EmailVerification] email = {}, code = {}", email, code);
sendEmail(email, code);

return code;
log.info("[EmailVerification] email = {}, code = {}", email, code);
}

public void verifyCode(String email, String code) {
Expand Down Expand Up @@ -68,6 +76,23 @@ private String generateCode() {
return String.format("%06d", value);
}

private void sendEmail(String email, String code) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email);
message.setSubject("[PlanGo] 아이디 찾기 인증번호 안내");
message.setText(buildEmailBody(code));
message.setFrom(fromAddress);

mailSender.send(message);
}

private String buildEmailBody(String code) {
return "안녕하세요, PlanGo 입니다.\n\n"
+ "아이디 찾기를 위한 인증번호는 다음과 같습니다.\n\n"
+ "인증번호: " + code + "\n\n"
+ "본 메일은 발신 전용 메일입니다.";
}

public String maskEmail(String email) {
if (email == null || email.isBlank()) {
return "";
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/plango/auth/presentation/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public ResponseEntity<CommonResponse<FindLoginIdPreviewResponse>> findLoginId(

@Operation(
summary = "아이디 찾기 추가 인증번호 발송",
description = "아이디를 찾기 위해 입력한 이메일로 6자리 인증번호를 전송합니다. 테스트 편의를 위해 응답에 인증번호가 함께 포함됩니다."
description = "아이디를 찾기 위해 입력한 이메일로 6자리 인증번호를 전송합니다."
)
@PostMapping("/find-id/send-code")
public ResponseEntity<CommonResponse<SendLoginIdVerificationCodeResponse>> sendFindIdCode(
Expand Down
14 changes: 12 additions & 2 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ spring:
baseline-on-migrate: true
baseline-version: 1

mail:
host: smtp.gmail.com
port: 587
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
starttls:
enable: true

jwt:
key: ${JWT_KEY} # 반드시 .env에 설정
access:
Expand Down Expand Up @@ -56,5 +68,3 @@ security:
kakao:
client-id: ${KAKAO_CLIENT_ID:}
redirect-uri: ${KAKAO_REDIRECT_URI:}


16 changes: 15 additions & 1 deletion src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ spring:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
format_sql: true

flyway:
enabled: true
url: ${DB_URL}
Expand All @@ -28,6 +29,19 @@ spring:
baseline-version: 1
locations: classpath:db/migration
out-of-order: true

mail:
host: smtp.gmail.com
port: 587
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
starttls:
enable: true

# --- 여긴 spring 밖(최상위) ---
jwt:
key: ${JWT_KEY}
Expand All @@ -50,7 +64,6 @@ cloud:
stack:
auto: false


security:
aes:
key: ${AES_KEY:}
Expand All @@ -63,6 +76,7 @@ security:
preview:
api:
url: ${PREVIEW_API_URL:http://localhost:3000}

oauth2:
kakao:
client-id: ${KAKAO_CLIENT_ID}
Expand Down