- 일정관리 API 를 제작한다.
- users 테이블과 schedules 테이블이 있다.
- users.userId 로 schedules.userId 외래키로 일정들을 가져와야한다.
- 그렇다면 JOIN 이 필요한데 나는 조인을 쓰지 않고 가져왔다.
나는 왜 그런 선택을 했을지에 대해서 정리해보도록 하겠다.
그리고, 내가 했던 방식과 JOIN 을 택한 방식 중 어떤 것이 더 효율적인가?
에 대해서 알아보도록 하겠다.
1:N 관계를 가진 ERD 다이어그램
해석하면 다음과 같다.
1. username 을 기준으로 userRepository 에서 Optional<User> 을 가져오겠다. 2. User 가 존재한다면 User 안에는 유저의 정보가 들어있을 것이다. 3. 그렇다면 User 에게서 userId 를 뽑아낸 이후 planItRepository 로 보내준다. 4. 그리고 Page 에 일정들을 담아서 반환...
1. 내가 SQL을 완전히 제어하고 싶었다. - 복잡한 조건이 아니라면 JOIN 없이 단순한 쿼리 조합으로 충분하다고 판단했다.2. users 테이블과 schedules 테이블 쿼리를 명확히 분리하고 싶었다. - 각 도메인(사용자, 일정)의 책임을 나눠 관리하는 것이 유지보수에 더 낫다고 느꼈다. - 사용자 존재 여부 확인과 일정 조회를 명확히 분리함으로써, 로직의 단계별 흐름이 드러나도록 설계했다.3. JOIN은 복잡하고, 성능 차이가 크지 않다고 생각했다. - 데이터 양이 방대하지 않은 경우, 2번 쿼리가 성능에 치명적이지 않다. - 복잡한 JOIN 문을 작성하는 대신, 단순하고 명확한 흐름이 더 중요하다고 생각했다.
1. 쿼리가 2번 실행되어 성능 손실이 발생할 수 있다. - users 테이블에서 사용자 ID를 조회한 뒤, schedules 테이블에서 일정을 다시 조회해야 하므로 DB 입장에서는 불필요한 네트워크 왕복이 생긴다. - 사용자 수나 호출 빈도가 늘어날수록, 이 방식은 병목 가능성이 있다.2. 조합 조건 검색에 유연하지 않다. - username과 date 같이 다중 조건으로 일정을 조회하려면 서비스 단에서 분기 로직이 복잡해진다. - JOIN 쿼리라면 SQL 레벨에서 한 번에 처리할 수 있지만, 이 방식은 조건이 늘어날수록 메서드가 쪼개지고 관리가 어려워진다.3. 도메인 간 연관 관계를 끊어 사용하는 구조다. - 실제로 schedule은 user와 명확한 외래키 관계를 가지고 있지만, 코드 상에서는 단순한 ID 매핑으로만 취급된다. - 이는 나중에 사용자와 일정 간의 연관성을 기반으로 한 기능(예: 사용자 정보와 함께 일정 조회) 을 구현할 때 불리하게 작용할 수 있다.4. 에러 처리를 별도로 신경 써야 한다. - Optional<User>에서 isPresent() 또는 orElseThrow() 등 명시적인 처리가 필요하다. - JOIN을 사용하면 조회 결과가 없을 경우 단순히 0행 처리로 끝나지만, 지금 방식은 예외 발생 가능성이 높다.
이런식으로 작성하면 username 만 넘겨줘도 된다.
![]()
UserService을 만들고PlanItServiceImpl에는UserRepository를 삭제했다.
처음에는 쿼리의 단순성과 흐름의 명확성을 우선시해서 JOIN을 사용하지 않았다.
사용자 정보를 조회한 후, 해당 ID를 기반으로 일정을 가져오는 방식이 더 직관적이고 테스트하기 쉬울 것이라고 판단했다.
하지만 이번에 JOIN 쿼리를 직접 적용해보면서 느낀 점은, 단순한 경우에는 지금 방식이 충분히 유효하지만,
조건이 복잡해지거나 성능을 고려해야 하는 상황에서는 JOIN이 더 효과적인 선택이 될 수 있다는 것이다.