목차
1. 양방향DB / 단방향 DB
2. 왜 JPA에서는 userId 대신 User를 넣어야 할까
3. 테스트 환경 쉽게 구축하는 법 구합니다
우리 팀은 여러 개의 노트를 사람이 사고를 확장하듯 서로 연결할 수 있는 기능을 만들고 있다.
각 노트는 독립된 데이터이지만, 필요에 따라 다른 노트와 관계를 맺는다.
내가 노트의 DB를 설계할때,
DB의 양방향과 단방향의 차이점을 깊게 생각하지 않았다,
막연하게 ‘양방향이면 더 편하겠지’ 라고 생각했다
노션에서 관계형 속성을 연결하는 것 처럼
DB나 JPA에서 양방향으로 구현하면 간단할 줄 알았다.
근데 커피챗에서 피치님께서
“양방향은 사용하지 않는다”
라는 말씀을 하셨다.
양방향 DB에게 단점이 있는것은 알았지만 ,
아예 사용이 금지일 정도로 문제가 될 수 있다고 생각해 보지 않았다.
그래서 내가 만들려고 한 구조에 양방향이 정말 필요한지 알아보려한다.
먼저 내가 알고있는 양방향에 대해 이야기 하자면 다음과 같다
A에서도 B를 볼 수 있고
B에서도 A를 볼 수 있는 관계
하지만 DB 입장에서는 처음부터 방향 관계 라는 개념이 없다
A→ B로 가는 단방향 관계 가 없는데
A를 알면 B의 값을 알 수 있다는 화살표 개념(→) 이 없다
왜냐하면
관계형 DB에서 관계는 외래 키(FK)로 표현되고 논리적인 연결만 된다
Sql문에서 FK을 이용해 A↔b을 만들 수 있다
SELECT * FROM A a JOIN B b ON a.b_id = b.ID;
SELECT * FROM B b JOIN A a ON a.b_id = b.ID;
외래키 == 양방향 이 성립한다.
그렇다면 내가 알고 있던 노션의 양방향은 뭘까?
이것은 DB의 개념이 아니라
JPA와 같은 ORM에서 객체를 다루기 위한 개념에 가깝다
JPA에서 양방향 관계란 DB의 외래 키가 두 개 생긴다는 뜻이 아니다.
단순히 객체 입장에서
필드를 양쪽에 모두 만들어 둔 상태를 말한다.
from_id (FK) : 시작 링크 (인덱스)to_id (FK) : 종료 링크
ItemLink(from_id, to_id) 테이블은
JPA에서 말하는 양방향 관계와는 아무 상관이 없다.
이 테이블은 단순히
“어떤 아이템이 어떤 아이템을 가리킨다”는
방향 있는 링크를 저장하는 역할만 한다.
DB 입장에서는 이게 끝이다.
중요한건
이 지점에서 JPA 엔티티를 어떻게 설계하느냐에 따라 생긴다
item 엔티티에 “연결된 링크 목록(ItemLink)” 을 추가하는 순간
이 구조는 JPA 기준에서 양방향 관계가 된다.
이때부터 객체는 테이블 처럼
Item → ItemLink
ItemLink → Item
서로를 참조할 수 있는 구조를 갖게 된다.
JPA에서 양방향 관계를 맺는다고 해서
두 객체가 동등하게 관계를 관리하는 건 아니다.
DB의 외래 키를 실제로 관리하는 쪽은 항상 한 곳이고,
JPA에서는 이를 연관관계의 주인이라고 부른다.
문제는 객체 입장에서는 양쪽 모두 필드를 가지고 있기 때문에
어디가 진짜 주인인지 쉽게 헷갈린다는 점이다.
Item에서 링크를 추가했는데
DB에는 반영되지 않거나,
반대로 ItemLink만 수정했는데
객체 상태가 어긋나는 상황이 생긴다.
양방향 관계를 사용하면
관계를 추가하거나 삭제할 때
항상 두 객체의 상태를 동시에 맞춰야 한다.
item.getLinks().add(link);
link.setItem(item);
둘 중 하나라도 빠지면
객체 그래프와 DB 상태가 어긋난다.
이 책임은 프레임워크가 대신 지지 않는다.
전부 개발자의 몫이다.
처음엔 헬퍼 메서드로 감싸서 해결한 것처럼 보이지만,
엔티티가 커지고 사용 지점이 늘어날수록
이 규칙은 점점 지켜지기 어려워진다.
이외에도
조회 편의 때문에 모델이 비대해지는 문제
JSON 직렬화와 순환 참조 문제
가 있는데 이해가 안돼서 패스한다.
MySQL에서는 외래 키 컬럼에 user_id 넣으면 끝인데,
왜 스프링(JPA)에서는 굳이 User객체를 넣어야 할까.
위에서 서술한 바와 같이 관계에서 방향이 없다
DB는 객체를 모르고 user_id의 의미를 모른다.
DB는 이 값이 저 테이블의 값과 일치하는지만 확인한다.
JPA에서 엔티티는
테이블을 그대로 옮겨 놓은 구조물이 아니라,
도메인 모델이다.
도메인 모델에서 중요한 건
컬럼 값보다 객체 사이의 관계다.
예를 들어서 우리 Linking 시스템중 folder 클래스에서
class folder{
Long userId;// DB 관점
}
이렇게 id로 관계를 표현하면
아이템 과 유저 관계가 표현되지 않는다
그래서 JPA는
관계를 id가 아니라 객체 참조로 표현하게 만든다.
class folder {
@ManyToOne
User user;
}
한명의 유저는 여러개의 폴더를 가질 수 있다
이번 주차에는
JPA를 단순히 테이블을 자바 객체로 옮기는 도구
로만 이해하고 있었던 내 생각이 틀렸다는 걸 알게 됐다.
로컬에서 잘 돌아가던 코드가 배포만 하면 깨지는 경우가 있다.
그래서 나는 배포하기 전마다
application.yml을 직접 수정하고,
테스트가 끝나면 다시 원래 상태로 되돌리는 방식으로 대응하고 있었다.
수정하는 내용은 대략 이렇다.
로컬 MySQL 연결 정보
배포용 DB id / password / 주소
당연히 번거롭고,
무엇보다 실수하기 쉬운 방식이다.
특히 이거 되돌리는거 까먹고 git push 입력했을때 매우 귀찮아진다
아래 글을 참고해서 Test환경을 구축하려 했다
https://tecoble.techcourse.co.kr/post/2020-09-21-application-properties/
Test 코드에서 프로파일을 어떻게 쓰는지를 전제로 설명하고 있었는데
나는 테스트 코드에서의 설정 적용 방식을 제대로 이해 하지 못한 상태라서
다른방법을 찾는중이다