Skip to content

Conversation

@daye200
Copy link
Contributor

@daye200 daye200 commented Nov 4, 2025

#78

Summary by CodeRabbit

  • 새로운 기능

    • 이메일 인증 기능 추가: 인증 코드 발송 및 코드 확인(웹 API)으로 이메일 인증 가능
    • 인증 이메일에 HTML 템플릿 적용(응답성·다크모드·인증 코드 표시, 만료 안내)
  • 버그 수정

    • 이메일 유효성·중복 검사 및 명확한 오류 응답(코드 미조회/불일치/이미 인증 등)
    • 인증 상태(24시간) 처리로 중복 요청 방지
  • 테스트

    • 이메일 인증 서비스에 대한 단위 테스트 추가

@daye200 daye200 requested a review from discipline24 as a code owner November 4, 2025 07:15
@coderabbitai
Copy link

coderabbitai bot commented Nov 4, 2025

Walkthrough

이 PR은 이메일 인증 기능을 추가합니다: 구성 프로퍼티, REST 엔드포인트(/api/email/send, /api/email/verify), EmailService 구현(코드 생성/저장/검증, Thymeleaf 이메일 전송), Redis 상태 관리, 에러 코드 추가, 템플릿, 단위테스트 및 빌드 의존성 확장.

Changes

Cohort / File(s) 변경 내용
의존성 변경
backend/build.gradle
테스트/런타임 의존성 추가: Mockito, AssertJ, GreenMail; 런타임 의존성 추가: Spring Mail, Commons Validator, Spring Data Redis, Thymeleaf
설정 POJO 추가
backend/src/main/java/org/sejongisc/backend/auth/config/EmailProperties.java
이메일 설정 바인딩 클래스 추가 (codeExpire, verifiedExpire, keyPrefix, code)
컨트롤러 추가
backend/src/main/java/org/sejongisc/backend/auth/controller/EmailController.java
/api/email 하위 POST /send, POST /verify 엔드포인트를 제공하는 REST 컨트롤러 추가
비즈니스 로직 추가
backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java
이메일 검증 흐름 구현: 이메일 포맷/중복 검사, 코드 생성(SecureRandom), Redis 저장/검증, Thymeleaf로 HTML 구성, JavaMailSender로 전송, 예외 매핑
보안 설정 변경
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java
/api/email/** 경로에 대해 permitAll(공개 접근) 규칙 추가
에러 코드 변경
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java
EMAIL_CODE_NOT_FOUND, EMAIL_CODE_MISMATCH, EMAIL_INVALID_EMAIL, EMAIL_ALREADY_VERIFIED 추가 (기존 EXPIRED_ACCESS_TOKEN 제거)
템플릿 추가
backend/src/main/resources/templates/mail/verificationEmail.html
Thymeleaf 기반 이메일 HTML 템플릿 추가 ([[${email}]], [[${code}]], 동적 연도 등)
단위 테스트 추가
backend/src/test/java/org/sejongisc/backend/auth/service/EmailServiceTest.java
Mockito 기반 EmailService 테스트 추가 (sendEmail 성공 시나리오), verifyEmail 테스트 스텁 포함

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant EmailController
    participant EmailService
    participant Redis
    participant TemplateEngine
    participant MailSender

    Client->>EmailController: POST /api/email/send?email=...
    EmailController->>EmailService: sendEmail(email)

    rect rgb(245,250,255)
    Note over EmailService,Redis: 사전검증(verified 조회, 이메일 형식/중복 확인)
    EmailService->>Redis: GET verified:{email}
    EmailService->>EmailService: validate email, check user
    end

    rect rgb(230,255,235)
    Note over EmailService: 코드 생성 및 전송
    EmailService->>EmailService: generateCode()
    EmailService->>Redis: SET verify:{email} = code (TTL)
    EmailService->>TemplateEngine: render(mail/verificationEmail, vars)
    EmailService->>MailSender: send(MimeMessage)
    end

    MailSender-->>EmailService: send result
    EmailService-->>EmailController: success
    EmailController-->>Client: 200 OK

    Client->>EmailController: POST /api/email/verify?email=...&code=...
    EmailController->>EmailService: verifyEmail(email, code)

    rect rgb(255,240,240)
    Note over EmailService,Redis: 코드 검증 및 상태 변경
    EmailService->>Redis: GET verify:{email}
    EmailService->>EmailService: compare codes
    EmailService->>Redis: DEL verify:{email}
    EmailService->>Redis: SET verified:{email} = true (TTL 24h)
    end

    EmailService-->>EmailController: success
    EmailController-->>Client: 200 OK
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 집중 검토 대상:
    • EmailService.java: Redis 키/TTL, 예외 매핑(CustomException/ErrorCode), 이메일 전송(예외 처리)
    • EmailServiceTest.java: verifyEmail 경로 및 실패 케이스 추가 필요
    • SecurityConfig.java: 새 permitAll 규칙이 기존 보안 규칙과 충돌하지 않는지 확인

Possibly related PRs

Poem

🐰 편지를 싣고 달려온 토끼가 말하네,
코드 한 줄, Redis 위에 빛나네.
Thymeleaf로 쓴 작은 편지지,
숫자 하나로 문이 열리고,
검증의 길에 깃든 따스한 봄. 🌱

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용을 정확하게 설명합니다. 이메일 전송 및 인증 로직이 추가되었으며, 제목이 이를 명확하게 반영합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-20251104-이메일인증기능추가

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0342b57 and 574fa9b.

📒 Files selected for processing (2)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
backend/src/main/resources/templates/mail/verificationEmail.html (1)

67-69: 인증 유효시간을 하드코딩하면 설정과 불일치할 수 있습니다

본문에 “3분”이 고정 표기되어 있는데, 실제 만료 시간은 EmailProperties.codeExpire 설정값에 따라 달라집니다. 설정이 바뀌면 사용자에게 잘못된 안내가 전달되므로, 컨텍스트에 만료 분(또는 시각)을 넣어 템플릿에서 동적으로 렌더링하도록 바꿔 주세요.

backend/src/test/java/org/sejongisc/backend/auth/service/EmailServiceTest.java (1)

85-87: 빈 테스트 케이스는 의미 있는 검증을 제공하지 않습니다

verifyEmail() 테스트가 비어 있어 아무 검증도 수행하지 않습니다. 실제 시나리오(코드 불일치, 만료, 성공 등)를 검증하도록 구현하거나 불필요하다면 제거해 주세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8028b8b and aab1e2e.

📒 Files selected for processing (8)
  • backend/build.gradle (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/config/EmailProperties.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/controller/EmailController.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1 hunks)
  • backend/src/main/resources/templates/mail/verificationEmail.html (1 hunks)
  • backend/src/test/java/org/sejongisc/backend/auth/service/EmailServiceTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (2)
backend/src/main/java/org/sejongisc/backend/common/exception/CustomException.java (1)
  • Getter (5-14)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorResponse.java (1)
  • Getter (7-21)
backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java (1)
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
  • Override (34-69)
backend/src/main/java/org/sejongisc/backend/auth/config/EmailProperties.java (1)
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (1)
  • Configuration (23-88)

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

깔끔하네요 고생하셨습니다!

@discipline24 discipline24 linked an issue Nov 5, 2025 that may be closed by this pull request
validator이나 비동기제거(인증이메일에 불필요) 에러코드 수정등 자잘한 수정
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)

53-57: 에러 메시지 마침표 일관성 확보 필요

일부 에러 메시지는 마침표로 끝나고(EMAIL_CODE_MISMATCH, EMAIL_ALREADY_VERIFIED) 일부는 마침표가 없습니다(EMAIL_CODE_NOT_FOUND, EMAIL_INVALID_EMAIL). 기존 에러 코드들과의 일관성을 위해 모든 메시지를 마침표로 통일해 주세요.

- EMAIL_CODE_NOT_FOUND(HttpStatus.NOT_FOUND, "이메일 인증 코드를 찾을 수 없습니다"),
+ EMAIL_CODE_NOT_FOUND(HttpStatus.NOT_FOUND, "이메일 인증 코드를 찾을 수 없습니다."),
  EMAIL_CODE_MISMATCH(HttpStatus.BAD_REQUEST, "인증 코드가 일치하지 않습니다."),
- EMAIL_INVALID_EMAIL(HttpStatus.BAD_REQUEST, "유효하지 않은 이메일입니다"),
+ EMAIL_INVALID_EMAIL(HttpStatus.BAD_REQUEST, "유효하지 않은 이메일입니다."),
  EMAIL_ALREADY_VERIFIED(HttpStatus.BAD_REQUEST, "24시간 이내에 이미 인증된 이메일입니다."),
backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java (3)

64-82: Redis 작업 간 경쟁 조건 가능성

hasKey 체크(Line 64)와 인증 코드 저장(Line 82) 사이에 다른 요청이 끼어들 수 있어, 동일한 이메일에 대해 여러 인증 메일이 동시에 발송될 수 있습니다. 심각한 문제는 아니지만, Redis의 SETNX 명령이나 Lua 스크립트를 사용해 원자적으로 처리하면 더 견고해집니다.

예시: setIfAbsent를 활용한 원자적 처리

  // 인증코드 생성
-     String code = generateCode();
+   String code = generateCode();
+   String verifyKey = emailProperties.getKeyPrefix().getVerify() + email;

  // Redis에 인증 코드 저장 (유효시간: 3분)
- redisTemplate.opsForValue().set(emailProperties.getKeyPrefix().getVerify() + email, code, emailProperties.getCodeExpire());
+   Boolean wasSet = redisTemplate.opsForValue().setIfAbsent(verifyKey, code, emailProperties.getCodeExpire());
+   if (Boolean.FALSE.equals(wasSet)) {
+     // 이미 진행 중인 인증이 있음 - 재전송 대기 등의 로직 추가 가능
+     log.info("인증 코드가 이미 존재합니다: {}", email);
+   }

79-79: 들여쓰기 정리

불필요한 들여쓰기가 있습니다.

- // 인증코드 생성
-     String code = generateCode();
+ // 인증코드 생성
+ String code = generateCode();

97-97: 코드 포맷팅 수정

getVerify() 뒤에 공백이 누락되었습니다.

- String key = emailProperties.getKeyPrefix().getVerify()+ email;
+ String key = emailProperties.getKeyPrefix().getVerify() + email;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aab1e2e and 0342b57.

📒 Files selected for processing (3)
  • backend/build.gradle (2 hunks)
  • backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java (1 hunks)
  • backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1 hunks)
🔇 Additional comments (3)
backend/build.gradle (1)

50-82: 의존성 추가가 적절합니다.

이메일 인증 기능 구현에 필요한 의존성들이 올바르게 추가되었습니다. 특히 commons-validator:1.10.0으로 업데이트하여 이전 리뷰에서 지적된 보안 취약점(CVE-2025-48734)이 해결되었습니다.

backend/src/main/java/org/sejongisc/backend/auth/service/EmailService.java (2)

42-57: LGTM!

Thymeleaf를 활용한 HTML 이메일 생성 로직이 올바르게 구현되었습니다.


118-129: LGTM!

SecureRandom을 사용한 안전한 인증 코드 생성 로직입니다.

@discipline24 discipline24 merged commit 907f62d into main Nov 5, 2025
1 check passed
@discipline24 discipline24 deleted the SISC1-20251104-이메일인증기능추가 branch November 5, 2025 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

⚙️ [기능추가][이메일] 이메일 기능 추가

2 participants