Skip to content

Commit

Permalink
Bp 24 implement user crud (#9)
Browse files Browse the repository at this point in the history
* Bp 25 implement user registration (#6)

* feat : 회원가입 api 권한 수정, 엔드포인트 prefix 메소드 추가

* feat : 에러코드, 커스텀 에러 추가, 전역 핸들러에 로직구현

* feat : 회원가입 DTO추가, 멤버 컨트롤러 추가

* feat : 멤버 서비스 추가, 회원가입 기능 추가

* test : 컨트롤러 테스트, 유닛테스트 작성

* feat : 유저 비밀번호 암호화 추가

* rename : 파일 위치 수정

* test : 암호화 로직 추가

* rename : DTO 컨트롤러 패키지로 이동

* refactor : 매직넘버 삭제, HttpStatus사용

* refactor : 기존 ErrorCode를 인터페이스화, 도메인마다 에러코드 정의

* refactor : 에러코드 구조 변경에 따른 코드 수정

* chore : spring-validation 추가

* Bp 26 implement user withdraw (#8)

* feat : 객체 생성 시 필드 체크를 위한 Validator생성

* feat : 멤버 엔티티객체 필드 기본값 수정, 탈퇴로직 구현

* feat : 회원 탈퇴 로직 구현

* feat : 시큐리티 회원가입, 로그인 csrf해제

* feat : 시큐리티 유틸성 클래스 추가

* feat : 멤버 서비스 예외 추가와 에러코드 추가

* style : 코드 포맷 수정

* test: 테스트코드 수정, 삭제 테스트코드 작성

* feat : Spring Validation에서 발생하는 에러 핸들링

* test : mock user 어노테이션추가

* refactor : 코드리뷰 적용

* fix ; csrf 비활성화

* feat : 식별자 전략 명시 IDENTITY
  • Loading branch information
ekgns33 authored Jul 22, 2024
1 parent abf9384 commit 38547a6
Show file tree
Hide file tree
Showing 31 changed files with 772 additions and 112 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security:2.3.3.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
@SpringBootApplication
public class PlatformCoreApplication {

public static void main(String[] args) {
SpringApplication.run(PlatformCoreApplication.class, args);
System.out.println("TESTCITEST");
}
public static void main(String[] args) {
SpringApplication.run(PlatformCoreApplication.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@

import com.fasterxml.jackson.databind.ObjectMapper;

import gdsc.konkuk.platformcore.global.exceptions.ErrorCode;
import gdsc.konkuk.platformcore.application.member.exceptions.MemberErrorCode;
import gdsc.konkuk.platformcore.global.responses.ErrorResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
Expand All @@ -22,10 +21,11 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF
private final ObjectMapper objectMapper;

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws
IOException, ServletException {
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws
IOException {

ErrorResponse errorResponse = ErrorResponse.of(ErrorCode.INVALID_USER_INFO);
ErrorResponse errorResponse = ErrorResponse.of(MemberErrorCode.INVALID_USER_INFO);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationS
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {

setDefaultTargetUrl("/");
SavedRequest savedRequest = requestCache.getRequest(request, response);

if(savedRequest != null){
if (savedRequest != null) {
String targetUrl = savedRequest.getRedirectUrl();
redirectStrategy.sendRedirect(request, response, targetUrl);
}else {
} else {
redirectStrategy.sendRedirect(request, response, getDefaultTargetUrl());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public String getPassword() {

@Override
public String getUsername() {
return member.getMemberId();
return Long.toString(member.getId());
}

@Override
public boolean isAccountNonExpired() {
return member.isActivated();
return member.isMemberActivated();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import gdsc.konkuk.platformcore.application.auth.exceptions.InvalidUserInfoException;
import gdsc.konkuk.platformcore.application.member.exceptions.MemberErrorCode;
import gdsc.konkuk.platformcore.domain.member.entity.Member;
import gdsc.konkuk.platformcore.domain.member.repository.MemberRepository;
import gdsc.konkuk.platformcore.global.exceptions.ErrorCode;
import gdsc.konkuk.platformcore.global.exceptions.BusinessException;
import lombok.RequiredArgsConstructor;

@Service
Expand All @@ -19,10 +19,8 @@ public class CustomUserDetailsService implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String memberId) throws UsernameNotFoundException {

Member member = memberRepository.findByMemberId(memberId)
.orElseThrow(()-> InvalidUserInfoException.of(ErrorCode.USER_NOT_FOUND));

.orElseThrow(() -> BusinessException.of(MemberErrorCode.USER_NOT_FOUND));
return new CustomUserDetails(member);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package gdsc.konkuk.platformcore.application.member;

import java.util.Optional;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import gdsc.konkuk.platformcore.application.member.exceptions.MemberErrorCode;
import gdsc.konkuk.platformcore.application.member.exceptions.UserAlreadyExistException;
import gdsc.konkuk.platformcore.application.member.exceptions.UserNotFoundException;
import gdsc.konkuk.platformcore.controller.member.MemberRegisterRequest;
import gdsc.konkuk.platformcore.domain.member.entity.Member;
import gdsc.konkuk.platformcore.domain.member.repository.MemberRepository;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberService {

private final PasswordEncoder passwordEncoder;
private final MemberRepository memberRepository;

@Transactional
public Member register(MemberRegisterRequest registerRequest) {

if (checkMemberExistWithMemberId(registerRequest.getMemberId())) {
throw UserAlreadyExistException.of(MemberErrorCode.USER_ALREADY_EXISTS);
}

String encodedPassword = passwordEncoder.encode(registerRequest.getPassword());
registerRequest.setPassword(encodedPassword);

return memberRepository.save(MemberRegisterRequest.toEntity(registerRequest));
}

@Transactional
public void withdraw(Long currentId) {
Member member = memberRepository.findById(currentId)
.orElseThrow(() -> UserNotFoundException.of(MemberErrorCode.USER_NOT_FOUND));
member.withdraw();
}

private boolean checkMemberExistWithMemberId(String memberId) {
Optional<Member> member = memberRepository.findByMemberId(memberId);
return member.isPresent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package gdsc.konkuk.platformcore.application.member.exceptions;

import gdsc.konkuk.platformcore.global.exceptions.CustomErrorCode;
import lombok.Getter;

@Getter
public enum MemberErrorCode implements CustomErrorCode {

USER_NOT_FOUND("사용자가 존재하지 않습니다. 다시 입력해주세요", "[ERROR] : 사용자 정보를 찾을 수 없음"),
INVALID_USER_INFO("사용자 정보가 올바르지 않습니다. 다시 입력해주세요", "[ERROR] : 사용자 정보가 올바르지 않음"),
DEACTIVATED_USER("비활성화된 사용자입니다. 다시 확인해주세요", "[ERROR] : 비활성화된 사용자"),
USER_ALREADY_EXISTS("이미 존재하는 사용자입니다.", "[ERROR] : 이미 존재하는 사용자"),
USER_ALREADY_DELETED("이미 삭제된 사용자입니다.", "[ERROR] : 이미 삭제된 사용자");

private final String message;
private final String logMessage;

MemberErrorCode(String message, String logMessage) {
this.message = message;
this.logMessage = logMessage;
}

@Override
public String getLogMessage() {
return this.logMessage;
}

@Override
public String getName() {
return this.name();
}

@Override
public String getMessage() {
return this.message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gdsc.konkuk.platformcore.application.member.exceptions;

import gdsc.konkuk.platformcore.global.exceptions.BusinessException;
import gdsc.konkuk.platformcore.global.exceptions.CustomErrorCode;

public class UserAlreadyDeletedException extends BusinessException {

protected UserAlreadyDeletedException(CustomErrorCode errorCode,
String logMessage) {
super(errorCode, logMessage);
}

public static UserAlreadyDeletedException of(CustomErrorCode errorCode) {
return new UserAlreadyDeletedException(errorCode, errorCode.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gdsc.konkuk.platformcore.application.member.exceptions;

import gdsc.konkuk.platformcore.global.exceptions.BusinessException;
import gdsc.konkuk.platformcore.global.exceptions.CustomErrorCode;

public class UserAlreadyExistException extends BusinessException {

protected UserAlreadyExistException(CustomErrorCode errorCode, String logMessage) {
super(errorCode, logMessage);
}

public static UserAlreadyExistException of(CustomErrorCode errorCode) {
return new UserAlreadyExistException(errorCode, errorCode.getLogMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package gdsc.konkuk.platformcore.application.member.exceptions;

import gdsc.konkuk.platformcore.global.exceptions.BusinessException;
import gdsc.konkuk.platformcore.global.exceptions.CustomErrorCode;

public class UserNotFoundException extends BusinessException {

protected UserNotFoundException(CustomErrorCode errorCode,
String logMessage) {
super(errorCode, logMessage);
}

public static UserNotFoundException of(CustomErrorCode errorCode) {
return new UserNotFoundException(errorCode, errorCode.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package gdsc.konkuk.platformcore.controller.member;

import java.net.URI;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import gdsc.konkuk.platformcore.application.member.MemberService;
import gdsc.konkuk.platformcore.domain.member.entity.Member;
import gdsc.konkuk.platformcore.global.utils.SecurityUtils;
import gdsc.konkuk.platformcore.global.responses.Response;
import gdsc.konkuk.platformcore.global.responses.SuccessResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/v1/members")
@RequiredArgsConstructor
public class MemberController {

private final MemberService memberService;

@PostMapping()
public ResponseEntity<Response> signup(@RequestBody @Valid MemberRegisterRequest registerRequest) {
Member registeredMember = memberService.register(registerRequest);
return ResponseEntity.created(getCreatedURI(registeredMember.getId())).body(SuccessResponse.messageOnly());
}

@DeleteMapping()
public ResponseEntity<Response> withdraw() {
Long currentId = SecurityUtils.getCurrentUserId();
memberService.withdraw(currentId);
return ResponseEntity.ok(SuccessResponse.messageOnly());
}

private URI getCreatedURI(Long memberId) {
return ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(memberId)
.toUri();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package gdsc.konkuk.platformcore.controller.member;

import gdsc.konkuk.platformcore.domain.member.entity.Member;
import jakarta.validation.constraints.NotEmpty;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Builder
public class MemberRegisterRequest {
@NotEmpty
private String memberId;
@NotEmpty
private String password;
@NotEmpty
private String name;
@NotEmpty
private String email;
private int batch;

public static Member toEntity(MemberRegisterRequest request) {
return Member.builder()
.memberId(request.getMemberId())
.password(request.getPassword())
.name(request.getName())
.email(request.getEmail())
.batch(request.getBatch())
.build();
}
}
Loading

0 comments on commit 38547a6

Please sign in to comment.