'모델, 리파지토리, 서비스'를 생성
3장으로 들어가기 전에 지금까지 다룬 내용을 간단히 요약해 보겠습니다.
1장에서는 스프링 이니셜라이저를 사용해 새로운 스프링 부트 프로젝트를 생성하고 기본 프로젝트 구조를 생성했습니다.
2장에서는 설정과 사용이 쉬운 인메모리 데이터베이스인 H2를 사용하여 게시판 서버용 데이터베이스를 구축하고 구성했습니다.
이제 3장에서는 '모델, 리파지토리, 서비스'를 생성해 보겠습니다.
"모델"
애플리케이션에서 조작하려는 도메인 객체를 나타냅니다.
사용자, 게시물, 댓글 등과 같은 데이터 엔티티를 나타내는 Java 클래스입니다. 이러한 엔티티를 쉽게 생성하고 데이터베이스에 매핑하기 위해 Spring Boot의 강력한 기능인 Spring Data JPA를 사용하겠습니다.
"리포지토리"
애플리케이션이 데이터베이스와 상호 작용할 수 있는 인터페이스를 제공합니다.
데이터베이스의 데이터에 액세스하고 조작하는 방법을 정의하는 인터페이스입니다. 이러한 인터페이스를 자동으로 생성하기 위해 Spring Data JPA를 사용하므로 작성해야 하는 상용구 코드의 양이 줄어듭니다.
"서비스"
애플리케이션의 비즈니스 로직을 구현합니다. 새 글 작성, 댓글 삭제, 사용자 프로필 업데이트 등 애플리케이션의 비즈니스 로직을 정의하는 클래스입니다. 이러한 서비스는 저희가 직접 구현합니다.
먼저 게시판 게시물을 나타내는 "게시물" 모델을 만들어 보겠습니다. IntelliJ에서 왼쪽의 프로젝트 탐색기로 이동하여 "src/main/java" 아래에 새 패키지를 생성합니다. 패키지 이름을 "com.example.demo.models"로 지정합니다. 그런 다음 패키지에서 새 클래스를 생성하고 이름을 "Post"로 지정합니다.
package com.example.demo.models;
import javax.persistence.*;
@Entity
@Table(name = "posts")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String body;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
여기서는 "게시물" 모델에 대해 "id", "제목", "본문"의 세 가지 필드를 정의합니다. "@Entity" 어노테이션을 사용하여 이 클래스가 JPA 엔티티임을 나타내고, "@Table" 어노테이션을 사용하여 이 엔티티가 매핑되는 데이터베이스 테이블의 이름을 지정합니다.
다음으로 Post 모델에 대한 리포지토리 인터페이스를 생성하겠습니다. IntelliJ에서 "src/main/java" 아래에 "com.example.demo.repositories"라는 이름의 새 패키지를 만듭니다. 그런 다음 패키지에서 새 인터페이스를 만들고 이름을 "PostRepository"로 지정합니다.
"PostRepository" 인터페이스에 다음 코드를 추가하여 리포지토리 메서드를 정의합니다:
다음으로, "게시물" 테이블의 데이터에 액세스하고 조작하는 메서드를 정의하는 "PostRepository" 인터페이스를 만들어 보겠습니다. "com.example.demo.repositories"라는 이름의 새 패키지를 만들고 그 안에 "PostRepository"라는 이름의 새 인터페이스를 만듭니다. 다음은 예제 코드 스니펫입니다:
package com.example.demo.repositories;
import com.example.demo.models.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}
여기서는 "JpaRepository" 인터페이스를 확장하고 "Post" 모델과 "Long" 유형을 일반 파라미터로 지정합니다. 이렇게 하면 "Post" 모델에 대한 여러 CRUD 메서드에 액세스할 수 있습니다. 또한 인터페이스에 "@Repository"라는 주석을 추가하여 이것이 Spring 리포지토리임을 나타냅니다.
마지막으로 Post 모델에 대한 서비스 인터페이스를 생성합니다. IntelliJ에서 "src/main/java" 아래에 "com.example.demo.services"라는 이름의 새 패키지를 만듭니다. 그런 다음 패키지에서 새 인터페이스를 생성하고 이름을 "PostService"로 지정합니다.
"PostService" 인터페이스에 다음 코드를 추가하여 서비스 메서드를 정의합니다:
package com.example.demo.services;
import com.example.demo.models.Post;
import java.util.List;
public interface PostService {
List<Post> getAllPosts();
Post getPostById(Long id);
Post createPost(Post post);
Post updatePost(Post post);
void deletePost(Long id);
}
여기에서는 "PostService" 인터페이스에 대한 다섯 가지 메서드, 즉 "getAllPosts", "getPostById", "createPost", "updatePost" 및 "deletePost"를 정의합니다. 이러한 메서드는 "포스트" 모델에서 수행하려는 CRUD 작업에 해당합니다. "List" 인터페이스를 사용하여 "Post" 객체 컬렉션을 표현합니다.
모델, 리포지토리 및 서비스 생성은 여기까지입니다.
그럼 계속해서 'Board' 엔티티를 만들어 보겠습니다.
Spring에서 엔티티는 데이터베이스 테이블을 나타내는 클래스입니다. "Board" 엔티티의 경우, 해당 속성을 정의하고 데이터베이스 테이블의 해당 열에 매핑해야 합니다. 또한 테이블의 기본 키도 정의해야 합니다.
다음 단계에 따라 "Board" 엔티티 클래스를 만들어 보겠습니다:
"src/main/java" 디렉터리 아래에 "com.example.bulletinboard.entity"라는 새 패키지를 만듭니다.
"entity" 패키지 내에 "Board"라는 새 Java 클래스를 생성합니다.
"Board" 클래스에 다음 코드를 추가합니다:
package com.example.bulletinboard.entity;
import javax.persistence.*;
@Entity
@Table(name = "boards")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
// Getters and setters
}
위의 코드에서는 "@Entity" 어노테이션을 사용하여 "Board" 엔티티를 정의합니다. 이 어노테이션은 Spring에게 이 클래스가 엔티티이며 엔티티 관리자가 관리해야 함을 알려줍니다.
또한 "@Table" 어노테이션을 사용하여 이 엔티티가 나타내는 데이터베이스 테이블의 이름을 지정합니다. 이 경우 테이블 이름은 "boards"입니다.
"@Id" 어노테이션은 테이블의 기본 키를 지정하는 데 사용됩니다. 데이터베이스가 기본 키 값을 자동으로 생성하도록 하려면 "@GeneratedValue" 어노테이션을 사용합니다.
"Board" 엔티티에 대해 두 가지 속성을 정의합니다: "제목"과 "내용". 이러한 속성은 '게시판' 테이블의 '제목' 및 '내용' 열에 해당합니다.
마지막으로 "id", "title" 및 "content" 속성에 대한 게터와 세터를 정의합니다.
이제 "Board" 엔티티를 만들었으므로 데이터베이스와 상호 작용할 리포지토리를 만들어야 합니다. 다음 장에서는 "보드" 테이블에서 CRUD 작업을 수행할 수 있는 "BoardRepository" 인터페이스를 만들겠습니다.
다음으로, 보드 엔티티에서 수행하려는 데이터베이스 작업을 정의하기 위해 "BoardRepository" 인터페이스를 만들겠습니다.
BoardRepository 인터페이스를 만들려면 다음 단계를 따르세요:
"com.example.demo" 패키지 아래에 "repository"라는 이름의 새 패키지를 만듭니다.
"저장소" 패키지를 마우스 오른쪽 버튼으로 클릭하고 새로 만들기 -> Java 인터페이스를 선택합니다.
인터페이스 이름을 "BoardRepository"로 지정하고 확인을 클릭합니다.
"BoardRepository" 인터페이스를 열고 다음 코드를 추가합니다:
package com.example.demo.repository;
import com.example.demo.model.Board;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BoardRepository extends JpaRepository<Board, Long> {
}
이 코드는 "JpaRepository"를 확장하는 "BoardRepository" 인터페이스를 선언하고 "Long" 유형의 ID를 가진 "Board" 유형의 엔티티를 관리하도록 지정합니다. "JpaRepository"는 엔티티 생성, 읽기, 업데이트 및 삭제와 같은 일반적인 데이터베이스 작업을 수행하기 위한 메서드를 제공하는 Spring 데이터 JPA 인터페이스입니다.
"JpaRepository"를 확장함으로써 "BoardRepository"는 "JpaRepository"에서 제공하는 모든 메서드를 상속하고 자체 메서드를 정의할 수도 있습니다. 상속된 메서드를 사용하여 "Board" 엔티티에 필요한 모든 데이터베이스 작업을 수행할 수 있으므로 "BoardRepository" 인터페이스에 대한 추가 메서드를 정의할 필요가 없습니다.
다음으로, 게시판 애플리케이션의 비즈니스 로직을 정의하기 위해 "BoardService" 클래스를 생성하겠습니다.
이제 보드 모델과 리포지토리를 만들었으므로 이제 보드 서비스 만들기로 넘어갈 수 있습니다.
보드 서비스는 보드 엔티티와 관련된 비즈니스 로직을 처리합니다. 여기에는 새 보드 만들기, 기존 보드 업데이트, 삭제 및 검색이 포함됩니다.
보드 서비스를 만들려면 "com.example.demo.board" 패키지에서 "BoardService"라는 이름의 새 Java 클래스를 만들어야 합니다. 이 클래스에는 Spring 서비스 빈임을 나타내기 위해 "@Service"라는 주석이 추가됩니다.
다음은 BoardService 클래스가 어떻게 생겼는지 보여주는 예시입니다:
@Service
public class BoardService {
private final BoardRepository boardRepository;
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
public List<Board> getAllBoards() {
return boardRepository.findAll();
}
public Board getBoardById(Long id) {
return boardRepository.findById(id).orElseThrow(() -> new BoardNotFoundException(id));
}
public Board createBoard(Board board) {
return boardRepository.save(board);
}
public Board updateBoard(Long id, Board updatedBoard) {
Board board = boardRepository.findById(id).orElseThrow(() -> new BoardNotFoundException(id));
board.setTitle(updatedBoard.getTitle());
board.setContent(updatedBoard.getContent());
return boardRepository.save(board);
}
public void deleteBoard(Long id) {
boardRepository.deleteById(id);
}
}
이 코드를 분석해 보겠습니다.
먼저, 클래스에 Spring 서비스 빈임을 나타내는 "@Service" 어노테이션이 있습니다.
다음으로, BoardRepository를 매개변수로 받는 생성자가 있습니다. 이를 통해 Spring의 종속성 주입을 사용하여 BoardRepository 인스턴스를 BoardService 인스턴스에 주입할 수 있습니다.
"getAllBoards" 메서드는 데이터베이스에 있는 모든 보드의 목록을 반환합니다. 이 작업은 BoardRepository에서 "findAll" 메서드를 호출하여 수행됩니다.
"getBoardById" 메서드는 Long id 매개변수를 받아 해당 ID를 가진 보드를 반환합니다. 보드를 찾지 못하면 보드 리포지토리에서 "findById" 메서드를 호출하면 됩니다. 보드를 찾을 수 없으면 BoardNotFoundException이 발생합니다.
"createBoard" 메서드는 보드 객체를 매개변수로 받아 BoardRepository의 "save" 메서드를 사용하여 데이터베이스에 새 보드를 생성합니다.
"updateBoard" 메서드는 Long id 매개변수와 업데이트된 보드 객체를 받습니다. 데이터베이스에서 지정된 id를 가진 보드를 검색하고 제목 및 콘텐츠 속성을 새 값으로 업데이트한 다음 BoardRepository의 "save" 메서드를 사용하여 업데이트된 보드를 데이터베이스에 다시 저장합니다.
마지막으로, "deleteBoard" 메서드는 긴 id 매개변수를 사용하여 데이터베이스에서 해당 id의 보드를 삭제하고 BoardRepository의 "deleteById" 메서드를 사용하여 삭제합니다.
BoardService 클래스가 완성되었으므로 이제 CRUD 작업을 위한 REST 엔드포인트 생성으로 넘어갈 수 있습니다.
이제 BoardService 클래스를 만들었으므로 여기에 몇 가지 메서드를 추가해 보겠습니다. 보드를 추가하고 검색하는 메서드와 보드에 대한 목록을 추가하고 검색하는 메서드가 필요합니다.
다음은 이러한 메서드가 추가된 BoardService 클래스의 예제 코드입니다:
class BoardService:
def __init__(self):
self.boards = []
def add_board(self, board):
self.boards.append(board)
def get_board(self, board_id):
for board in self.boards:
if board.id == board_id:
return board
return None
def add_list(self, board_id, list_name):
board = self.get_board(board_id)
if board:
list_id = board.add_list(list_name)
return list_id
else:
return None
def get_lists(self, board_id):
board = self.get_board(board_id)
if board:
return board.get_lists()
else:
return None
이러한 각 방법에 대해 자세히 살펴보겠습니다:
add_board(self, board): 이 메서드는 보드 목록에 새 보드를 추가합니다.
get_board(self, board_id): 이 함수는 보드 목록에서 주어진 board_id를 가진 보드가 있는지 검색하여 존재할 경우 반환합니다. 보드를 찾지 못하면 None을 반환합니다.
add_list(self, board_id, list_name): 이 함수는 먼저 get_board를 호출하여 주어진 board_id를 가진 보드를 검색합니다. 보드가 존재하면 보드의 add_list 메서드를 호출하여 주어진 list_name으로 새 목록을 추가합니다. 이 메서드는 새로 생성된 리스트의 ID를 반환합니다. 보드가 존재하지 않으면 None을 반환합니다.
get_list(self, board_id): 이 메서드는 먼저 get_board를 호출하여 주어진 board_id를 가진 보드를 검색합니다. 보드가 존재하면 보드의 get_lists 메서드를 호출하여 보드에 있는 모든 리스트를 가져옵니다. 이 메서드는 리스트 목록을 반환합니다. 보드가 존재하지 않으면 None을 반환합니다.
이 메서드를 사용하면 보드와 해당 보드의 목록을 추가하고 검색할 수 있습니다. 필요에 따라 카드 추가 및 검색, 목록 간 카드 이동, 기타 보드 관련 작업을 위한 다른 메서드도 추가할 수 있습니다.
이제 보드 서비스를 만들었으니 그 기능을 정의해야 합니다.
게시판 서비스의 주요 기능은 사용자가 새 게시판을 만들고, 기존 게시판을 검색하고, 기존 게시판을 업데이트하고, 게시판을 삭제할 수 있도록 하는 것입니다. 이러한 기능은 Django REST 프레임워크의 뷰셋과 라우터를 사용하여 정의할 수 있습니다.
다음은 보드 서비스에 대한 뷰셋을 정의하는 방법의 예입니다:
from rest_framework import viewsets
from .serializers import BoardSerializer
from .models import Board
class BoardViewSet(viewsets.ModelViewSet):
queryset = Board.objects.all()
serializer_class = BoardSerializer
위 코드에서는 필요한 종속 요소인 뷰세트, BoardSerializer, 보드 모델을 임포트합니다. BoardViewSet 클래스를 정의하고 데이터베이스에서 모든 보드를 검색하도록 쿼리 집합을 설정하고, 그 serializer_class를 앞서 정의한 BoardSerializer로 설정합니다.
그런 다음 장고 REST 프레임워크의 라우터를 사용하여 보드 서비스에 대한 URL 경로를 정의할 수 있습니다:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BoardViewSet
router = DefaultRouter()
router.register(r'boards', BoardViewSet)
urlpatterns = [
path('', include(router.urls)),
]
위의 코드에서는 필요한 종속성(경로, include, DefaultRouter)을 Django에서 가져오고, BoardViewSet을 views.py 파일에서 가져옵니다. 라우터를 정의하고 "boards" 엔드포인트 아래에 BoardViewSet을 등록합니다. 그런 다음 라우터의 URL 패턴을 urlpattern에 포함합니다.
위의 코드를 통해 사용자가 게시판을 만들고, 검색하고, 업데이트하고, 삭제할 수 있도록 게시판 서비스에 대한 뷰셋과 URL 경로를 정의했습니다.
다음으로 BoardService 클래스에서 create_board 메서드를 정의하겠습니다. 이 메서드는 새 보드를 생성하고 데이터베이스에 저장하는 데 필요한 매개 변수를 받습니다. 다음은 구현 예시입니다:
from .models import Board
class BoardService:
# previous methods...
def create_board(self, name, description, owner):
board = Board(name=name, description=description, owner=owner)
board.save()
return board
이 구현에서는 먼저 매개 변수 이름, 설명 및 소유자를 사용하여 새 보드 객체를 만듭니다. 그런 다음 저장 메서드를 사용하여 이 새 객체를 데이터베이스에 저장합니다. 마지막으로 새로 생성된 보드 객체를 반환합니다.
이 구현에서는 보드 모델에 이름, 설명 및 소유자에 대한 필드가 있다고 가정합니다. 이 구현이 올바르게 작동하려면 보드 모델에서 이러한 필드를 정의해야 합니다.
BoardService 클래스에서 create_board 메서드를 정의한 후에는 필요에 따라 새 보드를 만드는 데 사용할 수 있습니다. 예를 들어 양식 제출을 처리하는 보기 함수에서 이 메서드를 사용하여 양식 데이터를 기반으로 새 게시판을 만들 수 있습니다.