Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f4b1a87
#35 feat: ErrorCode 작성
Etwashoeren Mar 18, 2025
ff96d7b
#35 feat: ErrorResponse 작성
Etwashoeren Mar 18, 2025
ee29e14
#35 feat: CustomException 작성
Etwashoeren Mar 18, 2025
b3e1c1c
#35 fix: 유효성 관련 에러 메시지 수정
Etwashoeren Mar 18, 2025
ba2e624
#35 feat: 전역 예외처리 기능 추가
Etwashoeren Mar 18, 2025
d62ae95
#35 fix: 토큰 생성 시 사용자 role 값이 들어가도록 수정
Etwashoeren Mar 18, 2025
689bd95
#35 feat: 토큰 발급 및 반환 로직 수정
Etwashoeren Mar 18, 2025
957a279
#35 feat: 로그인 로직 변경
Etwashoeren Mar 18, 2025
bdd04fb
#35 refactor: 이메일 인증 확인 로직 위치 변경 및 MemberService 로직 변경
Etwashoeren Mar 18, 2025
abce718
#35 refactor: 비밀번호 재설정 이메일 인증 확인 컨트롤러 로직 변경
Etwashoeren Mar 18, 2025
5e8e761
#35 feat: 아이디 중복 체크 유효성 추가
Etwashoeren Mar 18, 2025
ade524e
#35 refactor: 사용자 아이디 중복 체크 로직 변경
Etwashoeren Mar 18, 2025
b160d54
#35 feat: 응답 템플릿 생성자 추가
Etwashoeren Mar 18, 2025
1ff00d1
#35 refactor: 사용자 아이디 중복 체크 컨트롤러 로직 변경
Etwashoeren Mar 18, 2025
a2f39ee
#35 refactor: 회원 가입 로직 수정
Etwashoeren Mar 18, 2025
59bf030
#35 refactor: 회원가입 반환 값 수정
Etwashoeren Mar 18, 2025
9f8fb4f
#35 refactor: 이메일 전송 로직 수정
Etwashoeren Mar 18, 2025
ac0e526
#35 refactor: 이메일 전송 예외처리 수정
Etwashoeren Mar 18, 2025
08a219b
#35 refactor: 이메일 인증 번호 확인 예외처리 변경
Etwashoeren Mar 18, 2025
8da0ee7
#35 refactor: 인증번호 확인 메소드 로직 변경
Etwashoeren Mar 18, 2025
722327b
#35 refactor: 비밀번호 재설정 이메일 인증 확인 컨드롤러 반환 값 변경
Etwashoeren Mar 18, 2025
c3f1e5a
#35 feat: 회원 가입 시 이메일 인증 확인 기능 추가
Etwashoeren Mar 18, 2025
3d24d7c
#35 refactor: 비밀번호 재설정 이메일 인증 확인 컨트롤러 반환 타입 변경
Etwashoeren Mar 18, 2025
c680aa8
#35 refactor: 인증 메일 전송 서비스 로직 변경
Etwashoeren Mar 18, 2025
bb310c8
#35 refactor: 비밀번호 재설정용 이메일 전송 로직 변경 및 예외처리 변경
Etwashoeren Mar 18, 2025
fd75902
#35 refactor: 각 컨트롤러 반환 값 변경
Etwashoeren Mar 18, 2025
f26671f
#35 refactor: 견적서 서비스 예외처리 로직 변경
Etwashoeren Mar 18, 2025
9878bc8
#35 refactor: 견적서 컨트롤러 반환 값 변경
Etwashoeren Mar 18, 2025
eb73c90
#35 feat: Swagger 관련 설정 추가
Etwashoeren Mar 18, 2025
72c948c
#35 refactor: 필드 주입 방식 생성자 주입 방식으로 변경
Etwashoeren Mar 18, 2025
4d2b5b2
#35 feat: Swagger 관련 설명 추가
Etwashoeren Mar 18, 2025
5e25922
#35 feat: Swagger 관련 설명 추가
Etwashoeren Mar 18, 2025
70d4add
#35 feat. 로그인 DTO 유효성 추가
Etwashoeren Mar 18, 2025
adac0de
Merge branch 'develop' into feature/#35-API-Response
rimeir Mar 19, 2025
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
Expand Up @@ -4,15 +4,16 @@
import com.postdm.backend.domain.auth.dto.IdCheckRequestDto;
import com.postdm.backend.domain.auth.dto.SignInRequestDto;
import com.postdm.backend.domain.auth.dto.SignUpRequestDto;
import com.postdm.backend.domain.member.domain.entity.Member;
import com.postdm.backend.domain.email.application.EmailService;
import com.postdm.backend.domain.email.dto.CheckCertificationRequestDto;
import com.postdm.backend.global.jwt.dto.TokenInfo;
import com.postdm.backend.global.template.ResponseTemplate;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -24,37 +25,54 @@
@RequestMapping("/api/v1/auth")
public class AuthController { // 로그인 및 회원 가입 컨트롤러

@Autowired
private AuthService authService;
private final AuthService authService;

@Operation(summary = "아이디 중복 확인 컨트롤러")
private final EmailService emailService;

public AuthController(AuthService authService, EmailService emailService) {
this.authService = authService;
this.emailService = emailService;
}

@Operation(summary = "아이디 중복 확인 컨트롤러", description = "아이디 중복 확인을 요청하는 컨트롤러 입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/id-check") // 아이디 중복 확인 요청
public ResponseTemplate<String> idCheck(@RequestBody IdCheckRequestDto idCheckRequestDto) {
String username = authService.idCheck(idCheckRequestDto.getUsername());
return new ResponseTemplate<>(HttpStatus.OK, "사용할 수 있는 아이디 입니다.", username);
public ResponseTemplate<?> idCheck(@RequestBody @Valid IdCheckRequestDto idCheckRequestDto) {
authService.idCheck(idCheckRequestDto.getUsername());
return new ResponseTemplate<>(HttpStatus.OK, "사용할 수 있는 아이디 입니다.");
}

@Operation(summary = "이메일 인증 확인 컨트롤러", description = "이메일 인증 번호 확인을 요청하는 컨트롤러 입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/check-certification")
public ResponseTemplate<?> checkCertificationNumber(@RequestBody @Valid CheckCertificationRequestDto checkCertificationRequestDto) {
emailService.checkCertificationNumber(checkCertificationRequestDto);

return new ResponseTemplate<>(HttpStatus.OK, "이메일 인증 성공");
}

@Operation(summary = "회원가입 컨트롤러")
@Operation(summary = "회원가입 컨트롤러", description = "회원가입을 요청하는 컨트롤러 입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/sign-up") // 회원 가입 요청
public ResponseTemplate<Member> signUp(@RequestBody @Valid SignUpRequestDto signUpRequestDto) {
Member member = authService.signUp(signUpRequestDto);
public ResponseTemplate<?> signUp(@RequestBody @Valid SignUpRequestDto signUpRequestDto) {
authService.signUp(signUpRequestDto);

return new ResponseTemplate<>(HttpStatus.OK, "회원가입 성공", member);
return new ResponseTemplate<>(HttpStatus.OK, "회원가입 성공");
}

@Operation(summary = "로그인 컨트롤러")
@Operation(summary = "로그인 컨트롤러", description = "로그인을 요청하는 컨트롤러 입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/sign-in") // 로그인 요청
public ResponseTemplate<String> signIn(@RequestBody @Valid SignInRequestDto signInRequestDto, HttpServletResponse response) {
String token = authService.signIn(signInRequestDto, response);
public ResponseTemplate<TokenInfo> signIn(@RequestBody @Valid SignInRequestDto signInRequestDto, HttpServletResponse response) {
TokenInfo token = authService.signIn(signInRequestDto, response);

return new ResponseTemplate<>(HttpStatus.OK, "로그인 성공", token);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,62 @@
import com.postdm.backend.domain.member.domain.entity.Member;
import com.postdm.backend.domain.member.domain.entity.MemberRole;
import com.postdm.backend.domain.member.domain.repository.MemberRepository;
import com.postdm.backend.global.common.exception.CustomException;
import com.postdm.backend.global.common.response.ErrorCode;
import com.postdm.backend.global.jwt.dto.TokenInfo;
import com.postdm.backend.global.jwt.util.JwtProvider;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class AuthService { // 로그인 및 회원가입 서비스

@Autowired
private MemberRepository memberRepository;

@Autowired
private CertificationRepository certificationRepository;

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Autowired
private JwtProvider jwtProvider;
private final MemberRepository memberRepository;
private final CertificationRepository certificationRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final JwtProvider jwtProvider;
private final int refreshedMS;

// 생성자 주입 방식
public AuthService(
MemberRepository memberRepository,
CertificationRepository certificationRepository,
BCryptPasswordEncoder bCryptPasswordEncoder,
JwtProvider jwtProvider,
@Value("${jwt.expiredMS}") int refreshedMS) {
this.memberRepository = memberRepository;
this.certificationRepository = certificationRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.jwtProvider = jwtProvider;
this.refreshedMS = refreshedMS;
}

@Value("${jwt.expiredMS}")
private int refreshedMS;

public String idCheck(String username) { // 아이디 중복확인 서비스
public void idCheck(String username) { // 아이디 중복확인 서비스
boolean existedUsername = memberRepository.existsByUsername(username); // 데이터베이스에서 사용자 아이디가 존재하는지 여부
if(existedUsername) {
throw new IllegalArgumentException("이미 사용중인 아이디 입니다.");
throw new CustomException(ErrorCode.DUPLICATED_ID);
}
return username;
}

public Member signUp(SignUpRequestDto signUpRequestDto) { // 회원가입 서비스
public void signUp(SignUpRequestDto signUpRequestDto) { // 회원가입 서비스
String nickname = signUpRequestDto.getNickname();

String username = signUpRequestDto.getUsername();
boolean existedUsername = memberRepository.existsByUsername(username); // 데이터베이스에서 사용자 아이디가 존재하는지 여부

if (existedUsername) {
throw new IllegalArgumentException("이미 사용중인 아이디 입니다.");
throw new CustomException(ErrorCode.DUPLICATED_ID);
}

String password = signUpRequestDto.getPassword();
String confirmPassword = signUpRequestDto.getConfirmPassword();

if(!password.equals(confirmPassword)) { // 입력한 비밀번호 일치 여부
throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
throw new CustomException(ErrorCode.NOT_MATCHED_PASSWORD);
}

String encodedPassword = bCryptPasswordEncoder.encode(password); // 비밀번호 암호화
Expand All @@ -66,7 +72,7 @@ public Member signUp(SignUpRequestDto signUpRequestDto) { // 회원가입 서비
boolean existedEmail = memberRepository.existsByEmail(email); // 데이터베이스에서 사용자 이메일이 존재하는지 여부

if (existedEmail) {
throw new IllegalArgumentException("이미 사용중인 이메일 입니다.");
throw new CustomException(ErrorCode.DUPLICATED_EMAIL);
}

CertificationEntity certificationEntity = certificationRepository.findByUsername(username); // 데이터베이스에서 사용자 이름으로 된 인증 번호 조회
Expand All @@ -78,7 +84,7 @@ public Member signUp(SignUpRequestDto signUpRequestDto) { // 회원가입 서비
boolean isMatched = certificationEntity.getEmail().equals(email) && bCryptPasswordEncoder.matches(certificationNumber, encodedCertificationNumber);

if(!isMatched) {
throw new IllegalArgumentException("인증번호가 일치하지 않습니다.");
throw new CustomException(ErrorCode.CERTIFICATION_FAILED);
}

String phone = signUpRequestDto.getPhone();
Expand All @@ -94,34 +100,33 @@ public Member signUp(SignUpRequestDto signUpRequestDto) { // 회원가입 서비
memberRepository.save(member); // 데이터베이스에 멤버 저장

certificationRepository.delete(certificationEntity); // 회원가입이 완료되면 데이터베이스에서 해당 인증번호 삭제

return member;
}

public String signIn(SignInRequestDto signInRequestDto, HttpServletResponse response) { // 로그인 서비스
public TokenInfo signIn(SignInRequestDto signInRequestDto, HttpServletResponse response) { // 로그인 서비스
String username = signInRequestDto.getUsername();

Member member = memberRepository.findByUsername(username);
if(member == null) {
throw new IllegalArgumentException("아이디 또는 비밀번호가 잘못되었습니다.");
throw new CustomException(ErrorCode.MEMBER_NOT_FOUND);
}

String password = signInRequestDto.getPassword();
String encodedPassword = member.getPassword();

boolean isMatched = bCryptPasswordEncoder.matches(password, encodedPassword);
if(!isMatched) {
throw new IllegalArgumentException("아이디 또는 비밀번호가 잘못되었습니다.");
throw new CustomException(ErrorCode.SIGN_IN_FAILED);
}

String role = member.getRole().name();

TokenInfo token = jwtProvider.generateToken(username, role); // 로그인이 완료되면 토큰 생성
String refreshToken = token.getRefreshToken();
String refreshToken = jwtProvider.generateRefreshToken(username, role);

response.addCookie(createCookie("Refresh", refreshToken)); // 쿠키에 refresh 토큰 담음

return token.getAccessToken(); // 응답 body에는 access 토큰 반환

return token; // 응답 body에는 access 토큰 반환
}

private Cookie createCookie(String name, String value) { // 쿠키 생성 메소드
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -12,6 +13,8 @@
@NoArgsConstructor
public class IdCheckRequestDto { // 아이디 중복 확인 데이터 전송을 위한 DTO

@Schema(description = "사용자 아이디", example = "test123")
@NotBlank
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z\\d]{7,15}$") // 영문자, 숫자를 포함한 7글자 이상 15글자 이하
private String username;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package com.postdm.backend.domain.auth.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Schema(description = "로그인 DTO")
@Data
public class SignInRequestDto { // 로그인 데이터 전송을 위한 DTO

@Schema(description = "사용자 아이디", example = "test123")
@NotBlank
private String username;

@Schema(description = "사용자 비밀번호", example = "test123")
@NotBlank
private String password;

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,33 @@
@NoArgsConstructor
public class SignUpRequestDto { // 회원가입 데이터 전송을 위한 DTO

@Schema(description = "사용자 이름", example = "홍길동")
@NotBlank
private String nickname;

@Schema(description = "사용자 아이디", example = "test123")
@NotBlank
@Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)[a-zA-Z\\d]{7,15}$") // 영문자, 숫자를 포함한 7글자 이상 15글자 이하
private String username;

@Schema(description = "사용자 비밀번호", example = "test123")
@NotBlank
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@#$%^&+=!]).{8,}$") // 영문자, 특수문자, 숫자를 포함한 최소 8자 이상의 문자
private String password;

@Schema(description = "사용자 비밀번호 확인", example = "test123")
private String confirmPassword;

@Schema(description = "사용자 이메일", example = "test1@test.com")
@NotBlank
@Email
private String email;

@Schema(description = "사용자 전화번호", example = "01012345678")
@NotBlank
private String phone;

@Schema(description = "이메일 인증 번호", example = "0000")
@NotBlank
private String certificationNumber;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -19,28 +18,31 @@
@RequestMapping("/api/v1/email")
public class EmailController { // 이메일 관련 컨트롤러

@Autowired
private EmailService emailService;
private final EmailService emailService;

public EmailController(EmailService emailService) {
this.emailService = emailService;
}

@Operation(summary = "회원가입용 이메일 전송 컨트롤러")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/email-certification") // 이메일 전송 요청 api
public ResponseTemplate<CertificationEntity> emailCertification(@RequestBody @Valid EmailCertificationRequestDto emailCertificationRequestDto) {
CertificationEntity certificationEntity = emailService.emailCertification(emailCertificationRequestDto);
public ResponseTemplate<?> emailCertification(@RequestBody @Valid EmailCertificationRequestDto emailCertificationRequestDto) {
emailService.emailCertification(emailCertificationRequestDto);

return new ResponseTemplate<>(HttpStatus.OK, "이메일이 성공적으로 발송되었습니다.", certificationEntity);
return new ResponseTemplate<>(HttpStatus.OK, "이메일이 성공적으로 발송되었습니다.");
}

@Operation(summary = "비밀번호 재설정용 이메일 전송 컨트롤러")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공"),
})
@PostMapping("/reset-certification")
public ResponseTemplate<CertificationEntity> resetCertification(@RequestBody @Valid EmailCertificationRequestDto emailCertificationRequestDto) {
CertificationEntity certificationEntity = emailService.resetCertification(emailCertificationRequestDto);
public ResponseTemplate<?> resetCertification(@RequestBody @Valid EmailCertificationRequestDto emailCertificationRequestDto) {
emailService.resetCertification(emailCertificationRequestDto);

return new ResponseTemplate<>(HttpStatus.OK, "이메일이 성공적으로 발송되었습니다.", certificationEntity);
return new ResponseTemplate<>(HttpStatus.OK, "이메일이 성공적으로 발송되었습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class EmailProvider { // 이메일 발송을 위한 Provider

private final String SUBJECT = "[포스트 디엠] 인증 메일 입니다.";

public void sendCertificationMail(String email, String certificationNumber) {
public boolean sendCertificationMail(String email, String certificationNumber) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
Expand All @@ -26,8 +26,10 @@ public void sendCertificationMail(String email, String certificationNumber) {
helper.setText(htmlContent, true);

mailSender.send(message);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

Expand Down
Loading