이 글은 김영한 님의 Infrean 강의를 학습한 내용을 정리하여 작성합니다.
서론
애플리케이션
: 객체 지향 언어 - [Java, Scala, ...]
데이터베이스
: 관계형 DB - [Oracle, MySQL, ...]
✨ 객체를 관계형 DB에 관리해야 한다.
자바 어플리케이션 개발 흐름
- 데이터를 다루는 모든 로직에서 SQL 문이 작성된다.
기존 데이터베이스 프로그래밍
- SQL의 반복
- DAO (Data Access Object)
- DB의 data에 접근하기 위한 객체이다.
- DB에 접근하기 위한 로직을 분리하기 위해 사용한다.
- 직접 DB에 접근해 data를 삽입, 삭제, 조회 등을 조작할 수 있는 기능을 수행한다.
- MVC 패턴의 Model에서 이와 같은 일을 수행한다.
- 즉, DAO는 DB를 사용해 데이터 조회 및 조작하는 기능을 전담하는 Object이다.
- DTO (Data Transfer Object)
- DTO는 계층 간 (Controller, View, Buisiness Layer) 데이터 교환을 위한 자바 빈즈 (Java Beans)를 의미한다.
- DTO는 로직을 가지지 않는 데이터 객체이다.
- getter / setter 메서드만 가진 클래스를 의미한다.
- SQL에 의존적인 개발 (요구사항 변경)
- 시나리오
- MemberDAO가 완성
- 갑자기 회원의 연락처도 함께 저장해야한다는 요구사항이 추가됨
- 즉, 테이블에 새로운 필드 추가
- 추가 절차
- 등록 코드 변경
- 조회 코드 변경
- 수정 코드 변경
- 연관된 객체 변경
등록 코드 변경
- tel 필드 추가
- INSERT SQL 수정
- 조회 코드 변경
- SELECT SQL 수정
- 수정 코드 변경
- MemberDAO의 update 쿼리 수정
- 만약 회원 객체를 데이터베이스가 아닌 자바 컬렉션으로 저장했다면?
- 자바 컬렉션으로 저장했다면 SQL에 의존적인 개발에서 좀 더 자유로워질 수 있다.
연관된 객체
- "회원은 어떤 한 팀에 필수로 소속"되어야 한다는 요구사항이 추가
- MemberDAO에 findWithTeam()이라는 새로운 메서드 추가
- find 메서드는 회원만 조회하는 SQL을 그대로 유지
- findWithTeam 메서드에 회원정보와 팀 정보를 함께 가져오는 SQL 작성
- DAO를 이용해 SQL을 숨겨도 어쩔 수 없이 DAO를 열어봐야 동작을 확인
- Member 객체가 연관된 Team 객체를 사용할 수 있을지 없을지는 전적으로 사용하는 SQL에 의존
- 계층형 아키텍처에서 엔티티 신뢰성 부족 문제 발생
- SQL과 객체 (엔티티)가 강한 의존관계를 가지고 있다 (계층 분할 불가능)
SQL 중심적인 개발의 문제점
● 무한 반복, 지루한 코드
CRUD
- INSERT INTO ...
- UPDATE ...
- SELECT ...
- DELETE ...
- 자바 객체를 SQL로 ...
- SQL을 자바 객체로 ...
-> SQL에 의존적인 개발을 피하기 어렵다.
● 패러다임의 불일치 - 객체 vs 관계형 데이터베이스
- 상속
- 연관관계
- 객체 그래프 탐색
- 비교
- 사상
- 관계형 데이터베이스
: 데이터를 정규화해 보관
- 객체
: 캡슐화
- '객체 지향 프로그래밍은 추상화, 캡슐화, 정보은닉, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다.'
- 객체를 영구 보관하는 다양한 저장소
- RDB
- NoSQL
- File
- OODB
-> 현실적인 대안은 관계형 데이터베이스
: 객체를 관계형 데이터베이스에 저장한다. -> 저장할 때 문제가 발생한다.
RDB는 집합적인 사고를 가지고 있으며 객체지향 개념이 존재하지 않는다.
객체와 관계형 데이터베이스의 차이
1. 상속
- 객체 : 존재
- 관계형 데이터베이스 : 유사한 게(Table 슈퍼타입 서브타입 관계) 존재하기는 하나 존재 X
2. 연관관계
- 객체 : 참조(Reference)
- 관계형 데이터베이스 : PK와 FK를 사용해 Join
3. 데이터 타입
4. 데이터 식별 방법
상속
Album 저장
1. 객체 분해
2. INSERT INTO ITEM ...
3. INSERT INTO ALBUM ...
-> 이때, 저장은 별다른 문제가 되지 않는다.
Album 조회
1. 각각의 테이블에 따른 조인 SQL 작성 ...
2. 각각의 객체 생성 ...
: 상상만 해도 복잡하다.
=> DB에 저장할 객체에는 상속 관계를 사용하지 않는다!
자바 컬렉션에 저장한다면?
list.add(album);
자바 컬렉션에 조회하면?
Album album = list.get(albumId);
- 부모 타입으로 조회 후 다형성 활용
Item item = list.get(albumId);
연관관계
- 객체는 참조를 사용한다: member.getTeam()
- 테이블은 외래 키를 사용한다: JOIN ON M.TEAM_ID = T.TEAM_ID
* 객체 연관관계
- Member에서 Team으로 갈 수 있다.
- 그러나, Team에서 Member로 갈 수는 없다 (단방향).
-> 테이블 연관관계에서는 가능하다 (양방향).
● 객체를 테이블에 맞추어 모델링한다.
- 테이블에 맞춘 객체 저장
: 객체지향스럽지 않다!!
● 객체다운 모델링
- 객체 모델링 저장
: TEAM_ID에 외래 키 값을 넣어야 하는데 Member를 확인하면 외래 값이 존재하지 않고 참조 값만 존재한다.
-> member.getTeam().getId();를 수행해 TEAM_ID 값을 넣어준다.
- 객체 모델링 조회 (문제점)
: 복잡하다.
● 객체 모델링, 자바 컬렉션에 관리
list.add(member);
Member member = list.get(memberId);
Team team = member.getTeam();
-> 이러한 코드를 DB에 넣는 순간 헝클어진다.
객체 그래프 탐색
- 객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다.
: 예를 들어 자바에서 Member.Team 처럼 사용할 수 있어야 함을 의미한다.
처음 실행하는 SQL에 따라 탐색 범위 결정
Q. 실제 위의 객체 그래프가 존재한다 가정했을 때, 과연 member.getOrder(); member.getDelivery(); 코드를 마음껏 호출할 수 있을까?
A. Nope!
: 처음 실행하는 SQL에 따라 탐색 범위가 결정된다.
-> 위 SQL문을 통해 MEMBER와 TEAM을 가져온다.
-> MEMBER와 TEAM이 존재하니 member.getTeam();은 수행된다.
-> Order가 존재하지 않으므로 member.getOrder();는 값이 null이다.
엔티티 신뢰 문제
: 이 코드에서 member.getTeam(); member.getOrder().getDelivery(); 가 제대로 호출될 수 있는지 알 수 없다.
-> 엔티티에 대한 신뢰 문제가 발생한다.
모든 객체를 미리 로딩할 수 없다.
: 상황에 따라 동일한 회원 조회 메서드를 여러벌 생성한다.
계층형 아키텍처 진정한 의미의 계층 분할이 어렵다.
비교하기
: MemberDAO 클래스의 getMember 메서드에서 새로운 Member 클래스를 생성해 반환하므로 member1과 member2는 다르다.
비교하기 - 자바 컬렉션에서 조회
- 특별한 컬렉션이 존재한다 가정한다.
-> 식별자 구분 값을 사용해 동일한 Member 객체를 가져오면 참조값이 동일하므로 이때 member1과 member2는 같다.
자바 컬렉션, SQL 사이 많은 Miss Match가 발생한다.
객체답게 모델링 할수록 매핑 작업만 늘어난다.
객체를 자바 컬렉션에 저장 하듯이 DB에 저장할 수는 없을까?
-> JPA
SQL에 의존적인 개발 정리
- 진정한 의미의 계층 분할이 어렵다.
- 엔티티 (Member, Team) 신뢰 문제
- SQL에 의존적인 개발을 피하기 어렵다.
- 개발에 대부분의 시간을 SQL과 객체를 mapping하는 데 할애
- (개발자 = SQL Mapper)
- 대표적은 SQL Mapper 프레임워크: MyBatis