User관련 CRUD 만들기
- 조건
- username:
최소 4자 이상, 10자 이하이며 알파벳 소문자(a~z), 숫자(0~9)
- password:
최소 8자 이상, 15자 이하이며 알파벳 대소문자(a~z, A~Z), 숫자(0~9), 특수문자
- 회원가입 기능
- 성공
- DB에 중복된
username이 없다면 회원을 저장한다.
- 클라이언트에 성공 메시지와 상태코드를 반환한다.
- 응답은 content-type application/json 형식입니다.
- 회원 권한 부여
- ADMIN
- USER
- 본인이 작성한 게시글, 댓글에 한하여 수정과 삭제 가능
- ⚠️ 필수 예외처리
- DB에 중복된
username이 이미 존재하는 경우
username,password 조건에 맞지 않는 경우
- 비밀번호 수정 조건
- 비밀번호 수정 시, 본인 확인을 위해 현재 비밀번호를 입력하여 올바른 경우에만 수정할 수 있습니다.
- 비밀번호는 현재 비밀번호 및 최근 사용한 세 개의 비밀번호와 다르게 설정해야 합니다.
Controller
@RequiredArgsConstructor
@RequestMapping("/api/user")
@RestController
public class UserController {
private final UserService userService;
@PostMapping("/sign-up")
public ResponseEntity<String> signUp(@RequestBody UserDto userDto , UserRoleEnum userRoleEnum) {
return userService.signUp(userDto, userRoleEnum);
}
@GetMapping("/{userId}")
public ResponseEntity<ProfileDto> getProfile(@PathVariable Long userId) {
return userService.getProfile(userId);
}
@PatchMapping("/{userId}")
public ResponseEntity<String> updateProfile(@PathVariable("userId") Long userId,
CustomUserDetails user ,ProfileDto profileDto) {
return userService.updateProfile(userId,user.getUser(),profileDto);
}
@PostMapping("/{userId}/sign-out")
public ResponseEntity<String> signOut(@PathVariable("userId") Long userId, CustomUserDetails user) {
return userService.signOut(userId, user.getUser());
}
}
Entity
User
@Getter
@Entity
@NoArgsConstructor
@Table(name = "users")
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(unique = true, nullable = false)
private String password;
@Column(nullable = false)
private String nickname;
private String userinfo;
@Column(nullable = false)
private UserStatusEnum status = UserStatusEnum.ACTIVE;
@Column(nullable = false)
private UserRoleEnum role;
private String refreshToken;
@ElementCollection(fetch = FetchType.LAZY)
private List<String> deniedPassword = new ArrayList<>(3);
@Column
private boolean expired = false;
public User(String username, String password, String nickname, String userinfo, UserRoleEnum role) {
this.username = username;
this.password = password;
this.nickname = nickname;
this.userinfo = userinfo;
this.role = role;
}
// 프로필 저장
public void updateProfile(ProfileDto profileDto) {
this.password = profileDto.getPassword();
this.nickname = profileDto.getNickname();
this.userinfo = profileDto.getUserinfo();
setDeniedPassword(profileDto.getPassword());
}
//최근 변경한 비밀번호 저장
private void setDeniedPassword(String password){
if(deniedPassword.size() > 2){
deniedPassword.remove(0);
}
deniedPassword.add(password);
}
//삭제처리 soft delete
public void deleteUser() {
this.status = UserStatusEnum.DENIED;
}
}
Timestamped
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
// 상속받는 클래스에 모두 creatAt, modifiedAt
@CreatedDate
// updatable = 생성 후 변경 불가 여부
@Column(updatable = false, nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createdAt;
@Column
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime deletedAt;
// 탈퇴를 할 때 값 저장.
protected void setDeletedAt(LocalDateTime now) {
this.deletedAt = now;
}
}
Enum
Role
public enum UserRoleEnum {
USER("user"),
ADMIN("admin");
private final String userRole;
UserRoleEnum(String value) {
this.userRole = value;
}
}
Status
public enum UserStatusEnum {
ACTIVE("Active"),
DENIED("Denied");
private final String userStatus;
UserStatusEnum(String userStatus) {
this.userStatus = userStatus;
}
}
Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findById(Long userId);
}
DTO
ProfileDTO
@Getter
public class ProfileDto {
@NotBlank(message = "필수로 입력해야됩니다.")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-z0-9](?=.*[!@#$%^&*()]).{8,15}$" , message = "비밀번호는 특수문자포함 최소 8자, 최대 15자입니다.")
private String password;
@NotBlank(message = "필수로 입력해야됩니다.")
private String nickname;
private String userinfo;
public ProfileDto(String nickname, String userinfo) {
this.nickname = nickname;
this.userinfo = userinfo;
}
public ProfileDto(String nickname, String userinfo, String password) {
this.nickname = nickname;
this.userinfo = userinfo;
this.password = password;
}
}
UserDTO
@Getter
public class UserDto {
@NotBlank(message = "필수로 입력해야됩니다.")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[0-9])[a-z0-9]{4,10}$" , message = "아이디는 최소 4자, 최대 10자입니다.")
private String username;
@NotBlank(message = "필수로 입력해야됩니다.")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-z0-9](?=.*[!@#$%^&*()]).{8,15}$" , message = "비밀번호는 특수문자포함 최소 8자, 최대 15자입니다.")
private String password;
@NotBlank(message = "필수로 입력해야됩니다.")
private String nickname;
private String userinfo;
private UserRoleEnum role;
}