Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1b3551e
:sparkles: feat: Spring μ„œλ²„μ—μ„œ 이메일 전솑을 μœ„ν•œ build.gradle μ˜μ‘΄μ„± μΆ”κ°€
ojy0903 Jan 26, 2026
dcefec3
:sparkles: feat: 이메일 μΈμ¦μ½”λ“œλ₯Ό μ €μž₯ν•˜κΈ° μœ„ν•œ RedisUtil 클래슀 μΆ”κ°€
ojy0903 Jan 26, 2026
4de758a
:sparkles: feat: 이메일 인증관련 Request, Response DTO μΆ”κ°€
ojy0903 Jan 26, 2026
11123c3
:sparkles: feat: 이메일 인증 μ²˜λ¦¬ν•˜λŠ” EmailService 클래슀 μΆ”κ°€
ojy0903 Jan 26, 2026
5e0259f
:sparkles: feat: 이메일 인증 κ΄€λ ¨ μ˜ˆμ™Έμ²˜λ¦¬ μ½”λ“œ UserErrorCode에 μΆ”κ°€
ojy0903 Jan 26, 2026
2b8e818
:sparkles: feat: κΈ°μ‘΄ νšŒμ›κ°€μž… λ‘œμ§μ—μ„œ 이메일 μΈμ¦λ˜μ–΄μ•Ό κ°€μž… κ°€λŠ₯ν•˜λ„λ‘ μˆ˜μ •
ojy0903 Jan 26, 2026
22101f5
:sparkles: feat: μΈμ¦μ½”λ“œ 전솑 API, μΈμ¦μ½”λ“œ 검증 API μΆ”κ°€(/api/users/...)
ojy0903 Jan 26, 2026
db7cdc4
:sparkles: feat: SecurityConfig μ—μ„œ 둜그인 인증 없이 이메일 인증 μ ‘κ·Ό κ°€λŠ₯ν•˜λ„λ‘ μˆ˜μ •
ojy0903 Jan 26, 2026
852dc4c
:bug: fix: test 디렉터리 application.yml μ„€μ • μΆ”κ°€ -> λΉŒλ“œ ν…ŒμŠ€νŠΈ 톡과 λͺ©μ 
ojy0903 Jan 28, 2026
dbb18f7
:bug: fix: test 디렉터리 application.yml μ‚­μ œ
ojy0903 Jan 28, 2026
da68b56
:sparkles: feat: .gitignore μ—μ„œ application.yml λ¬΄μ‹œ μ„€μ • 주석 처리 & .env λ¬΄μ‹œ …
ojy0903 Jan 28, 2026
5634c1c
:sparkles: feat: application.yml μΆ”κ°€, .env ν˜•μ‹ μ˜ˆμ‹œ 파일 .env.example μΆ”κ°€
ojy0903 Jan 28, 2026
ba60c4f
:sparkles: feat: application.yml JWT μ˜΅μ…˜ μ£Όμ„μ²˜λ¦¬
ojy0903 Jan 28, 2026
f9f14e7
:green_heart: build: ci.yml λ‚΄ env(ν™˜κ²½λ³€μˆ˜λͺ…) μˆ˜μ •
jinnieusLab Jan 28, 2026
b96059d
:green_heart: build: ci.yml λ‚΄ jwt secret μž„μ˜ 인코딩 κ°’μœΌλ‘œ μˆ˜μ •
jinnieusLab Jan 28, 2026
a8a1b59
:recycle: refactor: 이메일 인증 μš”μ²­ DTO λ₯Ό κΈ°μ‘΄ EmailRequest, EmailVerifyReque…
ojy0903 Jan 28, 2026
6b3dfec
:truck: rename: UserErrorCode λ‚΄λΆ€ USER_400_N μ½”λ“œ 숫자 μ •λ ¬
ojy0903 Jan 28, 2026
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
15 changes: 15 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Database
DB_URL=jdbc:mysql://localhost:3306/whereyouad?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
DB_USERNAME=root
DB_PASSWORD=

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# Mail
MAIL_USERNAME=your-email@gmail.com
MAIL_PASSWORD=email-app-password

# JWT
JWT_SECRET=your_very_long_and_secret_random_string_for_signing_tokens_to_match_HS512
13 changes: 8 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ jobs:
run: ./gradlew build
# CI용, ν™˜κ²½λ³€μˆ˜λ‘œ DB μ„€μ •
env:
SPRING_DATASOURCE_URL: jdbc:mysql://localhost:3306/where_you_ad?serverTimezone=Asia/Seoul
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root
SPRING_DATA_REDIS_HOST: localhost
SPRING_DATA_REDIS_PORT: 6379
DB_URL: jdbc:mysql://localhost:3306/where_you_ad?serverTimezone=Asia/Seoul
DB_USERNAME: root
DB_PASSWORD: root
REDIS_HOST: localhost
REDIS_PORT: 6379
MAIL_USERNAME: test@example.com
MAIL_PASSWORD: test_password
JWT_SECRET: dGhpcy1pcy1hLXRlc3Qtc2VjcmV0LWtleS1mb3ItY2ktdGVzdC1wdXJwb3Nlcw==
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ out/
### VS Code ###
.vscode/

src/main/resources/application.yml
#src/main/resources/application.yml
.env
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ dependencies {
//swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.0'

//Email
implementation 'org.springframework.boot:spring-boot-starter-mail'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.whereyouad.WhereYouAd.domains.user.application.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public class EmailRequest {

public record Send (
@NotBlank(message = "이메일은 ν•„μˆ˜μž…λ‹ˆλ‹€.")
@Email(message = "이메일 ν˜•μ‹μ΄ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
String email
) {}

public record Verify(
@NotBlank(message = "이메일은 ν•„μˆ˜μž…λ‹ˆλ‹€.")
@Email(message = "이메일 ν˜•μ‹μ΄ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.")
String email,
@NotBlank
String authCode
) {}
}
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 EmailSentResponse(
String message, //μΈμ¦μ½”λ“œλ₯Ό μ΄λ©”μΌλ‘œ μ „μ†‘ν–ˆμŠ΅λ‹ˆλ‹€.
String email, //μ „μ†‘ν•œ 이메일
long expireIn //λ§Œλ£Œμ‹œκ°„ (500L -> 500초)
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.whereyouad.WhereYouAd.domains.user.domain.service;

import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.exception.UserSignUpException;
import com.whereyouad.WhereYouAd.domains.user.exception.code.UserErrorCode;
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository;
import com.whereyouad.WhereYouAd.global.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Transactional
@Service
@RequiredArgsConstructor
@Slf4j
public class EmailService {

private final JavaMailSender emailSender;
private final RedisUtil redisUtil;
private final UserRepository userRepository;

//application.yml 적용 ν•„μš”
@Value("${spring.mail.username}")
private String senderEmail;

//μΈμ¦μ½”λ“œ 이메일 λ°œμ†‘ 둜직
public EmailSentResponse sendEmail(String toEmail) {

if (userRepository.existsByEmail(toEmail)) { //이미 ν•΄λ‹Ή μ΄λ©”μΌλ‘œ μƒμ„±ν•œ 계정이 있으면
throw new UserSignUpException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 쀑볡 μ˜ˆμ™Έ(νšŒμ›κ°€μž… μ‹œ μ‚¬μš©ν–ˆλ˜ μ˜ˆμ™Έ)
}

//μΈμ¦μ½”λ“œ μž¬μ „μ†‘ 둜직 -> 이미 Redis 에 ν•΄λ‹Ή 이메일 μΈμ¦μ½”λ“œκ°€ μžˆμ„μ‹œ μ‚­μ œ
String redisKey = "CODE:" + toEmail;
if (redisUtil.getData(redisKey) != null) {
redisUtil.deleteData(redisKey);
}

String authCode = createCode(); //μΈμ¦μ½”λ“œ 생성

if (isTestEmail(toEmail)) {
//ν…ŒμŠ€νŠΈμš© κ°€μ§œ 이메일은 μ„œλ²„ 둜그둜만 μΈμ¦μ½”λ“œλ₯Ό 보여주기
//μ‹€μ œ 이메일 λ°œμ†‘ X
//μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” 이메일 μ£Όμ†Œλ‘œ 계속 이메일을 보내면 μΈμ¦μ½”λ“œ μ „μ†‘μš© 이메일 계정이 슀팸처리 될 κ°€λŠ₯μ„± μ‘΄μž¬ν•˜μ—¬ 둜그둜 남기기
log.warn("{} 이메일 -> ν…ŒμŠ€νŠΈ or 개발용 κ°€μ§œ 이메일 μž…λ‹ˆλ‹€.", toEmail);
log.warn("[TEST λͺ¨λ“œ] μ‹€μ œ λ°œμ†‘μ„ κ±΄λ„ˆλœλ‹ˆλ‹€.");
log.warn("[TEST λͺ¨λ“œ] μˆ˜μ‹ μž: {}", toEmail);
log.warn("[TEST λͺ¨λ“œ] μΈμ¦μ½”λ“œ: {}", authCode);

} else { //μ‹€μ œ μ‘΄μž¬ν•˜λŠ” 이메일이라면

try {
//μ‹€μ œ 인증 μ½”λ“œκ°€ λ‹΄κΈ΄ 이메일 전솑
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(toEmail);
message.setSubject("whereyouad νšŒμ›κ°€μž… 인증번호");
message.setText("인증번호: " + authCode);
message.setFrom(senderEmail);

emailSender.send(message); //λ§Œμ•½ μ‹€μ œ μ‘΄μž¬ν•˜λŠ” 이메일인데 μ‚¬μš©μžκ°€ μ˜€νƒ€λ₯Ό λƒˆλ‹€λ©΄
} catch (MailException e) { //μ˜ˆμ™Έ λ°œμƒ
throw new UserSignUpException(UserErrorCode.USER_EMAIL_NOT_VALID); //톡합 응닡 처리 μ˜ˆμ™Έλ‘œ λ°˜ν™˜
}

}

//Redis에 μ €μž₯ (Key: "CODE:이메일", Value: "123456", μœ νš¨μ‹œκ°„: 300초(5λΆ„))
//ν…ŒμŠ€νŠΈ 계정도 인증은 ν•΄μ•Όν•˜λ‹ˆ Redis 에 μ½”λ“œκ°€ μ €μž₯ λ˜μ–΄μ•Ό 함.
//ν…ŒμŠ€νŠΈ κ³„μ •μ˜ 인증은 μ„œλ²„ 둜그λ₯Ό 톡해 μΈμ¦μ½”λ“œλ₯Ό μ–»μ–΄ μž…λ ₯.
redisUtil.setDataExpire("CODE:" + toEmail, authCode, 60 * 5L);

return new EmailSentResponse("μΈμ¦μ½”λ“œλ₯Ό μ΄λ©”μΌλ‘œ μ „μ†‘ν–ˆμŠ΅λ‹ˆλ‹€.", toEmail, 300L);
}

//μΈμ¦μ½”λ“œ 검증 λ©”μ„œλ“œ
public void verifyEmailCode(String email, String inputCode) {
//ν•΄λ‹Ή 이메일 κ°’μœΌλ‘œ Redis μ—μ„œ 쑰회
String key = "CODE:" + email;
String savedCode = redisUtil.getData(key);

//λ§Œμ•½ μΈμ¦μ½”λ“œκ°€ μ—†κ±°λ‚˜ 잘λͺ» μž…λ ₯ν–ˆλ‹€λ©΄,
if (savedCode == null || !savedCode.equals(inputCode)) {
throw new UserSignUpException(UserErrorCode.USER_EMAIL_AUTH_INVALID); //μ˜ˆμ™Έ λ°œμƒ(BAD_REQUEST)
}

//μ •μƒμ μœΌλ‘œ μΈμ¦μ½”λ“œλ₯Ό μž…λ ₯ν–ˆλ‹€λ©΄,
//κΈ°μ‘΄ Redis 에 μ €μž₯된 데이터λ₯Ό μ§€μš°κ³ ,
redisUtil.deleteData(key);
//"ν•΄λ‹Ή 이메일이 μ •μƒμ μœΌλ‘œ μΈμ¦λ˜μ—ˆλ‹€" λŠ” 값을 λ‹€μ‹œ Redis 에 μ €μž₯ -> 이후 νšŒμ›κ°€μž…(signup) λ‚΄λΆ€ λ‘œμ§μ—μ„œ ν™œμš©
redisUtil.setDataExpire("VERIFIED:" + email, "TRUE", 60 * 60L); //1μ‹œκ°„ λ’€ Expire
}

//ν…ŒμŠ€νŠΈμš© 이메일은, "test" 둜 μ‹œμž‘ν•˜κ±°λ‚˜, "example.com" 으둜 λλ‚˜μ•Ό ν•œλ‹€.
private boolean isTestEmail(String email) {
return email.startsWith("test") || email.endsWith("example.com");
}

//λ¬΄μž‘μœ„ μΈμ¦μ½”λ“œ κ°’ 생성
private String createCode() {
return String.valueOf((int)(Math.random() * (900000)) + 100000);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
import com.whereyouad.WhereYouAd.domains.user.persistence.entity.User;
import com.whereyouad.WhereYouAd.domains.user.persistence.repository.UserRepository;
import com.whereyouad.WhereYouAd.global.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
Expand All @@ -20,13 +21,23 @@ public class UserService {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final RedisUtil redisUtil;

//νšŒμ›κ°€μž… λ©”μ„œλ“œ
public SignUpResponse signUpUser(SignUpRequest request) {
if (userRepository.existsByEmail(request.email())) { //이미 μ΄λ©”μΌλ‘œ λ§Œλ“  계정이 μ‘΄μž¬ν•  μ‹œ
throw new UserSignUpException(UserErrorCode.USER_EMAIL_DUPLICATE); //이메일 쀑볡 μ˜ˆμ™Έ
}

//μΆ”κ°€ : 이메일 μΈμ¦λ˜μ—ˆλŠ”μ§€ 확인 -> μ•…μ˜μ  κ³΅κ²©μžκ°€ 이메일 인증을 κ±΄λ„ˆλ›°κ³  νšŒμ›κ°€μž… URL λ“±μœΌλ‘œ λ°”λ‘œ 듀어왔을 경우
//Redis 에 ν•΄λ‹Ή 이메일 인증이 μ™„λ£Œλ˜μ—ˆλŠ”μ§€ 값을 꺼내보기
String isEmailVerified = redisUtil.getData("VERIFIED:" + request.email());

//인증이 μ•ˆλ˜μ—ˆλ‹€λ©΄,
if (isEmailVerified == null || !isEmailVerified.equals("TRUE")) {
throw new UserSignUpException(UserErrorCode.USER_EMAIL_NOT_VERIFIED); //μ˜ˆμ™Έ λ°œμƒ(UNAUTHORIZED)
}

//λΉ„λ°€λ²ˆν˜Έ μ•”ν˜Έν™” -> SecurityConfig 클래슀 λ‚΄ μ—μ„œ BCryptPasswordEncoder λ₯Ό Bean λ“±λ‘ν•œκ±°λ‘œ μ‚¬μš©
String encodedPwd = passwordEncoder.encode(request.password());

Expand All @@ -38,7 +49,7 @@ public SignUpResponse signUpUser(SignUpRequest request) {
.phoneNumber(request.phoneNumber())
.profileImageUrl(null)
.status(UserStatus.ACTIVE)
.isEmailVerified(false)
.isEmailVerified(true) //νšŒμ›κ°€μž… ν•˜λŠ” μ‚¬μš©μžλŠ” λͺ¨λ‘ 이메일 인증 μ™„λ£Œλœ 것
.build();

User savedUser = userRepository.save(user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
@Getter
@AllArgsConstructor
public enum UserErrorCode implements BaseErrorCode {
USER_EMAIL_DUPLICATE(HttpStatus.BAD_REQUEST, "USER_400_2", "이미 μ‚¬μš©μ€‘μΈ 이메일 μž…λ‹ˆλ‹€."),
USER_EMAIL_DUPLICATE(HttpStatus.BAD_REQUEST, "USER_400_1", "이미 μ‚¬μš©μ€‘μΈ 이메일 μž…λ‹ˆλ‹€."),
USER_EMAIL_NOT_VALID(HttpStatus.BAD_REQUEST, "USER_400_2", "ν•΄λ‹Ή μ΄λ©”μΌλ‘œ 메일 전솑에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€."),
USER_EMAIL_AUTH_INVALID(HttpStatus.BAD_REQUEST, "USER_400_3", "인증 μ½”λ“œκ°€ μ˜¬λ°”λ₯΄μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."),

USER_EMAIL_NOT_VERIFIED(HttpStatus.UNAUTHORIZED, "USER_401_1", "이메일 인증이 μ§„ν–‰λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.whereyouad.WhereYouAd.domains.user.presentation;

import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.EmailSentResponse;
import com.whereyouad.WhereYouAd.domains.user.domain.service.EmailService;
import com.whereyouad.WhereYouAd.domains.user.domain.service.UserService;
import com.whereyouad.WhereYouAd.domains.user.application.dto.request.SignUpRequest;
import com.whereyouad.WhereYouAd.domains.user.application.dto.response.SignUpResponse;
Expand All @@ -19,14 +22,30 @@
public class UserController implements UserControllerDocs {

private final UserService userService;
private final EmailService emailService;

@PostMapping("/signup")
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request)
{
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request) {
SignUpResponse signUpResponse = userService.signUpUser(request);
return ResponseEntity.ok(
DataResponse.created(signUpResponse)
);
}

@PostMapping("/email-send")
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmail(@RequestBody @Valid EmailRequest.Send request) {
EmailSentResponse emailSentResponse = emailService.sendEmail(request.email());
return ResponseEntity.ok(
DataResponse.from(emailSentResponse)
);
}

@PostMapping("/email-verify")
public ResponseEntity<DataResponse<String>> verifyEmail(@RequestBody @Valid EmailRequest.Verify request) {
emailService.verifyEmailCode(request.email(), request.authCode());

return ResponseEntity.ok(
DataResponse.from("이메일 인증이 μ„±κ³΅μ μœΌλ‘œ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.whereyouad.WhereYouAd.domains.user.presentation.docs;

import com.whereyouad.WhereYouAd.domains.user.application.dto.request.EmailRequest;
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.SignUpResponse;
import com.whereyouad.WhereYouAd.global.response.DataResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -13,11 +15,32 @@
public interface UserControllerDocs {
@Operation(
summary = "λ‹¨μˆœ νšŒμ›κ°€μž… API",
description = "이메일, λΉ„λ°€λ²ˆν˜Έ, 이름, μ „ν™”λ²ˆν˜Έλ₯Ό λ°›μ•„ νšŒμ›κ°€μž…μ„ μ§„ν–‰ν•©λ‹ˆλ‹€(이메일 인증 λ―Έμ§„ν–‰μƒνƒœ)"
description = "이메일, λΉ„λ°€λ²ˆν˜Έ, 이름, μ „ν™”λ²ˆν˜Έλ₯Ό λ°›μ•„ νšŒμ›κ°€μž…μ„ μ§„ν–‰ν•©λ‹ˆλ‹€(λ¨Όμ € 이메일 인증이 μ§„ν–‰λ˜μ–΄μ•Ό νšŒμ›κ°€μž… κ°€λŠ₯)"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성곡"),
@ApiResponse(responseCode = "400_2", description = "이메일 쀑볡 νšŒμ› 쑴재")
})
public ResponseEntity<DataResponse<SignUpResponse>> signUp(@RequestBody @Valid SignUpRequest request);

@Operation(
summary = "이메일 μΈμ¦μ½”λ“œ 전솑 API",
description = "μž…λ ₯받은 μ΄λ©”μΌλ‘œ μΈμ¦μ½”λ“œλ₯Ό μ „μ†‘ν•©λ‹ˆλ‹€. μΈμ¦μ½”λ“œ μž¬μ „μ†‘λ„ ν•΄λ‹Ή API λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.\n\n" +
"ν…ŒμŠ€νŠΈμš© 이메일은 'test' 둜 μ‹œμž‘ν•˜κ±°λ‚˜ 'example.com' 으둜 λλ‚˜μ•Όν•©λ‹ˆλ‹€. ν…ŒμŠ€νŠΈμš© μ΄λ©”μΌμ˜ μΈμ¦μ½”λ“œλŠ” μ„œλ²„ 둜그둜 확인 κ°€λŠ₯ν•©λ‹ˆλ‹€."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성곡"),
@ApiResponse(responseCode = "400_3", description = "이메일 μ „μ†‘μ‹€νŒ¨(이메일 μ˜€νƒ€ λ“±)")
})
public ResponseEntity<DataResponse<EmailSentResponse>> sendEmail(@RequestBody @Valid EmailRequest.Send request);

@Operation(
summary = "이메일 μΈμ¦μ½”λ“œ 인증 API",
description = "이메일과 μΈμ¦μ½”λ“œλ₯Ό λ°›μ•„ μΈμ¦μ½”λ“œκ°€ λ§žλŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성곡"),
@ApiResponse(responseCode = "400_4", description = "μ‹€νŒ¨(μΈμ¦μ½”λ“œ 뢈일치)")
})
public ResponseEntity<DataResponse<String>> verifyEmail(@RequestBody @Valid EmailRequest.Verify request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll() //swagger μ ‘κ·Ό ν—ˆμš©
.requestMatchers("/api/users/signup", "/api/auth/**").permitAll() //둜그인, νšŒμ›κ°€μž… μ ‘κ·Ό ν—ˆμš©
.requestMatchers("/api/users/**", "/api/auth/**").permitAll() //둜그인, νšŒμ›κ°€μž…, 이메일 인증 μ ‘κ·Ό ν—ˆμš©
.anyRequest().authenticated() //이외 접근은 인증 ν•„μš”
)
//Spring Security 의 κΈ°λ³Έ UsernamePasswordAuthenticationFilter μ•žμ— JwtAuthenticationFilter 등둝
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.whereyouad.WhereYouAd.global.utils;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.Duration;

@Service
@RequiredArgsConstructor
public class RedisUtil {

private final StringRedisTemplate template;

//Redis 에 데이터 μ €μž₯(μœ νš¨μ‹œκ°„ μ„€μ •)
public void setDataExpire(String key, String value, long duration) {
ValueOperations<String, String> valueOperations = template.opsForValue();
Duration expireDuration = Duration.ofSeconds(duration);
valueOperations.set(key, value, expireDuration);
}

//Redis μ—μ„œ 데이터 κΊΌλ‚΄κΈ°(Value κΊΌλ‚΄κΈ°)
public String getData(String key) {
ValueOperations<String, String> valueOperations = template.opsForValue();
return valueOperations.get(key);
}

//Redis μ—μ„œ 데이터 μ§€μš°κΈ°(key κ°’ 기반)
public void deleteData(String key) {
template.delete(key);
}
}
Loading