ORM이 뭔디(JPA 찍먹)

김운채·2024년 3월 9일
0

Spring

목록 보기
3/10

이 글에서는 ORM이 무엇이고, Java의 대표적인 ORM 중 하나인 JPA를 같이 간단하게 설명하려고 한다.

Persistance

ORM 이 뭔지에 대해 설명하기 앞서 persistance 라는 개념을 짚고 가자.
Persistance는 데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터의 특성이다. 이걸 영속성이라고 한다.

소프트웨어 아키텍처 중 하나의 layered architecture 에 있는 persistance 계층에서는 도메인 모델인 객체에 영속성을 부여하는 역할을 한다.

이 persistance layer 를 어떻게 구현해야하나라는 관점으로 보면
크게 JDBC만을 이용하는 방법, persistance framework 를 이용하는 방법으로 나눌 수 있다.

JDBC

주인공은 이놈이 아니니 간단하게만 설명하고자 함

JDBC는 Java Database Connectivity, 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다.
자바 애플리케이션에서 DBMS의 종류에 상관없이 하나의 JDBC API를 이용해 DB작업을 처리하는 것이다.
이 때문에 JDBC 인터페이스를 구현한 각각의 DBMS 드라이버만 갈아끼우면 어느 DB에서든 접근할 수 있게 해주는 것이다.

하지만 JDBC만을 이용해 영속계층을 처리하였을 때 나타나는 여러 불편한 점이 있다.

  • 간단한 SQL을 실행하는 데도 중복된 코드를 반복적으로 사용해야 함
  • Connection과 같은 공유 자원을 제대로 반환해주지 않으면 시스템의 자원이 바닥나는 버그 발생하는데 이를 수동으로 닫아주어야 함
  • 기본적으로 DB에 따라 일관성 없는 정보를 가진채로 Checked Exception인 SQLException를 처리해야 함

등등의 이러한 불편한 점을 손쉽게 처리해주기위해 persistance framework 를 사용할 수 있다.

Persistance framework

JDBC 프로그래밍의 복잡함이나 번거로움 없이 간단한 작업만으로 데이터베이스와 연동되는 시스템을 빠르게 개발 할 수 있으며 안정적인 구동을 보장하는 것을 Persistence Framework라고 한다.

모든 Persistence Framework는 내부적으로 JDBC API를 이용하는데 이를 SQL MapperORM으로 나눌 수 있다.

SQL Mapper

SQL Mapper는 객체(Object)와 SQL 문을 매핑하여 데이터를 객체화하는 것인데,
객체와 관계를 매핑하는 것이 아니라 직접 작성한 SQL문의 질의 결과와 객체의 필드를 매핑하여 데이터를 객체화 하는 것이다.

이 글의 제목에서부터 ORM을 외치는 걸로 보아 SQL Mapper 또한 ORM 으로 가기위한 빌드업에 불과하는 걸 눈치챘을 것이다.

SQL Mapper 에서 자세한 설명은 하지 않았지만 SQL을 직접 작성한다고 했다.

하지만 SQL 을 직접 작성하게 되면 SQL에 의존적인 개발을 하게 될수밖에 없다.
이렇게 되면 객체간의 관계는 아웃오브안중이 되버리고 데이터베이스에 대한 처리에 집중하게 된다.

관계형 데이터베이스는 객체 구조와는 다른 데이터 중심의 구조를 가지고 있어서 추상화, 상속, 다형성 같은 개념을 가진 객체를 데이터베이스에 직접 저장하거나 조회하기에는 한계가 있다.

객체와 관계형 데이터베이스는 각각 지향하는 목적이 다르기 때문에, 사용방법과 표현방식에 차이가 있어서 객체의 구조를 테이블 구조에 저장하는 데에는 한계가 있다는 말이다.

이러한 문제를 패러다임 불일치 문제 라고 한다.
위와 같은 차이로 객체지향적인 설계일수록 패러다임 불일치는 심화되는 것이다.

ORM

이쯤 ORM이 등장해서 패러다임 문제를 해결하게 된다.

ORM이란 객체와 관계형 데이터베이스를 매핑하는 것이다.

객체와 테이블간 패러다임 불일치 문제를

  • 객체간의 관계를 바탕으로 sql 문을 자동으로 생성하고
  • 직관적인 메서드로 데이터를 조작하게 하여

해결할 수 있다.
객체를 통해 간접적으로 데이터베이스 데이터를 다루는 것이다.

JPA

ORM의 대표적인 예로 Java의 JPA가 있다.

JPA는 Java ORM 기슬 명세에 대한 api표준 명세로, 인터페이스들을 모아둔 것이다.
JPA 기술을 사용하려면 JPA 인터페이스를 구현한 ORM 프레임워크를 사용하여야 한다. 대중적인 프레임워크가 hibernate 이다.

이제는 JPA 가 어떻게 패러다임문제를 해결하는가에 대해서 알아볼 것이다.

1. 상속

객체는 상속이라는 기능을 가지고 있다.
but 관계형 데이터베이스의 기능은 상속이라는 개념이 없다.

그나마 데이터베이스 모델링에서 이야기하는 공통부분을 슈퍼타입으로 묶고, 공통으로부터 상속받아 다른 엔터티와 차이가 있는 속성에 대해서는 별도의 서브 엔터티로 구현하는 관계를 사용하면 상속과 가장 유사한 형태로 테이블을 설계할 수 있는데

예시로 person 이라는 객체를 상속받은 crew 객체를 저장한다고 할때,

abstract class Person {
	Long id;
    String name;
}
class Crew extends Person {
    String nickName;
}

crew 객체를 저장하기 위해 부모객체인 person 에 insert 쿼리를 날리고, crew 에서 자식데이터만 꺼내서 총 두번의 쿼리를 날리게 된다.

또한, crew 객체를 조회할때는 join 문이 들어간 쿼리를 작성하고 이결과로 crew 객체를 만든다.
만약 좀더 복잡한 상속관계애 있다면 쿼리문도 복잡하고 늘어나게 될것이다.

이런 패러다임 문제를 JPA 로 해결할 수 있다.

이전에 위와같은 상속관계에 있던 객체들을 저장하기 위해 일일히 작성했던 쿼리들을 JPA를 사용하면 JPA가 알아서 슈슉 날려준다.
조회도 적절한 메서드만 호출하면 알아서 해준다.

2. 연관관계

객체는 참조를 사용하여 다른 객체와 연관관계를 가지고 참조에 접근하여 연관된 객체를 조회한다.
but 관계형 데이터베이스의 테이블에서는 이를 외래키를 통하여 다른 테이블과 연관관계를 가지고 join 을 사용해 연관된 테이블을 조회하는 방식이다.

이렇게 둘은 연관관계에 있어서 다른 구현방식을 취하고 있다.

class Crew {
	Long id;
    Long teamId; //teamId FK
    String nickName;
}
class Team {
	Long id;
    String name;
}

만약 이렇게 두개의 객체를 사용할 경우, 테이블에서 저장하거나 조회할 때는 편리함을 느낄 수 있다.
왜냐믄 각각의 객체를 각각의 테이블에 처리해주면 되기 때문.

하지만 여기서 문제는 teamId 이다.

객체 입장에서는 참조를 보관해야 다음처럼 참조를 통해 연관된 객체를 찾을 수 있는데

Team team = crew.getTeam();

여기서는 crew 객체가 team 객체의 참조를 가지고 있지 않기 때문에 팀을 조회할 수 없다.

그럼 객체입장에서 다시 코드를 수정해보자.

class Crew {
	Long id;
    Team team;  //참조로 연관관계 생성
    String nickName;
    
    Team getTeam(){
    	return team;
    }
}
class Team {
	Long id;
    String name;
}

근데 이렇게 하면 테이블 입장에서도 억울함.
하나의 crew객체를 쪼개서 crew와 team 각각의 테이블에 나누는 로직을 생성해주어야 하기 때문이다.

결국은 이같은 패러다임 문제를 해결하기 위해서 개발자들이 변환 역할을 해줘야 하는 것이다.

하지만 이것도 JPA로 해결할 수 있다.

class Crew {
	Long id;
    Team team; 
    String nickName;
    ...
}
class Team {
	Long id;
    String name;
    ...
}

객체는 참조가 있는 방향으로만 조회할 수 있기 때문에 team 객체에서 crew를 조회하는 것이 불가능했다.

하지만 JPA에서는 크루와 팀의 관계를 설정하고 Persist()라는 메서드로 객체를 저장하기만 하면 된다.

이러면 JPA가 team의 참조를 외래키로 변환하여 INSERT문을 날리고, 조회할 때도 마찬가지로 JPA가 내부적으로 SELECT문을 외래키를 참조로 변환하여 처리하게 된다.

3. 객체간 비교

패러다임 불일치 문제는 객체간 비교에도 있다.

데이터 베이스는 기본 키의 값으로 각 로우의 동일성을 구분하지만
객체는 주소 값 비교인 동일성 비교객체 내부의 값을 비교하는 동등성 비교라는 두가지가 있다.

데이터 베이스에서는 둘은 같은 데이터이지만 객체의 측면에서 볼 때는 둘은 다른 인스턴스가 될 수 있다.

하지만 JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.

Crew crew1 = jpa.find(Crew.class, id) //DB에서 가져옴
Crew crew2 = jpa.find(Crew.class, id) //1차캐시에서 가져옴

crew1 == crew2 //true

JPA의 장점

JPA 를 사용하면서 sql에서 벗어날 수 없는 문제점을 해결하는 장점을 얻을 수 있다.

  1. 패러다임 불일치를 해결하여 객체지향언어가 가진 장점을 활용할 수 있다.

  2. 반복적인 CRUD sql 을 개발자가 직접 작성하지 않아 생산성을 높일 수 있다.

  3. 관계형 데이터베이스마다 미묘하게 다른 데이터 타입이나 sql로 인한 문제를 JPA에게 어느 데이터베이스를 사용한다고만 알려주면 데이터베이스를 변경해야하는 상황에서도 유연하게 대처할 수 있다.

  4. sql을 직접 다루면서 엔티티에 필드를 수정하면 쿼리와 코드를 수정해야했지만 JPA 에선 대신 수행해주기때문에 수정해야할 코드도 줄어든다.-> 유지보수가 편리하다

JPA의 단점

JPA를 사용하면 복잡한 쿼리 사용이 어렵다 생각할 수 있다.

하지만 JPA에서는 sql 과 유사한 언어인 jpql을 지원하고 있고, 물론 sql 자체 쿼리도 작성할 수 있도록 지원한다.(sql mapper와 혼용가능)

ORM 장점

지금까지 JPA를 사용하면서 패러다임 불일치 문제를 해결하는 내용을 알아보았다.

위와 같은 내용으로 보아 ORM을 사용함으로써 얻게 되는 이점은 다음과 같다.

  1. sql 의존적인 개발에서 벗어남
    Sql 문으로부터 분리된 설계, 즉 데이터베이스에 의존하지 않은 상태에서 개발할 경우, 도메인과 비즈니스로직 구현에 집중할 수 있다.

  2. 재사용 및 유지보수의 편리성 증가
    ORM을 사용하면 데이터베이스 스키마 변경 시 소스 코드의 변경을 최소하할 수 있다.
    ORM 프레임워크가 스키마 변경을 자동으로 처리하므로, 개발자는 데이터베이스에 대한 세부 사항을 신경 쓰지 않아도 된다.

0개의 댓글