사용자 관리 - MonewLabs/monew-web GitHub Wiki
- 사용자 관리 기능은 이메일, 닉네임, 비밀번호 기반으로 사용자 등록 및 관리를 지원하는 기능입니다.
- 닉네임 수정, 논리/물리 삭제, 로그인 처리까지 포함하며, 각 유효성 검사 및 예외 처리 로직이 적용됩니다.
- 회원가입 시 중복 이메일 체크, 로그인 시 사용자 인증 실패/성공 처리 등이 포함됩니다.
- 모든 요청은 로그인한 사용자에 의해 수행되며, 사용자 ID는
MoNew-Request-User-ID
헤더를 통해 전달됩니다.
Method | Endpoint | 설명 |
---|---|---|
POST | /api/users | 회원가입 |
POST | /api/users/login | 로그인 |
PATCH | /api/users/{userId} | 닉네임 수정 |
DELETE | /api/users/{userId} | 사용자 논리 삭제 |
DELETE | /api/users/{userId}/hard | 사용자 물리 삭제 |
-
작업 요약:
-
UserRegisterRequest
DTO 설계 (record
기반)-
이메일:
@Email
,@NotBlank
→ 중복 불가, 형식 유효성 검증 -
닉네임:
@Size(min=1, max=20)
,@NotBlank
-
비밀번호:
@Size(min=6, max=20)
,@NotBlank
-
이메일:
-
UserDto
응답 DTO 설계 (record
)-
id
,email
,nickname
,createdAt
-
- 사용자 등록 API 구현 (
POST /api/users
)- Controller → Service → Repository → Entity 저장
- 등록된 사용자 정보 반환
- 이메일 중복 체크 로직
-
UserRepository.existsByEmail(String email)
사용
-
- 예외 처리:
-
400 Bad Request
: 입력값 오류 →MethodArgumentNotValidException
처리 -
409 Conflict
: 이메일 중복 →CustomException(ErrorCode.CONFLICT, detail, ExceptionType.USER)
-
500
: 서버 오류 → 전역 처리
-
- 예외 응답 포맷 (
ErrorResponse
):
{ "timestamp": "2025-05-30T08:20:42.123Z", "code": "CONFLICT", "message": "리소스 충돌이 발생했습니다.", "details": { "reason": "EMAIL", "field": "email", "value": "[email protected]" }, "exceptionType": "userException", "status": 409 }
- 사용 클래스:
- DTO:
UserRegisterRequest
,UserDto
- Entity:
User
- Repository:
UserRepository
- Mapper:
UserMapper
- Service:
UserService.register()
,UserServiceImpl
- Controller:
UserController.register()
- Exception 관련:
CustomException
,ErrorDetail
,ExceptionType.USER
,ErrorCode.CONFLICT
- DTO:
-
-
작업 요약:
-
UserLoginRequest
DTO 설계 (record
기반)-
이메일:
@Email
,@NotBlank
-
비밀번호:
@NotBlank
-
이메일:
-
UserDto
응답 DTO 설계 (record
)-
id
,email
,nickname
,createdAt
-
- 사용자 로그인 API 구현 (
POST /api/users/login
)- Controller → Service →
CustomUserDetailsService
통해 사용자 조회 - 수동 인증 처리:
UsernamePasswordAuthenticationToken
,AuthenticationManager
사용 - 인증 성공 시:
-
SecurityContext
에 사용자 저장 - 세션 생성 (
HttpServletRequest.getSession(true)
)
-
- 사용자 정보 DTO로 응답 반환
- Controller → Service →
-
비밀번호 보안 강화:
BCryptPasswordEncoder
적용- 회원가입 시: 입력된 평문 비밀번호를 해싱하여 DB에 저장
- 로그인 시: 입력된 평문 비밀번호와 해싱된 DB 비밀번호를
matches()
로 비교 - 관련 클래스 변경:
-
SecurityConfig
:PasswordEncoder
Bean 등록 -
UserServiceImpl
: 비밀번호 저장 및 검증 방식 변경
-
- 인증 정보 확인용 커스텀 필터 추가 (
MoNewUserValidationFilter
)- 요청 필터링 시 SecurityContext로부터 인증된 사용자 정보 확인
- 인증 여부, 세션 사용자 ID, 요청 헤더 사용자 ID를 비교해 일치하지 않을 경우 예외 발생
- 프론트 CORS 설정 이슈 해결
-
axios.create({ withCredentials: true })
적용 - 서버 CORS 설정:
CorsFilter
→ 이후 제거 가능
-
- 예외 처리:
-
401 Unauthorized
: 잘못된 로그인 →BadCredentialsException
,CustomException
으로 래핑 -
403 Forbidden
: 사용자 불일치 →CustomException(ErrorCode.FORBIDDEN, detail, ExceptionType.AUTH)
-
500
: 서버 오류 → 전역 처리
-
- 예외 응답 포맷 (
ErrorResponse
):
{ "timestamp": "2025-06-13T01:08:59.000Z", "code": "FORBIDDEN", "message": "접근 권한이 없습니다.", "details": { "reason": "USER_ID_MISMATCH", "field": "userId", "value": "abc-uuid" }, "exceptionType": "authException", "status": 403 }
-
-
사용 클래스:
- DTO:
UserLoginRequest
,UserDto
- Entity:
User
- Repository:
UserRepository
- 인증 관련:
CustomUserDetailsService
,AuthenticationManager
,SecurityContextHolder
- 인증 필터:
MoNewUserValidationFilter
- Service:
UserService.login()
,UserServiceImpl
- Controller:
UserController.login()
- 보안 관련:
BCryptPasswordEncoder
,PasswordEncoder
- Exception 관련:
CustomException
,ErrorDetail
,ExceptionType.AUTH
,ErrorCode.FORBIDDEN
- DTO:
-
작업 요약:
-
UserUpdateRequest
DTO 설계 (record
기반)-
닉네임:
@Size(min=1, max=20)
,@NotBlank
적용
-
닉네임:
- 사용자 닉네임 수정 API 구현 (
PATCH /api/users/{userId}
)- 요청 헤더:
MoNew-Request-User-ID
로 요청자 식별 - 요청 바디: 닉네임 수정 요청 DTO (
UserUpdateRequest
)
- 요청 헤더:
- 닉네임 변경 처리
-
UserService.updateNickname()
내에서user.updateNickname()
호출 - 영속성 컨텍스트 변경 감지(Dirty Checking)를 통해 자동 반영
-
- 본인 확인 로직
-
userId != requesterId
인 경우403 Forbidden
예외 발생
-
- 예외 처리:
-
400 Bad Request
: 입력값 유효성 실패 →MethodArgumentNotValidException
-
403 Forbidden
: 요청자 ≠ 수정 대상 사용자 →CustomException(ErrorCode.FORBIDDEN)
-
404 Not Found
: 대상 사용자 없음 →CustomException(ErrorCode.RESOURCE_NOT_FOUND)
-
- 사용 클래스:
- DTO:
UserUpdateRequest
,UserDto
- Entity:
User
(updateNickname()
메서드 포함) - Service:
UserService.updateNickname()
,UserServiceImpl
- Controller:
UserController.updateNickname()
- Exception 관련:
CustomException
,ErrorDetail
,ExceptionType.USER
,ErrorCode.FORBIDDEN
,RESOURCE_NOT_FOUND
- DTO:
-
-
작업 요약:
- 사용자 삭제 기능 구현 (논리 삭제 기본, 물리 삭제 선택적)
- 요청자는 반드시 본인이어야 하며, 권한 확인 필수
- 삭제 시 연관된 도메인(댓글, 좋아요, 알림 등)까지 함께 처리
-
논리 삭제 처리:
- API:
DELETE /api/users/{userId}
-
User.deleted
필드를true
로 변경하여 논리 삭제 -
@Where(clause = "deleted = false")
적용으로, 이후 모든 조회에서 제외 - 본인만 삭제 가능 (
requesterId
와userId
비교하여 검증)
- API:
-
물리 삭제 처리:
- API:
DELETE /api/users/{userId}/hard
- 연관 정보 전부 삭제 (JPA cascade 활용)
-
@OneToMany(mappedBy = "user", cascade = REMOVE, orphanRemoval = true)
로 자동 삭제 처리 - 연관 대상:
Comment
,CommentLike
,ArticleView
,Subscription
,Notification
-
-
Activity
는@OneToOne
+@MapsId
구조이므로 cascade가 적용되지 않아,ActivityRepository.deleteByUser()
를 통해 수동 삭제 - 마지막에
userRepository.delete(user)
호출
- API:
-
예외 처리:
-
403 Forbidden
: 요청자와 대상이 일치하지 않는 경우 -
404 Not Found
: 존재하지 않는 사용자 -
500
: 서버 오류 → 전역 예외 처리기로 대응
-
-
사용 클래스:
- Entity:
User
(삭제 관련 연관관계 설정 포함),Activity
- Service:
UserService.deleteUser()
,UserService.deleteUserHard()
,UserServiceImpl
- Controller:
UserController.deleteUser()
,UserController.deleteUserHard()
- Repository:
UserRepository
,ActivityRepository.deleteByUser()
- Exception 관련:
CustomException
,ErrorDetail
,ExceptionType.USER
,ErrorCode.FORBIDDEN
,RESOURCE_NOT_FOUND
- Entity:
-
공통 에러 응답 구조:
ErrorResponse
{
"timestamp": "2025-04-23T05:39:06.152068Z",
"status": 400,
"message": "잘못된 요청입니다.",
"details": "닉네임은 필수입니다."
}
- 댓글 관리, 알림 기능 등은 사용자 ID를 기반으로 작동하며, 사용자가 삭제될 경우 후속 처리를 함께 고려해야 함.
-
로그인 이후 모든 API 호출 시
MoNew-Request-User-ID
헤더가 필요하며, 인증 관련 처리는 공통 인터셉터 또는 필터에서 관리됨.