Issues
2025. 8. 27.
Framer Code Boundaries: React 에러처리
Framer는 React 기반의 웹사이트 제작 도구로, 에러가 발생한 컴포넌트만 숨기고 사이트 전체의 정상 작동을 유지하는 '코드 경계' 기능을 구현하여 사용자 경험을 개선합니다.

Posted by

Translated by
목차
Table of Contents
본 문서는 Framer를 배우는 국내 사용자들이 한글 자료의 부족으로 겪는 어려움을 해결하고자, 공식 블로그의 내용을 한국어로 번역하고 실무에 유용한 정보를 추가했습니다. 프레이머를 사용하는 여러분께 작게나마 도움이 되길 바랍니다.
Framer와 커스텀 코드의 딜레마
Framer는 기본적으로 노코드 웹사이트 제작 도구입니다. 하지만 모든 사이트는 사실상 React로 제작된 앱이고, 사용자들은 언제든 커스텀된 React 컴포넌트나 오버라이드를 추가할 수 있습니다.
문제는 여기서 발생하는데요. React 앱에서 컴포넌트가 에러를 일으킨다면? 앱 전체의 구조가 깨지게 되고, 화면에는 하얀 빈 페이지만 남게 됩니다.

Framer 사이트도 예전까지는 이렇게 작동했습니다. 커스텀된 코드가 하나라도 잘못 된다면 전체 사이트가 멈춰버렸죠. 이를 고치기 위해 이번 겨울, 새로운 접근을 시작했습니다. 목표는 커스텀 코드가 망가지더라도 해당 컴포넌트만 숨기고 나머지 사이트는 정상 작동하도록 만드는 것이었습니다.
이제 본격적으로 React 에러를 핸들링하는 방법으로 들어가 보겠습니다.
Level 1: 에러의 범위(Error Boundaries)
표면적으로 보면, React에서 오류 경계하는 것은 바로 이런 경우에 대비해서 존재합니다. 각 사용자 정의 코드 컴포넌트의 오류 범위를 경계로 감싸고 → 오류를 포착하고 → 렌더링하고 null
(빈 값)→ 완료하는데요, 여기서 오류가 난 부분을 경계로 감싸서 숨기는 것을 ****에러 바운더리가 하는 일이지요.
이렇게 각 커스텀 코드 컴포넌트를 감싸면 에러가 나도 null
만을 렌더링 한다면 나머지 코드는 안전할 것 같습니다. 하지만 여기에서도 문제가 있는데요.

React 에러 바운더리는 클라이언트에서만 동작합니다. 서버 사이드 렌더링 중에는 완전히 무시되는 부분이죠. 따라서 서버에서 렌더링이 실패하면 페이지 자체가 최적화되지 못하고 성능도 떨어지게 됩니다.
Level 2: Suspense
서버 렌더링에서 React는 오류가 난 경계를 무시합니다. 대신 서버 측 오류가 발생한다면 React는 가까운 <Suspense>
바운더리를 찾아서 이를 대체할 코드를 렌더링합니다.
따라서 오류가 있는 구성 요소를 숨기려면, 오류 경계가 클라이언트용과 서버용 이렇게 두 개가 필요한 것입니다.
하지만 모든 코드에 <Suspense>
를 감싸는 건 몇 가지 어려움이 따르는데요. 서버에서 오류를 잡아내는 것 외에도 여러 가지 다른 작업을 수행하기 때문입니다.
Level 3: Suspense의 수많은 동작
<Suspense>
는 단순히 에러를 잡는 기능만 있는 게 아니라, 여러 동작을 동시에 수행하는데요. <Suspense>
는 꽤 원초적인 기능이며, 2025년 기준으로 대략적으로 아래과 같은 기능을 제공합니다.
<Suspense>
서버:무언가가 중단되면 대체 코드를 렌더링 (
stream.allReady
로 건너뛰기 가능)에러 발생 시 대체 코드를 렌더링
<Suspense>
클라이언트:무언가가 중단되면 대체 코드를 렌더링 (
startTransition()
으로 건너뛰기 가능)
여기서 렌더링을 할 때 사용자의 코드가 중단되면 어떻게 될까요?
서버에서는 stream.allReady
를 사용해 코드가 멈춰있는(suspend) 상태에서 벗어날 때까지 기다릴 수 있습니다.
하지만 클라이언트에서는 상황이 다릅니다. 컴포넌트가 멈추면 <ServerErrorBoundary>
안의 <Suspense>
가 대체 코드로 설정된 null
을 잠시 렌더링합니다. 즉, 화면에서 해당 컴포넌트가 사라졌다가 다시 나타나는 깜빡임(flash)이 생기는 것이죠.
물론 컴포넌트를 감싼 코드가 startTransition
안에서 실행된다면 이 현상을 피할 수 있지만, 사용자 코드까지 우리가 제어할 수는 없습니다. 그 결과, 사용자는 데이터를 가져오는 동안 컴포넌트가 잠깐 사라졌다 나타나는 UX측면에서 좋지 않은 경험을 하게 됩니다.
이 문제를 어떻게 해결할 수 있을까요?
<ServerErrorBoundary>
를 클라이언트에서 아예 렌더링하지 않는 방법을 사용해볼 수 있습니다. 하지만 이렇게 하면 하이드레이션 불일치(hydration mismatch)가 생깁니다.
그 이유는 <ServerErrorBoundary>
가 내부에서 <Suspense>
를 렌더링하기 때문입니다. <Suspense>
는 실제 DOM 요소를 만들지는 않지만, 대신 특수한 주석(<!--$-->
등)을 출력합니다.
서버에서 만들어 둔 이 주석들을 클라이언트가 하이드레이션 과정에서 정확히 찾아서 ‘같다’라고 확인해야 하는데, 클라이언트에서 <ServerErrorBoundary>
를 빼버리면 그 주석들이 없어집니다. 그러면 React는 “서버와 클라이언트가 다르다”고 판단하고 오류를 발생시키게 되는 거죠.

그렇다면 하이드레이션이 시작되기 전에 <Suspense>
의 특수한 주석들을 그냥 지워버리면 어떨까요? 이것도 안 됩니다. 사용자 코드가 서버에서는 에러로 인해 null
을 렌더링했는데, 클라이언트에서는 정상 동작(올바른 JSX를 렌더링 할 때 등)을 하게 되면 서버와 클라이언트의 결과가 달라지게 되는 것이죠. 이로 인해 하이드레이션 불일치가 발생하고, 결국 루트 전체가 다시 마운트(remount)되는 문제가 생깁니다.
그렇다면 <Suspense>
의 fallback
내부에서 일부러 멈춰버리게(suspend)시키면 어떨까요? 이 방법은 통할 수 있는데요, 규칙은 이렇습니다
→ 어떤 <Suspense>
가 fallback
(대체 요소)를 렌더링하는 경우 / 그 fallback
자체가 다시 멈추게(suspend)된다면, React는 그 <Suspense>
경계를 무시하게 될 것이다.
즉, fallback
이 스스로 멈추면 그 <Suspense>
는 없는 것처럼 취급된다는 것입니다!
문서에는 부모 <Suspense>
의 경계로 활성화 된다고만 되어 있지만, 부모의 경계가 없을 때도 마치 바운더리가 존재하지 않는 것처럼 동작할 수 있습니다. 덕분에 깜빡이는 현상 없이 원하는 동작만을 유지할 수 있게 되는 것입니다.
물론 문서화되지 않은 동작에 의존하는 건 위험하지만, 단기적으로는 꽤 안정적인 해결책이 될 수 있겠죠?
Level 4: 외부 컴포넌트
여기서 한 단계 더 복잡해져볼게요. Framer에서는 코드를 직접 작성할 수 있을 뿐만 아니라, 다른 사람이 작성한 코드를 재사용할 수도 있습니다. 또한 이 코드는 다른 사람이 만든 노코드 UI 안에 중첩될 수도 있죠.

이로 인해서 구현이 한층 더 복잡해질 수 있습니다. 예를 들어, 위와 같이 사용자가 지정한 코드의 Form
컴포넌트 안에 Input
코드가 있고, 이 Input
에 오류가 생긴다면 어떻게 해야 할까요? 단순히 경계가 충돌하는 요소인 Input
만 숨기는 게 맞을까요?

Framer의 철학상, Form
은 사이트 제작자에게 하나의 ‘불투명한 단위’처럼 보입니다. 자신의 컴포넌트가 아니기 때문에 내부를 뜯어볼 수 없기 때문이죠. 따라서 경계가 충돌할 시에도 내부 일부만 깨지는 게 아니라, 모든 Form이 전체적으로 함께 깨져야
합니다. 그래야 제작자가 직관적으로 충돌 상황을 이해할 수 있겠죠.
Level 5…N: 마지막 난제
우리가 해결해야 했던 마지막 난제를 소개드리겠습니다.
framer는 코드의 컴포넌트뿐 아니라 코드 오버라이드도 지원하기 때문에, Code Boundaries도 이 경우까지 모두 고려해야 했습니다.
많은 코드 오류는 기본적으로 난해한데, 특히나 코드가 축소된 경우엔 더더욱 그렇습니다. 이 때문에, 어떤 컴포넌트가 오류가 났는지 쉽게 찾을 수 있는 도구도 새로 만들어야 했습니다.

단순히 React 에러 바운더리를 해결하는 것에서 시작했지만, 서버·클라이언트 환경 차이, Suspense의 복잡한 동작, 외부 코드 재사용 등 수많은 난제를 해결한 후 드디어 Code Boundaries
를 완성시키게 되었습니다.
본 글은 Framer 공식 블로그의 ‘Rabbit hole of React error handling’을 번역·각색한 콘텐츠입니다.