프레임워크/Spring

[Spring Boot] Spring CRUD 구현 - Day 2

munsik22 2026. 3. 11. 20:12

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webmvc'
    implementation 'org.springframework.boot:spring-boot-starter-webservices'
    implementation 'org.projectlombok:lombok'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // 추가
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
    runtimeOnly 'com.h2database:h2' // 추가
    implementation 'org.springframework.boot:spring-boot-h2console' // 추가
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
  • Spring Boot 4.x 버전부터는 H2 콘솔을 사용하려면 h2console 의존성을 추가해야 한다.

application.properties

spring.application.name=spring-crud-back
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

 

  • H2 인메모리 DB 사용
  • 앱 실행 시 테이블 만들고 종료 시 정리
  • 실행되는 SQL을 콘솔에 출력
  • H2 콘솔 활성화

Post.java

package com.example.springcrudback.post;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;

@Getter @Entity
public class Post {

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

    @Setter
    private String title;
    @Setter
    private String content;

    public Post() {
    }

    public Post(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

  • Spring Boot는 @Entity가 붙은 클래스를 자동으로 스캔해 JPA 엔티티로 인식한다.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): 기본키 값을 개발자가 직접 넣지 않고 DB가 자동으로 만든다.

PostRequest.java

package com.example.springcrudback.post;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class PostRequest {

    private String title;
    private String content;

    public PostRequest() {
    }

    public PostRequest(String title, String content) {
        this.title = title;
        this.content = content;
    }

}
  • 어제 코드와 동일하다.

PostRepository.java

package com.example.springcrudback.post;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostRepository extends JpaRepository<Post, Long> {
}
  • JpaRepository<Post, Long>를 상속하면 기본 CRUD 메서드를 바로 사용할 수 있다.
  • Spring Data JPA는 리포지토리를 인터페이스로 정의해 데이터 접근을 쉽게 해주고, 이름 기반 쿼리 생성도 지원한다.

PostService.java

package com.example.springcrudback.post;

import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class PostService {

    private final PostRepository postRepository;

    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    public Post create(PostRequest request) {
        Post post = new Post(request.getTitle(), request.getContent());
        return postRepository.save(post);
    }

    public List<Post> findAll() {
        return postRepository.findAll();
    }

    public Post findById(Long id) {
        return postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Post with id " + id + " does not exist"));
    }

    public Post update(Long id, PostRequest request) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Post with id " + id + " does not exist"));

        post.setTitle(request.getTitle());
        post.setContent(request.getContent());

        return postRepository.save(post);
    }

    public void delete(Long id) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Post with id " + id + " does not exist"));

        postRepository.delete(post);
    }
}
  • PostController에서 정의했던 함수들을 PostService에 따로 정의했다.

PostController.java

package com.example.springcrudback.post;

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

import java.util.List;

@RestController @RequestMapping("/posts")
public class PostController {

    private final PostService postService;

    public PostController(PostService postService) {
        this.postService = postService;
    }

    @PostMapping @ResponseStatus(HttpStatus.CREATED)
    public Post create(@RequestBody PostRequest request) {
        return postService.create(request);
    }

    @GetMapping
    public List<Post> findAll() {
        return postService.findAll();
    }

    @GetMapping("/{id}")
    public Post findById(@PathVariable Long id) {
        return postService.findById(id);
    }

    @PutMapping("/{id}")
    public Post update(@PathVariable Long id, @RequestBody PostRequest request) {
        return postService.update(id, request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        postService.delete(id);
    }
}

실행 흐름

클라이언트 요청 → Controller → Service → Repository → H2 데이터베이스 → 결과 반환
  • 어제 코드와 가장 큰 차이점은 Map이 아니라 DB에 저장한다는 점이다.
  • 하지만 in-memory 모드로 사용하기 때문에 서버가 닫히면 저장된 데이터가 사라진다는 점은 같다.
  • 데이터를 영구적으로 저장하기 위해서는 file-based 모드를 사용하거나 MySQL 등을 사용해야 한다.