Fast Refresh
생성일: 2024-06-07
수정일: 2024-06-07
Fast Refresh는 React 컴포넌트의 변경 사항에 대해 즉각적인 피드백을 받을 수 있게 해주는 기능이다.
Fast Refresh는 기본적으로 활성화되어 있으며, 개발자 메뉴에서 Fast Refresh 버튼으로 토글할 수 있다.
동작 원리
Fast Refresh는 편집하는 파일의 종류에 따라 다르게 동작한다.
- React 컴포넌트만 내보내는 파일을 편집하는 경우:
- Fast Refresh는 해당 파일의 코드만 업데이트한다.
- 그리고 해당 React 컴포넌트를 다시 렌더링한다.
- 이 경우 파일 내의 스타일, 렌더링 로직, 이벤트 핸들러, effects 등 모든 것을 편집할 수 있다.
- React 컴포넌트가 아닌 다른 것(예: 함수, 상수 등)을 내보내는 파일을 편집하는 경우:
- Fast Refresh는 편집한 파일뿐만 아니라, 그 파일을 가져오는 다른 모든 파일도 다시 실행한다.
- 예를 들어, Button.js와 Modal.js 모두 Theme.js를 가져온다면, Theme.js를 편집하면 Button.js와 Modal.js 모두 업데이트된다.
- React 컴포넌트 외부(예: 일반 자바스크립트 파일)에서 가져온 파일을 편집하는 경우:
- Fast Refresh는 전체 앱을 리로드한다.
- 예를 들어, 파일이 React 컴포넌트를 렌더링하면서 동시에 React 컴포넌트가 아닌 곳에서 사용하는 상수를 내보낼 때 발생한다.
- 이 문제를 해결하려면 상수를 별도의 파일로 옮기고, 두 파일 모두에서 그 파일을 가져오는 것이 좋다.
즉, 대부분의 경우 Fast Refresh는 편집한 컴포넌트만 업데이트하지만, 때로는 관련된 다른 파일들까지 업데이트하거나 앱 전체를 리로드해야 할 수도 있다.
오류 회복력
오류 발생 시 Fast Refresh의 동작은 다음과 같다.
- 구문 오류 (Syntax Error):
- 구문 오류의 예로는 괄호 닫기 누락, 철자 오타, 세미콜론 누락 등이 있다.
- Fast Refresh는 구문 오류가 있는 모듈의 실행을 막는다. 이는 구문 오류가 있는 코드가 앱을 중단시키지 않도록 방지한다.
- 구문 오류를 수정하고 파일을 저장하면, Fast Refresh는 수정된 코드를 즉시 적용하고 앱을 계속 실행한다.
- 이 과정에서 앱 전체를 다시 로드할 필요가 없으므로, 개발 속도가 빨라진다.
- 런타임 오류 (Runtime Error) - 모듈 초기화 중:
- 런타임 오류는 코드가 실행되는 동안 발생하는 오류다.
- 모듈 초기화 중 발생하는 런타임 오류의 예로는 잘못된 함수 호출, 정의되지 않은 변수 사용 등이 있다.
- 이런 오류가 발생하면, Fast Refresh는 오류를 표시하는 빨간 상자를 보여준다.
- 오류를 수정하고 파일을 저장하면, Fast Refresh는 수정된 코드를 적용하고 빨간 상자를 제거하며, 앱을 계속 실행한다.
- 이 과정에서도 앱 전체를 다시 로드할 필요가 없다.
- 런타임 오류 (Runtime Error) - 컴포넌트 내부:
- 컴포넌트 내부에서 발생하는 런타임 오류의 예로는 undefined 속성 접근, 잘못된 타입의 props 전달 등이 있다.
- 이런 오류가 발생하면, 역시 빨간 상자가 표시된다.
- 오류를 수정하고 파일을 저장하면, Fast Refresh는 수정된 코드를 적용한다.
- 이 경우, React는 수정된 코드로 해당 컴포넌트를 다시 마운트한다. 이는 컴포넌트의 상태가 리셋되는 것을 의미한다.
- 이는 컴포넌트의 생명주기를 다시 시작하여, 수정된 코드의 Effects를 완전히 반영하기 위함이다.
- 오류 경계 (Error Boundary):
- 오류 경계는 하위 컴포넌트 트리에서 발생하는 자바스크립트 오류를 catch하고 대체 UI를 표시한다.
- 오류 경계가 없다면, 컴포넌트에서 오류가 발생했을 때 해당 오류가 전체 앱을 중단시킬 수 있다.
- 하지만 오류 경계를 사용하면, 오류가 발생한 컴포넌트 대신 오류 경계에서 제공하는 대체 UI(일반적으로 오류 메시지)를 표시할 수 있다.
- Fast Refresh와 오류 경계를 함께 사용하면, 오류를 수정한 후 파일을 저장했을 때, 오류 경계가 다시 원래의 컴포넌트 렌더링을 시도한다.
- 이를 통해 매번 루트 컴포넌트로 돌아가지 않고도 오류에서 빠르게 복구할 수 있다.
- 하지만 오류 경계를 너무 많이 사용하면 코드의 복잡성이 증가할 수 있으므로, 꼭 필요한 곳에만 의도적으로 사용해야 한다.
한계
Fast Refresh는 컴포넌트를 편집할 때 가능한 한 그 컴포넌트의 상태를 유지하려고 한다. 하지만 항상 그렇게 할 수 있는 것은 아니다.
파일을 편집할 때마다 로컬 상태가 초기화되는 이유를 하나씩 살펴보자.
- 클래스 컴포넌트의 상태 보존 불가:
- Fast Refresh는 함수 컴포넌트와 React Hook의 상태만 유지할 수 있다.
- 클래스 컴포넌트의 상태는 컴포넌트 인스턴스에 저장되는데, Fast Refresh 중에는 이 인스턴스가 새로 생성되므로 상태가 초기화된다.
- 따라서 클래스 컴포넌트를 편집하면 항상 상태가 초기화된다.
- React 컴포넌트 외의 내용을 내보내는 모듈 편집:
- 한 파일 내에서 React 컴포넌트와 함께 다른 내용(함수, 상수 등)을 내보내는 경우가 있다.
- 이런 파일을 편집하면, Fast Refresh는 React 컴포넌트의 상태를 유지할 수 없다.
- 이는 Fast Refresh가 React 컴포넌트만을 추적하도록 설계되었기 때문이다. 다른 내용이 섞여 있으면 상태 유지가 불가능하다.
- 고차 컴포넌트의 사용:
- 고차 컴포넌트(HOC)는 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수다.
- 예를 들어,
createNavigationContainer(MyScreen)
은MyScreen
컴포넌트를 감싸는 새로운 컴포넌트를 반환한다. - 만약 반환된 컴포넌트가 클래스 컴포넌트라면, 그 컴포넌트의 상태는 Fast Refresh 중에 초기화된다.
- 이는 위에서 언급한 클래스 컴포넌트의 상태 보존 불가능성 때문이다.
- 향후 전망:
- React에서는 함수 컴포넌트와 Hook의 사용을 권장하고 있다.
- 클래스 컴포넌트보다 함수 컴포넌트와 Hook을 사용하면 코드가 더 간결해지고 재사용성이 높아진다.
팁
- Fast Refresh는 기본적으로 함수 컴포넌트(및 Hook)의 React 로컬 상태를 보존한다.
- 때로는 상태를 강제로 재설정하고 컴포넌트를 다시 마운트하고 싶을 수 있다.
- 예를 들어 마운트 시에만 발생하는 애니메이션을 조정할 때 유용할 수 있다.
- 이렇게 하려면 편집 중인 파일 어디에나
// @refresh reset
지시문을 추가하면 된다. - 이 지시문은 파일에 국한되며 Fast Refresh에 모든 편집 시 해당 파일에 정의된 컴포넌트를 다시 마운트하도록 지시한다.
Fast Refresh와 Hook
Fast Refresh는 컴포넌트를 편집할 때 가능한 한 컴포넌트의 상태를 유지하려고 노력한다.
하지만 모든 상황에서 상태를 완벽하게 유지할 수 있는 것은 아니다.
특히 React Hook을 사용할 때는 Hook의 종류에 따라 다른 동작을 보인다.
useState
와useRef
Hook의 경우:- Fast Refresh 중에 이전 상태 값을 유지한다.
- 다만 Hook 호출의 순서나 인수가 변경되면 상태가 초기화된다.
useEffect
,useMemo
,useCallback
과 같이 의존성 배열을 가지는 Hook의 경우:- Fast Refresh 중에는 의존성 배열의 내용과 상관없이 항상 해당 Hook이 다시 실행된다.
- 예를 들어,
useMemo(() => x * 2, [x])
를useMemo(() => x * 10, [x])
로 변경하면,x
의 값이 변하지 않았더라도useMemo
는 다시 실행된다. - 이는 Fast Refresh가 코드 변경을 정확히 반영하기 위해 필요한 동작이다.
- 이로 인해 가끔 예상치 못한 동작이 발생할 수 있다.
- 예를 들어, 빈 의존성 배열(
[]
)을 가진useEffect
도 Fast Refresh 중에는 한 번 더 실행된다. - 하지만 Fast Refresh와 상관없이,
useEffect
가 가끔 불필요하게 실행되더라도 문제가 없도록 코드를 작성하는 것이 좋은 습관이다. - 이렇게 하면 나중에
useEffect
에 새로운 의존성을 추가하더라도 쉽게 적응할 수 있다.
- 예를 들어, 빈 의존성 배열(