서비스가 발전하면서
내부의 복잡성도 증가하게 됩니다.
지속 가능한 서비스는 발생되는 복잡성과의 싸움을 의미합니다
결론적으로 유지보수하기 쉬운 서비스가 되어야 합니다.
복잡성의 문제를 해결하기 위해 사용되는 기법이 객체지향 프로그래밍 입니다.
객체지향 프로그래밍에서는 추상화와, 캡슐화, 은닉, 상속, 다형성 등
시스템의 복잡성을 제어할 수 있는 기능을 제공합니다.
비즈니스 요구사항을 정의한 도메인 모델도
객체로 모델링하면 객체지향 언어가 가진 장점들을 활용할 수 있습니다.
여기서, 문제는 정의한 도메인 모델을 저장할 때 발생합니다.
특정 유저가 시스템에 회원 가입을 하면 회원이라는 객체 인스턴스가 생성됩니다.
그리고 객체의 메모리는 어딘가 영구적으로 보관해야만 합니다.
객체는 필드값(속성)과 메서드(기능)를 갖고 있습니다.
객체 기능은 클래스에 정의되어 있으며
객체의 인스턴스에 속성 값만 저장해놓고 필요할 때 호출해서 사용할 수 있습니다.
객체가 단순하면 객체의 모든 속성 값을 꺼내 DB에 저장하면 되지만
부모 객체를 상속 받거나, 다른 객체를 참조하고 있는 상태에서는
객체의 상태를 저장하는 것은 쉽지 않습니다
예를들어 회원 객체를 저장해야 하는데 회원 객체가 팀 객체를 참조하고 있다면
팀 객체도 함께 영구 저장하고 있어야 합니다
회원 객체만 저장한다면
참조하는 팀 객체를 잃어버리는 문제가 발생하게 됩니다.
자바에서는 이러한 문제를 고려해 객체를 파일로 저장하는
직렬화 기능과 저장된 파일을 객체로 복구하는 역직렬화 기능을 지원합니다.
하지만, 직렬화된 객체를 검색하는 것은 어렵기 때문에 현실성이 없는 해결책입니다.
대안은 관계형 DB에 객체를 저장하는 것인데
관계형 데이터베이스를 중심으로 구조화 되어있고, 집합적 사고를 요구합니다.
하지만, 객체와 관계형 데이터베이스는 지향하는 목적이 달라
둘의 기능과 표현 방법도 다릅니다.
이것을 패러다임 불일치 문제라고 이야기 할 수 있습니다.
객체와 RDB 사이에서 개발자는 어떻게 이 문제를 해결해야 할까요?
개발자가 직접 코드와 시간을 투입한다면 너무 많은 리소스가 투입됩니다.
객체에는 상속이라는 기능이 있습니다
테이블은 상속이라는 기능이 없습니다
다만, 데이터베이스는 슈퍼타입과 서브타입 관계를 사용하여
객체 관계와 비슷한 형태의 테이블 설계는 가능합니다.
객체를 두 테이블로 나누어 저장합.
객체를 조회할 때 조인하면 필요한 두 객체 데이터를 받을 수 있습니다
SELECT T.*, M.* FROM TEAM T JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
객체는 참조를 사용해 다른 객체와 연관관계를 갖고
참조에 접근해서 연관된 객체를 조회합니다.
테이블은 외래 키를 사용해 다른 테이블과 연관관계를 가지고
조인을 이용하여 연관 테이블을 조회합니다
Member 테이블에서 TEAM_ID 외래 키까지
관계형 데이터베이스가 사용하는 방식에 맞추게 되면
Member 객체와 연관된 Team 객체를 참조해서 조회할 수 없습니다
객체는 참조를 통해 관계를 맺습니다
class Member {
String id;
Team team;
String name;
Team getTeam() {
return team;
}
}
Team team = member.getTeam();
Member.team 필드는 외래 키 값을 그대로 보관하지 않고
연관된 Team의 참조를 보관합니다.
문제는 이러한 객체지향 모델링을 사용시
객체를 테이블에 저장, 조회하는 것이 어렵습니다.
객체 모델은 외래 키가 필요 없이
단지 참조 객체만 필요한 반면에
테이블은 객체 참조는 필요 없고
외래 키만 있으면 됩니다
개발자는 이 문제를
해결해야하는 어려움이 있습니다
객체를 데이터베이스에 저장하려면
team 필드를 TEAM_ID 외래 키 값으로 변환해야 합니다
외래 키 값을 찾아
INSERT SQL을 만들어야 하는데
MEMBER 테이블에 저장해야 할
TEAM_ID 외래 키는 TEAM 테이블의 기본 키이므로
member.getTeam().getId();로 구할 수 있습니다
필드값 -> 외래키 변환 -> 외래 키는 참조 테이블의 기본 키 값이므로 키로 조회
조회할 때 TEAM_ID 외래 키 값을
Member 객체의 team 참조로 변환하여 객체에 보관해야 합니다
member.setTeam(team)
저장, 조회 과정 모두 패러다임 불일치 해결을 위해
소모되는 비용들을 확인할 수 있었습니다.
JPA는 연관관계와 관련된 간단한 패러다임 불일치 문제를 해결해 줍니다.
meber.setTeam(team);
jpa.persist(member);
JPA는 team 참조를 외래키로 변환해 INSERT SQL로 데이터베이스 전달하는 역할을 합니다.
객체를 조회할 때도 외래 키를 참조로 변환하도록 JPA가 처리합니다.
객체 그래프 탐색
연관된 객체를 그래프 탐색한다면
SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SQL 에서는 member 객체를 조회하면
team 객체 데이터는 가져오는데 성공하지만
member.getPosition()
Position 객체를 조인해서 조회하지 않았다면
다른 객체를 가져올 수 없습니다.
실제 DAO 에서 SQL 을 직접확인해야만
객체 탐색이 어디까지 이뤄졌는지 확인이 가능하다는 한계가 있습니다.
JPA는 객체 그래프 탐색이 가능합니다.
member.getPosition().getPositionItem()
JPA는 연관된 객체를 사용하는 시점에 SELECT SQL을 실행합니다.
JPA를 사용하면 연관되어진 객체를 마음대로 조회가 가능합니다.
JPA는 객체를 사용하는 시점
Member member = jpa.find(Member.class, memberId);
Position position = member.getPosition();
position.getDate();
getDate와 같이 실제 Position 객체를 사용하는 시점에
JPA는 데이터베이스에서 Position 테이블을 조회합니다.
위와 같이 실제 객체를 사용되는 시점까지
데이터베이스의 조회를 미루는 것을 지연 로딩이라 합니다.
JPA 엔티티 CRUD(조회 등록 수정 삭제) (1) | 2024.01.01 |
---|---|
JPA 무엇인가 (사용하는 이유) (0) | 2023.12.31 |
[JUnit5] 모듈 구조 - Platform, Jupiter, Vintage (0) | 2021.08.02 |
[Stream] collect method (0) | 2020.08.04 |
[Stream] collect method (0) | 2020.07.31 |
댓글 영역