
자바는 객체 지향 프로그래밍 언어이다.
하지만 백엔드 서버에서 다루는 데이터는 결국 데이터베이스에 저장되고,
데이터베이스는 보통 테이블과 row 중심으로 데이터를 관리한다.
즉 애플리케이션에서는 객체를 다루지만,
데이터베이스에서는 테이블을 다루기 때문에 두 세계 사이에는 차이가 존재한다. (패러다임 불일치)
만약 데이터베이스의 값을 매번 SQL로 직접 조회하고, 조회 결과를 자바 객체에 일일이 옮겨 담고,
다시 저장할 때도 객체의 값을 하나씩 SQL로 작성해야 한다면 개발 과정은 매우 번거로워진다.
이러한 불편함을 줄이기 위해 등장한 개념이 ORM(Object-Relational Mapping)이다.
ORM은 객체와 관계형 데이터베이스의 테이블을 매핑하여,
개발자가 SQL 중심이 아니라 객체 중심으로 데이터를 다룰 수 있게 해준다.
그리고 자바에서는 ORM을 위한 표준 인터페이스로 JPA(Java Persistence API)가 제공된다.
객체와 테이블 사이의 매핑, 조회, 저장, 수정, 삭제를 일관된 방식으로 처리할 수 있도록 도와준다.
이번 포스트에서는 ORM과 JPA의 기본 개념부터 이러한 기술을 바탕으로,
어떻게 개발자가 비즈니스 로직에 집중할 수 있게 해주는지 알아본다.
ORM(Object-Relational Mapping)은 객체지향과 관계형 데이터베이스 사이의 간극을 줄여,
설계의 일관성과 코드의 생산성을 높여주는 기술이자 개념을 말한다.
주 목적은 앞서 작성한 내용과 동일 내용이지만, 패러다임 불일치를 해결함에 있다.
ORM 없이 객체 지향 언어와 관계형 DB의 패러다임 불일치가 일어난다면 다음과 같은 문제를 야기한다.
상속의 차이
객체지향에서 상속은 기본 요소인만큼 매우 자주 쓰이고 쉽게 사용하는 개념이다.
특정 상위 클래스를 상속받는 하위 클래스들을 만들어 낼 수 있는데,
관계형 데이터베이스에서는 이러한 객체의 상속 개념이 존재하지 않는다.
하위 클래스에 대해 데이터베이스에 저장하려면 DB 입장에서는
하위 클래스로 INSERT하고, 상위 클래스로도 INSERT하는 번거로움이 존재한다.
연관 관계
객체와 다른 객체는 참조를 통해 관계를 맺는다.
컴포지션으로 객체 내부에서 다른 객체를 멤버로 선언하여, member.getTeam()처럼
다른 객체를 참조할 수도 있다.
이때 객체 참조의 관계는 member -> team이라는 단방향을 띈다.
즉, member는 team을 알지만, team은 member를 모를 수 있다.
반면 데이터베이스의 경우 테이블간 관계는 FK를 통해 이루어진다.
회원테이블에서 team_id FK를 두고 이를 통해 JOIN하여 값을 확인할 수 있다.
즉, 외래키만 존재한다면 member에서 team을 조회할 수도,
team에서 member를 조회할 수도 있다. 양방향
결국 객체와 테이블을 ORM없이 연결하고자 한다면
객체 클래스 내부에는 참조를 위한 멤버 클래스를 추가로 작성해야하고,
데이터베이스에서는 각각에 맞는 FK를 추가 설정해야한다.
매우 번거롭고 객체지향이과 DB가 완전히 호환이 안되어 불편하다.
데이터 탐색
객체지향에서 데이터를 탐색할 때는 참조만 확인하면 된다.
member.getTeam().getCompany().getName()처럼, 참조와 메서드를 통해
원하는 데이터를 조회할 수 있다.
이를 SQL로 데이터베이스에서 조회하려한다면
SQL로 한번 가져온 데이터의 범위가 고정되어있기에,
처음 JOIN을 통해 잘못된 범위의 데이터를 가져온 경우 NPE가 발생할 수 있다.
결국 개발자는 해당 쿼리가 어디까지 데이터를 가져오는지 확인해야하는 불편한 과정을 진행해야한다.
이를 로직이 SQL에 강하게 종속되었다고 한다.
데이터 식별 방법
데이터베이스는 PK를 통해 행을 식별하지만, 자바는 객체의 메모리 주소값을 통해 각 객체를 식별한다.
Member member1 = memberDAO.getMember(100);
Member member2 = memberDAO.getMember(100);
member1과 member2는 동일 id값을 가지지만, 메모리 주소가 다르기에 다른 객체로 판단한다.
결국 데이터베이스에서는 같은 데이터인데, 자바에서는 다른 객체로 취급받는 모순이 생기는 것이다.
이러한 문제를 해결하는 것이 ORM으로 자바의 경우 JPA라는 표준 ORM 인터페이스를 사용한다.
객체지향 언어와 관계형 데이터베이스 사이의 패러다임 불일치 간극을 줄여주는 ORM 개념이
자바에서 구현된 것이 JPA(Java Persistence API)이다.
결국 JPA는 인터페이스이기에, 자바에서 ORM을 만들려면
이런 이름의 메서드를 만들고, 이런 규칙을 지키라고 정해둔 자바 진영의 공식 명세서이다.
Hibernate
JPA를 구현하는 대표적인 구현체로 점유율이 가장 높으며,
JPA를 사용한다는 것은 사실상 Hibernate를 사용하는 것이라 볼 수 있다.
JPA 구현체인 Hibernate는 JDBC를 직접 사용하지 않고도
객체를 데이터베이스에 저장하고 불러올 수 있도록 돕는 중간 계층 역할을 한다.
JDBC
JDBC(Java Database Connectivity)란
자바 어플리케이션과 데이터베이스를 연결해주는 표준 API로,
어떻게 DB와 커넥션을 맺고 통신을 처리하는지를 정의한 도구라 할 수 있다.
결국에는 자바에서 데이터베이스와 연결하기 위해 JDBC를 거쳐야하며과거의 경우 JDBC를 직접 다루고 처리하였는데 JPA(Hibernate)를 사용하면
자동으로 객체의 구조를 분석하여 SQL을 만들고 직접 JDBC를 호출하여 처리해준다.
DB JDBC 구현체 MySQL MySQL Connector/J PostgreSQL PostgreSQL JDBC Driver MariaDB MariaDB Connector/J Oracle Oracle JDBC Driver SQL Server Microsoft JDBC Driver
즉 백엔드 어플리케이션에서 DB에 접근하고자 할 때는 아래와 같은 과정을 거치는 것이다.
Spring 앱 -> JPA 인터페이스 -> Hibernate 구현체 -> JDBC -> DB