문제상황
GraphQL은 클라이언트가 필요한 필드로 바디를 구성하여 요청하고 서버는 요청받은 필드에 대해서 응답한다. 이는 언더페칭/오버페칭이 없다는 장점이 있지만 적절한 처리가 없이는 서버의 부하를 유발할 수도 있다.
우리 팀에서는 서버 부하를 줄이기 위해 DataLoader 적용 이외에도 각 요청의 필드값을 GraphQL요청 컨텍스트 객체를 사용하여 메모리에 캐싱하여 사용하고 있었다. (Dataloader 포스팅)
예를 들어 다음과 같은 요청이 들어오면, 서버는 posts 에 대해 한 번만 처리한다.
query {
user(id: 1) {
name
posts {
id
title
}
}
posts {
id
title
}
}
캐싱 로직을 자세히 살펴보지 않고 사용하던 중, 요구사항과 매칭되지 않는 데이터가 노출되는 이슈가 발생했다. 코드 내부를 살펴보니, 인풋과 관계없이 필드명으로만 캐싱한다는 것을 발견하였다.
예를 들어, 아래 쿼리로 요청하면 두 posts 모두 같은 데이터를 응답으로 받는 문제가 발생했다.
query {
user(id: 1) {
name
posts(category: "tech") {
id
title
}
posts(category: "life") {
id
title
}
}
}
이 문제를 해결하기 위해 캐시 키로 필드명과 함께 인풋을 사용하도록 하는 유틸함수를 새로 만들었다.
디스커션
- 이 동작을 global하게 설정하고, 만약 제외하고 싶다면 특정 데코레이터를 붙이는 방식으로 바꾸면 어떨지 생각을 해보았다.
- 목적과 기능이 다른 경우 필드명을 다르게 하는 전략도 있다고 한다(예: postOfTech). 안그래도 현재 게시글 조회 로직이 너무 복잡하고 거대해져서, 이처럼 목적에 따라 분리되면 좋을 것 같다. 클라이언트의 유연성과 서버 코드 유지보수의 트레이드오프가 있는 것 같다.
- 유틸 함수를 사용할 때는 AI 의 도움을 받아서라도 최소한의 동작 메커니즘을 이해하는 것이 필요하다고 느꼈다.
'배운 것들 > 그 외' 카테고리의 다른 글
| Nest.js의 인터셉터를 이용하여 인풋 필드에 권한 체크를 추가하기 (2) | 2025.12.24 |
|---|---|
| Elasticsearch 로 기업 검색 구현하기 (2) | 2025.11.06 |
| 스탬피드 캐싱 방지하기 (6) | 2025.07.24 |
| Elasticsearch 를 활용한 프로젝트를 경험하면서 느낀 점 (2) | 2025.06.26 |
| TypeScript 프로젝트에서 외부 파일을 이용할 때 경로 처리 (3) | 2025.04.12 |