🌲[깊은산골짜기] 기술스택 뭐 쓰지?

2024-12-24

이번 깊은산골짜기 프론트엔드 프로젝트에서는 기술스택 선정에 있어서 많은 고민을 거쳤습니다. 다양한 기술들을 사용해보고 싶은 욕구와 과연 이걸 왜 사용해야 하는지 등을 계속해서 고 민했던 것 같습니다. 깊은산골짜기 프론트엔드 팀에서 어떤 기술스택을 선택했는지 그 이유와 고민과정을 설명 드리려고 합니다.

우선 저희가 사용한 기술 스택은 다음과 같습니다.

Frontend: React, TypeScript, Zustand, React Query, Tailwind CSS, Chakra UI

CI/CD: GitHub Actions, AWS S3 + CloudFront, PWA/TWA

Tools: ESLint, Prettier, Vite, Vitest, Framer, Jest

Communication: GitHub Projects, Notion, Discord, Figma

이제 하나하나 각 기술스택을 선택한 이유를 설명드리겠습니다.

1. React vs Next.js

배경

프로젝트를 기획하면서 저희는 모바일 웹 환경을 베이스로 하기로 결정했습니다. 이는 단순히 게시판을 만드는 것이 아니라, 실제 방문한 장소에 대한 리뷰를 중심으로 하는 서비스이며, 사용자들이 주로 모바일 기기에서 실행한다는 가정 아래 진행되었습니다.


이러한 기획 의도를 기반으로 백엔드 팀은 저희에게 어플리케이션 개발을 요구하였지만,
아쉽게도 저희 프론트엔드 팀은 Flutter, React Native, 또는 네이티브 앱 개발 경험이
부족했습니다. 따라서, 모바일 웹을 기반으로 화면을 구성하는 방법을 선택하였습니다.


모바일 웹은 접근성과 개발속도 면에서 적절한 대안이었으며, 이를 통해 사용자 경험을
충분히 제공할 수 있다고 판단했습니다.


React와 Next.js 중 뭘 써야할까??

모바일 웹을 베이스로 개발을 진행하면서, 저희는 React와 Next.js 중 어떤 것을 선택할지 고민에 빠졌습니다.


Next.js는 서버 사이드 렌더링(SSR)을 지원하며 SEO에 유리하다는 장점이 있어 매력적인 선택지였습니다. 이러한 SEO와 더불어, Next에서 제공되는 다양한 기능들이 모바일 웹을 구성하는데 용이하다는 이야기도 있었고, 또한 최근 많은 기업들이 프론트엔드 개발자 채용 시 Next.js를 요구하고 있어 이번 기회에 Next.js를 경험해 보고 싶었습니다. 그러나 팀원 모두 Next.js 사용 경험이 없다는 것이 걸림돌이었습니다. 공식 문서를 통해 기본 개념을 학습한 정도였고, 실제로 주요 기능을 활용하려면 하나하나 구글링이 필요했기에 개발 생산성 면에서 큰 부담이 되리라 판단했습니다.


반면 React는 팀원들이 이미 익숙하게 사용해왔던 라이브러리였고, 프로젝트의 복잡성을 낮추고 학습 부담을 줄이는 데 유리했습니다. 프로젝트 일정과 팀원들의 경험 수준을 고려할 때 React가 더 적합한 선택이라고 판단했습니다.


또한, 배포의 간편함도 중요한 고려 요소였습니다.
이전 프로젝트인 '알고리즘 파이터' 에서는 프론트엔드 팀의 배포 경험이 없었습니다. 당시 백엔드 팀원이 Docker와 Kakao에서 제공하는 Crampolin IDE를 사용해 백엔드 배포는 무리 없이 진행했으나, 프론트엔드 배포에는 어려움이 있어 결국 배포를 성공적으로 마무리하지 못했습니다.


따라서 이번 프로젝트에서는 프론트엔드 배포를 반드시 프론트엔드 팀에서 책임진다는 목표를 세웠고, 제가 직접 배포 및 CI/CD를 진행하기로 했습니다. 하지만 AWS 사용 경험이 부족했기 때문에 Next.js와 React 중 어느 것을 선택할지 결정할 때 배포의 복잡성도 중요한 기준이었습니다.


React는 정적 파일을 생성하여 AWS S3와 CloudFront를 통해 비교적 간편하게 배포할 수 있었던 반면 Next.js는 SSR을 위해 EC2나 Elastic Beanstalk와 같은 서버 환경이 필요했기에, 배포경험이 없는 저희 프론트엔드 팀에게는 무리가 있다고 판단했습니다. Vercel로 배포하는 방법도 있지만, 이번 프로젝트에서는 구름에서 제공하는 AWS 비용지원을 받는 이유와 AWS를 사용법을 경험해보자는 팀원들의 의견에 따라 Vercel의 사용은 배제하였습니다.


이러한 이유로, React를 사용하여 모바일 웹을 개발하기로 하였고,
이는 개발 속도를 높이는 데 큰 도움이 되었으며, AWS + Cloudfront, Github Actions를 사용한 CI/CD 과정도 훨씬 손쉽게 해결할 수 있었습니다.


2. TypeScript

TypeScript는 코드의 안정성과 유지보수성을 고려했을 때, 팀원들의 만장일치로 선택하였습니다. 이전 프로젝트에서도 이미 사용해본 적 있고, 협업과정에서도 타입을 명시적으로 확인할 수 있어 개발 생산성을 높여주었습니다.


3. Zustand

프로젝트 초기 단계에서 저희는 상태 관리 도구를 선택하는 과정에서 많은 고민을 했습니다. 프로젝트 규모와 팀의 경험 수준, 상태 관리의 필요성을 고려하며 Zustand를 사용하기로 결정했는데, 이 선택에 있어 Reddit에서 발견한 아래의 조언이 큰 영향을 미쳤습니다:

Zustand가 다른 상태관리 도구에 비해 가볍고, 배우기 쉽다는 점에서 끌렸습니다. 따라서 저희 프론트엔드 팀 모두 상태관리 도구에 익숙하지 않았기에, 처음 사용해보는 팀원이 있었지만 무리없이 사용할 수 있을거라 생각했습니다. 다른 상태관리 도구의 경우 이전에 고려했던 Recoil은 더 이상 활발히 관리되지 않는다는 점에서 제외했고, Redux는 단기간에 배우기에는 지나치게 복잡하다고 느꼈습니다. 무엇보다도 Reddit의 조언처럼 Zustand와 React Query의 조합이 특히 적합하다는 의견에 따라, 서버 상태와 클라이언트 상태를 효율적으로 분리해 관리할 수 있을 것이라 판단했습니다.

하지만 막상 프로젝트를 완성하고 난 후, 돌이켜보니 '굳이 Zustand를 사용했어야 하나?'라는 고민을 하게 되었습니다. 이는 뒤에서 설명할 ChakraUI 덕분이기도 했는데, ChakraUI를 사용하면서 화면구현에 있어 고려해야할 상태관리들을 줄일 수 있었고, 각 주요 기능들에 대한 상태관리를 전역으로 할 필요가 없었기 때문입니다. 최종적으로 프로젝트가 끝나고 난 후, zustand를 사용해 전역적인 상태관리가 필요한 부분은 로그인부분과 유저의 정보를 저장하는 부분 외에는 사용되지 않았습니다. 차라리 'Context API를 사용해도 되지 않았을까' 라는 아쉬움이 남았습니다.


4. React Query

React Query를 도입하게 된 데에는 새로운 스택을 경험해보고 싶다는 개인적인 욕심이 컸다고 생각합니다. 최근 많은 곳에서 서버 상태 관리를 위해 React Query를 도입하고 있었고, 저 역시 이를 한번 사용해보고 싶다는 생각으로 아래 글들을 참고하며 도입을 결정했습니다:


React Query를 도입하면서 배운 점

React Query를 사용하며 새롭게 배운 점과 기존 문제를 개선한 점이 많았습니다.

첫번째로, 코드 가독성을 높일 수 있었습니다.
기존에는 서버 데이터를 가져오고 상태를 관리하기 위해 useEffect와 try-catch 구문을 많이 사용했습니다. 이 방식도 동작에는 문제가 없었지만, React Query를 사용하면서 onSuccess와 onError를 활용해 코드의 가독성을 높이고 에러 처리를 깔끔하게 정리할 수 있었습니다.

또, React Query를 사용하면서 캐싱(cache) 이라는 개념을 처음 배우게 되었습니다. 기존에는 데이터를 매번 API로 호출했습니다. React Query를 도입하면서, 공식 문서를 통해 invalidateQueries의 개념을 알게 되었고 React Query의 staleTime을 활용하면서 메인 페이지에서 로그인한 유저의 데이터를 가져오는 API의 호출횟수를 줄일 수 있었습니다.

사용자가 로그인 후 메인 페이지에 접근하면, 이후 프로필 수정 및 리뷰 작성, 조회 페이지로 이동하기 전에 메인 페이지에서 로그인한 유저의 정보를 관리하기로 했습니다. 문제는 프로필 수정 시 로그인 유저의 정보가 변경되었을 때, 변경된 정보만을 불러오는 것이 어려웠다는 점입니다. 유저 정보가 변경되더라도 기존 유저 정보가 남아 있었고, 페이지가 새로고침 된 후에야 제대로 된 유저 정보를 받아오는 오류가 있었습니다. 이를 해결하기 위해 useEffect를 사용하여 메인 페이지 접근 시 유저 정보를 받아오도록 했습니다. 하지만 이렇게 할 경우, 유저 정보가 바뀌지 않더라도 API를 호출하는 등 불필요한 API 호출이 발생했습니다.

React Query를 사용하면서 이를 해결할 수 있었습니다. 캐싱과 invalidateQueries를 활용하여 기존 데이터를 캐싱할 수 있었고, 프로필 수정 등으로 유저 정보가 변경되면 캐싱된 데이터를 무효화하여 필요할 때만 데이터를 호출하도록 관리할 수 있었습니다.


React Query를 사용하며 헷갈렸던 점

React Query를 처음 사용하면서 헷갈리는 점도 적지 않았습니다.

우선, React Query의 isLoading과 React의 Suspense를 활용한 로딩 처리 사이에서 많은 고민을 했습니다. isLoading을 사용하면 데이터를 가져오는 동안 로딩 상태를 명확하게 제어할 수 있었습니다. 그러나 Suspense와 isLoading의 차이를 이해하는 데 어려움이 있었고, 이중 어떤걸 사용해야 하는지에 대한 고민을 계속했습니다.

또, React Query와 React Router의 createBrowserRouter의 데이터 로딩 방식 차이도 헷갈렸습니다. createBrowserRouter의 경우 컴포넌트가 마운트되기 데이터를 미리 로드하는 반면 리액트 쿼리는 컴포넌트를 마운트한 후 데이터를 가져옵니다.

프로젝트 당시에는 createBrowserRouter를 사용했을 때 대기시간 동안 로딩 상태를 명확히 처리하는 방법을 몰랐습니다. 그래서 페이지 전환 이후의 비동기 작업만 React Query로 처리하는 것이 사용자 경험에서 더 나을 것이라고 판단하여 React Query를 사용하였습니다. 하지만 최근 createBrowserRouter를 공부하면서 이러한 문제를 해결할 수 있는 방법이 있다는 것을 알게 되었습니다.


5. Chakra UI & Tailwind CSS

이번 프로젝트에서는 Tailwind CSS와 Chakra UI를 함께 사용했습니다.

Tailwind CSS는 제가 가장 선호하는 CSS 라이브러리입니다. 인라인으로 쉽고 빠르게 작성하면서 다른 CSS 라이브러리에 비해 익숙했기에 선택하였습니다. 하지만 이번 프로젝트에서 Chakra UI를 사용하게 되면서 생각보다 Tailwind를 사용하게 될 일이 많이 없었습니다.

Chakra UI는 이전 프로젝트인 ' 알고리즘 파이터 '에서 느꼈던 문제들을 해결하고자 사용하였습니다. 이전 프로젝트를 진행하면서, form이나 Modal, Button같은 컴포넌트를 직접 만들어 사용하였는데 단순한 기능일지라도 생각보다 많은 시간이 소요되었고, 각자 작성한 공통 컴포넌트들을 통합하는 과정에서 발생하는 에러들을 해결하는데 어려움을 겪었습니다.

그 결과, 3주간의 프로젝트 중 30% 이상을 컴포넌트 작성에 소요하게 되었고 저희 서비스의 주요 로직을 남은 1주일 동안 급하게 작성하느라 제대로 된 서비스 코드를 작성하는데 어려움을 겼고, 프론트엔드 배포 또한 완성하지 못했습니다.

이를 해결하기 위해 Chakra UI를 도입하여 공통컴포넌트 작성 시간을 줄이고 일관성있는 디자인을 유지하고자 하였습니다. 결과는 만족스러웠고, Chakra UI에 내장된 디자인 시스템을 사용하면서 제가 맡은 부분에서는 Tailwind를 사용할 일이 많이 없게 되었습니다.


6. CI/CD

AWS S3 & CloudFront

프론트엔드 정적 파일을 배포하는 가장 간단한 방법으로 AWS S3를 선택했습니다. S3는 파일을 업로드하고 URL을 통해 쉽게 접근할 수 있어 정적 파일 배포를 처음 시도하는 초보자에게 적합한 도구였습니다. 처음 시도해보는 배포였지만, **참고자료가 방대한 덕에 쉽게 IAM User 설정이나 버킷정책으로 인한 403 에러들을 해결해나갈 수 있었습니다. **

처음에는 S3만으로 배포를 계획했지만, 현업에서 React 배포에 S3와 CloudFront 조합을 가장 많이 사용한다는 멘토님의 조언에 따라 CloudFront도 함께 사용하기로 결정했습니다. CloudFront는 S3에 업로드된 파일을 사용자에게 더 효율적으로 제공하기 위한 CDN(Content Delivery Network)입니다. S3를 단독으로 사용하는 대신, 여러 리전에 올린 캐싱된 콘텐츠를 통해 빠르게 응답이 가능하다는 장점이 있습니다.

하지만** 첫 배포에서 CloudFront를 꼭 사용해야 했는지에 대한 의문이 남았습니다.** 우선 저희 프로젝트 특성상 카카오맵 API를 사용하여 한국 사용자만을 대상으로 했기 때문에 글로벌 CDN의 장점을 충분히 활용하지 못했습니다. 또, 처음 사용하는 만큼 능숙하지 못한 탓에 매 배포마다 기존 캐시를 무효화해야 했습니다. 변경되지 않은 파일은 그대로 유지할 수 있는 설정을 할 수 있을것 같은데, 이를 활용하지 못한 점이 아쉬웠습니다.

Cloudfront에서 가장 유용했던 기능은 HTTPS 설정이었습니다. 기존에는 HTTPS 설정을 위해 Nginx나 별도의 인증서를 설정해야 했지만, CloudFront를 사용하면 한 번의 클릭으로 HTTPS를 활성화할 수 있었습니다. 이 점은 배포를 처음 시도해보는 저에게 큰 장점으로 다가왔고, 배포환경에서 백엔드와의 HTTPS 통신을 원할히 설정할 수 있었습니다.

프로젝트 도중 아쉬웠던 부분 중 하나는 CloudFront에서 제공하는 기본 도메인 주소를 그대로 사용했다는 점입니다. 백엔드 팀원으로 부터 저희가 구매한 도메인을 사용해달라는 요청을 받았으나, 당시 TWA를 적용하여 플레이스토어 심사를 받는 동시에, SEO 설정을 완료했던 터라 도메인을 변경 후 작업을 다시 하기에는 무리가 있다고 판단했습니다. 또, 인턴십과 프로젝트를 병행하면서 시간이 부족했고, 무엇보다 우선순위가 높았던 앱배포가 1달가까이 걸리는 바람에 서버의 비용문제로 인해 프로젝트 종료 시점까지 도메인 변경을 완료하지 못했습니다.

비록 첫 배포였던 만큼 미숙한 부분이 많았지만, 이번 경험을 통해 AWS S3와 Cloudfront 사용방법을 익히고, 배포에 대한 자신감을 얻을 수 있었습니다.

GitHub Actions

쇠뿔도 단김에 빼라는 말이 있듯이, 처음 배포를 시도하는 김에 Github Actions를 사용해 CI/CD를 구현해보았습니다.GitHub Actions는 GitHub 레포지토리와 완벽하게 통합되며, .yml 파일을 이용한 간단한 설정만으로 빌드 및 배포 자동화를 구현할 수 있었습니다.

저희 프론트엔드 팀은 Git-flow 전략을 사용하여 브랜치를 다음과 같이 구분했습니다:

  • main: 최종 배포 버전 브랜치.
  • develop: 팀의 개발 통합 브랜치.
  • features: 각 기능별로 분리하여 작업하는 브랜치
  • hotfix : 긴급수정

파이프라인은 **main 브랜치에 푸시(push)나 develop 브랜치에서 머지(merge)**가 발생할 때 자동으로 실행되도록 구성하였습니다. 이를 통해 모든 코드 변경 사항이 배포 전에 자동으로 테스트 및 빌드되며, 배포까지 일괄 처리되는 워크플로우를 구현할 수 있었습니다.

이를 통해 팀원들은 코드가 메인 브랜치에 커밋될 때마다 자동으로 빌드 및 배포가 이루어지는 CI/CD 환경을 경험할 수 있었고, 이는 프로젝트의 생산성을 높이는 중요한 요소였습니다.


7. PWA/TWA: 네이티브 앱 개발 없이 사용자 경험을 개선하기📱

앞선 1번에서 말씀드린 바와 같이 프로젝트 초기 기획 단계에서 깊은산골짜기 백엔드 팀은 저희 프론트엔드 팀에게 애플리케이션 개발을 요청했습니다. 하지만 네이티브 앱 개발 경험이 부족했던 저희 팀은 모바일 웹으로 프로젝트를 구성하기로 결정했습니다.

프로젝트가 완성된 후 지인들에게 서비스를 보여주었을 때, 공통적으로 나온 피드백은 다음과 같았습니다:

"왜 다른 어플들처럼 바로 접속이 안 되고, 주소를 검색하거나 직접 입력해야 하나요?"

개발자의 입장에서 크롬을 실행하는 것과 애플리케이션을 실행하는 것의 차이점을 아는것과는 달리, 사용자 입장에서는 이러한 불편함이 서비스 이용을 방해한다는 점을 깨달았습니다.

이 문제를 해결하기 위해 Flutter나 React Native를 공부하여 앱을 개발할지 고민했습니다. 하지만 추가적인 학습 시간과 프로젝트 범위를 고려했을 때, 새로운 앱 개발은 현실적으로 어려웠습니다.

그러던 중, **PWA(Progressive Web App)**라는 대안을 발견했습니다.


PWA란?

PWA는 구글에서 제안한 기술로, 웹 앱과 네이티브 앱의 장점을 결합한 애플리케이션 형태입니다.

웹 앱과 같이 설치 없이 브라우저에서 바로 접근 가능하면서도, 홈 화면에 아이콘 추가, 오프라인 작동, 푸시 알림 등 네이티브 앱과 유사한 사용자 경험 제공 등의 장점을 동시에 가지고 있습니다.
특히 PWA로 구성하면 사용자 경험(UX)을 개선할 수 있을 뿐만 아니라, 이를 안드로이드와 iOS 앱으로 쉽게 변환할 수 있다는 점이 매력적이었습니다.

결과

PWA를 사용하여 저희는 lighthouse 점수를 개선하고, 이를 TWA로 전환하여 플레이스토어에 배포할 수 있었습니다. 배포환경에서 PWA 설정과 플레이스토어 배포에만 거의 1달 반이상이 소요되었기에 아쉽게도 앱스토어에는 출시를 하지 못했지만, 고민하던 문제를 쉽게 해결할 수 있었습니다.


8. Tools🔧

ESLint & Prettier & Vite

이전 프로젝트에서 가장 문제가 되었던 것이 코드컨벤션이 일치하지 않아 협업 중 충돌이 빈번하게 발생하였고, 심지어 노드버전이 일치하지 않아 에러를 겪었던 적도 있었습니다. 이를 해결하고자 프로젝트 시작 전, 제가 ESLint, Prettier, Vite등 을 설정을 하였고 다른 팀원들도 완벽하게 동일한 환경에서 프로젝트를 시작하고자 했습니다.

번들러로 Vite를 선택했습니다. 이전 프로젝트에서는 Webpack 기반의 CRA를 사용했지만, 프로젝트가 완성됨에 따라 점점 개발환경에서 초기 빌드 속도가 눈에 띄게 느려졌고, 이를 해결하고자 Vite로 마이그레이션을 시도했지만, 여러 에러와 지식의 부족으로 실패하고 말았습니다.

이번 프로젝트에서는 초기설정부터 CRA가 아닌 Vite로 시작하였고, 빠른 개발환경 속도와 typescript, pwa 등 웬만한 건 다 지원되었기에 만족하며 사용할 수 있었습니다.

아쉬웠던 점은 이렇게 통합적으로 관리를 하여도 풀리퀘스트 시 윈도우와 맥 간의 파일이 중복 생성되는, 원인을 찾기 힘든 문제들이 발생하였습니다. 이는 Docker에 대해 관심이 생기게 되는 계기가 되었습니다.

Framer

랜딩페이지 애니메이션 구현에는 Framer를 사용했습니다.
랜딩페이지를 구현하며 당근, 토스, 배민과 같이 어플리케이션 중심이지만 웹 랜딩페이지를 가지고 있는 회사들의 랜딩페이지를 많이 참고하였습니다.

대부분 사용자의 스크롤에 따라 사진과 설명들이 팝업되고 있었고, 이를 어떻게 구현해야하나 고민하던 차에 인턴에서 사용하던 Framer 라이브러리를 떠올리고 이를 사용하였습니다.

다만, 단순히 팝업되는 비교적 단순한 애니메이션이기에 CSS로 처리가 가능하지 않았나 라는 아쉬움이 있었습니다.

Jest

프로젝트 종료 후, 기능을 확장하고 에러를 수정해나가면서 수정된 코드가 제대로 작동하는지를 테스트하기 위해 실제 사용자처럼 배포환경에 접속하여 이를 테스트해왔습니다. 처음에는 괜찮았지만, 매우 불필요하고 반복적인 작업들이 계속되다 보니 기능 개발에 어려움을 겪었습니다.

이를 이미지 업로드 문제와 리뷰 수정 API 수정 작업 중 가장 많이 느꼈고, 리뷰 수정 API 문제 해결이 어려워지자 기존 코드에 대한 단위 테스트를 작성하고자 jest를 공부하여 도입해보았습니다.

처음 사용해보았기에 해당 문제에 바로 적용이 어려웠고 이를 연습하고자 유저가 접속한 환경을 체크하는 isPWAInstalled() 함수와 회원가입 페이지에 우선적으로 적용했습니다.

// 유저가 TWA 환경(모바일 앱)에서 접속했는지 확인
 
it("userAgent에 TWA 문자열이 있을 때 True 반환", () => {
  Object.defineProperty(window.navigator, "userAgent", {
    value:
      "Mozilla/5.0 (Linux; Android 10; TWA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36",
    configurable: true,
  });
 
  expect(isPWAInstalled()).toBe(true);
});
 
// ... 생략

많은 시행착오 끝에 두 테스트는 작성하고 이를 CI를 만들 수 있었으나 제가 고민하던 문제는 적용하지 못했습니다. 왜냐하면

  1. 해당 문제의 원인이 프론트엔드인지, 백엔드인지를 몰랐기에 원인을 찾는 과정에서 프론트엔드 단위테스트만으로는 원인을 찾기가 불가능

  2. 테스트코드를 작성하기 전, 문제의 원인을 찾고 문제를 해결

  3. 배보다 배꼽이 더 큰, 테스트코드를 작성하는데 더 많은 시간을 사용

했기 때문입니다.

아쉽게도 도입을 결정하게 된 문제를 해결하는데는 사용하지 못하였으나 Jest를 사용한 테스트 코드 작성 연습을 해볼 수 있는 기회였습니다.


마치며

이렇게 깊은산골짜기 프론트엔드 팀이 사용한 기술 스택들이 어떤 고민 끝에 선택된 것인지 살펴보았습니다. 당시 프로젝트를 진행하며 기록해 두었던 노션과 메모를 다시 꺼내보며 글을 작성하는 과정에서, "차라리 이렇게 했으면 더 좋았을까?", "이걸 왜 이렇게 했지?" 같은 생각이 떠오르기도 했습니다. 하지만 무엇보다, 그때의 고민과 시행착오를 떠올리며 열정적으로 프로젝트에 매달렸던 순간들이 다시금 생생히 떠올라 뿌듯한 마음도 들었습니다.

이번 프로젝트를 통해 느낀 점은 **"기술은 결국 문제를 해결하기 위한 도구이며, 사용 목적과 팀의 상황에 따라 적합한 것을 선택하는 것이 가장 중요하다"**는 것이었습니다. 다음 프로젝트에서는 이번 경험을 바탕으로 더 효율적인 기술 선택을 하고자 합니다.


참고자료

https://www.reddit.com/r/reactjs/comments/12su914/zustand_vs_redux/