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
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.response;

import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider;

import java.util.List;

public record EmailSentResponse(
String message, //인증코드를 이메일로 전송했습니다.
String email, //전송한 이메일
long expireIn //만료시간 (500L -> 500초)
Long expireIn, //만료시간 (500L -> 500초)
boolean isProviderLinked,
List<Provider> providerTypes
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.response;

public record PasswordResetResponse(
String message, //인증코드를 이메일로 전송했습니다.
String email, //전송한 이메일
Long expireIn //만료시간 (500L -> 500초)
) {
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.whereyouad.WhereYouAd.domains.user.application.mapper;

import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider;
import com.whereyouad.WhereYouAd.domains.user.domain.constant.UserStatus;
import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User;
import com.whereyouad.WhereYouAd.global.security.oauth2.dto.OAuth2Response;
import com.whereyouad.WhereYouAd.global.security.oauth2.dto.OAuth2UserInfo;

import java.util.List;

public class UserConverter {

public static SignUpResponse toSignInResponse(User user) {
Expand Down Expand Up @@ -45,4 +50,27 @@ public static MyPageResponse toMyPageResponse(User user, String provider) {
provider
);
}

public static EmailSentResponse toEmailSentResponseSuccess(String email) {
return new EmailSentResponse("인증 코드를 이메일로 전송했습니다",
email,
180L,
false,
null);
}

public static EmailSentResponse toEmailSentResponseFail(String email, List<Provider> providers) {
return new EmailSentResponse("이미 소셜 계정으로 가입된 이메일 입니다.",
email,
null,
true,
providers);
}

public static PasswordResetResponse toPasswordResetResponse(String email) {

return new PasswordResetResponse("인증 코드를 이메일로 전송했습니다.",
email,
180L);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.whereyouad.WhereYouAd.domains.user.domain.service;

import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse;
import com.whereyouad.WhereYouAd.domains.user.application.mapper.UserConverter;
import com.whereyouad.WhereYouAd.domains.user.domain.constant.Provider;
import com.whereyouad.WhereYouAd.domains.user.exception.handler.UserHandler;
import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode;
import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount;
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.AuthProviderAccountRepository;
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository;
import com.whereyouad.WhereYouAd.global.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
Expand All @@ -14,6 +19,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

@Transactional
@Service
@RequiredArgsConstructor
Expand All @@ -23,6 +31,7 @@ public class EmailService {
private final JavaMailSender emailSender;
private final RedisUtil redisUtil;
private final UserRepository userRepository;
private final AuthProviderAccountRepository authProviderAccountRepository;

// application.yml 적용 필요
@Value("${spring.mail.username}")
Expand All @@ -34,19 +43,36 @@ public class EmailService {
// 인증코드 이메일 발송 로직 (최초 회원가입 시)
public EmailSentResponse sendEmail(String toEmail) {
if (userRepository.existsByEmail(toEmail)) { // 이미 해당 이메일로 생성한 계정이 있으면
throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE); // 이메일 중복 예외(회원가입 시 사용했던 예외)
//해당 이메일 값을 가진 AuthProviderAccount List 로 조회
List<AuthProviderAccount> authProviderAccounts = authProviderAccountRepository.findByUserEmail(toEmail);

//만약 AuthProviderAccount 가 없으면
if (authProviderAccounts.isEmpty()) {
//단순 이메일 회원가입에서 이메일 값이 중복인 것이므로 예외(기존 예외처리 로직)
throw new UserHandler(UserErrorCode.USER_EMAIL_DUPLICATE);
} else { //만약 AuthProviderAccount 가 있으면
//해당 소셜 로그인 플랫폼 타입을 추출해서 반환
List<Provider> providers = new ArrayList<>();
for (AuthProviderAccount authProviderAccount : authProviderAccounts) {
Provider provider = authProviderAccount.getProvider();
providers.add(provider);
}

return UserConverter.toEmailSentResponseFail(toEmail, providers);
}
}

//해당 이메일로 이미 생성된 계정 없으면 이메일 전송 진행
String type = "회원가입";

return emailSendTemplate(toEmail, type);
//템플릿 호출
return (EmailSentResponse) emailSendTemplate(toEmail, type);
}

// 비밀번호 재설정을 위한 인증코드 이메일 발송 로직 (이미 회원가입 된 상태에서 비밀번호 재설정)
public EmailSentResponse sendEmailForPwd(String toEmail) {
public PasswordResetResponse sendEmailForPwd(String toEmail) {
if (userRepository.existsByEmail(toEmail)) { // 이미 회원가입 되어있는 것이 확인되면
String type = "비밀번호 재설정";
return emailSendTemplate(toEmail, type); // 정상적으로 이메일 발송
return (PasswordResetResponse) emailSendTemplate(toEmail, type); // 정상적으로 이메일 발송
} else { // 만약 회원가입 되어있지 않다면
throw new UserHandler(UserErrorCode.USER_NOT_FOUND); // 예외발생
}
Expand All @@ -69,8 +95,9 @@ public void sendEmailForOrgInvitation(String token, String toEmail, String orgNa
}
}

// 기존 이메일 발송 로직 템플릿 화
private EmailSentResponse emailSendTemplate(String toEmail, String type) {
//기존 이메일 발송 로직 템플릿 화
//"회원가입 시 인증 이메일 발송" 과 "비밀번호 재설정 시 인증 이메일 발송" 에 대한 Response 분리 위해 반환값 Object로 변경 (fix/#39)
private Object emailSendTemplate(String toEmail, String type) {

// 인증코드 재전송 로직 -> 이미 Redis 에 해당 이메일 인증코드가 있을시 삭제
String redisKey = "CODE:" + toEmail;
Expand Down Expand Up @@ -111,7 +138,13 @@ private EmailSentResponse emailSendTemplate(String toEmail, String type) {
// 테스트 계정의 인증은 서버 로그를 통해 인증코드를 얻어 입력.
redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 3L);

return new EmailSentResponse("인증코드를 이메일로 전송했습니다.", toEmail, 180L);
if (type.equals("회원가입")) { //해당 템플릿 메서드를 "회원가입을 위한 이메일 인증" 에서 호출한 경우,
//해당 DTO 형식에 맞춰 반환
return UserConverter.toEmailSentResponseSuccess(toEmail);
} else { //"비밀번호 재설정" 에서 호출한 경우,
//해당 DTO 형식에 맞춰 반환
return UserConverter.toPasswordResetResponse(toEmail);
}
}

// 인증코드 검증 메서드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

import com.whereyouad.WhereYouAd.domains.user.persistence.entity.AuthProviderAccount;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface AuthProviderAccountRepository extends JpaRepository<AuthProviderAccount, Long> {
AuthProviderAccount findByProviderId(String username);

@Query("select apa from AuthProviderAccount apa where apa.user.email = :email")
List<AuthProviderAccount> findByUserEmail(@Param(value = "email") String email);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SmsRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.PwdResetRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SmsResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService;
Expand Down Expand Up @@ -68,11 +69,11 @@ public ResponseEntity<DataResponse<SmsResponse.SmsVerifiedResponse>> verifySms(
}

@PostMapping("/password-reset/request")
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) {
EmailSentResponse emailSentResponse = emailService.sendEmailForPwd(request.email());
public ResponseEntity<DataResponse<PasswordResetResponse>> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request) {
PasswordResetResponse response = emailService.sendEmailForPwd(request.email());

return ResponseEntity.ok(
DataResponse.from(emailSentResponse)
DataResponse.from(response)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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.MyPageResponse;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.PasswordResetResponse;
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;
Expand All @@ -32,7 +33,8 @@ public interface UserControllerDocs {
@Operation(
summary = "이메일 인증코드 전송 API",
description = "입력받은 이메일로 인증코드를 전송합니다. 인증코드 재전송도 해당 API 를 호출합니다.\n\n" +
"테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다."
"테스트용 이메일은 'test' 로 시작하거나 'example.com' 으로 끝나야합니다. 테스트용 이메일의 인증코드는 서버 로그로 확인 가능합니다.\n\n" +
"이미 소셜 로그인으로 가입된 이메일 값이 요청으로 들어올 경우, isProviderLinked = true, providerType = [KAKAO, NAVER...] 와 같이 값이 나오며 이메일은 전송되지 않습니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
Expand All @@ -59,7 +61,7 @@ public interface UserControllerDocs {
@ApiResponse(responseCode = "400_2", description = "이메일 전송실패(이메일 오타 등)"),
@ApiResponse(responseCode = "404_1", description = "해당 이메일로 가입한 회원 존재하지 않음")
})
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request);
public ResponseEntity<DataResponse<PasswordResetResponse>> sendEmailForPwdReset(@RequestBody @Valid EmailRequest.Send request);

@Operation(
summary = "사용자 비밀번호 재설정 API",
Expand Down