개발자

협업에 용이한 쿼리 vs 성능을 고려한 쿼리

2023년 07월 09일조회 3,418

안녕하세요. 백엔드에 대해 공부하고 있는 학생입니다. 스프링에서 JPA를 사용하다가 궁금한 점이 있어 질문드립니다. 요구사항에 해당하는 API를 개발하던 도중, 하나의 API에서 DB를 여러 번 방문해야 하는 일이 생겼는데요. 예를 들어 댓글 목록을 가져올 때 댓글마다 “요청한 유저의 좋아요 여부”와 같은 추가 정보를 함께 가져온다고 가정해 보겠습니다. 그러면 먼저 댓글을 가져오고 해당 댓글마다 좋아요 여부를 알기 위해 DB를 여러 번 방문하게 될 텐데, 이는 성능상 매우 안 좋을 것 같습니다. 하지만 JPA의 이점이 코드의 가독성을 높여 협업에 용이한 쿼리를 작성할 수 있다는 것이라고 알고 있는데요. 그래서 한 번에 여러 테이블을 조인하며 복잡한 동작을 하는 쿼리문을 작성하는 것이 성능면에서는 더 좋지만, 재사용성이 떨어지기 때문에 협업 측면에서는 안 좋다는 생각이 들었습니다. 이런 trade-off 관계에 대한 답이 있는지, 혹은 이런 상황에서 어떤 식으로 문제를 해결하시는지 궁금합니다. 감사합니다.

이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 5

인기 답변

손정현님의 프로필 사진

안녕하세요! 좋은 고민이네요 ㅎㅎ 개인적으로 JPA를 사용해본적 없지만 큰틀에서 보면 ORM인 것 같아서 제 경험을 빗대어 답변을 드리겠습니다. 우선 여러 테이블을 JOIN하는 복잡한 쿼리의 경우 재사용성이 어려운 것 맞는 것 같아요. 보통 동적으로 쿼리들을 생성해야할 경우 query builder를 사용합니다. JPA는 찾아보니 Criteria API라는 것을 제공하는 것 같네요. 다만, 해당 쿼리를 빌더로 잘게 쪼갠다고해도 재사용성이 높아질지는 개발 팀의 역량에 따라서 다를 수 있을 것 같아요. 잘만 짠다면 재사용성은 좋아질 것 같아요. 재사용성과는 다른 이야기지만 다른 접근 방법으로는 미리 JOIN된 값을 DB에 미리 저장해놓는 방식도 있습니다. 이 방법의 경우는 개별 테이블을 조인하는 쿼리를 던지는게 아니라 DB상에 미리 JOIN된 결과를 저장해놓은 다른 테이블/뷰를 조회하는 방식입니다. 다만, 이 방식도 메모리 관점에서 보면 비효율적일 수도 있고 구현 방식에 따라서 원본 테이블과의 싱크가 실시간으로 지원이 안될수도 있습니다. 이러나 저러나 각각의 장단점이 있는 것 같은데요. 제가 경험한 현업에서는 우선 조회하려고 하는 데이터가 얼마나 자주 요청되는지, 복잡한 쿼리를 날려도 감당할 수 있는 정도인지 등을 파악합니다. 감당 가능하다는 판단이 되면 복잡한 쿼리라도 그게 당장에 더 쉬운 구현 방법일시에 그대로 구현하는 것 같아요. 재사용성은 챙기면 좋지만 해당 쿼리를 개발하고 있을 당시에는 스프린트를 빠르게 돌고 있는 상태라서 리팩토링 할때 고민해본다라는 기조를 가지고 있어요. 그리고 실제로 해당 코드가 재사용되는 시점에 고민해보는게 나을거라는 판단을 자주하게 되어서 미리 재사용성을 고려해서 설계를 꼼꼼하게 하지는 않는 것 같습니다. (저희는 스프린트 사이에 부채를 처리하는 기간이 따로 있습니다) 결론은 "재사용성을 스프린트 사이클 내에 챙기지 않을때도 있다. 우선 재사용성을 어느정도 포기하고 기능을 구현한 뒤, 해당 쿼리/코드를 실제로 재사용 해야할때 고민해서 코드를 리팩토링 한다"가 제가 경험한 처리 방식입니다. 다른 방법들도 있을텐데 지금 당장 떠오르지는 않네요. Criteria API 링크 첨부할게요 참고해보세요 :) - https://docs.oracle.com/javaee/7/tutorial/persistence-criteria003.htm

인기 답변

밴쿠버리안님의 프로필 사진

결론부터 먼저 말씀드리자면, 현업에선 성능 가독성 모두 중요합니다. 성능 댓글을 N번 쿼리하는것은 안티 패턴입니다. 한번 쿼리로 가져올수 있게 쿼리쪽을 바꾸시거나 다른 예제상황에서 그게 불가피하다면 PM UX Frontend와 상의까지 할수 있습니다. 이 상황에서는 native query가 필요하겠으므로 ORM은 안좋다고 할수 있겠습니다. 다만 오버엔지니어링이 되지 않도록 주의하세요. 가독성 가독성 나쁘면 자신들에게 독이됩니다. 유지보수가 힘들어지고 스파게티 코드가 늘어나겠죠. 말씀하신부분이 예시이시겠지만, JPA가 가독성을 높인다는 것은 선택사항이지 가독성을 위한 필수는 아닙니다. JPA 없이도 구조 잘 짜고 네이밍을 잘해놓으면 충분히 가독성을 높일 수 있겠습니다. 코드 체크리스트 룰을 만드시고 코드리뷰등에서 일관성을 유지하세요. (예: 하나의 Repository 메소드에는 하나의 쿼리만 유지한다 등) 성능 가독성 둘다 1순위지만 그럼에도 불구하고 둘중 하나만 선택해야한다면 그것또한 경우에따라, 접근정도, 쿼리 비용, 프로덕션 데이터 상태등을 보고 상황에 맞게 선택해야 할 수 밖에 없습니다. 성능을 선택한다면 낮아진 가독성 예외상황에 대해 팀 내외부에 정기적 미팅등을 활용해서 충분한 공유가 되면 좋겠구요. 가독성을 선택한다면 낮아진 성능에대해 DBA쪽에 충분히 설득이 되어야 할 것입니다. 참고로 본문에 물어보신 코멘트 좋아요 예제에서는 이미 답은 정해져 있습니다. JPA에서 한번만 쿼리하는 법을 찾으시거나, 그게 어렵다면 네이티브 쿼리로 한번만 쿼리하셔야합니다. 가독성이 낮아진다고 생각하지는 않지만 그게 프로젝트에서 새로운 패턴이라면 앞으로 늘어날 비슷한 코드들도 일관성을 유지하는 방법으로 가독성을 높이실 수 있겟습니다. 지금 당장 한개의 메소드를 개발한다해도 프로덕션에서는 그 메소드만 한번만 쿼리되는게 아니라 수많은 메소드가 쉴세없이 쿼리되고잇습니다. 하나의 메소드로인해 db 쿼리가 많이 늘어나는것은 좋지않습니다. 그렇다고 항상 성능 > 가독성이라고 전제를 깔아버리는것은 오버엔지니어링을 유발시킬까 우려스럽기에 그렇다 말할 수 없겠습니다.

인기 답변

손유승님의 프로필 사진

저는 코드의 가독성을 높이 평가합니다. 가독성이 높은 코드는 나중에 재사용할 수도 있고, 새로운 기능을 추가하기도 편하며, 협업에도 유리합니다. 다만 서버 성능이 가독성 좋은 코드를 못 버티는 경우에는 조금 희생을 해야겠지요. 질문의 예시에서 좋아요에 조회수도 추가로 불러오기로 했다고 생각하면 편합니다. 조인을 많이 해서 복잡한 쿼리는...생각만으로도 끔찍해지는군요.

고형주님의 프로필 사진

N+1 문제를 묻고 계신 듯 합니다. - jpa fetch size - jpa n + 1 등으로 검색해보세요

psmon님의 프로필 사진

쿼리가 복잡해지는 것은 도메인의 복잡성에 기인하지 Jpa가 항상 협업에 용이한 코드를 만들어 내는것은 아닐것같습니다. n+1의 과같은 문제를 풀거나 3중이상의 조인및 서브쿼리가 필요한 복잡해진 테이블의 맵핑을 oop로 해결하는 방식이 결코 협업에 용이하리라 보지 않습니다. 오히려 잘 짜여진 네이티브쿼리가 튜닝도 할수있고 협업에 용이할수도 있다라고 봅니다. n+1 문제만 예를들어도 데이터베이스에 중요한 관계형을 객체로 전환하는 과정에 발생하는 불일치의 문제를 해결하는 코드는 부자연스러우며 심지어 orm을 채택한 언어및 프레임웍에서 lazy fetch등 해결방법이 모두 다릅니다. 그래서 orm이 가야할길은 db의 복잡성을 줄이고 oop적인 문제접근이여야한다고 보며~ 조금 다른 사고방식으로 orm을 ddd와 함께 사용하는 버전도 추천해봅니다. https://learn.microsoft.com/ko-kr/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로
키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!