diff --git a/.gitignore b/.gitignore index 0063918..88aec54 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ src/main/resources/.env *.bak *.swo +src/main/resources/application.properties + diff --git a/build.gradle b/build.gradle index b12e231..77a7619 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,10 @@ dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' + // Spring mail + implementation 'org.springframework.boot:spring-boot-starter-mail' + implementation 'com.sun.mail:jakarta.mail:2.0.1' + // Testing testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' diff --git a/src/main/java/dongguk/osori/domain/user/EmailService.java b/src/main/java/dongguk/osori/domain/user/EmailService.java new file mode 100644 index 0000000..f7f8854 --- /dev/null +++ b/src/main/java/dongguk/osori/domain/user/EmailService.java @@ -0,0 +1,76 @@ +package dongguk.osori.domain.user; + +import jakarta.mail.internet.MimeMessage; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +@RequiredArgsConstructor +public class EmailService { + + private final JavaMailSender javaMailSender; + private final RedisTemplate redisTemplate; + + @Value("${spring.mail.username}") + private String senderEmail; + + private static final long CODE_EXPIRE_MINUTES = 5; // 인증 코드 유효 시간 (5분) + + // 인증 번호 생성 + public static int createNumber() { + return (int)(Math.random() * 900000) + 100000; // 6자리 랜덤 숫자 + } + + // 이메일 작성 + public MimeMessage createMail(String recipientEmail, int authCode) { + MimeMessage message = javaMailSender.createMimeMessage(); + + try { + message.setFrom(senderEmail); + message.setRecipients(MimeMessage.RecipientType.TO, recipientEmail); + message.setSubject("[Osori] 회원가입을 위한 이메일 인증"); + + // 이메일 본문 + String body = "

안녕하세요. Osori입니다.

" + + "

회원가입을 위한 요청하신 인증 번호입니다.

" + + "
" + + "

회원가입 인증 코드입니다.

" + + "

" + authCode + "

" + + "
" + + "

감사합니다.

"; + + message.setText(body, "UTF-8", "html"); + } catch (Exception e) { + e.printStackTrace(); + } + + return message; + } + + // 이메일 전송 및 Redis 저장 + public void sendEmail(String recipientEmail) { + int authCode = createNumber(); + MimeMessage message = createMail(recipientEmail, authCode); + + // Redis에 인증 코드 저장, 5분 후 만료 + ValueOperations valueOperations = redisTemplate.opsForValue(); + valueOperations.set(recipientEmail, String.valueOf(authCode), CODE_EXPIRE_MINUTES, TimeUnit.MINUTES); + + // 이메일 전송 + javaMailSender.send(message); + } + + // 인증 코드 확인 + public boolean verifyAuthCode(String email, String inputCode) { + ValueOperations valueOperations = redisTemplate.opsForValue(); + String storedCode = valueOperations.get(email); + + return storedCode != null && storedCode.equals(inputCode); + } +} diff --git a/src/main/java/dongguk/osori/domain/user/UserController.java b/src/main/java/dongguk/osori/domain/user/UserController.java index 11dad40..6b0ac2a 100644 --- a/src/main/java/dongguk/osori/domain/user/UserController.java +++ b/src/main/java/dongguk/osori/domain/user/UserController.java @@ -1,5 +1,6 @@ package dongguk.osori.domain.user; +import dongguk.osori.domain.user.dto.EmailVerificationDto; import dongguk.osori.domain.user.dto.LoginRequestDto; import dongguk.osori.domain.user.dto.SignupUserDto; import dongguk.osori.domain.user.dto.UserProfileDto; @@ -9,6 +10,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Map; + @Slf4j @RequiredArgsConstructor @CrossOrigin(origins = "http://localhost:3000") @@ -17,18 +20,48 @@ public class UserController { private final UserService userService; + private final EmailService emailService; + + + // 이메일 인증 코드 전송 + @PostMapping("/signup/send-email") + public ResponseEntity sendSignupEmail(@RequestBody Map request) { + String email = request.get("email"); // 이메일 주소 추출 + if (email == null || email.isEmpty()) { + return ResponseEntity.status(400).body("이메일이 올바르지 않습니다."); + } + emailService.sendEmail(email); + return ResponseEntity.ok("인증 코드가 이메일로 전송되었습니다."); + } + + + // 이메일 인증 코드 확인 + @PostMapping("/signup/verify-code") + public ResponseEntity verifyEmailCode(@RequestBody EmailVerificationDto verificationDto) { + boolean isVerified = emailService.verifyAuthCode(verificationDto.getEmail(), verificationDto.getCode()); + + if (isVerified) { + return ResponseEntity.ok("이메일 인증이 완료되었습니다."); + } else { + return ResponseEntity.status(400).body("잘못된 인증 코드입니다."); + } + } + // 회원가입 @PostMapping("/signup") public ResponseEntity signup(@RequestBody SignupUserDto signupUserDto) { try { + // 이메일 인증이 완료된 사용자만 회원가입 가능 + if (!emailService.verifyAuthCode(signupUserDto.getEmail(), signupUserDto.getAuthCode())) { + return ResponseEntity.status(400).body("이메일 인증이 완료되지 않았습니다."); + } + userService.signup(signupUserDto); return ResponseEntity.ok("회원가입이 완료되었습니다."); } catch (IllegalArgumentException e) { - log.error("Signup failed: {}", e.getMessage()); return ResponseEntity.status(400).body(e.getMessage()); } catch (Exception e) { - log.error("Unexpected error during signup", e); return ResponseEntity.status(500).body("서버 에러 발생. 나중에 다시 시도해주세요."); } } diff --git a/src/main/java/dongguk/osori/domain/user/dto/EmailVerificationDto.java b/src/main/java/dongguk/osori/domain/user/dto/EmailVerificationDto.java new file mode 100644 index 0000000..6972299 --- /dev/null +++ b/src/main/java/dongguk/osori/domain/user/dto/EmailVerificationDto.java @@ -0,0 +1,13 @@ +package dongguk.osori.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EmailVerificationDto { + private String email; + private String code; +} diff --git a/src/main/java/dongguk/osori/domain/user/dto/SignupUserDto.java b/src/main/java/dongguk/osori/domain/user/dto/SignupUserDto.java index 0baa2ff..70d5257 100644 --- a/src/main/java/dongguk/osori/domain/user/dto/SignupUserDto.java +++ b/src/main/java/dongguk/osori/domain/user/dto/SignupUserDto.java @@ -11,4 +11,5 @@ public class SignupUserDto { private String nickname; private String email; private String password; + private String authCode; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 540f597..8302771 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,3 +24,17 @@ server.servlet.session.cookie.name=JSESSIONID server.servlet.session.cookie.secure=false server.servlet.session.cookie.http-only=true server.servlet.session.timeout=30m + +# Gmail smtp +spring.mail.host=smtp.gmail.com +spring.mail.port=587 +spring.mail.username=dgu.osori@gmail.com +spring.mail.password=giybewxenbvecvqr +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.properties.mail.smtp.ssl.trust=smtp.gmail.com +spring.mail.properties.mail.smtp.connectiontimeout=5000 +spring.mail.properties.mail.smtp.timeout=5000 +spring.mail.properties.mail.smtp.writetimeout=5000 +