Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User JPA 적용, BindException 처리 #26

Merged
merged 6 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.bbaemin.config.advice;

import org.bbaemin.config.response.ApiResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;

@RestControllerAdvice
yeonkyungJoo marked this conversation as resolved.
Show resolved Hide resolved
public class ControllerExceptionAdvice {

@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
BindingResult bindingResult = e.getBindingResult();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
return ApiResult.badRequest(fieldErrors);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package org.bbaemin.config.response;


import lombok.Getter;
import org.springframework.http.HttpStatus;

import static org.bbaemin.config.response.ApiResult.ResultCode.CREATED;
import static org.bbaemin.config.response.ApiResult.ResultCode.FAIL;
import static org.bbaemin.config.response.ApiResult.ResultCode.SUCCESS;

@Getter
public class ApiResult<T> {

private static final ApiResult<Void> OK = new ApiResult<>(SUCCESS);

enum ResultCode {
SUCCESS(200), FAIL(500);
SUCCESS(200),
CREATED(201),
FAIL(500);

private int code;

Expand All @@ -20,6 +25,7 @@ enum ResultCode {
}
}

@Getter
public static class Error<R> {
private HttpStatus httpStatus;
private R cause;
Expand Down Expand Up @@ -58,6 +64,10 @@ public static ApiResult<Void> ok() {
return ApiResult.OK;
}

public static <T> ApiResult<T> created(T result) {
return new ApiResult<>(CREATED, result);
}

public static <T> ApiResult<T> ok(T result) {
return new ApiResult<>(SUCCESS, result);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package org.bbaemin.user.controller;

import lombok.RequiredArgsConstructor;
import org.bbaemin.config.response.ApiResult;
import org.bbaemin.user.controller.request.JoinRequest;
import org.bbaemin.user.controller.request.UpdateUserInfoRequest;
import org.bbaemin.user.controller.response.UserResponse;
import org.bbaemin.user.service.UserService;
import org.bbaemin.user.vo.User;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 java.util.List;
import java.util.stream.Collectors;
Expand All @@ -20,21 +28,22 @@ public class UserController {

// 회원 리스트
@GetMapping
public List<UserResponse> listUser() {
return userService.getUserList().stream()
public ApiResult<List<UserResponse>> listUser() {
yeonkyungJoo marked this conversation as resolved.
Show resolved Hide resolved
List<UserResponse> userList = userService.getUserList().stream()
.map(UserResponse::new).collect(Collectors.toList());
return ApiResult.ok(userList);
}

// 회원 조회
@GetMapping("/{userId}")
public UserResponse getUser(@PathVariable Long userId) {
public ApiResult<UserResponse> getUser(@PathVariable Long userId) {
User user = userService.getUser(userId);
return new UserResponse(user);
return ApiResult.ok(new UserResponse(user));
}

// 회원 등록
@PostMapping
public UserResponse join(@RequestBody JoinRequest joinRequest) {
public ApiResult<UserResponse> join(@Validated @RequestBody JoinRequest joinRequest) {
User user = userService.join(
User.builder()
.email(joinRequest.getEmail())
Expand All @@ -43,25 +52,23 @@ public UserResponse join(@RequestBody JoinRequest joinRequest) {
.password(joinRequest.getPassword())
.phoneNumber(joinRequest.getPhoneNumber())
.build());
return new UserResponse(user);
return ApiResult.created(new UserResponse(user));
}

// 회원정보 수정
@PutMapping("/{userId}")
public UserResponse updateUserInfo(@PathVariable Long userId, @RequestBody UpdateUserInfoRequest updateUserInfoRequest) {
User user = userService.updateUserInfo(
User.builder()
.userId(userId)
.nickname(updateUserInfoRequest.getNickname())
.image(updateUserInfoRequest.getImage())
.phoneNumber(updateUserInfoRequest.getPhoneNumber())
.build());
return new UserResponse(user);
@PatchMapping("/{userId}")
public ApiResult<UserResponse> updateUserInfo(@PathVariable Long userId, @Validated @RequestBody UpdateUserInfoRequest updateUserInfoRequest) {
User user = userService.updateUserInfo(userId,
updateUserInfoRequest.getNickname(),
updateUserInfoRequest.getImage(),
updateUserInfoRequest.getPhoneNumber());
return ApiResult.ok(new UserResponse(user));
}

// 회원 탈퇴
@PatchMapping("/{userId}")
public void quit(@PathVariable Long userId) {
@PatchMapping("/{userId}/quit")
public ApiResult<Void> quit(@PathVariable Long userId) {
userService.quit(userId);
return ApiResult.ok();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@
import lombok.Builder;
import lombok.Getter;

import javax.validation.constraints.NotBlank;

@Getter
public class JoinRequest {

@NotBlank
private String email;
@NotBlank
private String nickname;
private String image;

// TODO - validation check : password.equals(passwordConfirm)
@NotBlank
private String password;
private String passwordConfirm;

@NotBlank
private String phoneNumber;

@Builder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import lombok.Builder;
import lombok.Getter;

import javax.validation.constraints.NotBlank;

@Getter
public class UpdateUserInfoRequest {

@NotBlank
private String nickname;
private String image;
@NotBlank
private String phoneNumber;

@Builder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.bbaemin.user.controller.response;

import lombok.Getter;
import org.bbaemin.user.vo.User;

@Getter
public class UserResponse {

private Long userId;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,68 +1,12 @@
package org.bbaemin.user.repository;

import org.bbaemin.user.vo.User;
import org.springframework.stereotype.Component;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Transactional(readOnly = true)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

@Component
public class UserRepository {

private static final Map<Long, User> map = new ConcurrentHashMap<>();
private static final AtomicLong id = new AtomicLong(0L);

public static AtomicLong getId() {
return id;
}

static {
map.put(getId().incrementAndGet(), User.builder()
.userId(getId().get())
.email("user1@email.com")
.nickname("user1")
.image(null)
.phoneNumber("010-1234-5678")
.build());
map.put(getId().incrementAndGet(), User.builder()
.userId(getId().get())
.email("user2@email.com")
.nickname("user2")
.image(null)
.phoneNumber("010-1111-2222")
.build());
}

public static void clear() {
map.clear();
}

public List<User> findAll() {
return new ArrayList<>(map.values());
}

public User findById(Long userId) {
return map.get(userId);
}

public User insert(User user) {
Long userId = getId().incrementAndGet();
user.setUserId(userId);
map.put(userId, user);
return user;
}

public User update(User user) {
map.put(user.getUserId(), user);
return user;
}

public void updateUserDeleted(Long userId) {
User user = map.get(userId);
user.setDeleted(true);
map.put(userId, user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
import org.bbaemin.user.repository.UserRepository;
import org.bbaemin.user.vo.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.NoSuchElementException;

@Transactional(readOnly = true)
@Service
@RequiredArgsConstructor
public class UserService {
Expand All @@ -18,19 +22,31 @@ public List<User> getUserList() {
}

public User getUser(Long userId) {
return userRepository.findById(userId);
return userRepository.findById(userId)
.orElseThrow(() -> new NoSuchElementException("userId : " + userId));
}

@Transactional
public User join(User user) {
return userRepository.insert(user);
return userRepository.save(user);
}

public User updateUserInfo(User user) {
return userRepository.update(user);
@Transactional
public User updateUserInfo(Long userId, String nickname, String image, String phoneNumber) {
// TODO - CHECK : update할 컬럼을 명시적으로 나타내주는 게 좋은가? 변경사항이 많은 경우에는?
// updateUserInfo(User user) vs updateUserInfo(Long userId, String nickname, String image, String phoneNumber)
User user = getUser(userId);
user.setNickname(nickname);
user.setImage(image);
user.setPhoneNumber(phoneNumber);
return user;
}

@Transactional
public void quit(Long userId) {
userRepository.updateUserDeleted(userId);
User user = getUser(userId);
user.setDeleted(true);
user.setDeletedAt(LocalDateTime.now());
}

}
39 changes: 37 additions & 2 deletions bbaemin-api/src/main/java/org/bbaemin/user/vo/User.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
package org.bbaemin.user.vo;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.time.LocalDateTime;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;

@Column(nullable = false, unique = true, updatable = false)
private String email;

@Column(nullable = false, unique = true)
private String nickname;
private String image;

@Column(nullable = false)
private String password;

@Column(nullable = false)
private String phoneNumber;

@Column(nullable = false, columnDefinition = "boolean default false")
private boolean deleted = false;
private LocalDateTime deletedAt;

@Builder
private User(Long userId, String email, String nickname, String image, String password, String phoneNumber) {
Expand All @@ -25,11 +48,23 @@ private User(Long userId, String email, String nickname, String image, String pa
this.phoneNumber = phoneNumber;
}

public void setUserId(Long userId) {
this.userId = userId;
public void setNickname(String nickname) {
this.nickname = nickname;
}

public void setImage(String image) {
this.image = image;
}

public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

public void setDeleted(boolean deleted) {
this.deleted = deleted;
}

public void setDeletedAt(LocalDateTime deletedAt) {
this.deletedAt = deletedAt;
}
}
Loading