사용자 관리 - MonewLabs/monew-web GitHub Wiki

1. 기능 개요

  • 사용자 관리 기능은 이메일, 닉네임, 비밀번호 기반으로 사용자 등록 및 관리를 지원하는 기능입니다.
  • 닉네임 수정, 논리/물리 삭제, 로그인 처리까지 포함하며, 각 유효성 검사 및 예외 처리 로직이 적용됩니다.
  • 회원가입 시 중복 이메일 체크, 로그인 시 사용자 인증 실패/성공 처리 등이 포함됩니다.
  • 모든 요청은 로그인한 사용자에 의해 수행되며, 사용자 ID는 MoNew-Request-User-ID 헤더를 통해 전달됩니다.

2. API 목록

Method Endpoint 설명
POST /api/users 회원가입
POST /api/users/login 로그인
PATCH /api/users/{userId} 닉네임 수정
DELETE /api/users/{userId} 사용자 논리 삭제
DELETE /api/users/{userId}/hard 사용자 물리 삭제

3. 기능별 상세 설계

📌 사용자 회원가입

  • 작업 요약:

    • 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

📌 사용자 로그인

  • 작업 요약:

    • 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로 응답 반환
    • 비밀번호 보안 강화: 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

📌 사용자 닉네임 수정

  • 작업 요약:
    • 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

📌 사용자 삭제 (논리/물리)

  • 작업 요약:
    • 사용자 삭제 기능 구현 (논리 삭제 기본, 물리 삭제 선택적)
    • 요청자는 반드시 본인이어야 하며, 권한 확인 필수
    • 삭제 시 연관된 도메인(댓글, 좋아요, 알림 등)까지 함께 처리
  • 논리 삭제 처리:
    • API: DELETE /api/users/{userId}
    • User.deleted 필드를 true로 변경하여 논리 삭제
    • @Where(clause = "deleted = false") 적용으로, 이후 모든 조회에서 제외
    • 본인만 삭제 가능 (requesterIduserId 비교하여 검증)
  • 물리 삭제 처리:
    • 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) 호출
  • 예외 처리:
    • 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

4. 예외 처리 및 응답 규격

  • 공통 에러 응답 구조: ErrorResponse
{
  "timestamp": "2025-04-23T05:39:06.152068Z",
  "status": 400,
  "message": "잘못된 요청입니다.",
  "details": "닉네임은 필수입니다."
}

5. 연관 기능 참고

  • 댓글 관리, 알림 기능 등은 사용자 ID를 기반으로 작동하며, 사용자가 삭제될 경우 후속 처리를 함께 고려해야 함.
  • 로그인 이후 모든 API 호출 시 MoNew-Request-User-ID 헤더가 필요하며, 인증 관련 처리는 공통 인터셉터 또는 필터에서 관리됨.
⚠️ **GitHub.com Fallback** ⚠️