이 글을 작성하기 앞서, 절대 이 글은 JPA가 RDBS와의 패러다임 불일치를 해결하는 좋은 도구라는 것을 인지하고 있으며, 아래 글을 앞서 언급한 내용을 말하고자 작성한 것이 아니기 때문에 이 점 유의부탁드립니다.
아래 내용은 어디까지나 제 의견일 뿐이고, 맞지 않을 수도 있습니다.
스프링 부트를 활용하는 프로젝트에서 대부분 우리는 ORM으로 JPA를 사용합니다.
왜 그럴까요?
저같은 경우 인프런의 김영한님의 강의를 들으면서 JPA는 RDMS와 객체지향의 패러다임 불일치를 해결해주는 좋은 도구라는 것을 시작으로 JPA를 학습해왔습니다. 또한 개발 트랜드 라는 것이 한 몫했던건 아니였을까 싶기도 합니다.
아마도 앞서 말한 개발 트랜드는 아래와 같은 그래프를 말하는 거겠죠?
이런 행위가 혹시 귀찮다고 여기시나요?
저는 마이바티스는 사용해본 적이 없습니다. 그러나, 마이바티스도 좋은 도구라 생각하며 심지어, 복잡한 도메인 안에서는 JPA보다는 마이바티스와 같은 Raw한 SQL이 좋지 않을까? 라는 생각도 해본적 있습니다. SQL를 직접 작성하고, 타입을 명시해주고-
이런 행위가 혹시 귀찮다고 여기시나요?
그림 1 에서 표현하는 것과 같이 JPA를 사용하는 이유로 김영한님의 책에서 말하길, 'p57. 반복적인 일을 JPA가 대신 처리해준다.' 라는 말이 있습니다. 반복적인 작업을 JPA가 처리해준다는 사실. 개발자의 입장에서 바라볼 때 솔직히 간편한 레토르트 음식값으로 고급 스테이크를 사먹는 기분이라 너무 좋습니다. 저도 이 말에 대해서는 공감합니다.
그러나, 시간이 지나면서- 서비스의 요구 사항이 점점 더 늘어나고, 도메인은 하나씩 커져가면서 우리의 API는 점점 더 많은 행위를 하게 됩니다. 그 과정에서 JPA의 순기능이였던 반복적인 일을 대신 처리해준 던 일은 점점 더 복잡한 일로 변하게 되는 현상을 발견하게 되었습니다.
복잡한 일로 변하게 되는 현상이란? 어느 날 저는 QueryDSL을 학습하고 있고, JPA의 Criteria API 를 단순히 CRUD가 아닌 우연적 복잡성에 마주하게 되었습니다. 이 내용은 타 포스팅에서 다뤘기 때문에 여기까지만 하고 넘어가겠습니다.
객체 지향 프로그래밍은 무엇일까요?
객체 지향 프로그래밍을 설명할 때 대다수의 사람들이 말하길, 현실세계를 모방해서 프로그래밍 한다. 라고 말을 합니다. 이 또한 틀린 말은 아닙니다. 그렇지만, 현실 세계의 중심에는 사람들이 존재하고, 이 말의 뜻은 곧, 물건이 사람한테 전달해주는 것이 아니라, 사람이 물건을 전달합니다. 그러므로, 현실세계를 모방해서 프로그래밍 한다. 라는 말은 모순 됩니다.
조영호님의 객체 지향의 사실과 오해 책을 살펴보면, 객체 지향이라는 말은 현실 세계를 재창조한다는 말을 합니다. 저는 이 말이 더욱 공감되어집니다. 이 책을 약 1년 전 처음 읽었을 때는 현실 세계를 재창조라는 말을 가볍게 넘어갔었습니다. 그렇지만, 도메인 주도 설계라는 이론을 학습하고 이해하는 과정에서 현실 세계를 재창조 라는 말을 아주 조금은 이해할 수 있었습니다. 현실 세계에서 숨겨진 도메인을 발견하는 것, 핵심 도메인을 드러나게 만드는 것. 예를 들어, 사람들이 소모임에 가입한다. 라는 요구사항이 있다면, 도메인으로서. 사람. 소모임. 그리고 Join 이라는 도메인이 드러날 수 있습니다. 현실세계에서 Join 이란 건 사실 없죠. 코드에서만 존재하는 도메인입니다.
이렇듯, 객체 지향이라는 말은 현실 세계를 재창조 하는 무에서 유를 창조하는 몹시 아름다운 행위라 생각합니다.
그럼 이쯤에서, 다시 JPA로 돌아가볼까요? JPA는 RDBMS와 객체 지향 패러다임의 불일치를 해결하기 위해서 나온 도구입니다. 즉, RDBMS를 객체처럼 사용할 수 있게 되었습니다. 그럼 이제 우리는 JPA을 등에 업고 현실 세계를 재창조하면 됩니다!
오... 뭔가 멋있어 보입니다. 다시, 저 자신에게, 그리고 이 글을 읽는 이들에게 여쭤보고 싶습니다. JPA를 활용해서 객체 지향 패러다임에 맞게 현실 세계를 재창조하고 계신가요?
연관관계
JPA를 학습하다 보면, 일대다, 다대다, 다대일 이런 식의 이름으로 연관관계를 부릅니다. 음... 저는 @ManyToOne, @OneToOne 이런 아이들을 말합니다. 인프런의 김영한님의 강의를 듣다보면 "JPA에서 이렇게 쓰지 마세요." 라고 하면서 양방향 맵핑관계, 다대다 관계 등은 사용하지 마세요. 라고 말하고 있습니다. 당연히 사용하면 안됩니다. 왜냐하면- 이는 유지보수, 기회비용과 관련이 있습니다. 한번 설계를 해놓으면 돌이킬 수 없는 강을 건너기 때문입니다.
그럼, 다대일, 일대다, 일대일와 같이 다른 것들은 사용해도 되는가? 라고 질문해본다면- 정말 사용해도 되는걸까요? 왜 사용해도 되죠? 이것들을 사용하면 유지보수, 기회비용이 발생하지 않을까요?
기회비용(機會費用), 오포튜니티 코스트(opportunity cost 또는 alternative cost)은 하나의 재화를 선택했을 때, 그로 인해 포기한 것들 중 가장 큰 것의 가치를 말한다.
결론부터 이야기하면, 아니요. 입니다. 발생합니다. 맵핑관계을 갖게되면 추후 요구사항 변경으로 인해 기회비용은 또 발생합니다. 사내 운영 서비스를 유지보수하면서 방대한 도메인에 @ManyToOne, @OneToMany와 같은 애노테이션으로 도배가 되있는 속에서 수정해야할 부분을 찾고, 이를 이해하기 위해 이리저리 왔다갔다 파헤치고 있는 저 자신에게 그 기회비용을 목격할 수 있었습니다.
연관관계를 맺는다 라는 말의 뜻은 무엇일까요? "A 라는 엔티티 객체 안에는 여러 개의 B 엔티티 객체를 가진다." 라고 연관 관계를 맺는다면, A라는 엔티티 객체안에 복수의 B 엔티티를 저장하기 위해서는 A의 식별가능한 ID를 찾고,복수의 B 객체들을 찾은 A에 저장합니다.
네, 이게 연관관계입니다. 최근 지인들과 함께하는 DDD 프로젝트겸 스터디에서 JPA의 연관관계는 또다른 문제를 일으킬 수 있다 라는 사실을 발견했습니다.
흔히 도메인 주도 설계에서 말하는 Aggregate 라는 것이 있습니다. 여기서 Aggregate 란 간략하게 설명하면 복수의 엔티티는 데이터의 변경 동시에 발생하는 단위 라고 설명할 수 있다. 즉, 같은 Aggregate안에는 같은 라이프 사이클을 가져야 한다는 것을 말하고자 합니다. 이 Aggregate는 요구사항에 의해서 변경될 수도 있으며, 엄격하게 AggregateRoot 라는 EndPoint를 통해 접근해야 합니다. 다시 JPA로 돌아가서 연관관계를 맺는다는 라는 말의 뜻으로 "A 라는 엔티티 객체안에는 여러 개의 B 엔티티 객체를 가진다." 라는 말은 A와 B의 라이프 사이클이 동일해야 함을 명시합니다. 이렇게 되면 추 후 변경사항이 발생할 경우 이 연관관계를 끊어야 하는, 또는 수정해야 되는 일이 발생할 수 있습니다. 즉. 위에서 말한 기회비용이 발생하게 됩니다.
에릭 에반스의 도메인 주도 설계책에는 다음과 같은 내용이 있습니다.
도메인의 특성이 반영되게끔 연관관계를 일관되게 제약하면 연관관계의 의사전달력이 풍부해지고 구현이 단순해지며, 나머지 양방향 연관관계도 의미를 지니게 된다. 관계의 양방향성이 도메인의 의미적 특징에 해당하고, 애플리케이션 기능에 그러한 양방향성이 필요하다면 두 가지 탐색 방향을 모두 유지하는 것은 그와 같은 사실을 나타내는 셈이다.
물론 당명한 문제에 필요한 것이 아니거나 중요한 의미를 담고 있는 모델 객체가 아니라면 궁극적인 단순화는 연관관계를 완전히 제거하는 것이다. 05장 - 소프트웨어에서 표현되는 모델
그럼 어떻게 하라고?
"그럼 어떻게 하라고?" 라고 생각할 수 있습니다. 저 또한 이 생각을 했습니다. 그러나, SOLID 원칙 중에 Open Closed Priciple(OCP) 라는 원칙이 있습니다. 이는 확장에는 열려 있고, 변경에는 닫혀 있어야 한다는 말이 됩니다.
다시, 이 말은 우리의 소프트웨어는 간결한 결합을 가지고 있어야 한다는 것을 의미하게 됩니다. 마치 레이어드 아키텍처와 같은 것을 말하죠. IoC컨테이너 또한 이에 해당되고, PSA, AoP 우리가 아는 많은 개념들은 간결한 결합을 만들기 위해서 태어난 습니다.
그러므로 간결한 결합을 고민하는 길이 어쩌면 JPA의 연관관계를 잘 쓸 수 있도록 도와주는 방법이지 않을까 싶습니다.
정답은 없습니다. 여전히 JPA는 저에게도 굉장히 어려운 친구라고 생각합니다.
'가치관 쌓기 > 개발 돌아보기' 카테고리의 다른 글
빠르게 실패하기(fail-fast) VS 안전하게 실패하기(fail-safe) (4) | 2021.04.16 |
---|---|
동시성에 대해서 생각해보자. (0) | 2021.02.09 |
테스트 주도 설계를 실천한다는 것 (0) | 2020.12.18 |
Hibernate, JPA, Querydsl는 과연 좋은 도구일까? (10) | 2020.10.16 |
코딩은 어떻게 해야하는가? - 1 (0) | 2020.07.25 |
댓글