기술 면접에서 놓친 N+1 문제

i-c-a-n-d-o·2024년 10월 30일

기술 면접에서 N+1 문제에 대한 질문을 받았다.

개념과 발생 원인뿐만 아니라 TypeORM에서의 해결 방법까지 찾아봤었고 기술 면접 노트에도 기록해두었던 문제였다. 하지만 공부를 위한 기록이 아니라 기록을 위한 기록에 그쳤다는 걸 깨달았다.

면접 중 정답을 듣고 나서야 "아!"하고 머릿속의 물음표가 느낌표로 바뀌었다 ㅠㅠ
분명히 풀 수 있었고, 풀었어야 했던 문제였다. 이전 직장에서 쿼리를 작성할 때도 그렇게 했었지만, 당시엔 왜 그렇게 작성해야 하는지 깊이 생각하지 않고 넘어갔던 것이 이번 면접에서 드러난 셈이였다.😭

충분히 알고 있다고 생각했지만, 정작 풀지 못했던 문제.
그렇다면 내가 정말 이 문제를 알고 있었던 걸까? 라는 의문이 들었다.
이런 일이 반복되지 않도록, 다음에 같은 상황이 또 온다면 이번에는 반드시 풀어내겠다는 다짐으로 이번 경험을 기록해두려 한다.

"Eager Loading"과 "Lazy Loading"

Eager Loading (즉시 로딩)

처음부터 모든 연관 데이터를 즉시 로딩하는 방법으로 접근할 때 1번의 쿼리로 참조 데이터를 모두 불러온다.

장점

  • 필요한 데이터를 한번에 모두 가져오기 때문에 추가 쿼리가 필요없다.
  • 필요한 데이터를 모두 불러왔으니, 빠른 응답이 가능하다.

단점

  • 연관된 모든 데이터를 가져오기 때문에 초기 로딩 시간이 길어진다.
  • 클라에서 불필요한 데이터를 가져오는 overfetching이 발생할 수 있다.
  • 엔티티가 많을 경우 Join 수의 증가로 성능 저하가 발생할 수 있다.

Lazy Loading (지연 로딩)

필요한 순간에 데이터를 가져오는 방식으로 참조 데이터가 실제로 필요할 때 쿼리를 실행한다.

장점

  • 연관 데이터를 모두 가져오지 않기 때문에 초기 로딩 시간을 줄일 수 있고 이는 성능 최적화와 연결된다.
  • 필요한 데이터만 로드하기 때문에 불필요한 자원 낭비를 줄일 수 있다.

단점

  • 필요한 데이터마다 쿼리가 발생하므로 Eager Loading에 비해 수행되는 쿼리의 수가 많다.
  • 많은 참조 데이터가 연관되어 있을 경우 추가 쿼리가 계속 발생해서 N+1문제가 발생할 수 있다.

N + 1 문제란?

1개의 쿼리로 N개의 데이터를 가져온 후 각각의 연관 데이터를 추가로 N번의 쿼리가 실행되는 문제를 말한다. 데이터 로딩 속도가 엄청 오래 걸릴 수도 있고, 이는 OOM을 발생시켜 예상치못한 버그를 발생시킬 수 있기 때문에 방지하는 것이 중요하다.

TypeORM에서 N + 1 을 해결하는 방법

  1. find 메서드에 relations 옵션을 적용하는 방법
  2. QueryBuilder를 사용하는 방법
  3. entity를 수정하는 방법
  4. 쿼리를 나눠서 실행하는 방법
    • 추가적인 쿼리의 실행으로 DB hit은 증가되지만, N+1의 문제는 해결할 수 있다.

코파일럿으로 해결법을 찾았을 때 쿼리빌더를 알려줬음. => 복붙해서 적용했는데 해결이 안됐던 원인이 뭐지? -> 이건 그냥 아직까진 ai따위가 개발자를 대체할 수 없다는 증거로 생각함 ^^.

단 한번의 DB 호출로 원하는 데이터를 가져올 수 있다면 best지만, N+1 문제가 발생할 수 있는 경우, (혹은 많은 부하가 걸리는 작업일 경우) 나눠서 호출하는 것도 하나의 방법이다.

역시
완벽한 해결책은 없나보다. 모든 것이 트레이드 오프인걸까 🤔

profile
나는 백엔드 개발자다!

0개의 댓글