종종 JPA 질문글이 올라오기도 하고, 한 대단하신 강사님이 JPA 추천하니까 다들 자연스럽게 "취준하는데 필요하니까 쓴다" 느낌으로 받아들여서 JPA 를 배우려고 하는 사람들이 많아보여서 내가 아는대로 씨부려봄. 글 내용은 별거 없어서, 실무에서 조금이라도 일해보거나, JPA 를 좀 깊게 써본적이 있다 하면 당연하게 생각하고 넘어갈 내용이긴 한데, 한번 되짚어보자는 생각에서 씀. 대상자는 이제 막 처음 JPA 를 처음 써보는 사람들임.


JPA 를 "DB 와 객체간의 패러다임이 다른거를 해결해주기 때문에 쓴다" 라는 말은 조금 피상적이고, 처음 접하는 사람들한테는 머리로는 이해되지만 쉽게 와닿지는 않는 표현이라고 생각함. JPA 를 몇번이라도 써봤으면 알겠지만, 처음에는 Spring data jpa 를 써보면 쿼리도 알아서 딱딱 쏴주고 편한데, 조금 깊게 들어가면 "왜 이딴식으로 만들었지?" 라는 생각이 들게 됨. 근데 우리보다 똑똑한 수많은 개발자들이 참여해서 만든 프레임워크인데, 왜 이렇게 될수밖에 없었는지에 대한 이해가 먼저 필요하다고 생각함.


우선 첫번째로, 니들이 쓰는 자바, 파이썬, C 같은 프로그래밍 언어 있지? 그 하위에는 DSL 이라고 범주화할수있는 다른 영역의 언어가 있음. Domain Specific Language 의 약자인데, 굳이 번역하면 "도메인 특화 언어" 정도가 됨. 도메인이 뭐냐 ? 결국 개발자에겐 문제를 해결하고자 하는 영역임. SQL 도 당연히 DSL 중 하나이고, SQL 이 다루는 문제는 "데이터베이스 다루기" 임.  이 DSL 이 가진 특징들이 몇 가지 있는데, 여기서 이야기하기하고싶은건 하나임. 예를들어, 프로그래머스같은 코테사이트에서, SQL 문제 풀어본적 있지? SQL 문제를 풀다보면 어느 순간 이런 생각이 듬 "아 이거 자바(or 파이썬 or 자바스크립트)로 짜면 쉽게 풀릴거같은데". 이게 의미하는바가 뭐냐면, DSL 은 구조적으로 잘 이해하고 있지 않으면 다루기가 꽤 까다롭다는거임.


두 번째로 알아야할 것은, DB 의 성능최적화임. 가령, 100만개의 row 가 들어있는 db 에서, 특정 조건을 만족하는 값 몇개를 뽑아서 그걸 사용자한테 보여주고 싶다고 가정해보자. 당연히 지금까지 DB 얘기만 했으니, 머릿속으로 "where 절로 거르면 되겠네" 라는 생각이 들거임. 근데 왜 where 절을 선택했을까 ? 100만개를 다 어플리케이션(스프링)으로 가져와서 거르면 안될까? 당연히 안됨. 왜 ? 느리니까. 그러면 반대로 생각하면 "where 절은 빠르니까"(당연히 인덱스는 앵간치 잘 걸려있다고 가정한다) 라고 생각한 이유가 뭘까 ? 당연하게도, JPA 와 마찬가지로, 수많은 똑똑한 개발자들이 where 절을 걸면 알아서 잘 빠르게 가져와주는 최적화에 대한 엄청난 노력을 들였기 때문임. 


자, 이제 다시 돌아와서, 니가 한 2000년대 초반에 일하고 있는 개발자라고 생각해보자. 당연히 하드웨어가 구리기 때문에, 성능에 대한 고민이 필요한 시점이 되면, 자연스레 어플리케이션보다는 DB 중점으로 살펴보게 됨. 어플리케이션이 아무리 빨라도 DB 가 느리면 말짱 꽝이니까. 그러다보니 자연스럽게 쿼리문이 길어지고, 흔히 말하는 "유지보수"가 어려워지게 됨. 쿼리는 뭐 하나만 잘못 수정해도, 이와 관련된 모든 다른 부분들이 엮여서 뻥뻥 터져버리니까 스트레스를 받음. 그런데 반대로 어플리케이션 코드는, 내가 메소드 하나를 잘못짜도 다른쪽에서 어지간히 방어로직이 되어있으면 문제없이 넘어갈 가능성도 있음. 그러다보니, 프로그래머스 코테문제 풀때마냥 "하, 이거 자바코드로 짰으면 금방 짰을텐데, SQL 이라 거지같네" 라는 생각이 드는데, 동시에 하드웨어가 발전해나가면서 "이거 잘 연결시켜서 DB 를 자바코드로 다룰 수 있을지도?" 라는 생각이 들게 되고, 그렇게 탄생하게 된게 JPA 임.


그런데 막상 JPA 를 만들려고 설계를 하다 보니까, 여러가지 문제에 당면하게 됨. 


첫 번째로는, 흔히 말하는 패러다임의 차이임. 이 패러다임의 차이라는게 단순히 한 면만 바라봐서는 이해하기가 어려운 문제임. 혹시 자바나 파이썬같은 프로그래밍 언어로, table 모델을 만들어서 구현해본적 있냐? 한번 해보면 알겠지만, 자연스럽게 2차원 배열(리스트)를 통해서 만들게 되는데, 이게 SQL 을 다룰때랑은 다르게 조금 어색함. 만약 100x100 테이블에서, 58번째 컬럼, 33번째 row 에 있는 값을 가져오라고 하면, 자바코드로는 쉽게 처리되지만 SQL 로는 "58번째, 33번째" 라는 개념이 좀 이상하게 들림. 왜냐면 DB에서 58번째, 33번째라는 개념은 그 "순서"에 강하게 얽매여있는 개념이 아니기 때문임. 반대로, table 모델에다 대고 "자바로 grouping 기능을 구현해보세요" 라고 하면 또 굉장히 어렵게 들림. 왜냐면 우리는 객체를 하나의 독립된 인스턴스로 바라보기 때문임.


두 번째로는, 이 패러다임의 차이에서 오는 구현의 문제임. 예를 들어, "돈" 이라는 객체를 표현하는 Money 클래스를 만들고, 여기에 10000 이라는 값을 넣었다고 가정해보자. 여기서 내가 5천원을 더한다는건, 메모리 힙영역에 있는 객체에 접근해서, 그 값을 10000 에서 15000 으로 바꾸는 행위잖아? 이 과정은 매우 빠르기 때문에, 자유롭게 몇백 몇번 몇만번을 바꿔도 큰 문제가 없음. 근데 반대로, DB 는 이 과정을 네트워크 통신부터 하드디스크라는 물리적인 영역에 저장하는 과정이 필요하기 때문에, 일종의 cache 가 필요함. 그 cache 가 바로 우리가 흔히 이야기하는 "영속성 컨텍스트" 인거임. 근데, 캐시의 가장 큰 문제점이 뭐다 ? "원본과 다를 수 있다". 이 점 때문에, 하드디스크에 들어있는 데이터와 캐시의 차이를 "사용자의 입장에서 최대한 숨기려고" 하다 보니, N+1 문제라느니 그런것들이 생겨나게 되는거임.


이런 내용들을 알고 JPA 를 공부하면, "하~ 시발 JPA 좆같이 만들었네" 가 아니라, "하 시발 이게 어쩔수 없는 문제긴 한데.. 이걸 어떻게든 만회하려고 별 똥꼬쇼를 다했구나" 라는 생각이 들게 되면서 그 기저에 있는 원리를 더 자연스럽게 이해할 수 있음.