1. 개요

JPA 엔터티 에 CRUD 기능을 제공하는 DAO 계층 의 구현은 대부분의 경우 피하고 싶은 반복적이고 시간 소모적 인 작업 일 수 있습니다. 다행스럽게도  Spring Boot 는 표준 JPA 기반 CRUD 리포지토리 레이어를 통해 CRUD 애플리케이션을 쉽게 만들 수 있습니다.

이 예제에서는 Spring Boot 및 Thymeleaf를 사용하여 CRUD 웹 애플리케이션을 개발하는 방법을 배웁니다 .

2. Maven 의존성

이 경우 간단한 의존성 관리, 버전 관리 및 플러그인 구성을 위해 spring-boot-starter-parent 를 사용합니다. 결과적으로 Java 버전을 재정의하는 경우를 제외하고 pom.xml 파일 에 프로젝트 의존성의 버전을 지정할 필요가 없습니다 .

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
</dependencies>

3. 도메인 계층

모든 프로젝트 의존성이 이미 준비되었으므로 이제 순진한 도메인 계층을 구현해 보겠습니다.

간단하게하기 위해이 레이어에는 User 엔터티 모델링을 담당하는 단일 클래스가 포함 됩니다.

@Entity
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    
    @NotBlank(message = "Name is mandatory")
    private String name;
    
    @NotBlank(message = "Email is mandatory")
    private String email;

    // standard constructors / setters / getters / toString
}

@Entity 어노테이션을 사용 하여 클래스에 어노테이션을 작성했음을 기억하십시오 . 따라서 Hibernate 인 JPA 구현은  이 경우 도메인 엔티티에서 CRUD 작업을 수행 할 수 있습니다. Hibernate에 대한 입문 사용방법(예제)를 보려면 Hibernate 5 with Spring 예제을 방문하십시오 .

또한 @NotBlank 제약 조건으로 이름이메일 필드를 제한했습니다. 이것은 데이터베이스에서 엔티티를 유지하거나 업데이트하기 전에 제한된 필드를 검증하기 위해 Hibernate Validator를 사용할 수 있음을 의미합니다.

이에 대한 기본 사항  은 Bean Validation에 대한 관련 예제을 확인하십시오 .

4. 리포지토리 계층

이 시점에서 샘플 웹 애플리케이션은 아무 작업도 수행하지 않습니다. 그러나 그것은 곧 바뀔 것입니다.

SpringData JPA를 사용하면 최소한의 번거 로움으로 JPA 기반 저장소 (DAO 패턴 구현의 멋진 이름)를 구현할 수 있습니다.

SpringData JPA 는 Spring Boot의 spring-boot-starter-data-jpa 의 핵심 구성 요소  로, JPA 구현 위에 배치 된 강력한 추상화 계층을 통해 CRUD 기능을 쉽게 추가 할 수 있습니다. 이 추상화 계층을 사용하면 처음부터 자체 DAO 구현을 제공하지 않고도 지속성 계층에 액세스 할 수 있습니다.

응용 프로그램에 User 객체 에 대한 기본 CRUD 기능을 제공하려면 CrudRepository 인터페이스 를 확장하기 만하면 됩니다.

@Repository
public interface UserRepository extends CrudRepository<User, Long> {}

그리고 그게 다야! CrudRepository 인터페이스를 확장함으로써 SpringData JPA는 우리를 위해 저장소의 CRUD 메소드에 대한 구현을 제공 할 것입니다.

5. 컨트롤러 계층

spring-boot-starter-data-jpa 가 기본 JPA 구현 위에 배치하는 추상화 계층 덕분에 기본 웹 계층을 통해 웹 애플리케이션에 일부 CRUD 기능을 쉽게 추가 할 수 있습니다 .

우리의 경우 단일 컨트롤러 클래스는 GET 및 POST HTTP 요청을 처리하는 데 충분하며이를 UserRepository 구현 에 대한 호출에 매핑합니다 .

컨트롤러 클래스는 Spring MVC의 주요 기능 중 일부에 의존합니다. Spring MVC에 대한 자세한 사용방법(예제)는 Spring MVC 예제을 확인하십시오 .

컨트롤러의 showSignUpForm ()addUser () 메서드 부터 시작하겠습니다 .

전자는 사용자 등록 양식을 표시하고 후자는 제한된 필드의 유효성을 검사 한 후 데이터베이스에 새 엔터티를 유지합니다.

엔터티가 유효성 검사를 통과하지 못하면 가입 양식이 다시 표시됩니다. 그렇지 않으면 엔티티가 저장되면 지속되는 엔티티 List이 해당보기에서 업데이트됩니다.

@Controller
public class UserController {
    
    @GetMapping("/signup")
    public String showSignUpForm(User user) {
        return "add-user";
    }
    
    @PostMapping("/adduser")
    public String addUser(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "add-user";
        }
        
        userRepository.save(user);
        return "redirect:/index";
    }

    // additional CRUD methods
}

/ index URL에 대한 매핑도 필요합니다 .

@GetMapping("/index")
public String showUserList(Model model) {
    model.addAttribute("users", userRepository.findAll());
    return "index";
}

UserController  내에는  데이터베이스에서 제공된 ID 와 일치하는 User 엔티티   를 가져 오는 역할을 하는 showUpdateForm () 메서드도  있습니다.

엔티티가 존재하는 경우 업데이트 양식보기에 모델 속성으로 전달되므로 양식은 이름이메일 필드 의 값으로 채울 수 있습니다 .

@GetMapping("/edit/{id}")
public String showUpdateForm(@PathVariable("id") long id, Model model) {
    User user = userRepository.findById(id)
      .orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
    
    model.addAttribute("user", user);
    return "update-user";
}

마지막으로 UserController 클래스 내에 updateUser ()deleteUser () 메서드가 있습니다 .

첫 번째 항목은 데이터베이스에 업데이트 된 항목을 유지하고 마지막 항목은 지정된 항목을 제거합니다.

두 경우 모두 지속되는 엔터티 List이 그에 따라 업데이트됩니다.

@PostMapping("/update/{id}")
public String updateUser(@PathVariable("id") long id, @Valid User user, 
  BindingResult result, Model model) {
    if (result.hasErrors()) {
        user.setId(id);
        return "update-user";
    }
        
    userRepository.save(user);
    return "redirect:/index";
}
    
@GetMapping("/delete/{id}")
public String deleteUser(@PathVariable("id") long id, Model model) {
    User user = userRepository.findById(id)
      .orElseThrow(() -> new IllegalArgumentException("Invalid user Id:" + id));
    userRepository.delete(user);
    return "redirect:/index";
}

6. 뷰 레이어

이 시점에서 사용자 엔터티 에 대해 CRUD 작업을 수행하는 기능적 컨트롤러 클래스를 구현했습니다 . 그럼에도 불구하고이 스키마에는 여전히 누락 된 구성 요소 인 뷰 레이어가 있습니다.

세 이하 SRC / 메인 / 자원 / 템플릿은 우리가 가입 양식 업데이트 양식을 표시하고, 지속 List 렌더링에 필요한 HTML 템플릿을 생성 할 필요가 폴더를 사용자 개체를,

소개에서 언급했듯이 템플릿 파일을 구문 분석하기위한 기본 템플릿 엔진으로 Thymeleaf를 사용합니다.

다음은 add-user.html 파일 의 관련 섹션입니다 .

<form action="#" th:action="@{/adduser}" th:object="${user}" method="post">
    <label for="name">Name</label>
    <input type="text" th:field="*{name}" id="name" placeholder="Name">
    <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
    <label for="email">Email</label>
    <input type="text" th:field="*{email}" id="email" placeholder="Email">
    <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
    <input type="submit" value="Add User">   
</form>

우리가 사용했던 방법 공지 사항 @ {/ adduser 명령} 형태의 지정 URL 식을 행동 특성과 $ {} 등의 값으로 템플릿에 동적 컨텐츠를 삽입하는 변수 표현, 이름이메일 필드와 사후 검증을 오류 .

add-user.html유사하게 update-user.html 템플릿의 모양은 다음과 같습니다.

<form action="#" 
  th:action="@{/update/{id}(id=${user.id})}" 
  th:object="${user}" 
  method="post">
    <label for="name">Name</label>
    <input type="text" th:field="*{name}" id="name" placeholder="Name">
    <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
    <label for="email">Email</label>
    <input type="text" th:field="*{email}" id="email" placeholder="Email">
    <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
    <input type="submit" value="Update User">   
</form>

마지막으로 기존 항목을 편집하고 제거하기위한 링크와 함께 지속되는 항목 List을 표시 하는 index.html 파일이 있습니다.

<div th:switch="${users}">
    <h2 th:case="null">No users yet!</h2>
        <div th:case="*">
            <h2>Users</h2>
            <table>
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Email</th>
                        <th>Edit</th>
                        <th>Delete</th>
                    </tr>
                </thead>
                <tbody>
                <tr th:each="user : ${users}">
                    <td th:text="${user.name}"></td>
                    <td th:text="${user.email}"></td>
                    <td><a th:href="@{/edit/{id}(id=${user.id})}">Edit</a></td>
                    <td><a th:href="@{/delete/{id}(id=${user.id})}">Delete</a></td>
                </tr>
            </tbody>
        </table>
    </div>      
    <p><a href="/signup">Add a new user</a></p>
</div>

단순함을 위해 템플릿은 다소 골격 적으로 보이며 불필요한 외형 을 추가하지 않고 필요한 기능 만 제공합니다 .

HTML / CSS에 너무 많은 시간을 들이지 않고 템플릿에 개선되고 눈길을 끄는 모양을 제공 하기 위해 Shards 와 같은 무료 Twitter Bootstrap UI 키트를 쉽게 사용할 수 있습니다  .

7. 응용 프로그램 실행

마지막으로 애플리케이션의 진입 점을 정의하겠습니다. 대부분의 Spring Boot 애플리케이션과 마찬가지로 일반적인 이전 main () 메서드로 이를 수행 할 수 있습니다 .

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

이제 IDE에서 "실행"을 누른 다음 브라우저를 열고 http : // localhost : 8080 을 가리 킵니다 .

빌드가 성공적으로 컴파일 된 경우 새 엔티티를 추가하고 기존 엔티티를 편집 및 제거하기위한 링크가있는 기본 CRUD 사용자 대시 보드가 표시됩니다.

8. 결론

이 예제에서는 Spring Boot 및 Thymeleaf를 사용하여 기본 CRUD 웹 애플리케이션을 빌드하는 방법을 배웠습니다.

평소와 같이 문서에 표시된 모든 코드 샘플 은 GitHub에서 사용할 수 있습니다 .