백엔드

JPA vs MyBatis

anyway03 2025. 4. 27. 15:30

백엔드 프로젝트를 개발하면서 가장 먼저 맞닥뜨리는 선택지 중 하나는 데이터베이스와 통신하는 방법을 정하는 일이다.

 

부트캠프 과정을 회고하면, 마이바티스 사용이 대부분이었다.

스프링 프로젝트의 공식 프로젝트인 펫클리닉 REST에서는 JPA방식을 사용하고 있다.


이 두 방법은 각각 장단점이 명확하게 다르기 때문에, 정리해보려고 한다.

1-1. JPA 방식 (Java Persistence API)

JPA는 표준 ORM(Object Relational Mapping)이다.

ORM? : Object-relational mapping(객체 관계 매핑), 객체는 객체대로, db는 db대로 설계, orm 프레임워크가 매핑.


Entity(클래스)와 테이블을 연결해서 DB를 객체처럼 다룰 수 있도록 해준다.

 

  • Entity 클래스를 기반으로 SQL을 자동 생성하거나 실행할 수 있다.
  • JPQL이라는 객체 지향 쿼리 언어를 사용한다.
  • 복잡한 조인이나 최적화 쿼리는 따로 튜닝해야 한다.

장점:

  • 개발자가 직접 SQL을 작성할 일이 적어진다.
  • 유지보수성, 코드 일관성이 높다.
  • 트랜잭션, 캐시 관리 등을 일관성 있게 처리할 수 있다.

단점:

  • 학습 비용이 높다. (영속성 컨텍스트, 지연로딩 등 개념 이해 필요)
  • 내부 동작을 모르면 디버깅이 어려울 수 있다.
  • 복잡한 쿼리는 오히려 비효율이 생길 수 있다.
@Entity
@Table(name = "owners")
public class Owner extends Person {

    @Column(name = "address")
    private String address;

    @Column(name = "city")
    private String city;

    @Column(name = "telephone")
    private String telephone;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner")
    private Set<Pet> pets = new HashSet<>();

    // getters and setters 생략
}

펫클리닉 REST 버전(PetClinic REST API)의 일부

 

 

  • @Entity: 이 클래스가 DB 테이블과 매핑되는 JPA 엔티티라는 뜻.
  • @Table(name = "owners"): DB의 owners 테이블과 매핑하겠다는 뜻.
  • @Column(name = "address"): 필드를 DB의 address 컬럼과 연결.
  • @OneToMany: Owner 한 명이 여러 Pet을 가질 수 있다는 관계 설정.

객체 자체를 테이블과 매핑하는 느낌.

CRUD같은 기본 작업은 JPA의 메서드가 알아서 해 줘서 편한데, ( save(), findById()) 조인이나 서브쿼리를 쓰게 되면 학습 난이도가 올라간다.

 

 

1-2. MyBatis 방식

MyBatis는 SQL을 직접 작성하는 방식의 데이터 매핑 프레임워크다. ORM은 아니고, SQL Mapper라고 부른다.

 

  • SQL을 XML 파일이나 어노테이션에 명시적으로 작성한다.
  • 복잡한 쿼리도 개발자가 자유롭게 튜닝할 수 있다.
  • 객체 매핑은 수동으로 매핑 규칙을 설정해줘야 한다.

장점:

  • SQL 최적화나 튜닝이 쉬운 편이다.
  • 쿼리 구조를 명확하게 컨트롤할 수 있다.
  • 복잡한 비즈니스 로직(DB 레벨) 처리가 편하다.

단점:

  • 쿼리가 많아질수록 코드 관리가 어려워질 수 있다.
  • SQL이 하드코딩 -> 유지보수 시 실수가 늘어날 수 있다.
 
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM users WHERE email = #{email}")
    Optional<User> findByEmail(String email);

    @Insert("INSERT INTO users (email, password, name) VALUES (#{email}, #{password}, #{name})")
    void insertUser(User user);

    @Update("UPDATE users SET password = #{password}, name = #{name} WHERE email = #{email}")
    void updateUser(User user);

    @Delete("DELETE FROM users WHERE email = #{email}")
    void deleteUser(String email);
}
 

 

  • @Mapper: 이 인터페이스가 MyBatis 매퍼임을 명시.
  • 각 메서드마다 직접 SQL을 작성해서 DB 작업을 수행.
  • #{} 문법으로 파라미터를 직접 바인딩.
  • CRUD 모두 명시적으로 SQL이 필요.

 

직접 SELECT 구문을 적어주고, 파라미터 바인딩도 명시적으로 처리해야 한다.

주의 : 파라미터를 받을때 ${파라미터} 방식을 쓰면 인젝션 공격에 취약함. (값이 그대로 SQL에 삽입될 수 있음)


결론적으로,

  • 단순한 CRUD + 규칙적인 구조 → JPA
  • 복잡한 비즈니스 로직, 최적화 중심 → MyBatis
  • 지금까지는, 사용하기 쉬운 쪽은 마이바티스인 것 같다.(어노테이션이 알아서 해줌...)

 

 

'백엔드' 카테고리의 다른 글

디스패처 서블릿이란?  (1) 2026.02.14
배포 연습 서버로 Ubuntu를 선택한 이유  (0) 2025.04.27