
Dataracy ํ๋ก์ ํธ์์๋ ํ์๊ฐ์
, ๋น๋ฐ๋ฒํธ ์ฐพ๊ธฐ, ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ๊ณผ์ ์์ ์ด๋ฉ์ผ ์ธ์ฆ์ ํ์๋ก ์ ์ฉํ๋ค.
ํ์๊ฐ์
์ ์ฌ์ฉ์๋ 6์๋ฆฌ ์ธ์ฆ๋ฒํธ๋ฅผ ์ด๋ฉ์ผ๋ก ์์ ํ๊ณ , ํด๋น ์ฝ๋๋ฅผ ์
๋ ฅํด ๊ฒ์ฆ์ ํต๊ณผํด์ผ ๊ฐ์
์ด ์๋ฃ๋๋ค.
๋น๋ฐ๋ฒํธ๋ฅผ ๋ถ์คํ์ ๊ฒฝ์ฐ์๋ ๋์ผํ๊ฒ ์ด๋ฉ์ผ ์ธ์ฆ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ค.
์ด ๊ธ์์๋ ์ค์ ์ ์ฉํ ์ฝ๋๋ฅผ ๊ธฐ์ค์ผ๋ก ์ด๋ฉ์ผ ์ธ์ฆ ๊ตฌํ(SendGrid) โ ์ด๋ฉ์ผ ๋ฐ์ก โ Redis ๊ธฐ๋ฐ OTP ๊ด๋ฆฌ โ ๊ฒ์ฆ API๊น์ง ํ๋ฆ์ ์ ๋ฆฌํ๋ค.
๋ํ ํฅ์ฌ๊ณ ๋ ์ํคํ
์ฒ ๊ธฐ๋ฐ์ผ๋ก Port / Adapter / Application / Domain ๋ ์ด์ด๋ฅผ ๋ช
ํํ ๋ถ๋ฆฌํ๋ค.
[Web Controller] โ [UseCase (SendEmailUseCase / VerifyEmailUseCase)]
โ
[Application Service] (EmailCommandService / EmailVerifyService)
โ
[Port Out] (SendEmailPort, VerifyEmailPort)
โ
[Adapter] (SendGrid Adapter, Redis Adapter)
โ
์ธ๋ถ ์๋น์ค (SendGrid / Redis)
EmailCommandService๊ฐ ๋ค์ ๋จ๊ณ๋ฅผ ์ํํ๋ค.
SecureRandom์ผ๋ก 6์๋ฆฌ ์ซ์ ์ฝ๋ ์์ฑํ๋ค. EmailContentFactory๋ก ์ด๋ฉ์ผ ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ์ ์์ฑํ๋ค. SendEmailPort๋ฅผ ํตํด SendGrid API๋ฅผ ํธ์ถํ์ฌ ์ด๋ฉ์ผ์ ์ ์กํ๋ค. VerifyEmailPort.saveCode๋ฅผ ํตํด Redis์ ์ธ์ฆ ์ฝ๋๋ฅผ ์ ์ฅํ๋ค. @Override
public void sendEmailVerificationCode(String email, EmailVerificationType type) {
String code = generateCode(); // 6์๋ฆฌ ์ซ์
EmailContent content = EmailContentFactory.generate(type, code);
try {
sendEmailPort.send(email, content.subject(), content.body());
} catch (Exception e) {
throw new EmailException(EmailErrorStatus.FAIL_SEND_EMAIL_CODE);
}
verifyEmailPort.saveCode(email, code, type);
}
SendEmailSendGridAdapter๊ฐ SendGrid API๋ฅผ ํธ์ถํ๋ค.
@Override
public void send(String email, String subject, String body) {
Email from = new Email(sender);
Email to = new Email(email);
Content content = new Content("text/plain", body);
Mail mail = new Mail(from, subject, to, content);
Request request = new Request();
request.setMethod(Method.POST);
request.setEndpoint("mail/send");
request.setBody(mail.build());
Response response = sendGrid.api(request);
if (response.getStatusCode() >= 400) {
throw new RuntimeException("SendGrid ์ ์ก ์คํจ");
}
}
EmailRedisAdapter๊ฐ Redis์ ์ธ์ฆ ์ฝ๋๋ฅผ ์ ์ฅํ๋ค.
email:verification:{purpose}:{email} CommonException์ผ๋ก ๋ณํํ๋ค. @Override
public void saveCode(String email, String code, EmailVerificationType type) {
String key = getEmailKey(email, type);
redisTemplate.opsForValue().set(key, code, EXPIRE_MINUTES, TimeUnit.MINUTES);
}
EmailVerifyService๊ฐ ๊ฒ์ฆ์ ์ํํ๋ค.
EmailException์ ๋ฐ์์ํจ๋ค. PASSWORD_SEARCH) ๋ชฉ์ ์ผ ๊ฒฝ์ฐ ๋น๋ก๊ทธ์ธ ์ ์ ์ด๊ธฐ ๋๋ฌธ์ email์ ๋ด์ JWT ๊ธฐ๋ฐ Reset Token์ ๋ฐ๊ธํ๋ค. @Override
public GetResetTokenResponse verifyCode(String email, String code, EmailVerificationType type) {
String savedCode = cacheEmailPort.verifyCode(email, code, type);
if (savedCode == null) throw new EmailException(EmailErrorStatus.EXPIRED_EMAIL_CODE);
if (!savedCode.equals(code)) throw new EmailException(EmailErrorStatus.FAIL_VERIFY_EMAIL_CODE);
cacheEmailPort.deleteCode(email, type);
if (type.equals(EmailVerificationType.PASSWORD_SEARCH)) {
String resetToken = jwtGenerateUseCase.generateResetPasswordToken(email);
cacheResetTokenUseCase.saveResetToken(resetToken);
return new GetResetTokenResponse(resetToken);
}
return new GetResetTokenResponse(null);
}
EmailCommandController๋ API ์์ฒญ์ ์ฒ๋ฆฌํ๋ค.
OK_SEND_EMAIL_CODE_SIGN_UP OK_SEND_EMAIL_CODE_PASSWORD_SEARCH OK_SEND_EMAIL_CODE_PASSWORD_RESET๊ฐ์ ๋ชฉ์ ์ ๋ฐ๋ผ ์ด๋ฉ์ผ ์ ๋ชฉ๊ณผ ๋ด์ฉ์ enum์ผ๋ก ์ค์ ํ์ฌ ๋ชฉ์ ์ ๋ง๋ ์ด๋ฉ์ผ์ ๋ณด๋ผ ์ ์๊ฒ ์ค์ ํ์๋ค.
@Override
public ResponseEntity<SuccessResponse<Void>> sendCode(SendEmailWebRequest webRequest) {
EmailVerificationType type = EmailVerificationType.of(webRequest.purpose());
sendEmailUseCase.sendEmailVerificationCode(webRequest.email(), type);
return switch (type) {
case SIGN_UP -> ok(EmailSuccessStatus.OK_SEND_EMAIL_CODE_SIGN_UP);
case PASSWORD_SEARCH -> ok(EmailSuccessStatus.OK_SEND_EMAIL_CODE_PASSWORD_SEARCH);
case PASSWORD_RESET -> ok(EmailSuccessStatus.OK_SEND_EMAIL_CODE_PASSWORD_RESET);
};
}
ํฅ์ฌ๊ณ ๋ ์ํคํ
์ฒ ์ ์ฉ
๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ธ๋ถ ์์กด์ฑ(SendGrid, Redis)์ ๋ถ๋ฆฌํ๋ค.
๋ณด์ ๊ณ ๋ ค
์ธ์ฆ๋ฒํธ๋ 6์๋ฆฌ ์ซ์๋ก ์์ฑํ๋ฉฐ TTL 5๋ถ์ ๋๊ณ Redis์ ์ ์ฅํ๋ค.
๋น๋ฐ๋ฒํธ ์ฐพ๊ธฐ ์ JWT Reset Token์ ์ถ๊ฐ ๋ฐ๊ธํ๋ค.
๋ก๊ทธ ์ฒด๊ณ
LoggerFactory ๊ธฐ๋ฐ์ผ๋ก API / Service / Redis / SendGrid ํ๋ฆ์ ๊ตฌ๋ถํด ๊ธฐ๋กํ๋ค.
Dataracy ํ๋ก์ ํธ์์๋ ํ์๊ฐ์ / ๋น๋ฐ๋ฒํธ ์ฐพ๊ธฐ / ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ๊ณผ์ ์ ์ด๋ฉ์ผ ์ธ์ฆ์ ์ ์ฉํ์ฌ ๊ณ์ ๋ณด์์ ๊ฐํํ๋ค.
โ ์ด ๊ตฌ์กฐ๋ฅผ ํตํด ์คํธ๋ฅ ์ ์ค์ด๊ณ , ๋ณด์์ ๊ฐํํ๋ฉฐ, ์ ์ง๋ณด์์ฑ์ ๋์ผ ์ ์๊ฒ ๋์๋ค. ๋ํ, ํ API ๊ธฐ๋ฅ์ผ๋ก ์ฌ๋ฌ ๋ชฉ์ ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ด๋ฉ์ผ์ ๋ณด๋ผ ์ ์๊ธฐ์ ํจ์จ์ด ์ข๋ค๋ ์๊ฐ์ด ๋ ๋ค.