지난 6개월 회고
정말 오랜만에 블로그 글을 쓰게 되었습니다...
2023년 올해 초 부터 회사에서 시작하였던 신규 서비스 준비에 매우 정신없이 보냈습니다. 한 마디로 영혼을 갈아 넣었죠..
8월에 MVP 출시 목표가 9월, 10월, 11월 까지 미뤄지고, 결국 서비스 출시는 12월 까지 미뤄지게 되었습니다.
지금은 본서비스를 위한 프리런칭 버전의 서비스를 출시를 하고, 협약할 기관들을 컨텍 중이랍니다.
이런거 보면 사업이라는게 참 쉽지 않다고 느껴지네요.
기획이라는 것은 곧 사람 생각이니, 다음날 아침이면 바뀌고, 뒤돌아서면 바뀌고, 눈깜빡이면 바뀌는 것 같습니다.
프론트엔드 개발자로 이번 서비스를 하면서 사용해본 것들, 느낀점 한번 시원하게 털어놔 보겠습니다.
Tailwind.css
css 파일에 스타일 작성하는 것 부터 시작해서, css-in-js 를 주로 사용하였다보니 Tailwind 에 대해 알 수 없는 반항심이 있었는데요.
지금은 이런 효자가 또 없긴 합니다.. 이제는 css-in-js 가 너무 불편해서 쓰기 싫어질 정도입니다.
장단점
tailwind.config 파일 하나에서 모든 theme, media-query 의 break point 설정, 그리고 component, utility 설정할 수 있고,
스타일 적용하는 코드 작성할때 타 스타일 라이브러리에 비해 압도적인 생산성과 편안함을 줍니다.
스타일 코드만을 위한 css, ts 파일을 따로 만들지 않아도 된다는 점도 좋습니다.
css-in-js 에 비해 스타일 코드 재활용성이 떨어지지 않느냐 싶기도 하지만, 사실 이점은 component, utility 를 잘 활용하면 어느정도는 해결되는 것 같습니다. 그러면 css-in-js 은 스타일 코드 재활용성이 높았으냐 하면은, 그렇다고 생각할 수도 있지만, Tailwind 에 비해서는 스타일 코드 부피가 훨씬 크기 때문에 열심히 정리해놓아도, 스크롤이 길어져서 파악하기 어려운 느낌이었습니다.
결국엔 뭘 쓰던 평소에 정리 잘하고 설계를 잘 해두지 않았으면 복붙 스타일 코드가 여기저기 돌아다녔을 겁니다.
그러나 tailwind 는 결국 코드 재활용을 하더라도 그것을 합쳐서 적용시킬 때 tailwind-merge 와 같은 라이브러리로 병합해주지 않으면 className 에 작성한 스타일이 의도한 대로 반영되지 않으므로, 매번 라이브러리 가져와서 합쳐줘야 하는게 불편하다고 느껴질 때도 있습니다.
조건부 스타일 적용이 까다로운 점이 단점으로 보일 수 있지만, clsx, tailwind-merge 와 같은 라이브러리를 통해 해결할 수 있습니다.
그러나 정말 치명적인 단점은 바로 tailwind 는 동적 스타일 적용이 안된다는 점입니다. 그래서 정말 울며 겨자먹기로 css-in-js 를 섞어 쓴다거나 하는 다른 방법을 섞어야 합니다.
예를 들어서 width 스타일을 매개변수로 받아서 동적으로 적용한다고 했을때, tailwind 는 빌드 시점에 css 스타일 코드로 변환되서 적용되다보니 런타임에서 아무리 열심히 매개변수가 바껴봤자 적용이 안되는 것으로 이해하고 있습니다.
twin-macro
그래서 나온게 twin-macro 같은 라이브러리가 있죠.
그러면 이걸 쓰면 tailwind 문법과 css-in-js 를 동시에 사용가능하니 모든게 해결되는게 아닌가 싶지만, twin-macro 는 그저 tailwind 문법을 사용할 수 있다 뿐이지, 중복된 스타일에 대한 최적화를 지원해주지 않습니다.
예를 들어서 Tailwind 의 className="bold" 대신 tw-macro 의 tw`bold` 로 할 경우 새로운 별도의 스타일이 생겨나는 것입니다.
결론
그래서 결론은 뭐냐 물어본다면, 결론을 아직 내지 못한 상태입니다.
스타일 코드 최적화 + 압도적 생산성을 위해 tailiwnd.css 와 동적 스타일 적용을 위한 css-in-js 를 섞어서 써라.
동적 스타일 적용 부분도 되도록 tailwind.css 로 해결해보려고 노력하자. 이정도로 정리되는 것 같습니다.
Next.js
Next.js 에서 해볼 수 있는 것들을 다 최대한 다 해보려고 노력했습니다.
현재 Next 가장 최신 버전인 v13.5.X 를 설치하여 프로덕션 빌드에 사용하고 있습니다.
app 디렉토리는 여전히 너무나 이른 단계이기 때문에 보류 중 입니다.
app 디렉토리를 사용하겠다는 것은 서버 컴포넌트를 사용하겠다는 것과 동일한 말인데요.
서버 컴포넌트를 잘 이용한다면 최적화 영역에서 큰 성과를 낼 수 있을 지 모르지만, React.js 본진에서 아직 정식으로 개발이 완료된 상태가 아닌 실험적 기능이라는 점이 가장 큰 걸림돌이고, Next.js 에서는 괜찮다고 하더라도 아직은 시간이 더 필요하지 않은가 생각이 듭니다. 프로젝트에 사용되는 주요 라이브러리들도 서버 컴포넌트를 지원해야하고, 이슈도 해결해야 되기 때문입니다.
로컬에서 Turbopack 써보려고 시도해보앗으나, 한글명 파일은 지원 안하는것 같더군요... 사실 로컬에서는 원래도 워낙 빨랐기 때문에 천천히 사용해볼 생각입니다.
굳이 파일 이름을 한글로 왜 사용하느냐 라고 물어본다면, 도메인에 따라서는 일반인이 살면서 한 번도 들어보지 못하는 특이한 단어들이 많기 때문이다 라고 답변할 수 있을 것 같습니다.
SSR, SSG
React.js 가 아닌 Next.js 를 쓰는 이유를 한가지 꼽자면, SSR,SSG 라고 할 수 있죠.
최근에 개발 하면서 getStaticPaths, getStaticProps 조합으로 정적페이지 생성의 기회가 있었는데요,
수백만,수천만.. 개수를 가늠할 수 없이 많은 정적페이지를 생성하는건 불가능 했기 때문에, 범위를 좁혀서 유저들이 최대한 많이 검색하여 방문할 것으로 기대되는 타겟을 설정하였습니다. 그래도 너무 많아서 그 중에 1만개의 정적페이지 생성을 목표로 하였으나, 이것도 너무 오래걸리더군요.. M1 Mac 에서 15분. Github Actions 똥컴으로는 56분... 😢😢😢
사실 목표는 SEO 대응이라서 정적페이지가 아니더라도 서버사이드 렌더링으로도 충분히 대응은 가능합니다.
정적페이지로 했을때 장점은 압도적으로 빠른 응답속도, 방문할 때마다 API 요청 안해도 되는 것. 정도 이겠죠.
이것은 최적화의 영역입니다.
그러나 조금의 향상을 위해서 너무 많은 피를 흘려야 해서 일단 한 수 접게 되었습니다. (빌드가 1시간이라니 ㅠㅠ)
본문은 다 빼고 meta tag 부분만 next-seo 로 추가해서 SSG 했는데도 저렇게 시간이 나오더군요.
로컬에서 SSG 를 돌려서 html 파일만 EC2 에 올려준다면?.. 등의 뭔가 묘수는 있을지 모르지만, 당장은 시간 관계상 보류입니다. 인력은 항상 부족하고 일은 많기 때문이죠.
CRA(Create-React-App) vs Next.js
SEO 를 전혀 신경 쓸 필요 없는 즉, SSR 필요 없는 백오피스 같은 서비스라면 CRA 해도 괜찮다고 생각 합니다.
그게 아니라면 Next.js 를 무조건 쓰는게 좋다는 입장인데요.
일단, CRA 같은 경우 거의 2년 이상 업데이트가 없습니다. 전원 해고를 당한 건지 무슨 일이 일어난 것인지 모르겠네요?
그에 비해 Next.js 는 정말 실시간으로 뜨겁게 개발되고 업데이트 되고 발전하고 있습니다.
또한 router/link/font/webpack 등 프로젝트에 필수적인 기능들을 자체 지원해주고 있습니다.
Next 버전만 올려주면 머리 아프게 버전 관리해줄 필요가 없죠.
그리고 CRA 로 서비스 만들었다가 SEO 고려해야하는 상황 오면, html 파일 전달을 위한 프론트엔드를 위한 서버를 만들어야 하는 상황이 오게 되는데요.
Next 에 이미 서버가 있고, SSR,SSG,ISR,미들웨어 등 강력한 서버사이드 기능을 지원해주기 때문에 사실 개발자 입장에서는 다 차려진 밥상에서 밥이랑 반찬만 잘 집어 먹으면 됩니다. 항상 개발 인력은 부족하고 개발할 것은 많기 때문에 비용을 아끼는 것은 매우 중요한 일 입니다.
결론
프론트엔드 개발자도 마냥 UI 만 찍어낼 게 아니라 서버를 적절히 활용해서 더욱 최적화된 사용 경험을 제공할 수 있도록 노력해야 합니다.
프로젝트 관리에 들어가는 인적 비용은 한정적이기 때문에 최대한 아끼고 아껴야 합니다.
이제는 Next.js 는 선택이 아닌 필수입니다.
graphql
이번 서비스에서 야심차에 graphql 을 도입했는데요.
처음에는 도저히 Rest API 와의 차이를 느끼지 못했다가, 최근에는 이만한 효자가 또 없다고 느낍니다.
담당 백엔드 개발자가 바뀌면서, 백엔드 서버를 Node.js + Express + Apollo Server + Prisma 구성으로 사용하다가, 최근에 Nest.js 로 마이그레이션 하게 되었는데요.
Nest.js 에서 똑같은 API 를 Rest API 방식으로도 Graphql 방식으로도 모두 이용할 수 있게 만들 수 있어서 좋았습니다.
Apollo Server 를 쓰는 순간부터 Apollo 만의 구조를 따라야해서 Rest API, Graphql 동시 지원 같은 것을 안되는건 아닌데 구조 짜고 관리하기가 까다롭다고 하더군요. 이해가 됩니다.
Rest API 와의 차이
graphql 이 효자라고 느낀 건, 어떠한 무거운 데이터를 query(get) 해야할 때 필요한 것만 가져올 수 있다는 점이 확실히 메리트를 주는 시점 부터 였습니다.
처음에는 graphql 을 쓰더라도 응답값을 전부다 가져다 쓰는 경우가 10에 9이었다면, 기획이 바뀌고, 서비스에 기능이 추가될 때마다 새로운 API 없이도 graphql 로 대응 가능한 경우가 점점 많아졌습니다.
graphql 를 쓰면 딱 필요한 데이터만 깔끔하게 가져와서 쓸 수 있고, 브라우저 network 텝에서도 불필요한 데이터를 노출시키지 않을 수 있기 때문에 보안적인 면에서 강한 이점이 있다고 말할 수 있습니다.
물론 부분 데이터만 가져올 수 있는 Rest API 를 만들어 달라고 백엔드 개발자에게 부탁하면 되지 않느냐~ 라고 할 수도 있지만, 그것도 한 두번이지 언제 기획이 바뀔지 모르는 판국에 매번 그러기가 쉽지 않습니다.
graphql 에는 큰 단점이 있는데요.
/graphql 이라는 단일 엔드포인트를 사용하기 때문에 에러 로깅 같은게 Rest API 에 비해 까다롭습니다.
Apollo Client 만의 문제인지는 모르겠는데 프론트엔드에서도 에러 처리가 까다롭습니다. 에러 응답값이 그냥 data { message: "어쩌고" } 이렇게 들어오는게 아니라 엄청 복잡한 객체 형태로 받아지거든요..
저는 객체의 타입을 파악해서 에러 핸들링 해주는 함수로 작성해서 처리하고 있는데, 아직 완벽하지가 않습니다.
graphql codgen
graphql 쓰면서 알게된 기술 중에 graphql codgen 때문에 이젠 graphql 안 쓰던 시절로 돌아갈 수 없는 몸이 되어 버렸습니다.
이게 진짜 요물 입니다..
프론트엔드에서 원하는 입맛대로 주문서 (Graphql Operation Document) 를 작성해 두면, 백엔드 서버의 graphql 엔드포인트에 접근해서 Type, DocumentNode 또는 hook 등을 모조리 자동으로 만들어 줍니다.
Rest API 와 graphql 같이 쓰자. tanstack-query 와 apollo-client
이건 결론 부분에 쓰려고 했는데 결국 Rest API 와 graphql 둘다 장단점이 있기 때문에 함께 쓰는 것이 베스트 라고 생각됩니다.
그런데 Rest API 는 axios + tanstack-query 라이브러리가 모두 정복한 상태라서 apollo-client 같은 graphql 전용 라이브러리 또 설치해야 할 지 참 애매모호 하긴 합니다.
저는 A 라는 서비스는 tanstack-query 라이브러리가 메인이고, /graphql 엔드포인트로 POST 요청 하는 방식으로 쓰고 있고, B 라는 서비스는 Apollo Client 만 설치해서 graphql 만 쓰고 있습니다.
참고로 프론트에서 Apollo Client 같은 라이브러리 없어도 graphql Query 요청할 수 있습니다. /graphql 엔드포인트에 POST 요청으로 body 에 gql 작성해서 날리면 됩니다.
tanstack-query 라이브러리는 API 응답에 대한 콜백 처리, 응답값 변형, 캐싱, 재요청 등 응답에 관하여 강력한 기능이 많지만, API 요청 자체는 axios 쓰고 미들웨어 기능은 axios intercepter 설정해서 처리해야 한다는 점. 로컬 상태관리는 Redux, Recoil 같은 타 라이브러리 또 써야 되는다는게 역할의 경계선이 명확히 나눠져 있다고 볼 수 있는데, 이게 사실은 참 손이 많이가고 귀찮긴 합니다...
apollo-client 는 axios 없이도 요청, 미들웨어, 응답, 심지어 상태 관리까지 모든 기능을 제공하기 때문에 하나로도 다 되는 슈퍼 라이브러리 인데요, 코드도 상당히 짧아지기도 하고, 개발 경험이 너무 좋습니다.
결론
Rest API + graphql 같이 쓰는 것이 베스트 라고 생각됩니다.
그러면 저 둘을 어떻게 나눠서 쓰는게 좋은가 하는 제 생각인데요.
데이터 전체를 사용해야만 하는 목록 조회 같은 API 는 어차피 응답값 그대로 사용하기 때문에 Rest API 로 구현하는게 좋고요.
어떤 도메인 데이터에 수 많은 정보들이 담겨 있어서 부분 Query 가 가능한 경우 graphql 로 만들어두는게 미래를 위해 좋았습니다.
GET 요청 말고 삭제,수정,추가 같은 Mutation 에 해당하는 요청은 사실 굳이 graphql 써야하는 이유를 잘 모르겠더군요.
아직 많은 케이스를 다뤄보지 못해서 그런 걸 수도 있지만, 아직은 Rest API 로 하는것과 차이를 느끼진 못했습니다.
graphql codegen 쓴다면 graphql 로 작성해두는게 편할 것 같다는 생각도 드네요.
'프로젝트 > 프로젝트 기록' 카테고리의 다른 글
넘블 금융앱 리뷰얼 for 액티브 시니어 프로젝트 개발 회고록 (0) | 2022.11.08 |
---|