lighthouse 성능 개선을 위해 홈 화면 SSR 전환 후 일부 GET 요청 실패
기존 CSR 구조에서는 홈 진입 후 브라우저에서 다음 데이터를 조회했다.
- 경기 목록 조회
- 구단 목록 조회
SSR 전환 후에는 page.tsx에서 서버 사이드로 데이터를 미리 조회한 뒤, 클라이언트 컴포넌트에 props로 전달하는 구조로 변경했다.
그러나 SSR 전환 이후 홈 화면에서 일부 GET 요청이 정상적으로 동작하지 않는 문제가 발생했다.
대표적으로 구단 목록 조회 요청이 정상 응답하지 않거나, 서버 로그에서 403 응답이 확인되었다.
CSR에서는 API 요청이 브라우저에서 실행된다.
즉 요청 주체가 사용자의 브라우저이며, 브라우저 환경에서 필요한 쿠키, 클라이언트 헤더, 공통 fetch 설정이 함께 적용될 수 있다.
반면 SSR에서는 API 요청이 브라우저가 아니라 Next.js 서버에서 실행된다.
따라서 SSR로 변경하면 API 서버 입장에서는 요청 출처와 실행 환경이 달라지므로 이로 인해 기존 CSR에서는 정상 동작하던 GET 요청이 SSR 환경에서는 실패할 수 있다.
기존 클라이언트 코드에서는 getMatches, getClubs 가 내부적으로 공통 API wrapper를 사용하고 있다.
// 예시
export const getMatches = async (date?: string) => {
const params = date ? `?date=${date}` : "";
return await pub.get<MatchesData>(`${API_BASE_URL}/order/matches${params}`);
};여기서 pub.get은 단순 fetch가 아니라 공통 요청 설정을 포함하는 wrapper다.
wrapper에 공통 base config, Content-Type, 인증 관련 헤더와 같은 정보가 삽입 되어있지만 SSR 전환 과정에서 서버 용 함수를 만들며 직접 fetch()을 사용함으로 기존 pub.get이 자동으로 붙여주던 요청 설정이 빠졌다.
따라서 SSR용 fetch 요청이 기존 CSR 요청과 동일한 형태로 만들어 지지 않은 것이다.
SSR을 적용해도 홈 화면 전체가 서버 중심으로 단순해지는 구조가 아니며 이미 홈 화면은 브라우저 상태와 상호 작용 의존도가 높기 때문에 CSR로 유지하는 것이 현재 구조에서 더 안정적이라고 판단했다.
따라서 홈 화면은 CSR 구조를 유지하고, 성능 개선은 다음 방식으로 진행했다.
- LCP 이미지 priority 적용
- 스켈레톤 크기를 실제 카드 크기와 동일하게 조정
- 초기 loading 상태를 true로 설정해 layout shift 방지
- 날짜 계산은 클라이언트 마운트 이후 수행
- 구단 카드/경기 카드 스켈레톤 크기 고정