스프링

 

RESTful API   Todo 만들기
 

 

RESTful API URI 경로

  1. 사용자의 모든 Todo 목록 조회

    • HTTP 메소드: GET
    • URI: /api/users/{userId}/todos
    • 설명: 특정 사용자의 모든 Todo 항목을 페이지네이션하여 조회합니다.
  2. 특정 Todo 조회

    • HTTP 메소드: GET
    • URI: /api/users/{userId}/todos/{todoId}
    • 설명: 특정 사용자의 특정 Todo 항목의 세부 정보를 조회합니다.
  3. 새로운 Todo 생성

    • HTTP 메소드: POST
    • URI: /api/users/{userId}/todos
    • 설명: 특정 사용자에게 새로운 Todo 항목을 생성합니다.
  4. 기존 Todo 수정

    • HTTP 메소드: PUT
    • URI: /api/users/{userId}/todos/{todoId}
    • 설명: 특정 사용자의 특정 Todo 항목을 수정합니다.
  5. Todo 삭제

    • HTTP 메소드: DELETE
    • URI: /api/users/{userId}/todos/{todoId}
    • 설명: 특정 사용자의 특정 Todo 항목을 삭제합니다.

 

 

 

 

URI 설계 원칙

 

  • 자원(Resource) 중심: URI는 명사로 구성되며, 액션을 나타내는 동사를 사용하지 않습니다.

    예: /todos 대신 /api/users/{userId}/todos

  • 계층적 구조: Todo는 User에 속한 자원이므로, URI에 User의 식별자를 포함시켜 계층적 관계를 표현합니다.

    예: /api/users/{userId}/todos/{todoId}

  • 일관성: 모든 CRUD(Create, Read, Update, Delete) 작업에 대해 일관된 패턴을 유지하여 API의 예측 가능성을 높입니다.

  • RESTful 원칙 준수: HTTP 메소드(GET, POST, PUT, DELETE)를 적절히 사용하여 자원의 상태를 관리합니다.

 

 

추가 고려사항

  • 버전 관리: API의 변경에 대비하여 URI에 버전 정보를 포함시키는 것을 고려할 수 있습니다.

    예: /api/v1/users/{userId}/todos

  •  

  • 검색 및 필터링: 필요에 따라 쿼리 파라미터를 사용하여 Todo를 검색하거나 필터링할 수 있습니다.

    예: /api/users/{userId}/todos?done=true

  •  

  • 페이지네이션: 대량의 데이터를 효율적으로 처리하기 위해 페이지 번호와 페이지 크기를 쿼리 파라미터로 받을 수 있습니다.

    예: /api/users/{userId}/todos?page=1&size=10

 

 

 

1. ApiUserController

package net.macaronics.springboot.webapp.api.controller;


import java.net.URI;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import org.springframework.http.CacheControl;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import net.macaronics.springboot.webapp.dto.ResponseDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoCreateDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoResponseDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoUpdateDTO;
import net.macaronics.springboot.webapp.service.TodoService;
import net.macaronics.springboot.webapp.utils.PageMaker;

@RestController
@RequestMapping("/api/users/{userId}/todos")
@RequiredArgsConstructor
public class ApiTodoController {

    private final TodoService todoService;

    /**
     * HATEOAS 링크 추가 메서드
     */
    private TodoResponseDTO addTodoLinks(TodoResponseDTO todoResponse) {
        todoResponse.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ApiTodoController.class)
        		.getTodoById(todoResponse.getUserId(), todoResponse.getId()))
                .withSelfRel());
        return todoResponse;
    }

    /**
     * 1. GET /api/users/{userId}/todos
     *    전체 Todo 목록 조회
     */
    @Operation(summary = "전체 Todo 목록 조회", description = "특정 사용자의 모든 Todo를 페이지네이션하여 조회합니다.")
    @GetMapping
    public ResponseEntity<?> getAllTodos(@PathVariable Long userId,@Valid PageMaker pageMaker) {

        int pageInt = pageMaker.getPage() == null ? 0 : pageMaker.getPage();
        PageRequest pageable = PageRequest.of(pageInt, 10); // 예: 페이지당 10개

        Page<TodoResponseDTO> todoPage = todoService.getTodosByUserId(userId, pageable);

        List<TodoResponseDTO> todosWithLinks = todoPage.getContent().stream().map(this::addTodoLinks).collect(Collectors.toList());

        CollectionModel<TodoResponseDTO> collectionModel = CollectionModel.of(todosWithLinks);
        collectionModel.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ApiTodoController.class).getAllTodos(userId, pageMaker)).withSelfRel());

        return ResponseEntity.ok().cacheControl(CacheControl.maxAge(60, TimeUnit.SECONDS))
                .body(ResponseDTO.builder()
                        .code(1)
                        .message("success")
                        .data(collectionModel)
                        .build());
    }
    
    

    /**
     * 2. GET /api/users/{userId}/todos/{todoId}
     *    개별 Todo 조회
     */
    @Operation(summary = "개별 Todo 조회", description = "특정 사용자의 특정 Todo를 조회합니다.")
    @GetMapping("/{todoId}")
    public ResponseEntity<?> getTodoById(
            @PathVariable Long userId,
            @PathVariable Long todoId) {

        TodoResponseDTO todo = todoService.getTodoById(userId, todoId);
        addTodoLinks(todo);

        return ResponseEntity.ok(todo);
    }

    
    /**
     * 3. POST /api/users/{userId}/todos
     *    Todo 생성
     */
    @Operation(summary = "Todo 생성", description = "특정 사용자에게 새로운 Todo를 생성합니다.")
    @PostMapping
    public ResponseEntity<?> createTodo(
            @PathVariable Long userId,
            @Valid @RequestBody TodoCreateDTO todoCreateDTO,
            BindingResult bindingResult) throws Exception {

        if (bindingResult.hasErrors()) {
            throw new MethodArgumentNotValidException(null, bindingResult);
        }

        TodoResponseDTO createdTodo = todoService.createTodo(userId, todoCreateDTO);
        addTodoLinks(createdTodo);

        URI location = WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ApiTodoController.class).getTodoById(userId, createdTodo.getId())).toUri();

        return ResponseEntity.created(location)
                .body(ResponseDTO.builder()
                        .code(1)
                        .message("Todo created successfully")
                        .data(createdTodo)
                        .build());
    }

    
    
    /**
     * 4. PUT /api/users/{userId}/todos/{todoId}
     *    Todo 수정
     */
    @Operation(summary = "Todo 수정", description = "특정 사용자의 특정 Todo를 수정합니다.")
    @PutMapping("/{todoId}")
    public ResponseEntity<?> updateTodo(
            @PathVariable Long userId,
            @PathVariable Long todoId,
            @Valid @RequestBody TodoUpdateDTO todoUpdateDTO,
            BindingResult bindingResult) throws Exception {

        if (bindingResult.hasErrors()) {
            throw new MethodArgumentNotValidException(null, bindingResult);
        }

        TodoResponseDTO updatedTodo = todoService.updateTodo(userId, todoId, todoUpdateDTO);
        addTodoLinks(updatedTodo);

        return ResponseEntity.ok(ResponseDTO.builder()
                .code(1)
                .message("Todo updated successfully")
                .data(updatedTodo)
                .build());
    }

    
    
    /**
     * 5. DELETE /api/users/{userId}/todos/{todoId}
     *    Todo 삭제
     */
    @Operation(summary = "Todo 삭제", description = "특정 사용자의 특정 Todo를 삭제합니다.")
    @DeleteMapping("/{todoId}")
    public ResponseEntity<?> deleteTodo(
            @PathVariable Long userId,
            @PathVariable Long todoId) {

        todoService.deleteTodo(userId, todoId);

        CollectionModel<?> collectionModel = CollectionModel.empty();
        collectionModel.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodOn(ApiTodoController.class).getAllTodos(userId, new PageMaker())).withRel("all-todos"));

        ResponseDTO<?> response = ResponseDTO.builder()
                .code(1)
                .message("Todo deleted successfully")
                .data(collectionModel)
                .build();

        return ResponseEntity.ok(response);
    }
    
    
    
}

 

 

  • 기본 URI: @RequestMapping("/api/users/{userId}/todos") 어노테이션은 모든 Todo 관련 엔드포인트의 기본 URI를 설정하여,

  • 각 Todo가 항상 특정 사용자의 컨텍스트에서 접근되도록 보장합니다.

 

  • HATEOAS 링크: 각 TodoResponseDTO에는 HATEOAS 링크가 포함되어 있어 API의 발견 가능성과 탐색성을 높입니다. addTodoLinks 메서드는 각 Todo 리소스에 self 링크를 추가합니다.

  •  

  • 페이징: getAllTodos 메서드는 PageMaker 유틸리티를 통해 페이징을 지원하여 클라이언트가 Todo 목록을 적절한 크기로 나눠서 받을 수 있게 합니다.

  •  

  • 유효성 검사: 입력 유효성 검사는 @Valid 어노테이션과 BindingResult로 처리됩니다. 유효성 검사가 실패하면 MethodArgumentNotValidException이 발생하며, 전역적으로 처리됩니다.

  •  

  • 응답 구조: 모든 응답은 일관성을 위해 ResponseDTO로 감싸져 있으며, 여기에는 코드, 메시지, 데이터 페이로드가 포함됩니다.

 

 

2. TodoCreateDTO

package net.macaronics.springboot.webapp.dto.todo;

import java.time.LocalDate;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class TodoCreateDTO {

	
	 @NotBlank(message = "내용은 필수 입니다.")
	 @Size(min = 5, max = 35, message = "글자는 10자에서 35자 사이여야 합니다.")
	 private String description;
	    
	 
	 @NotNull(message = "목표 날짜는 필수입니다.")
	 private LocalDate targetDate;
	    
	    
}

 

 

 

3. TodoUpdateDTO

package net.macaronics.springboot.webapp.dto.todo;

import java.time.LocalDate;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class TodoUpdateDTO {
    
	@NotBlank(message = "내용은 필수 입니다.")
	@Size(min = 5, max = 35, message = "글자는 10자에서 35자 사이여야 합니다.")
	private String description;
	    
	 
	@NotNull(message = "목표 날짜는 필수입니다.")
	private LocalDate targetDate;
    
    private boolean done;
    
}

 

 

 

4. TodoResponseDTO

package net.macaronics.springboot.webapp.dto.todo;

import java.time.LocalDate;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.hateoas.RepresentationModel;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TodoResponseDTO  extends RepresentationModel<TodoResponseDTO> {

	private Long id;

	private Long userId;
	
	private String username;

	private String description;

	@DateTimeFormat(pattern = "yyyy-MM-dd")
	private LocalDate targetDate;

	private boolean done;
	
	private Long num;  // 목록에서 번호를 매길 필드
	
	 // 매개변수 있는 생성자 추가 (Projections.constructor에서 요구하는 타입)
    public TodoResponseDTO(Long id, Long userId, String username, String description, LocalDate targetDate, Boolean done) {
        this.id = id;
        this.userId = userId;
        this.username = username;
        this.description = description;
        this.targetDate = targetDate;
        this.done = done;
    }


}

 

 

 

 

5. TodoService

 

package net.macaronics.springboot.webapp.service;

import java.time.LocalDate;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import net.macaronics.springboot.webapp.dto.todo.TodoCreateDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoResponseDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoUpdateDTO;
import net.macaronics.springboot.webapp.entity.Todo;
import net.macaronics.springboot.webapp.entity.User;
import net.macaronics.springboot.webapp.exception.ResourceNotFoundException;
import net.macaronics.springboot.webapp.mapper.TodoMapper;
import net.macaronics.springboot.webapp.repository.TodoRepository;
import net.macaronics.springboot.webapp.repository.UserRepository;

import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;

@Service
@Transactional  // 이 서비스의 모든 메서드는 기본적으로 트랜잭션 내에서 실행됨
@RequiredArgsConstructor  // 필수 필드의 생성자를 자동으로 생성해주는 Lombok 어노테이션
public class TodoService {

    private final UserRepository userRepository;  // User 정보를 관리하는 Repository
    private final TodoRepository todoRepository;  // Todo 정보를 관리하는 Repository
    
    private final TodoMapper todoMapper;
    
        
    /**
     * todo 목록 가져오기
     * @param userId
     * @param pageable
     * @return
     */
    @Transactional(readOnly = true)
	public Page<TodoResponseDTO> getTodosByUserId(Long userId, PageRequest pageable) {	
		return todoRepository.findByUserId(userId, pageable).map(todoMapper::convertTodoResponseDTO);
	}
	
    
    
    /**
     * todo 상세정보 가져오기
     * @param userId
     * @param todoId
     * @return
     */
    @Transactional(readOnly = true)
	public TodoResponseDTO getTodoById(Long userId, Long todoId) {
		Todo todo=todoRepository.findByIdAndUserId(todoId, userId).orElseThrow(
				()->new ResourceNotFoundException("Todo not found"));		
		return todoMapper.convertTodoResponseDTO(todo); 
	}
	
    

    /**
     * todo 저장하기
     * @param userId
     * @param createDTO
     * @return
     */
    public TodoResponseDTO createTodo(Long userId, TodoCreateDTO createDTO) {
    	User user=userRepository.findById(userId).orElseThrow(()->new ResourceNotFoundException(userId +" 유저를 찾츨 수 없습니다."));
    	    	
    	Todo todo=todoMapper.ofTodo(createDTO);
    		 todo.setUser(user);
    		 todo.setDone(false);
    		 
        Todo savedTodo = todoRepository.save(todo);
    	return todoMapper.convertTodoResponseDTO(savedTodo);    	
    }
    
    
    /**
     *  todo Update
     * @param userId
     * @param todoId
     * @param updateDTO
     * @return
     */
    public TodoResponseDTO updateTodo(Long userId, Long todoId, TodoUpdateDTO updateDTO) {
        Todo todo = todoRepository.findByIdAndUserId(todoId, userId)
                .orElseThrow(() -> new ResourceNotFoundException("Todo not found"));

        //더티 체킹
        todo.setDescription(updateDTO.getDescription());
        todo.setTargetDate(updateDTO.getTargetDate());
        todo.setDone(updateDTO.isDone());

        return todoMapper.convertTodoResponseDTO(todo);    
    }

    
	
	/**
	 * todo 삭제
	 * @param userId
	 * @param todoId
	 */
	public void deleteTodo(Long userId, Long todoId) {
		  Todo todo = todoRepository.findByIdAndUserId(todoId, userId)
	                .orElseThrow(() -> new ResourceNotFoundException("Todo not found"));
		  todoRepository.delete(todo);
	}
    
    
    
    
  
	
}

 

 

 

6.TodoMapper

package net.macaronics.springboot.webapp.mapper;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

import net.macaronics.springboot.webapp.dto.todo.TodoCreateDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoFormDTO;
import net.macaronics.springboot.webapp.dto.todo.TodoResponseDTO;
import net.macaronics.springboot.webapp.entity.Todo;

/**
 * MapStruct를 사용하면, 인터페이스 기반으로 매핑 메서드를 정의하고, 컴파일 시점에 매핑 구현체가 자동으로 생성됩니다.
 * componentModel = "spring" 설정을 통해 Spring의 빈으로 등록됩니다.
 */
@Mapper(componentModel = "spring")
public interface TodoMapper {

	
    @Mapping(source = "user.username", target = "username")
	TodoResponseDTO convertTodoResponseDTO(Todo todo);

    // 필요에 따라 다른 매핑 메서드도 추가
    // Todo toEntity(TodoRequestDTO dto);
    
    
    @Mapping(source = "username", target = "user.username")
    Todo ofTodo(TodoFormDTO todoFormDTO);

    
    
    @Mapping(source = "description", target = "description")
    Todo ofTodo(TodoCreateDTO  todoCreateDTO);
    
    
    
}


		



 

 

 

 

 

7.TodoRepository

package net.macaronics.springboot.webapp.repository;

import java.util.Optional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import net.macaronics.springboot.webapp.entity.Todo;
import net.macaronics.springboot.webapp.entity.User;

public interface TodoRepository extends JpaRepository<Todo, Long>, TodoRepositoryCustom {

	Todo findByIdAndUser(Long id, User user);

	void deleteByUserId(Long id);
	
	
	Page<Todo> findByUserId(Long userId, Pageable pageable);
	
	Optional<Todo> findByIdAndUserId(Long id, Long userId);

	
}

 

 

 

 

 

8.GlobalExceptionHandler

package net.macaronics.springboot.webapp.exception;

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

import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

import lombok.extern.slf4j.Slf4j;
import net.macaronics.springboot.webapp.dto.ResponseDTO;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
   
    /**
     * 모든 예외 처리
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<?> handleAllExceptions(Exception ex, WebRequest request){
        log.error("Unhandled exception occurred", ex);
        ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), ex.getMessage(), request.getDescription(false));
        
        ResponseDTO<?> response = ResponseDTO.builder()
                .code(-1)
                .message("Internal Server Error")
                .data(errorDetails)
                .errorCode("INTERNAL_SERVER_ERROR")
                .build();
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    
    /**
     * 공통 404 예외 처리: NotFoundException
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(ResourceNotFoundException.class)
    public final ResponseEntity<?> handleNotFoundException(ResourceNotFoundException ex, WebRequest request){
        log.warn("Resource not found exception: {}", ex.getMessage());
        ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), ex.getMessage(), request.getDescription(false));
        
        ResponseDTO<?> response = ResponseDTO.builder()
            .code(-1)
            .message("Resource Not Found")
            .data(errorDetails)
            .errorCode("NOT_FOUND")
            .build();
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }

    /**
     * bindingResult.hasErrors() 에러시 반환 처리한다
     * 유효성 체크 에러 처리
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleValidationExceptions(MethodArgumentNotValidException ex, WebRequest request){
        List<String> errors = ex.getBindingResult()
                                .getAllErrors()
                                .stream()
                                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                                .collect(Collectors.toList());
        
        ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), "Validation Failed", request.getDescription(false));
        log.warn("Validation failed: {}", errorDetails.getMessage());

        ResponseDTO<?> response = ResponseDTO.builder()
            .code(-1)
            .message(errors.get(0))
            .data(errorDetails)
            .errorCode("VALIDATION_ERROR")
            .build();
        return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
    }
    
    // 기타 특정 예외 핸들러 추가 가능
    
    
    
}

 

 

9. ResourceNotFoundException

package net.macaronics.springboot.webapp.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;



@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "찾을 수 없습니다.")
public class ResourceNotFoundException extends RuntimeException{

	private static final long serialVersionUID = -2997057852151179119L;

	public ResourceNotFoundException(String message) {		
		 super(message);
	}

	
}

 

 

 

10.ErrorDetails

 

package net.macaronics.springboot.webapp.exception;

import java.time.LocalDateTime;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorDetails {

	private LocalDateTime timestamp;
	private String message;
	private String details;
		
}

 

 

 

 

 

 

 

 

11. 더미 데이터 JSON 샘플

 

1.1. Todo 생성 (POST 요청)

새로운 Todo를 생성할 때 사용할 JSON 예시입니다.

{
    "description": "Spring Boot 프로젝트 설정하기",
    "targetDate": "2024-05-01"
}

 

1.2. Todo 수정 (PUT 요청)

기존 Todo를 수정할 때 사용할 JSON 예시입니다.

{
    "description": "Spring Boot 프로젝트 설정 및 초기화",
    "targetDate": "2024-05-05",
    "done": true
}

 

1.3. Todo 응답 예시 (GET 요청)

특정 Todo를 조회할 때 서버에서 반환하는 JSON 예시입니다.

{
    "id": 1,
    "userId": 1,
    "description": "Spring Boot 프로젝트 설정하기",
    "targetDate": "2024-05-01",
    "done": false,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/users/1/todos/1"
        }
    }
}

 

1.4. Todo 목록 응답 예시 (GET 요청)

Todo 목록을 조회할 때 서버에서 반환하는 JSON 예시입니다.

{
    "code": 1,
    "message": "success",
    "data": {
        "_embedded": {
            "todoResponseDTOList": [
                {
                    "id": 1,
                    "userId": 1,
                    "description": "Spring Boot 프로젝트 설정하기",
                    "targetDate": "2024-05-01",
                    "done": false,
                    "_links": {
                        "self": {
                            "href": "http://localhost:8080/api/users/1/todos/1"
                        }
                    }
                },
                {
                    "id": 2,
                    "userId": 1,
                    "description": "데이터베이스 설계",
                    "targetDate": "2024-05-10",
                    "done": false,
                    "_links": {
                        "self": {
                            "href": "http://localhost:8080/api/users/1/todos/2"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://localhost:8080/api/users/1/todos?page=0&size=10"
            }
        }
    }
}

 

1.5. Todo 삭제 응답 예시 (DELETE 요청)

Todo를 삭제한 후 서버에서 반환하는 JSON 예시입니다.

{
    "code": 1,
    "message": "Todo deleted successfully",
    "data": {
        "_links": {
            "all-todos": {
                "href": "http://localhost:8080/api/users/1/todos?page=0&size=10"
            }
        }
    }
}

 

 

1. Todo 생성 (POST)

  • HTTP 메소드: POST

  • URI: http://localhost:8080/api/users/{userId}/todos

    예를 들어, userId가 1인 경우:

http://localhost:8080/api/users/1/todos


 

  • 헤더 설정:

    • Content-Type: application/json
  • 바디 설정:

    • Body 탭을 클릭하고, JSON 형식을 선택한 후 아래와 같이 더미 데이터를 입력합니다
{
    "description": "Spring Boot 프로젝트 설정하기",
    "targetDate": "2024-05-01"
}

 

  • 요청 전송: 설정이 완료되면 "Send" 버튼을 클릭하여 요청을 전송합니다.

 

 

 

 

 

 

 

 

 

 

 

2.3.2. Todo 목록 조회 (GET)

  • HTTP 메소드: GET

  • URI: http://localhost:8080/api/users/{userId}/todos

    예를 들어, userId가 1인 경우:

 

http://localhost:8080/api/users/1/todos?page=0&size=10


 

  • 헤더 설정: 특별한 헤더 설정이 필요하지 않습니다.

  • 요청 전송: "Send" 버튼을 클릭하여 요청을 전송합니다.

 

2.3.3. 특정 Todo 조회 (GET)

  • HTTP 메소드: GET

  • URI: http://localhost:8080/api/users/{userId}/todos/{todoId}

    예를 들어, userId가 1이고 todoId가 1인 경우:

 

http://localhost:8080/api/users/1/todos/1

 

2.3.4. Todo 수정 (PUT)

  • HTTP 메소드: PUT

  • URI: http://localhost:8080/api/users/{userId}/todos/{todoId}

    예를 들어, userId가 1이고 todoId가 1인 경우:

http://localhost:8080/api/users/1/todos/1

 

  • 헤더 설정:

    • Content-Type: application/json
  • 바디 설정:

    • Body 탭을 클릭하고, JSON 형식을 선택한 후 아래와 같이 더미 데이터를 입력합니다

 

{
    "description": "Spring Boot 프로젝트 설정 및 초기화",
    "targetDate": "2024-05-05",
    "done": true
}

 

 

  • 요청 전송: "Send" 버튼을 클릭하여 요청을 전송합니다.

2.3.5. Todo 삭제 (DELETE)

  • HTTP 메소드: DELETE

  • URI: http://localhost:8080/api/users/{userId}/todos/{todoId}

    예를 들어, userId가 1이고 todoId가 1인 경우:

 

http://localhost:8080/api/users/1/todos/1


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

돈만 있으면 귀신도 부린다 , 돈으로 못 할 일이 없다는 말.

댓글 ( 0)

댓글 남기기

작성