- 일정 작성 기능
할일 제목
,할일 내용
,담당자
,비밀번호
,작성일
을 저장할 수 있습니다.- 저장된 게시글의 정보를 반환 받아 확인할 수 있습니다.
- 반환 받은 게시글의 정보에
비밀번호
는 제외 되어있습니다.- 선택한 일정 조회 기능
- 선택한 일정의 정보를 조회할 수 있습니다.
- 반환 받은 일정 정보에
비밀번호
는 제외 되어있습니다.- 일정 목록 조회 기능
- 등록된 일정 전체를 조회할 수 있습니다.
- 반환 받은 일정 정보에
비밀번호
는 제외 되어있습니다.- 조회된 일정 목록은
작성일
기준 내림차순으로 정렬 되어있습니다.- 선택한 일정 수정 기능
- 선택한 일정의
할일 제목
,할일 내용
,담당자
을 수정할 수 있습니다.
- 서버에 일정 수정을 요청할 때
비밀번호
를 함께 전달합니다.- 선택한 일정의
비밀번호
와 요청할 때 함께 보낸비밀번호
가 일치할 경우에만 수정이 가능합니다.- 수정된 일정의 정보를 반환 받아 확인할 수 있습니다.
- 반환 받은 일정의 정보에
비밀번호
는 제외 되어있습니다.- 선택한 일정 삭제 기능
- 선택한 일정을 삭제할 수 있습니다.
- 서버에 일정 삭제를 요청할 때
비밀번호
를 함께 전달합니다.- 선택한 일정의
비밀번호
와 요청할 때 함께 보낸비밀번호
가 일치할 경우에만 삭제가 가능합니다.
기능 | Method | URL | Request Param | Request Body | Response Body |
---|---|---|---|---|---|
일정 등록 | POST | /todo | - | { 'title' : title, 'contents' : contents, 'username' : username, 'date' : date, 'password' : password } | { 'id' : id, 'title' : title, 'contents' : contents, 'username' : username, 'date' : date, 'password' : password } |
일정 전체 목록 조회 | GET | /todo | - | - | { 'id' : id, 'title' : title, 'contents' : contents, 'username' : username, 'date' : date } , {...} |
일정 조회 | GET | /todo/param | ?'id'=id | - | { 'id' : id, 'title' : title, 'contents' : contents, 'username' : username, 'date' : date } |
일정 수정 | PUT | /todo/param | ?'id'=id&'password'=password | { 'title' : title, 'contents' : contents, 'username' : username } | { 'id' : id, 'title' : title, 'contents' : contents, 'username' : username, 'date' : date } |
일정 삭제 | DELETE | /todo/param | ?'id'=id&'password'=password | - | - |
- 일정의 ID, 제목, 내용, 담당자, 비밀번호, 작성일을 담고있는 클래스
- ID값은 Controller 클래스에서 자동으로 부여하여 고유한 번호로 구현
package com.sparta.todoproject.entity;
import com.sparta.todoproject.dto.TodoCreateRequestDto;
import com.sparta.todoproject.dto.TodoUpdateRequestDto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class Todo {
private Long id;
private String username;
private String title;
private String contents;
private Long password;
private String date;
public Todo(TodoCreateRequestDto requestDto) {
this.username = requestDto.getUsername();
this.title = requestDto.getTitle();
this.contents = requestDto.getContents();
this.password = requestDto.getPassword();
this.date = requestDto.getDate();
}
public void update(TodoUpdateRequestDto requestDto) {
this.title = requestDto.getTitle();
this.contents = requestDto.getContents();
this.username = requestDto.getUsername();
}
}
- User가 등록할 일정의 제목, 내용, 담당자, 비밀번호, 작성일을 담고있는 dto 클래스
package com.sparta.todoproject.dto;
import lombok.Getter;
import java.util.Date;
@Getter
public class TodoCreateRequestDto {
private String title;
private String contents;
private String username;
private String date;
private Long password;
}
- User에게 반환할 일정의 ID, 제목, 내용, 담당자, 작성일을 담고있는 dto 클래스
- 비밀번호는 반환하지 않는다
package com.sparta.todoproject.dto;
import com.sparta.todoproject.entity.Todo;
import lombok.Getter;
@Getter
public class TodoResponseDto{
private Long id;
private String title;
private String contents;
private String username;
private String date;
public TodoResponseDto(Todo todo) {
this.id = todo.getId();
this.title = todo.getTitle();
this.contents = todo.getContents();
this.username = todo.getUsername();
this.date = todo.getDate();
}
}
- User가 수정할 일정의 제목, 내용, 담당자를 담고있는 dto 클래스
package com.sparta.todoproject.dto;
import lombok.Getter;
@Getter
public class TodoUpdateRequestDto {
private String title;
private String contents;
private String username;
}
- User의 요청을 처리하는 Controller 클래스
- 일정에 대한 정보를 key값으로 조회할 수 있도록 Map 으로 구현
- 일정을 입력받을때 자동으로 고유한 ID값을 부여
package com.sparta.todoproject.controller;
import com.sparta.todoproject.dto.TodoCreateRequestDto;
import com.sparta.todoproject.dto.TodoResponseDto;
import com.sparta.todoproject.dto.TodoUpdateRequestDto;
import com.sparta.todoproject.entity.Todo;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@RequestMapping("/todo")
public class TodoController {
private final Map<Long, Todo> todoList = new HashMap<>();
@PostMapping("")
public TodoResponseDto createTodo(@RequestBody TodoCreateRequestDto requestDto){
Todo todo = new Todo(requestDto);
Long maxId = todoList.size() > 0 ? Collections.max(todoList.keySet()) + 1 : 1;
todo.setId(maxId);
todoList.put(todo.getId(), todo);
TodoResponseDto todoResponseDto = new TodoResponseDto(todo);
return todoResponseDto;
}
@GetMapping("")
public List<TodoResponseDto> getAllTodo(){
List<TodoResponseDto> todoResponseDtoList = new ArrayList<>(todoList.values().stream().map(TodoResponseDto::new).toList());
Collections.sort(todoResponseDtoList, (o1, o2) -> o2.getDate().compareTo(o1.getDate()));
return todoResponseDtoList;
}
@GetMapping("/param")
public TodoResponseDto getTodoById(@RequestParam Long id){
if(todoList.containsKey(id)){
Todo todo = todoList.get(id);
TodoResponseDto todoResponseDto = new TodoResponseDto(todo);
return todoResponseDto;
} else {
throw new IllegalArgumentException("존재하지 않는 일정입니다.");
}
}
@PutMapping("/param")
public TodoResponseDto updateTodo(@RequestParam Long id, @RequestParam Long password, @RequestBody TodoUpdateRequestDto requestDto){
if(todoList.containsKey(id)){
Todo todo = todoList.get(id);
if(todo.getPassword().equals(password)){
todo.update(requestDto);
TodoResponseDto todoResponseDto = new TodoResponseDto(todo);
return todoResponseDto;
}
else{
throw new IllegalArgumentException("비밀번호가 다릅니다.");
}
}
else{
throw new IllegalArgumentException("존재하지 않는 일정입니다.");
}
}
@DeleteMapping("/param")
public String deleteTodo(@RequestParam Long id, @RequestParam Long password){
if(todoList.containsKey(id)){
Todo todo = todoList.get(id);
if(todo.getPassword().equals(password)){
todoList.remove(id);
return "삭제되었습니다";
}
else{
throw new IllegalArgumentException("비밀번호가 다릅니다.");
}
}
else{
throw new IllegalArgumentException("존재하지 않는 일정입니다.");
}
}
}
일정 등록( POST
/todo )
일정 전체 목록 조회 ( GET
/todo )
일정 수정 ( PUT
/todo/param?'id'=id&'password'=password)
일정 조회 ( GET
/todo/param?'id'=id )
일정 삭제 ( DELETE
/todo/param?'id'=id&'password'=password)
우려했던것 보다는 스무스하게 잘 진행된거같다.
다만.. 일정 전체 목록을 조회하는 과정에서 작성일 기준으로 내림차순 정렬을 해야하는데,
Collections.sort()
함수를 사용하여 객체를 정렬하려고 했으나, 정렬이 되지 않아서
따로 선언한 Comparator 문제인줄 알고 구글링도 해보고 튜터님께 질의도 했으나
계속 왜인지 모르겠으나 정렬이 되지 않았다.
그렇게 5시간을 붙잡고 매달리다가 다시 다른 튜터님께 질문을 하여 결국 해결했다!
List<TodoResponseDto> todoResponseDtoList = todoList.values().stream().map(TodoResponseDto::new).toList(); Collections.sort(todoResponseDtoList, (o1, o2) -> o2.getDate().compareTo(o1.getDate()));
- 이 코드는 정렬이 작동하지 않지만
List<TodoResponseDto> todoResponseDtoList = new ArrayList<>(todoList.values().stream().map(TodoResponseDto::new).toList()); Collections.sort(todoResponseDtoList, (o1, o2) -> o2.getDate().compareTo(o1.getDate()));
이 코드는 정렬이 정상적으로 작동한다
코드를 보면 두 코드 모두 정렬이 정상적으로 작동할 것 같이 생겼다. 물론 빌드에도 문제가 없었다. 하지만 함수가 호출되면
UnsupportedOperationException
오류가 발생한다.
과연 왜 그럴까?
위의 코드의 경우, todoResponseDtoList 는 Immutable list이기 때문에 정렬이 불가능하다!
반면에, 아래 코드의 todoResponseDtoList 는 mutable list이기 때문에 정렬이 가능하다!
new ArrayList<>를 이용하여 Immutable list를 mutable 하게 만든것이다!
결국 어찌저찌 완성까지 했다. 굉장히 스무스하게 뚝딱뚝딱 만들었는데, 정렬하는쪽에서 오류가 발생하는데 왜 발생하는지 정말 모르겠어서 힘들었다.
돌이켜 생각해보면 Comparator를 만드는게 익숙하지 않아서, Comparator를 잘못 선언한 것으로 섣불리 판단하고 Comparator측면에서만 바라본것 같다!
(사실 방금 이 글을 작성하면서UnsupportedOperationException
를 구글에 검색했더니 1초만에 나와 같은 오류를 겪은 사람의 글을 찾았다... 나는 Comparator문제인줄알고 Exception은 검색해볼 생각조차 못했다)그래도 이러한 경험을 하면서 점점 성장하는게 아니겠는가!
그리고 사실 일정목록을 Database로 구현했다면 이러한 오류도 마주치지 못했을 것이다!
이번 프로젝트는 개인적으로 API를 다루는데에 익숙해지자는 마음으로 Database까지 연동할 생각은 못했는데, 어짜피 서버는 반드시 Database가 연동되어야 하니, Database를 공부하고 시간이 된다면 Map형태가 아닌, Database로 다시 구현해봐야겠다!