captureOwnerStack
captureOwnerStack๋ ๊ฐ๋ฐ ํ๊ฒฝ์์ ํ์ฌ Owner Stack์ ์ฝ๊ณ , ์ฌ์ฉํ ์ ์๋ค๋ฉด ๋ฌธ์์ด ๋ฐํํฉ๋๋ค.
const stack = captureOwnerStack();๋ ํผ๋ฐ์ค
captureOwnerStack()
captureOwnerStack์ ํธ์ถํ์ฌ ํ์ฌ Owner Stack์ ๊ฐ์ ธ์ต๋๋ค.
import * as React from 'react';
function Component() {
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log(ownerStack);
}
}๋งค๊ฐ๋ณ์
captureOwnerStack๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์ง ์์ต๋๋ค.
๋ฐํ๊ฐ
captureOwnerStack์ string์ด๋ null์ ๋ฐํํฉ๋๋ค.
Owner Stacks์ ๋ค์ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์ปดํฌ๋ํธ ๋ ๋๋ง ์
- Effect (์:
useEffect) - React ์ด๋ฒคํธ ํธ๋ค๋ฌ (์:
<button onClick={...} />) - React ์ค๋ฅ ํธ๋ค๋ฌ (React ๋ฃจํธ ์ต์
onCaughtError,onRecoverableError,onUncaughtError)
Owner Stack์ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ, null์ ๋ฐํํฉ๋๋ค. (๋ฌธ์ ํด๊ฒฐ: Owner Stack์ด null์ธ ๊ฒฝ์ฐ)
์ฃผ์ ์ฌํญ
- Owner Stack์ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ง ์ฌ์ฉํ ์ ์์ต๋๋ค.
captureOwnerStack์ ๊ฐ๋ฐ ํ๊ฒฝ ๋ฐ์์๋ ํญ์null์ ๋ฐํํฉ๋๋ค.
์์ธํ ์ดํด๋ณด๊ธฐ
The Owner Stack is different from the Component Stack available in React error handlers like errorInfo.componentStack in onUncaughtError.
์๋ฅผ ๋ค์ด ๋ค์ ์ฝ๋๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
import {captureOwnerStack} from 'react'; import {createRoot} from 'react-dom/client'; import App, {Component} from './App.js'; import './styles.css'; createRoot(document.createElement('div'), { onUncaughtError: (error, errorInfo) => { // The stacks are logged instead of showing them in the UI directly to // highlight that browsers will apply sourcemaps to the logged stacks. // Note that sourcemapping is only applied in the real browser console not // in the fake one displayed on this page. // Press "fork" to be able to view the sourcemapped stack in a real console. console.log(errorInfo.componentStack); console.log(captureOwnerStack()); }, }).render( <App> <Component label="disabled" /> </App> );
SubComponent์์ ์ค๋ฅ๊ฐ ๋ ์ ์์ต๋๋ค.
ํด๋น ์ค๋ฅ์ ์ปดํฌ๋ํธ Stack์ ๋ค์๊ณผ ๊ฐ์ ๊ฒ์
๋๋ค.
at SubComponent
at fieldset
at Component
at main
at React.Suspense
at App๊ทธ๋ฌ๋, Owner Stack์๋ ๋ค์ ๋ด์ฉ๋ง ๋ํ๋ฉ๋๋ค.
at ComponentApp๊ณผ DOM ์ปดํฌ๋ํธ๋ค(์: fieldset)์ SubComponent๋ฅผ ํฌํจํ๋ ๋
ธ๋๋ฅผ โ์์ฑํ๋โ ๋ฐ์ ๊ธฐ์ฌํ์ง ์๊ธฐ ๋๋ฌธ ์ด ์คํ์ ํฌํจ๋์ง ์์ต๋๋ค. App๊ณผ DOM ์ปดํฌ๋ํธ๋ค์ ๋
ธ๋๋ฅผ ์ ๋ฌํ ๋ฟ์
๋๋ค. App์ <SubComponent />๋ฅผ ํตํด SubComponent๋ฅผ ํฌํจํ ๋
ธ๋๋ฅผ ์์ฑํ๋ Component์ ๋ฌ๋ฆฌ children ๋
ธ๋๋ง ๋ ๋๋งํฉ๋๋ค.
Navigation๊ณผ legend๋ <SubComponent />๋ฅผ ํฌํจํ๋ ๋
ธ๋์ ํ์ ์์์ด๊ธฐ ๋๋ฌธ์ ์คํ์ ์ ํ ํฌํจ๋์ง ์์ต๋๋ค.
SubComponent๋ ์ด๋ฏธ ํธ์ถ ์คํ์ ํฌํจ๋์ด ์๊ธฐ ๋๋ฌธ์ Owner Stack์ ๋ํ๋์ง ์์ต๋๋ค.
์ฌ์ฉ๋ฒ
์ปค์คํ ์ค๋ฅ ์ค๋ฒ๋ ์ด ๊ฐ์ ํ๊ธฐ
import { captureOwnerStack } from "react";
import { instrumentedConsoleError } from "./errorOverlay";
const originalConsoleError = console.error;
console.error = function patchedConsoleError(...args) {
originalConsoleError.apply(console, args);
const ownerStack = captureOwnerStack();
onConsoleError({
// Keep in mind that in a real application, console.error can be
// called with multiple arguments which you should account for.
consoleMessage: args[0],
ownerStack,
});
};console.error ํธ์ถ์ ๊ฐ๋ก์ฑ์ ์ค๋ฅ ์ค๋ฒ๋ ์ด์ ํ์ํ๊ณ ์ถ๋ค๋ฉด, captureOwnerStack์ ํธ์ถํ์ฌ OwnerStack์ ํฌํจํ ์ ์์ต๋๋ค.
import { captureOwnerStack } from "react"; import { createRoot } from "react-dom/client"; import App from './App'; import { onConsoleError } from "./errorOverlay"; import './styles.css'; const originalConsoleError = console.error; console.error = function patchedConsoleError(...args) { originalConsoleError.apply(console, args); const ownerStack = captureOwnerStack(); onConsoleError({ // Keep in mind that in a real application, console.error can be // called with multiple arguments which you should account for. consoleMessage: args[0], ownerStack, }); }; const container = document.getElementById("root"); createRoot(container).render(<App />);
๋ฌธ์ ํด๊ฒฐ
Owner Stack์ด null์ธ ๊ฒฝ์ฐ
captureOwnerStack์ด setTimeout ์ฝ๋ฐฑ๊ณผ ๊ฐ์ด React๊ฐ ์ ์ดํ์ง ์๋ ํจ์ ๋ฐ๊นฅ์์ ํธ์ถ๋์ ๊ฒฝ์ฐ, fetch ํธ์ถ ํ, ์ปค์คํ
DOM ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฑ์์๋ Owner Stack์ด null์ด ๋ฉ๋๋ค. ๋ ๋๋ง ์ค์ด๋ Effect, React ์ด๋ฒคํธ ํธ๋ค๋ฌ, React ์ค๋ฅ ํธ๋ค๋ฌ (์: hydrateRoot#options.onCaughtError) ๋ด์์๋ง ์์ฑ๋ฉ๋๋ค.
์๋ ์์์์, ๋ฒํผ์ ํด๋ฆญํ๋ฉด ๋น Owner Stack์ด ๋ก๊ทธ๋ก ์ถ๋ ฅ๋ฉ๋๋ค. ๊ทธ ์ด์ ๋ captureOwnerStack์ด ์ปค์คํ
์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ด์์ ํธ์ถ๋์๊ธฐ ๋๋ฌธ์
๋๋ค. Owner Stack์ ๋ ์ด๋ฅธ ์์ , ์๋ฅผ ๋ค์ด ์ดํํธ ๋ด๋ถ์์ captureOwnerStack๋ฅผ ํธ์ถํ๋๋ก ์ด๋์์ผ์ผ ์ฌ๋ฐ๋ฅด๊ฒ ์บก์ฒํ ์ ์์ต๋๋ค.
import {captureOwnerStack, useEffect} from 'react'; export default function App() { useEffect(() => { // Should call `captureOwnerStack` here. function handleEvent() { // Calling it in a custom DOM event handler is too late. // The Owner Stack will be `null` at this point. console.log('Owner Stack: ', captureOwnerStack()); } document.addEventListener('click', handleEvent); return () => { document.removeEventListener('click', handleEvent); } }) return <button>Click me to see that Owner Stacks are not available in custom DOM event handlers</button>; }
captureOwnerStack์ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ
captureOwnerStack์ ๊ฐ๋ฐ ํ๊ฒฝ ๋น๋์์๋ง Export๋ฉ๋๋ค. ํ๋ก๋์
ํ๊ฒฝ ๋น๋์์๋ undefined์
๋๋ค. captureOwnerStack์ด ๊ฐ๋ฐ๊ณผ ํ๋ก๋์
์ด ๋ชจ๋ ๋ฒ๋ค๋ง๋๋ ํ์ผ์์ ์ฌ์ฉ๋ ๋๋ ๋ค์์คํ์ด์ค import๋ฅผ ์ฌ์ฉํ๊ณ ์กฐ๊ฑด๋ถ๋ก ์ ๊ทผํด์ผ ํฉ๋๋ค.
// Don't use named imports of `captureOwnerStack` in files that are bundled for development and production.
import {captureOwnerStack} from 'react';
// Use a namespace import instead and access `captureOwnerStack` conditionally.
import * as React from 'react';
if (process.env.NODE_ENV !== 'production') {
const ownerStack = React.captureOwnerStack();
console.log('Owner Stack', ownerStack);
}