본문 바로가기

Spring/JPA

JPA, 객체지향과 RDB

JPA 모던 자바 데이터 저장 기술

Java : 객체 지향 언어 데이터베이스 : 관계형 DB [ Oracle, MySQL, .. ]이다.

지금 시대는 "객체"를 "관계형 DB"에 관리하고 있다.

그래서 RDB는 SQL을 짜야 함. SQL! SQL!! SQL!!!

SQL 중심적인 개발의 문제

-무한 반복, 지루한 코드.. CRUD, CRUD, CRUD 자바 객체 ↔ SQL

public class Member{
	private String memberId;
	private String name;
	...
}

멤버 객체를 만들었습니다. 그리고 뭐하겠어요? 쿼리 짜야죠.

INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES ();
SELECT MEMBER_ID, NAME FROM MEMBER M;
UPDATE MEMBER SET ... =?

앗 개발자님! 연락처가 빠졌네요? 연락처 추가해주세요! 멤버 객체에 private String tel 넣고, 쿼리 필요한 부분에 tel을 넣습니다. 필요한 부분에 일일이 다 찾아서 넣습니다.. 수 백 개면? 수 백 개 쿼리 수정 해야 햄... 뭐 해야지...

엔티티 신뢰 문제

class MemberService{
	...
	public void process(String id){
		Member member = memberDAO.find(id);
		member.getTeam(); // ???
		member.getOrder().getDelivery(); // ???
	}
}

객체 지향적으로 생각했을 때 이런 식으로 가져올 수 있다. 그냥! 근데 이렇게 가져오려면? 이런 식으로 만들어 둬야해. 안에 Member안에 Team안에도 데이터 들어가 있어야 하고, Member안에 Order안에 Delivery에도 데이터 들어가 있어야 해. 안에 logic 까봐서 제대로 들어가 있는지 체크해봐야 함.. null 들어있을 수 있지. 생각한 거랑 달라. 왜냐고? DB에서 테이블 연결해서 가지고 오고 있어야 하는거지...

SQL에 의존적인 개발을 피하기 어려움

어떤 쿼리를 짜냐에 따라 비즈니스 코드 생긴 게 달라짐...

★패러다임의 불일치

객체 vs 관계형 데이터베이스

관계형 데이트 베이스는 어떻게 데이터 잘~저장할까에 포커싱 되어 있음. 객체는 저장이 목적이 아니라, 어떻게 추상화하고 관리 잘 할까?가 목표.

패러다임이 다른 두 개를 연결하려다 보니 안 맞는 부분이 생긴다.

현실적인 대안은 관계형 데이터베이스

저장하려면? 객체를 SQL로 바꿔야지. 한 땀, 한 땀 뜯어서 바꿔야 해! 개발자 = SQL 매퍼 ㅋㅋ

객체와 관계형 데이터베이스의 차이

상속

객체 지향적으로 설계하면, 그림 왼쪽 처럼 상속 관계를 설계할 수 있다. DB는? 그림 오른쪽처럼 만들게 된다. DTYPE 넣어서 join하는 식으로 사용한다.

관계형 DB에는 상속 관계? 없다. 둘 비슷하게 생겼지만 전혀 다른 패러다임으로 움직이고 있어.

Album 추가해서 팔거야! Album 정보를 DB에 저장해보세요!

  1. 객체 분해
  1. INSERT INTO ITEM...
  1. INSERT INTO Album...

그럼 Album 정보 조회를 해보자.

  1. 각각의 테이블에 따른 조인 SQL 작성

movie 가져올꺼야? book 가져올꺼야? 모두 조인 SQL 작성해야지! 그래서 나온 꼼수가ㅋㅋ 그냥 super item DTO를 쓰게 됨 아이템, album, movie, book 다 들어 있는거 만들어서 씀 ㅋㅋ 그래서 DB에 넣을 때 상속 관계 크게 신경 안 씀 ㅋㅋ

↔비교해서 생각해봐

자바 컬렉션에 저장하면? list.add(album); 이걸 converting하려니까 복잡해지는 거야.

Album album = list.get(albumId);
Item item = list.get(albumId);

객체 지향적으로 설계를 할 수 없어 ㅋㅋ DB에다가 sql로 객체 담으려다보니까...

연관 관계

객체는 참조를 사용 : member.getTeam();

테이블은 외래 키를 사용 : JOIN ON M.TEAM_ID = T.TEAM_ID

객체는 그냥 member.getTeam()이런 식으로 Team 객체를 가져올 수 있어. DB는 FK를 사용해서 JOIN해서 가져 와.

둘이 비슷한 것 같지만 전혀 다르다. member.getTeam()에서 Team객체를 가져왔어. 반대로 team.getMember() 가능할까? 불가능하다. (방향성이 있다)

RDB는 member에서 team 정보를, team에서 member 정보를 불러올 수 있어. (방향성이 없다. 양방향)

객체를 테이블에 맞추어 모델링한다면?

class Member{
	String id;//MEMBER_ID 컬럼 사용
	Long teamId;//TEAM_ID FK 컬럼 사용 **
	String username; //USERNAME 컬럼 사용
}

class Team{
	Long id; //TEAM_ID PK 사용
	String name; //NAME 컬럼 사용
}

실제 이렇게 함. Member 클래스에 객체 Team team을 넣는 게 아니라, DB에서 썼던 것 처럼 teamId 외래 키를 그대로 넣는다.

객체 지향적이지 못함... 더 객체 지향적?.. Team team이 있어야 하는데 id만 꺼낼 수 있음. team자체만 꺼낼 수 있는 게 아냐.

그럼에도 왜 teamId 그냥 쓰냐? 그래야 쿼리 짤 때 좀 더 쉽기 때문이야;

INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...

선배님! 왜 객체 지향 언어인데 객체 지향적으로 언어를 안 짜는 건가요?? 객체 지향적으로 모델링을 해보자.

class Member{
	String id; //MEMBER_ID 컬럼 사용
	Team team; //참조로 연관관계를 맺는다.
	String username; //USERNAME 컬럼 사용

	Team getTeam(){
		return team;
	}
}

class Team{
	Long id; // TEAM_ID PK 사용
	String name; // NAME 컬럼 사용
}

Member member = new Member(); member.getTeam()하면 객체를 가져올 수 있게 생겼다!

쿼리를 짜보자!

INSERT INTO MEMBER(MEMBER_ID, TEAM_ID, USERNAME) VALUES ...

TEAM_ID 없네? 그럼 이렇게 member.getTeam().getId(); 꺼내서 넣자. FK니까 이렇게 할 수 있지. 굿굿 실제로 잘 동작도 하네! 보십쇼 팀장님 좋지 않습니깡

자! 조회를 해보자 ㅋㅋ

	SELECT M.*, T.*
	FROM MEMBER M
	JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
// ORACLE 방식은... 다른 DB로 바꿨을 때 안 돌아간다. JOIN ON으로 학습해야 함 ㅠㅠ
public Member find(String memberId){
	//SQL실행 ...
	Member member = new Member();
	//데이터베이스에서 조회환 회원 관련 정보를 모두 입력
	Team team = new Team();
	//데이터베이스에서 조회한 팀 관련 정보를 모두 입력
	
	//회원과 팀 관계 설정
	member.setTeam(team); //**
	return member;
}

SQL실행하고 Member객체 만들고 값 넣고, Team객체 만들고 값 넣고, member.setTeam(team)해 줘 코드가 오지게 길어짐......

그래서 그냥 super 클래스 만들어서 다 때려 넣음...;

↔객체 세상이었다면? list에다가 객체를 채로 넣어! 그러면 리스트에 Team객체를 담고 있는 Member 객체가 들어가있겠지?

list에서 필요한 Member의 getTeam().getId()하면 그대로 값을 가져올 수 있음.

list.add(member);

Member member = list.get(memberId);
Team team = member.getTeam();

패러다임이 다른 곳에 넣었다 빼려니.... 코드가 길어졍 ㅎㅎ

객체 그래프 탐색

객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. 연관 되어 있다? 그럼 그냥 조회할 수 있어야 되는거야. get팀.get멤버.get오더.get오더아이템.get아이템.get카테고리 근데 RDB 쓰면서 그게 안돼... 만들어져 있어야만 쓸 수 있어.

처음 실행하는 SQL에 따라 탐색 범위가 결정될 뿐이다.

SELECT M.*, T.*
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
member.getTeam(); //ok
member.getOrder(); //null 조회할 수 없어. 왜냐고? 쿼리에 그렇게 안 만들었으니까.
//근데 제대로 내가 쿼리 만들었나? 확인하려면 까봐야 알 수 있음 ㅋㅋ 한 20개 까진 기억하지
//100개-200개 되면? 다른 사람이 만들면? 또 쿼리문 작성하고?

다시 초반에 봤던 코드를 보면

class MemberService{
	...
	public void process(String id){
		Member member = memberDAO.find(id);
		member.getTeam(); // ???
		member.getOrder().getDelivery(); // ???
	}
}

다른 사람이 로직 짜 놨어. 써도 돼? nullPointerException 터지면 그냥 장애남... 또 하나하나 까봐야지

모든 객체를 미리 로딩할 수 없다

이걸 해결하려면? 상황에 따른 모든 쿼리를 짜놓아 ㅋㅋ

memberDAO.getMemeber(); //member만 조회
memberDao.getMemeberWithTeam(); // member와 team 조회
member.getMemeberWithOrderWithDelivery(); //Member,Order,Delivery

복잡해질 수록? 경우의 수 너무 많아집니다 ㅋㅋㅋㅋㅋ 뭔가 이상하넹...

비교하기

String memberId = "100";
Member member1 = memberDAO.getMemeber(memberId);
Member member2 = memberDAO.getMemeber(memberId);

member1 == member2; //다르다

class MemberDAO{
	public member getMemeber(String memberId){
		String sql = "SELECT * FROM MEMEBER WHERE MEMBER_ID =?";
		...
		//JDBC API, SQL 실행
		return new Memeber(...);
	}
}

member1과 member2는 같지 않다. 객체를 따로 따로 만들어서 값 가져오고 셋팅했기 때문에.

만약 자바의 컬렉션이었다면?

String memberId = "100");
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);

member1 == member2; // 같다

이런 것도.. 다르지? 패러다임이 뭔가 달라. list에 담아서 보관하는 거랑 sql에 담아서 보관하는 거랑 달라. list에 넣는 게 객체 지향 패러다임에 좀 더 가까워.

해당 포스팅은 T아카데미에서 진행한 김영한 강사님의 JPA 유튜브 강의를 듣고 정리한 것입니다 : ) https://www.youtube.com/watch?v=WfrSN9Z7MiA&list=PL9mhQYIlKEhfpMVndI23RwWTL9-VL-B7U

반응형

'Spring > JPA' 카테고리의 다른 글

JPA 필드와 칼럼 매핑  (0) 2021.04.11
JPA persistence.xml과 라이브러리 설정  (0) 2021.04.11
JPA 기초와 매핑 : 실습  (0) 2021.04.11
JPA 기초와 매핑  (0) 2021.04.11
JPA에 대해서  (0) 2021.04.11