Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7eec086
feat: ErrorCode 정의
jjinwo0 Dec 16, 2023
6f3348b
feat: TodoDto 및 Envelope 정의
jjinwo0 Dec 16, 2023
86a67b6
feat: 전역 예외 처리
jjinwo0 Dec 16, 2023
9d7bfd8
feat: TodoRepository 및 TodoService 생성
jjinwo0 Dec 16, 2023
6fac3f6
feat: Todo 생성 API
jjinwo0 Dec 16, 2023
76a5f2a
feat: 단전조회 & 전체조회 API 생성
jjinwo0 Dec 16, 2023
dc45e96
feat: 수정, 삭제 API 생성
jjinwo0 Dec 16, 2023
03c77a0
refactor: TodoList, TodoRepository, TodoControllerTest 커밋 로그 삭제 후 재작성
jjinwo0 Dec 17, 2023
3301448
refactor: 단건조회 수정
jjinwo0 Dec 16, 2023
3c15a4d
test: 삭제 API 작동 테스트
jjinwo0 Dec 16, 2023
d218f49
refactor: Repository 메서드명 수정
jjinwo0 Dec 16, 2023
e361665
refactor: Envelope 메서드 수정
jjinwo0 Dec 19, 2023
a97e503
refactor: Envelope success 메서드 추가, Controller return type 변경
jjinwo0 Dec 19, 2023
5b2571c
refactor: Envelope fail 메서드 추가, 전역 예외 처리 return type 수정
jjinwo0 Dec 19, 2023
9db357a
refactor: ErrorCode return type 수정
jjinwo0 Dec 19, 2023
1b9bee4
build: MySQL, Spring Data Jpa dependency 추가
jjinwo0 Dec 22, 2023
df7abd6
refactor: TodoRepository 수정
jjinwo0 Dec 22, 2023
50df128
refactor: TodoList 엔티티 id, GeneratedValue 추가
jjinwo0 Dec 22, 2023
7cd4f9c
refactor: TodoService delete 메서드 수정
jjinwo0 Dec 22, 2023
f478dbe
refactor: TodoList Status 컬럼 추가
jjinwo0 Dec 22, 2023
29b5421
refactor: update Status 수정
jjinwo0 Dec 22, 2023
83dbd37
refactor: TodoList Builder 패턴 수정
jjinwo0 Dec 22, 2023
6ebff2e
test: TodoList 객체 생성 수정
jjinwo0 Dec 22, 2023
a2be24f
feat: Member Entity 정의
jjinwo0 Dec 24, 2023
a843165
refactor: 패키지 경로 수정
jjinwo0 Dec 24, 2023
a13e4ad
refactor: Response 객체 status 컬럼 수정
jjinwo0 Dec 24, 2023
e00b7cd
refactor: 패키지 경로 수정
jjinwo0 Dec 24, 2023
5b63bc5
refactor: Member Entity 생성자 어노테이션 추가
jjinwo0 Dec 24, 2023
fa46e03
test: test 임시 수정
jjinwo0 Dec 24, 2023
7e1abc7
refactor: TodoList Status update 메서드 수정
jjinwo0 Dec 24, 2023
a735fb9
feat: MemberRepository 생성
jjinwo0 Dec 24, 2023
9d778d4
refactor: 패키지 경로 수정
jjinwo0 Dec 24, 2023
d53b1b8
refactor: todoService Transactional 어노테이션 추가
jjinwo0 Dec 24, 2023
3674dfc
feat: Todo Max Count 초과 Exception 정의
jjinwo0 Dec 24, 2023
afcaf58
feat: Member Service 정의
jjinwo0 Dec 24, 2023
2aa6510
refactor: ErrorCode 정의
jjinwo0 Dec 24, 2023
3409fc2
feat: todo 생성 bulk 연산 구현
jjinwo0 Dec 24, 2023
975d82d
refactor: Todo 등록 최대 수 초과 예외 처리 handler 추가
jjinwo0 Dec 24, 2023
6c04b1e
feat: bulk 연산 controller 추가
jjinwo0 Dec 24, 2023
55ebefb
refactor: 단건 생성 수정
jjinwo0 Dec 24, 2023
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
2 changes: 2 additions & 0 deletions todolist/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
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
@@ -0,0 +1,98 @@
package study.todolist.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import study.todolist.dto.TodoDto;
import study.todolist.global.Envelope;
import study.todolist.entity.todo.TodoList;
import study.todolist.service.TodoService;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/todo")
public class TodoController {

private final TodoService todoService;


// 생성
@PostMapping("/create/{id}")
public Envelope createTodo(@PathVariable("id") Long id,
@RequestBody TodoDto.Request request){

TodoList todo = todoService.createTodo(id, request.getTitle());

TodoDto.Response response = TodoDto.Response.of(todo);

return Envelope.success(response);
}

@PostMapping("/create/bulk/{id}")
public Envelope<List> bulkCreate(@PathVariable("id") Long id,
@RequestParam int count){

List<TodoDto.Response> response = todoService.bulkTodo(id, count);

return Envelope.success(response);
}

// 단건조회
@GetMapping("/find/{id}")
public Envelope findById(@PathVariable("id") Long id){

TodoList todo = todoService.findById(id);

TodoDto.Response response = TodoDto.Response.of(todo);

return Envelope.success(response);
}

// 전체조회
@GetMapping("/find/all")
public Envelope findAll(){

List<TodoDto.Response> responseList = todoService.findAll().stream()
.map(TodoDto.Response::of)
.collect(Collectors.toList());

return Envelope.success(responseList);
}

// 수정
@PatchMapping("/update/{id}")
public Envelope updateTodo(@PathVariable Long id,
@RequestBody TodoDto.Request request){

Long findId = todoService.updateTitle(id, request.getTitle());
TodoList todo = todoService.findById(findId);

TodoDto.Response response = TodoDto.Response.of(todo);

return Envelope.success(response);
}

// check
@PatchMapping("/check/{id}")
public Envelope checkTodo(@PathVariable Long id,
@RequestBody TodoDto.ChangeStatus status){

todoService.updateStatus(id, status.getStatus());
TodoDto.Response response = TodoDto.Response.of(todoService.findById(id));

return Envelope.success(response);
}

// 삭제
@DeleteMapping("/delete/{id}")
public Envelope deleteTodo(@PathVariable Long id){

todoService.delete(id);
TodoDto.Response response = TodoDto.Response.of(todoService.findById(id));

return Envelope.success(response);
}
}

39 changes: 39 additions & 0 deletions todolist/src/main/java/study/todolist/dto/TodoDto.java
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTO 잘 작성하셨네요!
현재 코드에선 TodoDto.Request, TodoDto.ChangeStatus, TodoDto.Response가 모두 TodoDto 클래스 내에 정의되어있는데 저는 클래스로 분리해서 사용하고 있습니다
@DaTa를 붙여서 하는 방법도 있었네요 ㅎㅎ 배워갑니다.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DTO활용은 개인차가 좀 있다보니 제 방식이 조금 생소할수도 있다고 느껴졌는데 좋게 봐주셔서 감사합니다 :)

@DaTa 어노테이션은 @Getter, @Setter, @tostring이 합쳐져있다고만 알고 있어 간단하게 사용하고자 붙였습니다!
@Setter 활용을 좋아하지 않는 분들도 많으셔서 저도 사용하는데 좀 고민이 되는 부분이네요 ㅎㅎ

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package study.todolist.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import study.todolist.entity.todo.Status;
import study.todolist.entity.todo.TodoList;

public class TodoDto {

@Data
public static class Request{
private String title;
}

@Data
public static class ChangeStatus{
private Status status;
}

@Data @Builder
@NoArgsConstructor @AllArgsConstructor
public static class Response{
private Long id;
private String title;
private Status status;

public static Response of(TodoList todoList) {

return Response.builder()
.id(todoList.getId())
.title(todoList.getTitle())
.status(todoList.getStatus())
.build();
}
}
}

22 changes: 22 additions & 0 deletions todolist/src/main/java/study/todolist/entity/member/Member.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package study.todolist.entity.member;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import study.todolist.entity.todo.TodoList;

@Entity @Getter
@NoArgsConstructor @AllArgsConstructor
public class Member {

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

private String username;

@OneToOne
@JoinColumn(name = "todo_id")
private TodoList todoList;
}
6 changes: 6 additions & 0 deletions todolist/src/main/java/study/todolist/entity/todo/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package study.todolist.entity.todo;

public enum Status {

BEFORE, PROGRESS, COMPLETE
}
39 changes: 39 additions & 0 deletions todolist/src/main/java/study/todolist/entity/todo/TodoList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package study.todolist.entity.todo;

import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import study.todolist.entity.member.Member;

@Entity
@Getter
@NoArgsConstructor
public class TodoList{

@Id @Column(name = "todo_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Enumerated(value = EnumType.STRING)
private Status status;

@OneToOne(mappedBy = "todoList")
private Member member;

public void updateTitle(String title){
this.title = title;
}

public void updateStatus(Status status){
this.status = status;
}

@Builder
public TodoList(String title, Status status, Member member) {
this.title = title;
this.status = status;
this.member = member;
}
}

32 changes: 32 additions & 0 deletions todolist/src/main/java/study/todolist/global/Envelope.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package study.todolist.global;


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter @Builder
@NoArgsConstructor @AllArgsConstructor
public class Envelope<T> {

private T data;
private int error;
private String message;

public static <T> Envelope<T> of (T data, int error, String message){

return new Envelope<>(data, error, message);
}

public static <T> Envelope<T> success (T data){

return of(data, HttpStatus.OK.value(), "성공");
}

public static <T> Envelope<T> fail (int error, String message){

return of(null, error, message);
}
}
26 changes: 26 additions & 0 deletions todolist/src/main/java/study/todolist/global/error/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package study.todolist.global.error;

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

@Getter
public enum ErrorCode {

NOT_FOUND_TODO(HttpStatus.NOT_FOUND, "T-001", "TODO를 찾을 수 없습니다."),
OVER_MAX_COUNT(HttpStatus.BAD_REQUEST, "T-002", "한번에 생성 가능한 수를 초과하였습니다."),

NOT_FOUNT_MEMBER(HttpStatus.NOT_FOUND, "M-001", "Member를 찾을 수 없습니다.")
;


ErrorCode(HttpStatus httpStatus, String errorCode, String message) {
this.httpStatus = httpStatus;
this.errorCode = errorCode;
this.message = message;
}

private HttpStatus httpStatus;
private String errorCode;
private String message;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package study.todolist.global.error;

import lombok.Builder;
import lombok.Getter;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import java.util.List;

@Getter
@Builder
public class ErrorResponse {

private String errorCode;
private String errorMessage;

public static ErrorResponse of(String errorCode, String errorMessage){

return ErrorResponse.builder()
.errorCode(errorCode)
.errorMessage(errorMessage)
.build();
}

public static ErrorResponse of(String errorCode, BindingResult bindingResult){

return ErrorResponse.builder()
.errorCode(errorCode)
.errorMessage(createErrorMessage(bindingResult))
.build();
}

private static String createErrorMessage(BindingResult bindingResult) {

StringBuilder sb = new StringBuilder();
boolean isFirst = true;

List<FieldError> fieldErrors = bindingResult.getFieldErrors();

for (FieldError error : fieldErrors){

if (!isFirst){
sb.append(", ");
} else isFirst = false;

sb.append("[");
sb.append(error.getField());
sb.append("]");
sb.append(error.getDefaultMessage());
}

return sb.toString();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package study.todolist.global.error;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import study.todolist.global.Envelope;
import study.todolist.global.error.exception.BusinessException;
import study.todolist.global.error.exception.EntityNotFoundException;
import study.todolist.global.error.exception.OverMaxCountException;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
* EntityNotFoundException 발생 메시지 처리
*/
@ExceptionHandler(EntityNotFoundException.class)
public Envelope handleEntityNotFoundException(EntityNotFoundException e){
log.error("EntityNotFoundException", e);

return Envelope.fail(e.getErrorCode().getHttpStatus().value(), e.getMessage());
}

@ExceptionHandler(OverMaxCountException.class)
public Envelope handleOverMaxCountException(OverMaxCountException e){
log.error("OverMaxCountException", e);

return Envelope.fail(e.getErrorCode().getHttpStatus().value(), e.getMessage());
}

@ExceptionHandler(BusinessException.class)
protected Envelope handleException(BusinessException e){
log.error("Exception", e);

return Envelope.fail(e.getErrorCode().getHttpStatus().value(), e.getMessage());
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 exception들이 BusinessException을 상속받는데 ErrorCode와 메시지를 함께 쓰기 위해서 인가요 ?? 의도가 궁금합니다 ㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞습니다!
기존에 사용했던 방식을 그대로 고수하여 사용자 정의 ErrorCode를 활용하고자 상속하는 방식을 사용하고 있습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package study.todolist.global.error.exception;

import lombok.Getter;
import study.todolist.global.error.ErrorCode;

@Getter
public class BusinessException extends RuntimeException{

private ErrorCode errorCode;

public BusinessException(ErrorCode errorCode){
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
Loading