JPA와 MyBatis

 

결론부터 말하자면 JPA와 MyBatis 둘 다 Persistence Framework이지만 JPA는 ORM이고 MyBatis는 SQL Mapper이다. 

ORM? SQL Mapper? Persistence? 

먼저 Persistence가 무엇인지 알아보도록 하자.

 

Persistence란?? 

Persistence(영속성)란 프로그램이 종료되어도 사라지지 않는 것을 의미한다. 예를 들어 애플리케이션을 사용하다가 종료하고 다시 실행했을 때 이전에 저장한 데이터를 다시 불러올 수 있는 것을 말한다. 

 

Persistence Framework

Persistence Framework란 데이터의 저장, 조회, 변경, 삭제 (CRUD)를 다루는 클래스 및 설정 파일들의 집합으로, Persistence Framework를 사용하면 JDBC 프로그래밍의 복잡함이나 번거로움 없이 간단한 작업만으로 데이터베이스와 연동되는 시스템을 빠르게 개발할 수 있다. 

SQL 문장으로 직접 DB 데이터를 다루는 SQL Mapper와 객체를 통해 간접적으로 DB 데이터를 다루는 ORM(Object-Relational mapper)이 있다. 

 

 

SQL Mapper

Object와 SQL의 필드를 매핑하여 데이터를 객체화하는 기술이다.

객체와 테이블 간의 관계를 매핑하는 것이 아니라, SQL 문을 직접 작성하고 쿼리 수행 결과를 객체의 필드와 매핑한다. SQL에 의존적인 방법이다. 대표적인 프레임워크로는 MyBatis가 있다.

 

MyBatis

 

SQL 쿼리들을 XML 파일에 작성하여 코드와 SQL을 분리하여 관리한다. (관심사 분리)

기존의 JDBC API만 사용한다면 결과를 가져와서 객체의 인스턴스에 매핑하기 위한 많은 코드가 필요하겠지만, MyBatis는 그 코드들을 작성하지 않아도 되게 해준다. 또한 JDBC만 사용했을 때 작성했던 중복되는 작업 대부분을 없앨 수 있고, DAO로부터 SQL 문을 분리하므로 코드의 간결성 및 유지 보수성을 향상시킨다. 

이를 통해 JDBC만을 사용하여 개발할 때보다 많은 편리함을 느낄 수 있었지만 아직 해소되지 않은 문제점들이 있다.

 

  • SQL 중심적인 개발
  • 비슷한 CRUD 작업 반복
  • 패러다임 불일치

 

Member.java

public class Member {
    private Long id; // 아이디
    
    private String name; // 이름

    // getters and setters
}

 

 

MemberMapper.xml

<mapper namespace="MemberMapper">
    <insert id="insertMember" parameterType="Member">
        INSERT INTO Member (name, id) VALUES (#{name}, #{id})
    </insert>
    <select id="getMemberById" resultType="Member">
        SELECT * FROM Member WHERE id = #{id}
    </select>
    <update id="updateMember" parameterType="Member">
        UPDATE Member SET name = #{name} WHERE id = #{id}
    </update>
    <delete id="deleteMember" parameterType="int">
        DELETE FROM Member WHERE id = #{id}
    </delete>
</mapper>

예를 들어 위의 코드처럼 Member 테이블이 있고, 해당 테이블은 아이디와 이름만 가지고 있었다고 가정해 보자.

처음에는 아이디와 이름만 있었지만, 추후에 나이 필드도 추가된다면?

아래처럼 Member와 관련된 모든 SQL 문과 객체의 필드를 수정해 주어야 한다. 

 

Member.java

public class Member {
    private Long id; // 아이디
    
    private String name; // 이름
    
    private int age; // 나이 ++++++ 추가 ++++++ 

    // getters and setters
}

 

MemberMapper.xml

<mapper namespace="MemberMapper">
    <insert id="insertMember" parameterType="Member">
        INSERT INTO Member (name, id, age) VALUES (#{name}, #{id}, #{age})
    </insert>
    <select id="getMemberById" resultType="Member">
        SELECT * FROM Member WHERE id = #{id}
    </select>
    <update id="updateMember" parameterType="Member">
        UPDATE Member SET name = #{name}, age = #{age} WHERE id = #{id}
    </update>
    <delete id="deleteMember" parameterType="int">
        DELETE FROM Member WHERE id = #{id}
    </delete>
</mapper>

이렇듯 변경될 때마다 개발자가 직접 관련된 코드를 수정하고, 추가하게 된다. 수정 과정에서 하나라도 놓칠 경우 - 예를 들어서 회원 정보를 업데이트할 때 나이 변경해 주는 쿼리를 추가 안 해주었다면? 회원은 나이를 수정하였지만, 나이가 변경되지 않는 버그가 발생하게 된다.

결국 물리적으로 코드와 SQL을 분리하였지만 논리적으로는 강한 의존성을 갖게 된다.

또한, 테이블마다 비슷한 CRUD(생성, 조회, 수정, 삭제) 작업이 계속해서 일어나는데 이를 개발자가 직접 일일이 쿼리를 작성하므로 반복적인 DAO 개발을 피할 수 없게 된다. 이렇게 된다면 비즈니스 로직 구현보다 데이터베이스 접근 로직 구현에 집중하는 시간이 많아지게 된다. 

 

마지막으로, 우리가 자주 사용하는 관계형 데이터베이스는 객체 구조와는 다른 데이터 중심의 구조를 갖고 있어서 상속, 다형성과 같은 개념을 가진 객체를 데이터베이스에 직접 저장하거나 조회하기에 어려움이 있다. 객체와 관계형 데이터베이스는 각각 지향하는 목적이 다르기 때문에 사용방법과 표현 방식에 차이가 있을 수밖에 없다. 때문에 객체지향적인 설계일수록 패러다임 불일치는 심화될 수밖에 없다. 이를 해결하기 위해 나온 것이 ORM이다. 

 

ORM

ORM은 Object-Relational Mapping의 약자로, 객체와 관계형 데이터베이스를 매핑하는 도구를 의미한다. 

객체 간의 관계를 바탕으로 SQL 문을 자동으로 생성하고 직관적인 메서드로 데이터를 조작한다. 대표적인 예로는 JPA가 있다. 

 

JPA

Java Persistence API의 약자로, Java ORM 표준 명세로, 인터페이스들을 모아둔 것이다. 때문에 JPA를 사용하려면 JPA 인터페이스를 구현한 ORM 프레임워크를 사용해야 한다. 이를 구현한 프레임워크는 Hibernate, EclipseLink 등이 있다. 

JPA를 사용한다면 SQL 중심적인 개발이 아닌 객체 중심 개발이 가능해지고, 패러다임의 불일치를 해결할 수 있다. 또한 기존에는 개발자가 직접 SQL 문을 작성하기 때문에 유지 보수가 어려웠지만, JPA는 SQL 문을 자동으로 생성하여 주므로 바뀐 필드가 있어도 필드를 추가만 해준다면 해당 Entity를 사용하는 SQL 문은 JPA가 처리를 해준다.  

 

Member.java

@Entity
public class Member {
    @Id
    private Long id;
    
    private String name;

    // getters and setters
}

 

CRUD

public interface MemberRepository extends JpaRepository<Member, Long> {

	// 조회
	Optional<Member> findById(Long id);
 	// 저장
	Member save(Member member);
 	// 삭제
	void deleteById(Long id);
  
}

이렇게 개발자가 쿼리를 직접 작성하지 않아도 되므로(물론 복잡한 쿼리는 직접 작성할 수도 있다.) 비즈니스 로직에 더 집중할 수 있다. MyBatis는 테이블 정보가 바뀌면 그에 관련된 모든 코드와 쿼리를 함께 수정해 주어야 하는 반면에, JPA는 Entity만 바꾸면 된다. 즉, Member에 age 필드가 추가된다면 위에서 봤던 MyBatis처럼 쿼리 수정을 해줄 필요 없이 Member Entity에 age 필드만 추가하면 되는 것이다. 

 

이 외에도 JPA는 

  • 1차 캐시
  • 동일성 보장
  • 쓰기 지연(transactional write-behind), 변경 감지(Dirty Checking), 지연 로딩(Lazy Loading)을 제공하여 성능상 이점
  • 객체 중심으로 데이터 관리 가능
  • 컴파일 타임에 오류 확인 가능 

의 이점들이 있다. 

 

 

 


 

 

 

[Reference]

'Development > 데이터베이스' 카테고리의 다른 글

JPA, Spring Data JPA 차이점  (0) 2025.07.11
왜 데이터베이스 인덱스로 B tree 계열을 사용할까?  (0) 2025.01.07
Redis  (0) 2024.12.04
RDB와 NoSQL  (0) 2024.11.23