이전 MyBatis 프로젝트에서 사용하던 Mapper와 XML을 이용한 데이터 접근 방식 대신 Spring Data JPA를 이용하여 데이터를 관리하였다.
이전에는 SQL을 직접 작성하고 MyBatis가 SQL과 객체를 연결해 주는 구조였다면, 이번 프로젝트에서는 JPA와 Hibernate가 SQL을 자동으로 생성하여 실행한다는 점이 가장 큰 변화였다.
프로젝트를 진행하면서 Entity를 중심으로 데이터를 관리하는 방식과 Repository 인터페이스를 활용한 CRUD 구현 방법을 새롭게 학습하였다.
이전 프로젝트에서는 Entity가 단순히 테이블의 컬럼을 표현하는 역할을 하였다.
이번 프로젝트에서는 Entity에 다양한 JPA 어노테이션을 추가하여 데이터베이스 테이블의 구조와 제약 조건까지 함께 정의하였다.
@Entity
@Table(name = "books")
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(
name = "book_name",
nullable = false,
length = 100,
unique = true
)
private String title;
private String author;
}
가장 새롭게 배운 부분은 JPA 어노테이션이다.
@Entity
는 해당 클래스를 데이터베이스의 테이블과 연결하는 역할을 한다.
@Table(name = "books")
는 테이블 이름을 지정한다.
또한
@Id
@GeneratedValue(...)
를 사용하여 기본키를 자동으로 생성하도록 설정하였다.
이전 MyBatis에서는 SQL에서 AUTO_INCREMENT를 직접 사용하거나 Mapper를 통해 INSERT를 수행하였다.
하지만 JPA에서는 Entity에 어노테이션만 작성하면 Hibernate가 자동으로 처리해 준다는 점이 가장 큰 차이였다.
이전 프로젝트에서는
Controller
↓
Service
↓
Mapper
↓
XML
↓
SQL
구조였다.
이번 프로젝트에서는
Controller
↓
Service
↓
Repository
↓
Hibernate
↓
Database
구조로 변경되었다.
public interface BookRepository
extends JpaRepository<Book, Long> {
}
이번 프로젝트에서 가장 신기했던 부분은 Repository 안에 아무런 메서드도 작성하지 않았다는 것이다.
기존 MyBatis에서는
등의 메서드를 직접 선언하고 XML까지 작성해야 했다.
하지만
extends JpaRepository<Book, Long>
한 줄만 작성해도
등의 CRUD 기능을 바로 사용할 수 있었다.
즉, CRUD를 직접 구현하지 않아도 Spring Data JPA가 기본 기능을 제공한다는 점을 새롭게 배웠다.
기존 MyBatis에서는
insert()
↓
update()
를 각각 구현하였다.
하지만 JPA에서는
bookRepository.save(book);
하나만 사용한다.
Service를 보면
public void addBook(Book book) {
bookRepository.save(book);
}
등록에도
public void updateBook(Book book) {
bookRepository.save(book);
}
수정에도 동일한 메서드를 사용한다.
JPA는 객체의 id 값을 확인하여
를 자동으로 수행한다.
이전 프로젝트에서는 등록과 수정 SQL을 각각 작성해야 했지만, JPA에서는 save() 하나로 두 기능을 처리할 수 있다는 점이 인상적이었다.
조회 방식도 이전 프로젝트와 달라졌다.
return bookRepository.findById(id)
.orElseThrow();
MyBatis에서는 조회 결과가 없으면 null을 반환하는 경우가 많았다.
하지만 JPA의
findById()
는 Optional을 반환한다.
따라서
.orElseThrow()
를 이용하여 데이터가 존재하지 않으면 예외를 발생시키도록 구현하였다.
이를 통해 null을 직접 검사하지 않고도 안전하게 데이터를 처리하는 방법을 배울 수 있었다.
입력 데이터는 이전 프로젝트처럼 DTO를 이용하였다.
public record BookFormDTO(
String title,
String author
) {
public Book toEntity() {
return Book.builder()
.title(title)
.author(author)
.build();
}
}
사용자가 입력한 데이터를 바로 Entity로 사용하는 것이 아니라 DTO를 통해 Entity로 변환하였다.
이 구조는 이전 프로젝트에서도 사용하였지만, JPA에서도 동일한 방식으로 DTO를 활용할 수 있다는 점을 확인하였다.
Controller는 크게 달라지지 않았다.
@PostMapping
public String addBook(
@ModelAttribute BookFormDTO dto) {
bookService.addBook(dto.toEntity());
return "redirect:/";
}
Controller는 사용자의 요청을 받아 DTO를 생성하고 Service를 호출하는 역할만 수행한다.
즉,
Controller
↓
DTO
↓
Service
↓
Repository
흐름은 유지되었지만,
데이터 접근 계층이 Mapper에서 Repository로 변경되었다는 점이 가장 큰 차이였다.
| MyBatis 프로젝트 | JPA 프로젝트 |
|---|---|
| Mapper + XML | Repository + JpaRepository |
| SQL 직접 작성 | SQL 자동 생성(Hibernate) |
| insert(), update() 분리 | save() 하나로 처리 |
| Mapper 구현 | JpaRepository 상속 |
| SQL 중심 개발 | Entity 중심 개발 |
| findById() → 객체 | findById() → Optional |
이번 프로젝트를 통해 가장 크게 느낀 점은 JPA는 SQL을 작성하는 기술이 아니라 객체를 중심으로 데이터를 관리하는 기술이라는 것이다. 이전에는 SQL을 직접 작성하고 MyBatis가 이를 실행하도록 구성했지만, JPA에서는 @Entity, @Id, @Column과 같은 어노테이션으로 객체를 정의하면 Hibernate가 적절한 SQL을 생성해 준다는 점이 매우 인상적이었다.
또한 JpaRepository를 상속하는 것만으로 기본 CRUD 기능을 사용할 수 있었고, save() 하나로 등록과 수정을 처리하거나 findById()가 Optional을 반환하는 구조를 경험하면서, SQL 중심의 개발 방식에서 객체 중심의 개발 방식으로 사고를 전환하는 계기가 되었다.