-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
인증 코드 메일 전송 및 검증 기능 구현 #32
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다 레오씨
처음 구현하는건데 잘하셨네유
확인하고 리뷰 남겼으니 봐주세여
|
||
public interface AuthCodeSender { | ||
|
||
public void send(String destination, String authCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public void send(String destination, String authCode); | |
void send(String destination, String authCode); |
@Override | ||
public void send(String destination, String authCode) { | ||
SimpleMailMessage mailMessage = new SimpleMailMessage(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어디서 보냈는지는 설정안해도 되나여?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그러게요 .!!?
sender
나 title
같은거는 안써줘도될까요 ?
받는입장에서 뭔가 당황스러울수도..?
@@ -0,0 +1,20 @@ | |||
package com.example.parking.auth.authcode.event; | |||
|
|||
public class AuthCodeSendEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
record로 만들어도 될거 같아유
@Enumerated(EnumType.STRING) | ||
private AuthCodeCategory authCodeCategory; | ||
|
||
private Boolean isUsed = Boolean.FALSE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isUsed
말고 세션처럼 만료시간을 두는건 어떻게 생각하시나요?
@Where(clause = "expired_at > CURRENT_TIMESTAMP")
이거 객체단에 붙여서 조회할 때도 유효한 AuthCode
만 가져올 수 있을거 같은데
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
private String destination; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
destination
을 AuthCodePlatform
랑 합쳐서 입력받는 타입을 지켜줘도 좋을거 같은데 어떻게 생각하시나여?
(Email이면 Email 형식이 맞는지 검증도 하고)
.orElseThrow(() -> new InValidAuthCodeException("존재하지 않는 인증 코드 발급 행위입니다.")); | ||
} | ||
|
||
public String getCategoryName() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Getter
// this.authCodeRepository = authCodeRepository; | ||
// this.authService = new AuthService(memberSessionRepository, authCodeRepository, fixAuthCodeGenerator, | ||
// applicationEventPublisher); | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안쓰는 코드인가유?
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatNoException; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@Transactional | ||
@SpringBootTest | ||
class AuthServiceTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 테스트 깨지는데 혹시 확인좀 가능할까유?
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatNoException; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
@Transactional |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Transactional
어노테이션을 클래스단에 붙인 이유를 알 수 있을까유?
이렇게 클래스단에 붙여버리면 authService
의 메소드들의 트랜잭션 범위를 테스트할 수 없을거같은데
String newAuthCode = authService.createAuthCode( | ||
new AuthCodeRequest(authCodeDestination, authCodePlatform.getPlatform(), | ||
authCodeCategory.getCategoryName()) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 생각했을 때 이 부분 테스트하려면 Mock 객체를 사용하거나
첫번째 호출할 때는 111... 두번째 호출할 때는 222.. 이런식으로 생성하는 AuthCodeGenerator를 만들어볼거 같아유
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
영호씨 고생하셨습니다 !
깔끔하네용
생각해보면 좋을 것들 커멘트 남겼습니다 ㅎ_ㅎ
public void certificateAuthCode(AuthCodeCertificateRequest authCodeCertificateRequest) { | ||
String destination = authCodeCertificateRequest.getDestination(); | ||
AuthCodePlatform authCodePlatform = AuthCodePlatform.find(authCodeCertificateRequest.getAuthCodePlatform()); | ||
AuthCodeCategory authCodeCategory = AuthCodeCategory.find(authCodeCertificateRequest.getAuthCodeCategory()); | ||
|
||
AuthCode authCode = authCodeRepository.findRecentlyAuthCodeBy(destination, authCodePlatform, authCodeCategory) | ||
.orElseThrow(() -> new InValidAuthCodeException("해당 형식의 인증코드가 존재하지 않습니다.")); | ||
authCode.certificate(authCodeCertificateRequest.getAuthCode()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
authCode
의 만료기간은 없을까요? 5분 이살 지났으면 유효하지 않다던지 ?
정책적인 문제긴한데, 조금 필요해 보여서요..!
private final AuthCodeSender authCodeSender; | ||
|
||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void sendAuthCode(AuthCodeSendEvent authCodeSendEvent) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 이벤트 리스너를 잘 몰라서 여쭤봅니다 !
파라미터 타입은 보고 이 리스너가 작동될지 결정을 하는 것인가요 ?
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void sendAuthCode(AuthCodeSendEvent authCodeSendEvent) { | ||
String destination = authCodeSendEvent.getDestination(); | ||
String authCode = authCodeSendEvent.getAuthCode(); | ||
authCodeSender.send(destination, authCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 가지 얘기를 해보고 싶습니다 !
첫 번째는 특정 이벤트를 리슨하는 클래스를 만들어주셨는데요 !
지금은 메일전송 밖에 없지만, 이 이벤트에 반응하여 다양한 로직이 이뤄져야 한다고 쳤을 때, 이렇게 하나의 이벤트를 리슨하는 클래스를 만들어서 안에서 한 번에 실행이 되게끔 하는게 관리의 용이성 때문인가요 ?
저는 authCodeSender
가 이벤트를 리슨한다고 생각했었는데, 궁금해서 여쭤봅니다 !
두 번째는 Zero-payload
방식에 대해서 얘기해보고 싶습니다.
현재 이벤트안에 다양한 정보가 들어가 있는데요 !
저는 뭔가 이렇게 페이로드에 여러 정보를 넣어서 보내면 이벤트를 발행하는 쪽이 이벤트를 구독하는 애들이 뭐가 필요한지 간접적으로 알고있는거 아닌가? 라는 생각이 들었습니다.
만약 이 이벤트를 구독하여 다른 기능들을 실행해야하는 객체들이 많아지면, 각자 원하는 정보가 다를 수도 있을 것같은데요. 그 정보는 자기들이 알아서 찾도록 authCode
의 pk값만 Payload에 담아주는 것은 어떤가요 ?
물론 현재 상황에서는 오버엔지니어링으로 보이긴합니다 !
그냥 이벤트안의 내용물에 뭐가 들어가야할지에 대해 토의해보고싶습니다 !
@Component | ||
public class MailAuthCodeSender implements AuthCodeSender { | ||
|
||
private final JavaMailSender mailSender; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신기방기하네요
@Override | ||
public void send(String destination, String authCode) { | ||
SimpleMailMessage mailMessage = new SimpleMailMessage(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그러게요 .!!?
sender
나 title
같은거는 안써줘도될까요 ?
받는입장에서 뭔가 당황스러울수도..?
@Override | ||
public String generateAuthCode() { | ||
StringBuilder stringBuilder = new StringBuilder(); | ||
for (int i = 0; i < MAX_LENGTH; i++) { | ||
stringBuilder.append(random.nextInt(9)); | ||
} | ||
return stringBuilder.toString(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
6자리 번호면 10^6 만큼의 경우의 수가 있을 것 같은데요 !
brute force attack에 대해서도 어떻게 하면 좋을지 생각해보면 좋을 것 같아요 !
시도횟수를 둔다던지 ??
약간 카드 결제 시도횟수같은 느낌 ?
🤔세부 내용
🫵리뷰 중점사항
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다. 영호씨
도커에서 계속 오류가나서 테스트는 못해봤고
코드보면서 생각들 커맨트 남겼습니다~
// jdbcTemplate.execute("TRUNCATE TABLE parking"); | ||
// jdbcTemplate.execute("TRUNCATE TABLE member_session"); | ||
// jdbcTemplate.execute("TRUNCATE TABLE search_condition"); | ||
// jdbcTemplate.execute("TRUNCATE TABLE member"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 지우셔도 될거같아유
applicationEventPublisher.publishEvent(new AuthCodeSendEvent(destination, randomAuthCode)); | ||
String authCodeKey = AuthCodeKeyConverter.convert(randomAuthCode, destination, authCodePlatform.getPlatform(), | ||
authCodeCategory.getCategoryName()); | ||
redisTemplate.opsForValue().set(authCodeKey, "true"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"true" 대신 authCode가 value에 들어가는게 맞는거 같아여
key는 destination([email protected]), categoryName(findPassword) 이것만 있어도 충분할거 같아유
그리고 저장할 때, 만료시간도 설정하면 될거같아유
.withExposedPorts(6379); | ||
REDIS_CONTAINER.start(); | ||
System.setProperty("spring.data.redis.host", REDIS_CONTAINER.getHost()); | ||
System.setProperty("spring.data.redis.port", String.valueOf(REDIS_CONTAINER.getRedisPort())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
21번 라인에 .withExposedPorts(6379);
랑 위에서 포트 지정해주는거랑 무슨 차이인가여?
import lombok.Getter; | ||
|
||
@Getter | ||
public class AuthCodeCreateEvent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이벤트 같은건 record로 해도 될거같은데여
String authCodeCategory = authCodeCreateEvent.getAuthCodeCategory(); | ||
|
||
String authCodeKey = AuthCodeKeyConverter.convert(authCode, destination, authCodePlatform, authCodeCategory); | ||
taskScheduler.schedule(() -> redisTemplate.delete(authCodeKey), Instant.now().plusSeconds(authCodeExpired)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redis에 만료 시간두는게 낫지 않을까여?
|
||
public interface PlatformValidator { | ||
|
||
boolean inInvalidForm(String destination); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
boolean inInvalidForm(String destination); | |
boolean isInvalidForm(String destination); |
오타인가여?
# Conflicts: # src/test/java/com/example/parking/auth/MemberSessionRepositoryTest.java
Test Results120 tests 120 ✅ 3s ⏱️ Results for commit bd7c135. |
👍관련 이슈
🤔세부 내용
🫵리뷰 중점사항
인증 코드 생성기(AuthCodeGenerator) 의존성
인증 코드 검증 시 동시성 이슈