이번에는 3단계를 마무리 작업했다.
- 5단계 : 유저 CRUD
🔻 조건
- 유저를 저장, 단건 조회, 전체 조회, 수정, 삭제할 수 있습니다.
- 유저는 유저명, 이메일, 작성일, 수정일 필드를 갖고 있습니다.
- 일정은 이제 작성 유저명 필드 대신 유저 고유 식별자 필드를 가집니다.
- 일정을 작성한 유저는 추가로 일정 담당 유저들을 배치할 수 있습니다.
- 유저와 일정은 N:M 관계입니다. (@ManyToMany 사용 금지!)
유저를 관리하기 위해 User 엔티티를 생성하고 user와 todo테이블을 연관관계를 생성하기 위해 중간 테이블을 생성하기 위한 UserTodo 엔티티를 생성한다.
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "user")
public class User extends Timestamped {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false)
private String username;
@Column(name = "email", nullable = false)
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<UserTodo> userTodos = new HashSet<>();
public User(UserRequestDto requestDto) {
this.username = requestDto.getUsername();
this.email = requestDto.getEmail();
}
public void update(UserRequestDto requestDto) {
this.username = requestDto.getUsername();
this.email = requestDto.getEmail();
}
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "user_todo")
public class UserTodo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "todo_id")
private Todo todo;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}
user 테이블에 대한 dto, 3 Layer Architecture를 만든다.
@Getter
public class UserRequestDto {
private String username;
private String email;
}
@Getter
public class UserResponseDto {
private Long id;
private String username;
private String email;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
public UserResponseDto(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.email = user.getEmail();
this.createdAt = user.getCreatedAt();
this.modifiedAt = user.getModifiedAt();
}
}
@RestController
@RequestMapping("/api")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/user")
public ResponseEntity<UserResponseDto> createUser(@RequestBody UserRequestDto userRequestDto) {
return ResponseEntity.status(HttpStatus.CREATED).body(userService.createUser(userRequestDto));
}
@GetMapping("/user/{id}")
public ResponseEntity<UserResponseDto> getUser(@PathVariable Long id) {
return ResponseEntity.status(HttpStatus.OK).body(userService.getUser(id));
}
@GetMapping("/user")
public ResponseEntity<List<UserResponseDto>> getAllUser() {
return ResponseEntity.status(HttpStatus.OK).body(userService.getAllUser());
}
@PutMapping("/user/{id}")
public ResponseEntity<UserResponseDto> updateUser(@PathVariable Long id, @RequestBody UserRequestDto userRequestDto) {
return ResponseEntity.status(HttpStatus.OK).body(userService.updateUser(id, userRequestDto));
}
@DeleteMapping("/user/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
}
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
@Transactional
public UserResponseDto createUser(UserRequestDto userRequestDto) {
User user = new User(userRequestDto);
User newUser = userRepository.save(user);
return new UserResponseDto(newUser);
}
public UserResponseDto getUser(Long id) {
User user = findUser(id);
return new UserResponseDto(user);
}
public List<UserResponseDto> getAllUser() {
List<User> users = userRepository.findAll();
return users.stream().map(UserResponseDto::new).collect(Collectors.toList());
}
@Transactional
public UserResponseDto updateUser(Long id, UserRequestDto userRequestDto) {
User user = findUser(id);
user.update(userRequestDto);
userRepository.save(user);
return new UserResponseDto(user);
}
@Transactional
public void deleteUser(Long id) {
User user = findUser(id);
userRepository.delete(user);
}
private User findUser(Long id) {
return userRepository.findById(id).orElseThrow(() ->
new IllegalArgumentException("선택한 유저가 존재하지 않습니다."));
}
}
public interface UserTodoRepository extends JpaRepository<UserTodo, Long> {
void deleteByTodo(Todo todo);
UserTodo findByTodoAndUser(Todo todo, User user);
}
user, todo와 연결하고 있는 user_todo 테이블에도 등록되기 위하여 repositroy를 만들고, todo와 관련된 코드도 수정한다.
Todo 엔티티에 추가
@OneToMany(mappedBy = "todo", cascade = CascadeType.REMOVE, orphanRemoval = true)
private Set<UserTodo> userTodos = new HashSet<>();
존재하는 일정에 담당 유저 추가하기 위한 메서드 생성
@PostMapping("/todo/{id}/users")
public ResponseEntity<TodoResponseDto> addUsersToTodo(@PathVariable Long id, @RequestBody Map<String, List<Long>> request) {
List<Long> userIds = request.get("userIds");
TodoResponseDto updatedTodo = todoService.addUsersToTodo(id, userIds);
return ResponseEntity.ok(updatedTodo);
}
userTodoRepository에도 저장한다.
@Transactional
public TodoResponseDto createTodo(TodoRequestDto requestDto) {
// User Entity 조회
User user = findUser(requestDto.getUserId());
// RequestDto -> Entity
Todo todo = new Todo(requestDto);
// DB 저장
Todo newTodo = todoRepository.save(todo);
// UserTodo 관계 설정
UserTodo userTodo = new UserTodo();
userTodo.setTodo(newTodo);
userTodo.setUser(user);
userTodoRepository.save(userTodo);
// 추가 담당 유저 설정
for (Long additionalUserId : requestDto.getUserIds()) {
User additionalUser = findUser(additionalUserId);
UserTodo additionalUserTodo = new UserTodo();
additionalUserTodo.setTodo(newTodo);
additionalUserTodo.setUser(additionalUser);
userTodoRepository.save(additionalUserTodo);
}
return new TodoResponseDto(newTodo);
}
@Transactional
public TodoResponseDto addUsersToTodo(Long todoId, List<Long> userIds) {
// 해당 일정 조회
Todo todo = findTodo(todoId);
// 추가 담당 유저 설정
for (Long userId : userIds) {
User user = findUser(userId);
UserTodo userTodo = new UserTodo();
userTodo.setTodo(todo);
userTodo.setUser(user);
// 중복된 담당 유저 추가 방지
if (userTodoRepository.findByTodoAndUser(todo, user) == null) {
userTodoRepository.save(userTodo);
}
}
return new TodoResponseDto(findTodo(todoId));
}
@Getter
public class UserRequestDto {
private String username;
private String email;
}
@Getter
public class UserResponseDto {
private Long id;
private String username;
private String email;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
public UserResponseDto(User user) {
this.id = user.getId();
this.username = user.getUsername();
this.email = user.getEmail();
this.createdAt = user.getCreatedAt();
this.modifiedAt = user.getModifiedAt();
}
}
잘 실행이 된다!