본문 바로가기
Q&A2026년 5월 29일2분 읽기

Astro 5.5 server islands — hydration mismatch 일부 페이지에서만 발생

정
정민호
조회 709댓글 4

Astro 5.5 server islands로 정적 부분 + 동적 부분 분리 중인데 일부 페이지에서 hydration mismatch가 나옵니다.

---
// src/pages/index.astro
import LoginStatus from '../components/LoginStatus.astro'
---
<Layout>
  <Hero />
  <LoginStatus server:defer fallback={LoginSkeleton} />
  <FeatureList />
</Layout>

3개 페이지 중 1개만 console에 "Hydration completed but contains mismatches" 경고. 같은 LoginStatus 컴포넌트인데...

경고 발생 페이지

  • 홈 (/) — A/B 테스트 분기가 있음
  • 제품 페이지 — 정상
  • 블로그 — 정상

A/B 테스트 분기는 cookies()로 그룹 결정하는데, 이게 문제일까요?

댓글 4

김김영삼5개월 전

네, A/B 테스트 분기가 원인이 맞습니다. Astro의 server islands는 "전체 페이지가 정적, 일부만 동적"이라는 가정인데, A/B 테스트로 페이지 전체가 분기되면 그 가정이 깨집니다.


패턴 1 — Edge Function으로 A/B 분기


Astro 페이지에 들어오기 전 edge에서 분기 결정 후 응답 헤더에 그룹 명시. 페이지는 그룹별로 별도 캐시.


// middleware.ts (Vercel Edge)
export const middleware = (req) => {
const group = getABGroup(req.cookies)
const headers = new Headers()
headers.set('x-ab-group', group)
// group별로 다른 페이지 응답 캐시
return rewrite(`/groups/${group}${req.nextUrl.pathname}`, { headers })
}

패턴 2 — A/B 분기 자체를 client island로


페이지는 모두 정적, A/B 결정은 client 측에서. 깜빡임 발생할 수 있지만 hydration은 안전.


<ABBanner client:idle />  <!-- 클라이언트에서 분기 결정 -->

패턴 3 — A/B를 server island 안으로 격리


A/B 분기를 server:defer 컴포넌트 안에서만. 페이지 외부는 정적 유지.


<Hero />  <!-- 정적 -->
<ABBanner server:defer fallback={null} /> <!-- A/B는 여기 안에만 -->
<FeatureList />

왜 다른 페이지는 OK?


제품 페이지/블로그는 cookies() 호출이 server island 안에서만 발생. 홈은 페이지 컴포넌트 자체에서 호출. Astro는 페이지 컴포넌트에서 cookies() 호출하면 전체 페이지를 동적으로 전환합니다.


3번 패턴이 가장 깔끔하고 성능 좋습니다. 페이지 셸은 CDN 캐시, A/B만 동적.

프프론트수진5개월 전

저는 A/B 도구로 GrowthBook 쓰는데, edge SDK가 있어서 middleware에서 분기 → header inject → server island가 header 읽어서 처리하는 패턴이 깔끔합니다. cookies() 직접 안 만지고요.

최최재훈(guest)5개월 전
참고로 Astro 5.5의 server:defer는 첫 응답에 fallback HTML만 보내고 island를 별도 fetch 합니다. 그 fetch가 실패하면 hydration mismatch처럼 보일 수도 있어요. Network 탭에서 island fetch 상태 확인해보세요.
깃깃허브유저(guest)5개월 전
이 부분은 vendor가 다음 minor 버전에서 개선한다는 로드맵 봤어요. 그때까진 위 우회 패턴이 답인 것 같습니다.
Ctrl+Enter로 등록