본문 바로가기
쿤즈 프로젝트/To-do List Application

[Spring Boot] To-Do List 애플리케이션: Chap6. API 계층별 구현 (2) 특정 할 일 조회(상세조회)

by Koonz:) 2024. 7. 22.
728x90

앞선 글에서는 API 계층별 구현 첫 번째인 전체 할 일 조회 API를 만들어 보았습니다. 내가 가지고 있던 모든 할 일을 조회하는 기능이었어요.

 

이번 글에서는 To-Do List 애플리케이션의 특정 할 일을 조회하는 API를 구현하는 방법에 대해 설명합니다. 이 API는 클라이언트가 특정 ID로 할 일을 조회할 수 있도록 합니다.


프로젝트 구조 설정

프로적테의 기본 구조는 다음과 같습니다. 현재까지 구성되어있는 레이어에 기능들을 추가해 보도록 할게요.

src/main/java
└── com.koonsland.todo
    ├── controller
    │   └── ToDoItemController.java
    ├── model
    │   └── ToDoItem.java
    ├── repository
    │   └── ToDoItemRepository.java
    ├── service
    │   └── ToDoItemService.java
    └── TodoApplication.java

 

LAYERED
LAYERED
Controller
Controller
Service
Service
Model
Model
Repository
Repository
koonsland.tistory.com
koonsland.tistory.com
Spring Boot Layered
Spring Boot Layered
Database
Database
Text is not SVG - cannot display

 

Controller: 클라이언트의 요청을 처리하고 응답을 반환하는 컨트롤러 클래스가 위치합니다. 실제 요청을 받을 수 있는 API 클래스들을 만들 수 있습니다.

Service: 비즈니스 로직을 구현하는 서비스 클래스가 위치합니다. 비즈니스 로직은 데이터를 저장, 조회, 수정, 삭제 기능을 호출하는 로직들을 담는 클래스들입니다.

Repository: 데이터베스와의 상호작용을 처리하는 Repository 인터페이스가 위치합니다.

Model: 데이터베이스 테이블과 매핑되는 엔티티 클래스가 위치합니다.


특정 할 일 (할 일 상세) 조회 API 구현

특정 할 일의 조회는 보통 상세 조회 또는 단건 조회, 세부 조회라는 여러가지 표현들을 사용합니다. 전체 할 일 조회는 요약(Summary) 정보를 조회하는 경우가 보통이며 선택해서 세부 정보들을 볼 수 있는 기능이 필요하고 이 API 개발을 통해 세부 정보를 볼 수 있도록 하는 기능을 만들 예정입니다.


ToDoItem 엔티티 클래스

TodoItem 엔티티 클래스는 변함없이 그대로입니다. 이미 데이터베이스 모델링과 설계 진행하면서 필요한 필드들은 모두 추가되어 있는 상태입니다.

package com.koonsland.todo.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;

import java.time.LocalDateTime;

@Entity
@Table(name = "todo_item")
public class ToDoItem {

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

    @Column(name = "title", nullable = false)
    private String title;

    @Column(name= "description", length = 500)
    private String description;

    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        LocalDateTime now = LocalDateTime.now();
        this.createdAt = now;
        this.updatedAt = now;
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }

    // Getters
    // Setters 생성 안함 (필요한 비즈니스 메서드만 생성 예정)
    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }

    public LocalDateTime getCreatedAt() {
        return createdAt;
    }

    public LocalDateTime getUpdatedAt() {
        return updatedAt;
    }
}

 

🧑🏻‍💻 Lombok 롬복
롬복을 사용하면 Getter, Setter를 사용하지 않고 어노테이션만으로 줄일 수 있습니다. 다만 이 프로젝트는 가장 기본적인 내용으로 진행 중이며 롬복을 알기 전에 어떤 메서드들로 구성되고 수정해야 하는지 알기 위해 사용하지 않고 진행합니다.

ToDoItemRepository 인터페이스

ToDoItemRepository 인터페이스를 그대로 사용합니다. 추가적으로 해야할 작업은 없습니다.

package com.koonsland.todo.repository;

import com.koonsland.todo.model.ToDoItem;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ToDoItemRepository extends JpaRepository<ToDoItem, Long> {
}

 

 

특정 ID를 조회하는 인터페이스는 CrudRepository 인터페이스에 이미 구현되어 있습니다. JpaRepository 인터페이스는 내부적으로 CrudRepository 인터페이스를 상속받았기 때문에 기본적인 메서드는 제공됩니다.

 

여기서 리턴타입이 Optional 입니다.

 

🧑🏻‍💻 Optional 타입
Java 8에서 도입된 클래스로, 값이 존재할 수도 있고 존재하지 않을 수도 있는 상황을 표현하기 위해 사용됩니다. 이를 통해 null 검사를 간경하게 처리할 수 있고, NullPointException을 방지할 수 있습니다.

ToDoItemService 클래스

특정 ToDoItem을 조회하는 비즈니스 로직을 ToDoItemService 클래스에 구현해 보도록 하겠습니다.

package com.koonsland.todo.service;

import com.koonsland.todo.model.ToDoItem;
import com.koonsland.todo.repository.ToDoItemRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.NoSuchElementException;

@Service
public class ToDoItemService {

    private final ToDoItemRepository toDoItemRepository;

    public ToDoItemService(ToDoItemRepository toDoItemRepository) {
        this.toDoItemRepository = toDoItemRepository;
    }

    /**
     * 모든 할 일 조회
     */
    public List<ToDoItem> findAll() {
        return toDoItemRepository.findAll();
    }

    /**
     * 특정 할 일 조회
     */
    public ToDoItem findById(Long id) {
        return toDoItemRepository.findById(id)
                .orElseThrow(() -> new NoSuchElementException("할 일을 찾을 수 없어요. id: [" + id +"]"));
    }
}

 

특정 할 일 조회는 return 타입이 ToDoItem 입니다. 이때 Repository에서 반환된 값이 Optional<ToDoItem> 이므로 값이 없을 경우을 처리해 주어야 합니다. Optional 타입에서는 여러가지를 사용할 수 있지만 이 프로젝트에서는 .orElseThrow() 메서드를 사용해서 바로 Exception을 처리하도록 진행했습니다.


ToDoItemController 클래스

ToDoItemController 클래스는 클라이언트의 요청을 처리하고 특정 ID를 입력받아서 ToDoItem 하나를 반환하는 Rest API를 만들어 보도록 하겠습니다.

package com.koonsland.todo.controller;

import com.koonsland.todo.model.ToDoItem;
import com.koonsland.todo.service.ToDoItemService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/api/todos")
public class ToDoItemController {

    private final ToDoItemService toDoItemService;

    public ToDoItemController(ToDoItemService toDoItemService) {
        this.toDoItemService = toDoItemService;
    }

    /**
     * 모든 ToDoItem 조회
     */
    @GetMapping
    public List<ToDoItem> getAllToDoItems() {
        return toDoItemService.findAll();
    }

    /**
     * 특정 ID를 이용한 ToDoItem 조회
     */
    @GetMapping("/{id}")
    public ToDoItem getToDoItemById(@PathVariable Long id) {
        return toDoItemService.findById(id);
    }
}

 

@GetMapping 어노테이션을 이용하고 url에 path로 id를 입력받습니다. 이렇게 입력 받은 값은 @PathVariable을 이용해서 데이터를 넘겨받을 수 있습니다. 이렇게 Long 타입의 id 값으로 데이터가 들어오고 위에서는 ToDoItemService의 findById() 메서드를 이용해서 id를 넘겨주어 결과를 반환합니다.

 

이제 각 레이어별로 ToDoItem을 특정 ID를 이용한 상세 조회 기능 구현은 끝났습니다. 이제 다시 서버를 실행하고 테스트를 진행해 볼게요.


API 테스트

모든 할 일 조회 API를 테스트하기 위해 Postman 앱을 사용할 수 있습니다.

 

웹사이트: https://www.postman.com

 

Postman API Platform | Sign Up for Free

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.

www.postman.com

 

앱을 설치하고 테스트를 진행해 볼게요.

Postman 앱을 실행하여 API 만들기를 선택하여 특정 할 일 조회 API를 만들어 줍니다.

 

메서드는 GET이며 주소는 localhost:8080/api/todos/{id}로 입력하고 Send 버튼을 누르면 아래 Status: 500 Internal Server Error 와 같이 출력되며 에러가 나는 상황을 볼 수 있습니다. 데이터가 없기 때문에 NoSucElementException의 결과입니다. 서버 로그을 보면 다음과 같이 출력됩니다.

Hibernate: 
    select
        tdi1_0.id,
        tdi1_0.created_at,
        tdi1_0.description,
        tdi1_0.title,
        tdi1_0.updated_at 
    from
        todo_item tdi1_0 
    where
        tdi1_0.id=?
2024-06-26T10:35:24.908+09:00 ERROR 23675 --- [todo] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.util.NoSuchElementException: 할 일을 찾을 수 없어요. id: [1]] with root cause

java.util.NoSuchElementException: 할 일을 찾을 수 없어요. id: [1]
	at com.koonsland.todo.service.ToDoItemService.lambda$findById$0(ToDoItemService.java:31) ~[main/:na]
	...

 

사실 서버입장에서는 500에러는 좋지 못합니다. 알 수 없는 부분에서 에러가 발생했다는 이야기죠. 그래서 Exception 처리를 별도로 해 주어야 합니다. 이 부분은 다음 포스팅에서 이어서 진행할게요.

 

정상적인 데이터 조회를 위해서 데이터를 추가 후 조회해보겠습니다.

H2 콘솔을 이용해서 데이터를 넣고 다시 조회한 결과입니다. 정상적으로 데이터가 조회되는 것을 확인할 수 있습니다.


이번 글에서는 To-Do List 애플리케이션의 특정 ID로 할 일을 조회하는 API를 구현하는 방법을 설명했습니다. Controller, Service, Repository 계층 구현 및 엔티티 정의를 통해 API를 완성했습니다.

 

이 글을 통해서 특정 할 일 조회 API를 구현하는 방법을 명확하게 이해할 수 있길 바랍니다. 다음 글에서는 새로운 할 일 생성 API를 구현해 보도록 할게요. 추가적으로 필요하거나 궁금한 사항이 있다면 댓글로 남겨주세요. 도움이 되시길 바랍니다. 감사합니다. 🙇🏻

 

이전 글

2024.06.16 - [쿤즈 프로젝트/To-do List Application] - [Spring Boot] To-Do List 애플리케이션: Chap1. 프로젝트 소개

 

[Spring Boot] To-Do List 애플리케이션: Chap1. 프로젝트 소개

이번 시리즈에서는 Spring Boot 3 버전을 사용해서 간단하게 To-Do List 애플리케이션을 만드는 프로젝트를 진행하려 합니다. 그래서 이 포스팅에서는 애플리케이션을 만드는 방법을 단계별로 소개할

koonsland.tistory.com

2024.06.20 - [쿤즈 프로젝트/To-do List Application] - [Spring Boot] To-Do List 애플리케이션: Chap2. 요구사항 정리

 

[Spring Boot] To-Do List 애플리케이션: Chap2. 요구사항 정리

지난 Chap1에서는 To-Do List 애플리케이션에 대해서 어떤 것을 만들고 어떤 과정으로 프로젝트를 진행하겠다는 계획에 대해서 정리했습니다. 또한 To-Do List 애플리케이션과 같은 기능을 가진 앱들

koonsland.tistory.com

2024.06.26 - [쿤즈 프로젝트/To-do List Application] - [Spring Boot] To-Do List 애플리케이션: Chap3. 기본 프로젝트 설정

2024.06.27 - [쿤즈 프로젝트/To-do List Application] - [Spring Boot] To-Do List 애플리케이션: Chap4. 데이터 모델링

2024.06.26 - [쿤즈 프로젝트/To-do List Application] - [Spring Boot] To-Do List 애플리케이션: Chap5. API 계층별 구현 (1) 모든 할 일 조회

 

댓글