저번에 username추가 하고 프로필 조회, 수정, 비밀번호 변경을 구현했다.
유저 엔티티다
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "user")
public class User {
@Id
private Long id;
private String email;
private String password;
private String intro;
private String nickname;
private int status;
private Long kakaoId;
// 상태: 인증 전, 인증 완료, 탈퇴
public enum Status {
ACTIVATE, DEACTIVATE
}
@Builder
public User(Long id, String email, String password, String intro, String nickname, Status status, Long kakaoId) {
this.id = id;
this.email = email;
this.password = password;
this.intro = intro;
this.nickname = nickname;
this.status = status == null ? Status.ACTIVATE.ordinal() : status.ordinal();
this.kakaoId = kakaoId; // kakaoId 필드 초기화
}
public void updateKakaoId(Long kakaoId) {
this.kakaoId = kakaoId;
}
public boolean checkActivate() {
return this.status == Status.ACTIVATE.ordinal();
}
public void updateUsername(String nickname) {
this.nickname = nickname;
}
public void updateIntro(String intro) {
this.intro = intro;
}
public void updatePassword(String password) {
this.password = password;
}
}
dto다
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class ChangePasswordRequestDto {
private String password;
private String newPassword;
}
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class UpdateProfileRequestDto {
private String username;
private String intro;
}
컨트롤러다
@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
public class UserController {
private final UserFacade userFacade;
@PostMapping("/signup")
public Mono<ResponseEntity<UserResponseDto>> signup(@RequestBody UserRequestDto req) {
return userFacade.signup(req).map(ResponseEntity::ok);
}
@PostMapping("activate/{code}")
public Mono<ResponseEntity<String>> activate(@PathVariable("code") int code, @RequestBody VerificationRequest req) {
return userFacade.verifyCode(code, req)
.then(Mono.just(ResponseEntity.ok("계정 활성화가 완료되었습니다.")));
}
@GetMapping("/test")
public Mono<?> test(@AuthenticationPrincipal UserPrincipal userPrincipal) {
return Mono.just(userPrincipal.getUser().getId());
}
@GetMapping("/profile")
public Mono<ResponseEntity<UserResponseDto>> getProfile(@AuthenticationPrincipal UserPrincipal userPrincipal) {
return userFacade.findUserById(userPrincipal.getUser().getId())
.map(UserResponseDto::new)
.map(ResponseEntity::ok);
}
@PutMapping("/profile")
public Mono<ResponseEntity<UserResponseDto>> updateProfile(@AuthenticationPrincipal UserPrincipal userPrincipal,
@RequestBody UpdateProfileRequestDto req) {
return userFacade.updateProfile(userPrincipal.getUser().getId(), req)
.map(UserResponseDto::new)
.map(ResponseEntity::ok);
}
@PutMapping("/password")
public Mono<ResponseEntity<String>> changePassword(@AuthenticationPrincipal UserPrincipal userPrincipal,
@RequestBody ChangePasswordRequestDto req) {
return userFacade.changePassword(userPrincipal.getUser().getId(), req)
.then(Mono.just(ResponseEntity.ok(UserSuccessCode.PASSWORD_CHANGE_SUCCESS.getMsg())));
}
}
userservice이다
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
protected Mono<User> signup(UserRequestDto req) {
return checkDuplicateEmail(req.getEmail())
.subscribeOn(Schedulers.boundedElastic())
.then(Mono.just(User.builder()
.email(req.getEmail())
.password(passwordEncoder.encode(req.getPassword()))
.intro(req.getIntro())
.username(req.getUsername())
.status(User.Status.TEMPORARY)
.build())
)
.flatMap(userRepository::save)
.doOnError(error -> System.err.println("Error: " + error.getMessage()));
}
protected Mono<User> save(User user) {
return userRepository.save(user);
}
protected Mono<User> findByUserById(Long id) {
return null;
}
protected Mono<User> activateUserStatus(User user) {
user.activateStatus();
return userRepository.save(user);
}
public Mono<Void> checkVerificationCodeAndActivateUser(int code, String email) {
return findByEmail(email)
.filter(user -> user.checkVerificationCode(code))
.switchIfEmpty(Mono.error(new CustomException(UserErrorCode.INCORRECT_VERIFICATION_NUMBER)))
.flatMap(this::activateUserStatus).then();
}
protected Mono<User> findByEmail(String email) {
return userRepository.findByEmail(email)
.switchIfEmpty(Mono.error(new CustomException(UserErrorCode.USER_NOT_FOUND)));
}
protected Mono<Boolean> existsByEmail(String email) {
return userRepository.existsByEmail(email);
}
protected Mono<Void> checkDuplicateEmail(String email) {
return existsByEmail(email)
.filter(isDuplicated -> !isDuplicated)
.switchIfEmpty(Mono.error(new CustomException(UserErrorCode.ALREADY_EXIST_EMAIL)))
.then();
}
public Mono<User> findById(Long id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new CustomException(UserErrorCode.USER_NOT_FOUND)));
}
public Mono<User> updateProfile(Long userId, UpdateProfileRequestDto req) {
return findById(userId)
.flatMap(user -> {
user.updateUsername(req.getUsername());
user.updateIntro(req.getIntro());
return save(user);
});
}
public Mono<Void> changePassword(Long userId, ChangePasswordRequestDto req, PasswordEncoder passwordEncoder) {
return findById(userId)
.flatMap(user -> {
if (!passwordEncoder.matches(req.getPassword(), user.getPassword())) {
return Mono.error(new CustomException(UserErrorCode.PASSWORD_NOT_MATCH));
}
user.updatePassword(passwordEncoder.encode(req.getNewPassword()));
return save(user);
})
.then();
}
}
userfacade
@RequiredArgsConstructor
@Component
public class UserFacade {
private final UserService userService;
private final MailService mailService;
private final PasswordEncoder passwordEncoder;
public Mono<UserResponseDto> signup(UserRequestDto req) {
return userService.signup(req)
.publishOn(Schedulers.boundedElastic())
.doOnNext(user -> {
Mail mail = Mail.builder()
.to(req.getEmail())
.subject("계정 확인 메일")
.body(Mail.createSignupMailBody(String.valueOf(user.getVerificationCode())))
.build();
mailService.sendMail(mail).subscribe(data -> System.out.println("메일 전송 완료"));
})
.map(UserResponseDto::new);
}
public Mono<Void> verifyCode(int code, VerificationRequest req) {
return userService.checkVerificationCodeAndActivateUser(code, req.getEmail());
}
public Mono<User> findUserByEmail(String email) {
return userService.findByEmail(email);
}
public Mono<User> findUserById(Long id) {
return userService.findById(id);
}
public Mono<User> updateProfile(Long userId, UpdateProfileRequestDto req) {
return userService.updateProfile(userId, req);
}
public Mono<Void> changePassword(Long userId, ChangePasswordRequestDto req) {
return userService.changePassword(userId, req, passwordEncoder)
.then();
}
}
usererrorcode이다
@Getter
@AllArgsConstructor
public enum UserErrorCode implements BaseCode {
INCORRECT_VERIFICATION_NUMBER(HttpStatus.BAD_REQUEST, 100, "인증번호가 올바르지 않습니다.", "인증번호가 올바르지 않습니다."),
USER_NOT_FOUND(HttpStatus.BAD_REQUEST, 101, "해당하는 유저를 찾을 수 없습니다.", "해당하는 이메일을 찾을 수 없습니다."),
ALREADY_EXIST_EMAIL(HttpStatus.BAD_REQUEST, 102, "이미 존재하는 이메일입니다.", "이미 존재하는 이메일입니다."),
ALREADY_ACCOUNT_ACTIVATED(HttpStatus.BAD_REQUEST, 103, "이미 인증 완료된 계정입니다.", "이미 활성화 완료된 계정입니다."),
PASSWORD_NOT_MATCH(HttpStatus.BAD_REQUEST, 104, "패스워드가 일치하지 않습니다.", "패스워드가 일치하지 않습니다."),
;
private final HttpStatus httpStatus;
private final int code;
private final String msg;
private final String remark;
@Override
public CommonReason getCommonReason() {
return CommonReason.builder()
.status(httpStatus)
.code(code)
.msg(msg)
.build();
}
}
usersuccesscode이다.
@Getter
@AllArgsConstructor
public enum UserSuccessCode implements BaseCode {
PASSWORD_CHANGE_SUCCESS(HttpStatus.OK, 200, "비밀번호가 성공적으로 변경되었습니다.", "비밀번호 변경 성공");
private final HttpStatus httpStatus;
private final int code;
private final String msg;
private final String remark;
@Override
public CommonReason getCommonReason() {
return CommonReason.builder()
.status(httpStatus)
.code(code)
.msg(msg)
.build();
}
}
깃허브 이슈! pr!
'TIL' 카테고리의 다른 글
TIL - 2024/08/01 (0) | 2024.08.10 |
---|---|
TIL - 2024/07/31 (0) | 2024.08.05 |
TIL - 2024/07/29 (0) | 2024.08.05 |
TIL - 2024/07/28 (0) | 2024.08.02 |
TIL - 2024/07/27 (0) | 2024.07.31 |