Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[팀 서비스 클론 코딩] 가브리엘(윤주현) 미션 제출합니다. #24

Merged
merged 13 commits into from
Oct 16, 2023

Conversation

gabrielyoon7
Copy link
Member

@gabrielyoon7 gabrielyoon7 commented Oct 11, 2023

🎨 1단계 - 팀 서비스의 일부 페이지 클론 코딩

🚀 구현한 페이지의 주소와 방식

✅ 체크리스트

선택한 렌더링 방식의 이유

  • 선택한 렌더링 방식이 어떤 페이지에 적합한지, 왜 선택했는지 이유 설명

코드 재사용

  • 기존 서비스에서 구현한 리액트 컴포넌트의 재사용 여부와 활용 방식에 대해 설명

안녕하세요 코난?

카페인 서비스를 next.js 13으 마이그레이션 해봤습니다.
일단 저희 서비스를 소개하겠습니다.

저희 카페인 서비스는 실시간 전기자동차 충전소 지도 및 통계 조회 서비스로, 원래 서비스 부터가 단일 페이지로 서비스되고 있었습니다.

별도의 페이지가 존재하지 않고, 단일 페이지 내에서 동적으로 렌더링 하는 구조로 설계되어 있습니다.
(단, 로그인을 위한 리다이렉트 페이지가 존재하였으나 기준 url 연결이 되어있지 않아 이번 미션에서는 로그인 기능을 제거하였습니다.)

별도의 페이지도 존재하지 않는데다가 매번 다른 데이터를 받아올 수 밖에 없는 형태의 웹앱이다 보니 ISR을 고려하는 것은 사실상 불가능하였습니다.

서버 측에 정보를 요청 보낼 때 현재 스크린의 위치(위도, 경도), 스크린 사이즈에 따른 화면 변화량(위도/경도 델타 값) 이 매번 다르기도 하며, 페이지의 역할을 하는 부분들도 전국 6만여 개의 충전소를 대상으로 하기에 특정한 부분을 서버 컴포넌트로 둔다는 것은 굉장히 어려웠습니다.

심지어 CSR 이던 시절과 SSG인 시절 모두 큰 성능차이도 보이지 않습니다. (최초 로드 시점이 약간 빠르지만, 체감하기 어려운 수준이었습니다.)

따라서 기존 서비스를 거의 그대로 마이그레이션한 상태입니다.

[기존]
image

[next js 13]
image

언제나 렌더링에서 SSG가 이점을 보이긴 하지만, 그 시간이 크게 차이가 나지 않아 체감하기 어렵습니다 ㅠㅠ

참고로 로그인을 제외한 기존 서비스에서 구현한 컴포넌트나 기능들을 100% 재활용 하였습니다.

@gabrielyoon7 gabrielyoon7 changed the title ㅇㅇ [팀 서비스 클론 코딩] 가브리엘(윤주현) 미션 제출합니다. Oct 11, 2023
@cruelladevil cruelladevil self-assigned this Oct 11, 2023
@cruelladevil cruelladevil self-requested a review October 11, 2023 07:54
Copy link

@cruelladevil cruelladevil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 가브리엘! 이번 미션에 대해서 어떻게 리뷰하면 좋을지 고민하다 리뷰가 늦어서 죄송해요 😅

SSG에 대한 설명

단일 페이지에다가 CSR에 적합한 프로젝트여서 이번 미션을 진행하면서 많은 고민을 하셨을텐데요!
SSG라고 설명해주셨는데 getStaticProps라던지 async로 된 컴포넌트는 확인하지 못하였어요 😭
SSG에 대한 공식문서에서 나온 예제와 같은 컴포넌트를 찾지 못하였는데 혹시 설명해 주실 수 있을까요?!

또한 메인페이지 자체가 클라이언트 컴포넌트라서 그 이하의 모든 컴포넌트가 클라이언트 컴포넌트일텐데요!
SSG에서는 클라이언트 컴포넌트가 이렇게 많아도 되나요?! (잘 몰라서 물어보는거에요!)

미션을 진행하면서 코난이 느낀점

개인적으로 이번 미션을 진행하면서 느낀점은 클라이언트 컴포넌트를 최대한 작게 쪼개서
필요한 부분만 클라이언트 컴포넌트로 만드는게 렌더링 방식의 이점을 이용하는 것 같았는데요!
여기서 필요한 부분은 동적으로 렌더링이 필요하다던지 사용자와 상호작용이 많다던지 여러 기준이 있을 것 같더라구요!
이런 부분도 고민해보면 추후 next를 사용하면서 도움이 되지 않을까 싶어요!

수정사항은?

현재는 완전 CSR로 이루어진 SPA와 다를바가 없는 것 같아요😭
야미와 센트는 지도가 로딩되기 전 서버 사이드로 탭을 렌더링 시켜서 보내주더라구요?!
이 부분을 도전해봐도 좋을 것 같고,
시간이 부족하시다면은 SSG로 하신 부분이 어떻게 되는지만 설명해주셔도 좋을 것 같아요!

가브리엘 초기 렌더링

image

센트, 야미 초기 렌더링

image

package-lock.json Outdated Show resolved Hide resolved
next.config.js Show resolved Hide resolved
"react": "^18",
"react-dom": "^18",
"react-icons": "^4.11.0",
"styled-components": "^6.0.4",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 렌더링 방식을 SSR로 정하고 미션을 진행했는데요!
SSR의 장점을 최대한 살리려다보니 css-in-js 방식의 컴포넌트를 최대한 안쓰려고 하게 되어 CSS module로 진행했습니다!
아래 글들도 참고해보면 좋을 것 같아요!

Shopify에서 vanilla-extract를 선택한 이유
Runtime stylesheets와 Static CSS extraction의 차이

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아무래도 자바스크립트가 로드 된 이후에 무언가 그릴 수 있다보니 초기 페이지에서도 css가 빠진 상태로 보여서 네트워크가 느린 환경에서는 다소 이상한 것 같아요 ㅠㅠ
코드량이 상당하다보니 이를 이식하는 것이 쉽지 않았지만 만약에 처음부터 nextjs를 사용했다거나, 시간이 충분히 주어졌다면 css module, vanilla-extract를 선택했을 것 같아요.

src/app/layout.tsx Outdated Show resolved Hide resolved
src/app/layout.tsx Outdated Show resolved Hide resolved
src/app/page.tsx Show resolved Hide resolved
@gabrielyoon7
Copy link
Member Author

유일한 페이지이자 루트 페이지에서 'use client'를 사용한 이유

초기 로딩 컴포넌트 <Loading />를 제외하면 나머지는 전부 client component인 상황입니다.

따라서 csr과 크게 다를바가 없는게 맞습니다.

하지만, 몇 가지 이유에 의해서 이 방식을 포기하기 어려웠습니다.

<ReactWrapper/> 의 역할

저희 팀에서 지도를 로드하는 방식으로는 google maps api로부터 지도 객체를 수신받을 때 @googlemaps/react-wrapper 를 활용합니다.
이 컴포넌트에서 구글 지도 라이브러리를 수신받지 못하면 window 객체에 google 객체가 들어오지 못합니다.
즉, 프로젝트 초기 로드 시 반드시 필요한 컴포넌트입니다.

구글 지도 객체가 없다면 해당 객체에 접근을 시도하는 경우 undefined 처리가 되므로 오류가 발생할 것입니다.

<ReactWrapper/> 은 hook을 사용중이다.

그런데 이 컴포넌트 내부에 진입해보면 useState나 useEffect를 사용중입니다.
따라서 ReactWrapper 컴포넌트를 사용하는 순간 'use client'를 반드시 사용해야 합니다.
아시겠지만 use client를 사용하면 자식들은 서버 컴포넌트로 돌아갈 수 없습니다.
자식 컴포넌트들이 전부 client component로 포함되기 때문입니다.

따라서 필요한 부분만 클라이언트 렌더링을 하려고 시도하였으나, 모든 컴포넌트들이 어떤 훅(react-wrapper 내부에서 마저 js-loader훅을 사용)이 라이브러리를 수신 한 이후에 사용할 수 있기에 불가능했습니다

window.google이 없다면 지도 객체를 사용하는 거의 모든 컴포넌트에서 오류가 발생한다.

거의 모든 기능들이 지도 기능을 활용하고 있습니다

지도 뿐만 아니라 document나 storage같은 전역 객체를 접근해야 하는 코드가 너무 많았습니다.

없는 객체를 참조하려는 문제를 해결하기 위해서는 너무 많은 코드를 수정해줘야 했습니다 ㅠㅠ

팀원들 미션에서 오류가 나지 않았던 이유는 기능을 완전히 빼고 일부(지도 로드, 디자인 컴포넌트 로드)만 이식했기 때문인데, 저처럼 기능까지 전부 옮기려고 했을 때에는 반드시 문제가 발생했을 것입니다.

실제로 제 프로젝트에서 내비게이션 바만 옮기더라도 수많은 전역 객체 참조 오류가 발생합니다 ㅠㅠ

만약 사용자에게 내비 바를 조금이라도 더 빨리 보여주려고 한다면 로딩 화면 시에 로딩 스피너만 보이는 것이 아닌, 가짜 내비바 디자인을 넣는 방법은 있을 것 같습니다.

SSG에서는 클라이언트 컴포넌트가 이렇게 많아도 되는지 모르겠습니다

상황에 따라서 서버 컴포넌트와 클라이언트 컴포넌트를 잘 조합하면 좋을 것 같은데, 단일 페이지에 거의 모든 기능들을 동적으로 그려내려다보니 이런 문제가 생긴 것 같습니다.

개인적으로는 괜찮을지도 팀의 페이지 처리 방식을 참고해보는 것도 좋을 듯 한데, 지금 상태에서는 어느 것 하나 뜯어내기가 어렵게 맞물려 돌아가는 상황이라 어쩔 수 없었던 것 같습니다 ㅠㅠ

SSG라고 생각하는 이유

(슬랙에 댓글을 달았던 내용도 일부 복붙하겠습니당)

저도 첨에 데이터 페칭하는 메서드를 사용 안하면 SSR이라고 생각 했었는데 여러가지 이유로 SSG라고 보는게 맞는 것 같아요
https://nextjs.org/docs/app/building-your-application/rendering/server-components#using-server-components-in-nextjs
우선 nextjs13버전에서는 기본적으로 서버컴포넌트를 사용한다고 되어있고, 요청이 없더라도 빌드 시에 미리 페이지가 정적 생성되어있다는 것이 SSG라고 보는게 맞지 않은지 생각이 들었습니다…!

데이터 페칭을 사전에 사용하지 않은 경우에는 static generation without data으로 분류 하는 것 같고, generateStaticParams나 getStaticProps 같이 외부에서 데이터를 수신하여 찍어내는 경우는 사용하는 경우에는 static generation �with data로 판단하는 것 같아요

지도를 미리 렌더링 할 수 없는 점, 접속 위치에 따라서 달라지는 위치, 사용자의 접속한 디바이스 크기에 따라 달라지는 요청으로 인해 충전소 마커 정보가 사용자마다 다르게 출력되는 점 등을 고려했을 때 사전에 데이터를 받아서 준비하는 것은 너무 어려웠습니다.

++ pages router 문서에는 rendering 에는 ssr, ssg, csr, isr 에 대한 분류가 있었는데, app router 문서에는 server component와 client component로 바뀌어 있어서 이 부분이 아직도 혼란스러운 것 같아요ㅠㅠ

@cruelladevil
Copy link

<ReactWrapper/>에 대해서 자세히 설명해주신 덕분에
다른 렌더링 방식을 사용하지 못할 정도의 태생 SPA라는 것을 잘 이해할 수 있었어요 👍👍

공식 문서의 data fetching 방식을 확인하면 next 13의 app router에서는 fetch api 기반으로 하려는 것 같아요.
generateStaticParams나 getStaticProps를 사용할 수 없는 것 같더라구요. 제가 시도했을 때는 안됐어요 😭

저는 이번에 학습하면서 app router에서는 렌더링을 위한 함수가 따로 있는 것이 아니라
data fetching에 따라 렌더링 방식이 바뀌는 것으로 이해했어요!
제 미션 PR에서 간단하게 코멘트로 남겨놨는데 한 번 읽어보시면 좋을 것 같아요! #34 (comment)

step1 고생 많으셨고 step2에서 다시 뵈요 👍

@cruelladevil cruelladevil merged commit 404431b into woowacourse:gabrielyoon7 Oct 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants