![[Next.js] API Routes로 DynamoDB 정렬하기 (feat. AWS Amplify)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXmjt0%2FbtsKNZbPhrq%2Fpbxda2JWQYCt5Ub58DE4uK%2Fimg.png)
시작하며...
이번 Codeit Resoucres 프로젝트에서 기술스택으로 React, AWS Amplify Gen2, NoSQL 데이터베이스인 DynamoDB를 사용하고 있다.
마주친 문제점
처음에 데이터베이스에 데이터를 추가할 때, 데이터가 시간순이나 id값을 기준으로 자동 정렬될 것이라 예상했지만, 실제로 사용을 해보니 DynamoDB에서는 데이터가 특정 순서 없이 저장되는 것을 발견하게 되었다.
프로젝트 요구사항을 구현하기 위해서는 시간순, 이름순으로 정렬된 데이터를 보여줘야 했기 때문에, 이를 어떻게 해결할지 많은 고민을 하게 되었다.
해결 과정?!?!
AWS Amplify Gen2 공식 문서와 GitHub 레퍼런스를 통해 전체 데이터를 정렬하는 방식을 찾아보았으나...
DynamoDB에서는 기본적으로 데이터 정렬 기능을 제공하지 않다는 것을 알게 되었다.
그래서 초기 React 프로젝트에서는 클라이언트에서 직접 정렬 로직을 수행하도록 구현했다.
이후 프로젝트를 Next.js로 마이그레이션 하면서, 클라이언트 측이 아닌 API Route를 활용해 서버에서 데이터 정렬하는 방식으로 전환하였다.
⇒ 결론적으로, 해당 기능을 구현하기까지 어떤 과정을 거치고 고민을 했는지 기록하기 위해 글을 작성하게 되었다.
DynamoDB와 NoSQL
졸업 작품에서 firebase 사용하면서 처음 NoSQL을 접하게 되었다. 당시에는 단순히 "관계형 DB보다 빠르다!" 라는 것만 알고 있었다.
하지만 이번 프로젝트에서 DynamoDB를 사용하면서 스키마를 직접 설계해 보고, 프론트엔드 개발에 필요한 로직을 구현하면서 NoSQL의 특징을 이전보다 더 이해하게 되었다.
NoSQL 특징
NoSQL은 빠른 읽기와 쓰기 성능에 최적화된 데이터베이스이다.
DynamoDB의 데이터 정렬
NoSQL에서는 기본적으로 데이터 정렬을 제공하지 않는 대신, 사용자가 직접 어떻게 정렬할지 설계할 수 있도록 되어 있다.
DynamoDB에서는 Partition Key와 Sort Key를 설정하여 데이터를 정렬할 수 있다.
- Partition Key: 데이터를 저장할 파티션을 결정
- Sort Key: 파티션 내에서 데이터를 어떻게 정렬할지를 결정
프로젝트에서 사용한 DynamoDB
프로젝트 요구사항
멤버 관리 페이지에서 다음과 같은 정렬이 필요했다.
- 전체 멤버 목록:
- 최신순, 가나다순, 오래된 순 정렬
- role별 멤버 목록:
- 최신순, 가나다순, 오래된순 정렬
- 소속 부서별 멤버 목록:
- 최신순, 가나다순, 오래된순 정렬
DynamoDB 스키마 설계 고민
초기 접근 방식
전체 데이터를 정렬하고 싶었으나 맨 위에서 언급했다시피 공식문서와, GitHub 레퍼런스를 찾아봐도 방법을 찾지 못하였다.
그래서 전체 데이터를 정렬을 위해 아래와 같은 방법을 고려해 보았다.
- 의미 없는 공통된 값을 가진 필드를 추가하여 Partition Key로 설정
- username과 createAt을 Sort Key로 설정
그러나 결국 해당 방식을 채택하지는 않았다.
왜냐하면 모든 데이터가 동일한 파티션에 몰리게 되면서 DynamoDB의 분산 처리의 장점을 잃어버리게 되고, 정렬 로직 정도는 프론트엔드 측에서 충분히 해결 가능한 문제라고 판단이 들었기 때문이다. (내가 틀릴 수 있음.. 그냥 그렇게 생각이 듦)
최종 구현 방안
기존 필드에 있던 role 값만 Partition Key로 설정하고, username과 createdAt을 Sort Key로 설정하여 최신순/오래된 순/이름순 정렬을 할 수 있게 구성했고, 나머지 기능은 직접 클라이언트 측에서 정렬하기로 결정했다.
React 클라이언트에서 정렬 구현
전체 멤버 정렬
sort 메서드를 사용하여 최신순/오래된 순/이름순 정렬을 하였다.
Role 별 멤버 정렬
Role에 따른 정렬은 Partition Key와 Sort Key를 활용하여 DB 레벨에서 정렬을 하여, 쿼리 시점에 정렬된 데이터를 바로 가져올 수 있었다.
소속 부서 별 멤버 정렬
소속 부서는 teams 필드에 id값들이 배열로 저장되어 있기 때문에, 특정 팀에 소속된 멤버 목록을 가져온 후에 전체 멤버 정렬할 때 사용한 함수를 호출했다.
성능 최적화
TanStack Query를 사용하여 데이터를 캐싱하고, 불필요한 API 호출을 줄여 성능을 최적화했습니다.
고민 사항
구현 자체는 했지만 조금 걱정이 많았다...
프론트엔드에서 데이터 정렬 로직을 수행하는 게 과연 맞을까??
지금은 규모가 작지만 만약 규모가 큰 프로젝트에서는 어떻게 수행해야 되는 걸까??
의문이 들었지만 이를 해결하지 못한 상태로 이렇게 인턴 기간을 마치게 되고 프로젝트가 끝이 났다.
(는 아니고...)
React에서 Next.js로의 마이그레이션
구글 캘린더 연동 요구사항
인턴 기간을 마치고, 인수인계 문서까지 작성하여 이제 끝이 났다고 생각했던 프로젝트였지만...
해당 프로젝트를 실제 직원분들의 피드백을 받아보기 위해서는 구글 캘린더와 연동되어야 한다고 했다.
그래서 팀원들과 상의한 결과, 프로젝트를 여기서 끝내는 것이 아니라 구글 캘린더 연동도 해보자! 라는 의견이 나와 프로젝트를 더 진행하게 되었다.
Next.js로 마이그레이션 한 이유
구글 캘린더와 연동을 하기 위해서는 서버가 필요한데 별도의 node.js 서버를 구축하는 과정이 번거롭다고 생각이 들었기 때문에
서버를 하나 만드는 대신 Next.js의 내장된 서버를 사용하자는 의견이 나왔다.
그래서 기존 React 프로젝트는 Next.js의 API Route 기능을 사용하기 위해 Next.js로 마이그레이션 하기로 결정하였다.
추가적으로 클라이언트 측에서 처리 하고 있던 정렬 로직도 API Route를 사용하여 서버에서 수행하기로 했다.
(내가 맡은 부분뿐만 아니라 다른 부분도 프론트엔드에서 정렬을 직접 하고 있었음)
Next.js API Route란?
Next.js API Route는 프론트엔드 애플리케이션 내에서 백엔드를 구현할 수 있는 기능이다.
주요 장점
따로 백엔드 서버를 구축하는 대신 다음과 같은 장점이 있다.
- 통합된 개발 환경
- 프론트엔드와 백엔드 코드를 한 프로젝트에서 관리할 수 있게 된다.
- 개발 워크플로우가 단순하다.
- 간단한 서버 로직 구현
- 외부 API(구글 캘린더) 연동할 수 있다.
- 간단한 CURD
- 데이터 정렬
- 배포 프로세스
- 단일 애플리케이션으로 배포 가능
그리고 팀원 모두 프론트엔드 개발자라서 직접 구현할 수 있는 범위 내에서 Next.js를 선택하는 방식이 최선의 방식이고, 외부 API 연동 기능을 수행할 것이기 때문에 가장 최적의 방식이었다.
API Route를 활용한 정렬 구현
API 요청
'/users'에 category와 order를 쿼리 파라미터로 전달하며 API 요청을 보낸다.
API Route 핸들러 구현
해당 API 요청을 받기 위해서 설정한 경로에 따라 '/api/user' 에서 handler 함수를 통해 요청을 받는다.
쿼리 파라미터 값을 받아와 값에 따라 다르게 처리하여 성공적으로 데이터를 가져오면 200 OK 상태 코드와 함께 데이터를 반환하고, 오류가 발생하면 500 상태 코드와 함께 에러 메시지를 반환한다.
클라이언트 정렬 vs API Route 정렬
기존 React에서는 프론트엔드 측에서 데이터 정렬을 직접 처리했지만, Next.js로 마이그레이션 하면서 정렬 로직을 API Route를 통해 서버에서 처리하였다.
실제로 성능 차이가 있는지 궁금하여 비교해 보았다.
구글 Lighthouse 성능 측정
정량적으로 비교를 하기 위해 Lighthouse로 성능을 측정하였다.
배포 환경에서 시크릿 모드로 여러 번 측정한 결과, React 클라이언트 정렬과 Next.js API Route 정렬 간에 성능 차이가 있음을 확인했다.
데이터 개수가 1개 차이로 결과에 큰 영향을 미칠 것 같지 않지만..... 일단 성능 비교를 진행했다.
결론적으로 결과는 차이가 있었다. 분명 같은 코드이고 Next.js에서도 SSR을 사용한 것이 아니라 CSR을 사용하였고, 정렬 로직만 클라이언트 대신 API Route를 사용해서 서버에서 구현했을 뿐인데 수치로 차이가 났다.
성능 비교 결과
- 전체 성능
- 클라이언트 정렬: 94점
- API Route 정렬: 99점 (전반적인 성능 5점 상승)
- FCP (최초 컨텐츠풀 페인트)
- 클라이언트 정렬: 0.9초
- API Route 정렬: 0.6점 (페이지 렌더링 시간 0.3초 단축)
- Speed Index (속도 지수)
- 클라이언트 정렬: 0.9초
- API Route 정렬: 0.6점 (페이지 콘텐츠 시각적 표는 시간 0.3초 개선)
결과적으로, 서버에서 처리하는 방식이 클라이언트에서 처리하는 것보다 성능 면에서 더 뛰어났다!!
어떤 방법이 더 좋을까?
성능 측면에서 Next.js API Route에서 처리하는 방식이 더 유리하다는 결론을 내릴 수 있다.
그럼에도 불구하고, 추가적으로 장단점을 비교해 보았다.
장점
- 클라이언트 부하 감소
- 데이터 정렬을 서버에서 처리하므로 클라이언트 리소스 사용이 줄어들어 성능 부담이 감소한다.
- 비즈니스 로직 관리
- 클라이언트에서는 복잡한 비즈니스 로직을 처리하지 않게 되어 코드가 간결해진다.
- 서버에서 정렬 로직을 처리함으로써, 비즈니스 로직이 분리되어 유지보수성이 증가한다.
단점
- 서버 부하 증가
- 클라이언트 요청마다 서버에서 데이터를 처리해야 하므로, 서버에 부하가 증가할 수 있다.
- 코드 복잡도 증가
- 클라이언트와 서버 간의 역할 분담이 명확해지지만, 클라이언트와 서버 모두에서 코드를 관리해야 하므로 프로젝트의 복잡도가 증가할 수 있다.
단점 해결 방안
- 서버 부하 문제
- TanStack Query를 사용하여 데이터를 캐싱함으로써 서버에 대한 요청을 줄이고, 리소스 사용 증가 문제를 어느 정도 해결할 수 있다.
- 코드 복잡도 문제
- 관심사 분리를 통해 클라이언트와 서버의 역할을 명확하게 구분하고, 로직이 잘 분리되었기 때문에 코드 복잡도는 크게 문제 되지 않다고 생각이 든다.
마치며...
DynamoDB는 NoSQL 데이터베이스로, 전체 데이터를 정렬하는 기본 메서드가 없어 클라이언트에서 직접 정렬 로직을 처리해야 했다.
하지만 Next.js로 마이그레이션 하면서, 클라이언트 대신 서버에서 정렬 로직을 처리하게 되어 성능 향상은 물론, 비즈니스 로직을 분리할 수 있었다.
이 경험을 통해 기술 스택 선정이 얼마나 중요한지 다시 한번 실감하게 되었다...
또한, Next.js의 API Route 가 매우 유용한 도구라는 것을 느끼게 되었다.
지금까지 Next.js로 두 개의 프로젝트를 진행했지만 API Route 기능을 사용해 본 적이 없어 늘 아쉬움을 가지고 있었는데, 이번 경험을 통해 백엔드 서버 없이 간편하게 백엔드 기능을 개발할 수 있어 좋았다.
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/002.gif)
'💜 리액트 > Next.js' 카테고리의 다른 글
[Next.js] 채널톡 연동하기 구현 (feat. app router, typescript) (1) | 2024.08.28 |
---|---|
[Next.js / TanStack Query] Server Side Rendering 하기 (feat. app router) (0) | 2024.08.27 |
[Next.js] Server Sider Rendering 특정 사용자 접근 제한 (feat. App Router) (0) | 2024.08.11 |
[Next.js] 메타태그, 오픈그래프 컴포넌트 SEO 향상 (feat. Page Router) (0) | 2024.07.12 |
[Next.js] Framer Motion 화면 전환 애니메이션 적용 (feat. Page Router) (7) | 2024.07.10 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!