๐ React Native์์ OpenTelemetry ์ฌ์ฉํ๊ธฐ, OTEL Web SDK๋ก ๊ตฌํํด๋ณด์
React Native ์ฑ์ ์ต์ ๋น๋ฆฌํฐ(Observability)๋ฅผ ๋์ด๊ณ ์ถ์ด์ OpenTelemetry(OTEL)๋ฅผ ์ฐพ์๋ดค์ต๋๋ค.
๋ฌธ์๋ ์ฝ๊ณ , SDK ๋ชฉ๋ก๋ ์ดํด๋ดค๋๋ฐ, ์์ต๋๋ค. RN ์ ์ฉ SDK๊ฐ.
Web SDK๋ ์๊ณ , Node SDK๋ ์๊ณ , iOS/Android ๋ค์ดํฐ๋ธ SDK๋ ์๋๋ฐ, React Native๋ฅผ ์ํ JavaScript SDK๋ ๊ณต์์ ์ผ๋ก ์กด์ฌํ์ง ์์ต๋๋ค.
๊ทธ๋์ Web SDK๋ฅผ RN ์์์ ๋๋ฆฌ๊ธฐ๋ก ํ์ต๋๋ค.
์๊ฐ๋ณด๋ค ์ ๋ฉ๋๋ค. ๋จ, metro ๋ฒ๋ค๋ฌ๊ฐ ํ์กฐํ๊ธฐ ์ ๊น์ง๋์.
ํ๊ฒฝ ์ ๋ณด
- React Native 0.80.2
- @opentelemetry/sdk-trace-web 2.0.0
- @opentelemetry/instrumentation-fetch 0.200.0
- @opentelemetry/exporter-trace-otlp-http 0.200.0
- @opentelemetry/resources 2.0.1
๐ ์ ์ต์ ๋น๋ฆฌํฐ์ธ๊ฐ, ์ OTEL์ธ๊ฐ
์ฑ์ด ์ฃฝ์ผ๋ฉด ์๋๋ค. Firebase Crashlytics๊ฐ ์๋ ค์ฃผ๋๊น์.
๊ทธ๋ฐ๋ฐ "์ฃฝ๊ธฐ ์ ์ ๋ญ ํ๊ณ ์์๋์ง"๋ ๋ชจ๋ฆ ๋๋ค.
API ์๋ต์ด ๋๋ฆฐ ๊ฑด์ง, ์ด๋ค ํ๋ฉด์์ ์ฌ์ฉ์๊ฐ ์ดํํ๋์ง, ํน์ ๊ธฐ๋ฅ์ด ์ค์ ๋ก ์ผ๋ง๋ ์์ฃผ ํธ์ถ๋๋์ง, ์ด๋ฐ ๊ฒ๋ค์ ํฌ๋์ ๋ก๊ทธ๋ก ์กํ์ง ์์ต๋๋ค.
์ต์ ๋น๋ฆฌํฐ๊ฐ ํ์ํ๋ ์ด์ ์ ๋๋ค.
๋จ์ํ ์๋ฌ ์ถ์ ์ ๋์ด, ์ฑ ๋ด๋ถ์์ ๋ฌด์จ ์ผ์ด ๋ฒ์ด์ง๋์ง "๋ณด๊ณ " ์ถ์์ต๋๋ค.
OpenTelemetry๋ฅผ ์ ํํ ์ด์ ๋ ๊ฐ๋จํฉ๋๋ค.
๋ฒค๋ ์ค๋ฆฝ์ ์ธ ํ์ค์ด๋ผ, ์์ฅ์ ์ฃผ์ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ค์ด OTEL์ ์ง์ํฉ๋๋ค.
๋๋ถ์ ์ด๋ค ์๋น์ค๋ฅผ ์ฐ๋๋ผ๋ SDK ์ฝ๋๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ฉด์ ๋ชจ๋ํฐ๋ง ํด๋ง ์ค์ ์ผ๋ก ๊ต์ฒดํ ์ ์์ต๋๋ค.
๋ฌธ์ ๋ RN ๊ณต์ SDK๊ฐ ์๋ค๋ ์ ์ด์์ต๋๋ค.
CNCF ํ๋ก์ ํธ ํ์ด์ง์ JavaScript SDK๋ ์์ง๋ง, ์ด๊ฑด ๋ธ๋ผ์ฐ์ ์ Node ํ๊ฒฝ์ฉ์ ๋๋ค.
RN์ ๋ ๋ค ์๋๋๋ค. ์ ํํ๋ ๋ ๋ค์ ์ค๊ฐ ์ด๋๊ฐ์ฃ .
๐ OTEL ํต์ฌ ๊ฐ๋
SDK๋ฅผ ์ง์ ๋ง๋ค๊ธฐ ์ ์ ํต์ฌ ๊ฐ๋ ์ธ ๊ฐ์ง๋ฅผ ์ง๊ณ ๋์ด๊ฐ๊ฒ ์ต๋๋ค.
๐ Trace์ Span (์๋ช ์ฃผ๊ธฐ)
Trace๋ ํ๋์ ์์ฒญ์ด ์์คํ ์ ํต๊ณผํ๋ ์ ์ฒด ์ฌ์ ์ ๋๋ค.
Span์ ๊ทธ ์ฌ์ ์ ๊ฐ ๋จ๊ณ์ ๋๋ค.
API ํธ์ถ ํ๋, ํจ์ ์คํ ํ๋๊ฐ ๊ฐ๊ฐ ํ๋์ Span์ ๋๋ค.
Span์ ์์(startSpan)๊ณผ ์ข
๋ฃ(span.end())๊ฐ ๋ช
์์ ์ผ๋ก ์์ต๋๋ค.
๊ทธ ์ฌ์ด์ attributes๋ฅผ ๋ถ์ด๊ฑฐ๋, ์๋ฌ๋ฅผ ๊ธฐ๋กํ๊ฑฐ๋, ์ํ๋ฅผ ์ค์ ํฉ๋๋ค.
์ข
๋ฃ๋์ง ์์ Span์ OTLP ์๋ํฌ์ธํธ๋ก ์ ์ก๋์ง ์์ผ๋, end()๋ฅผ ๋น ๋จ๋ฆฌ์ง ์๋ ๊ฒ ์ค์ํฉ๋๋ค.
๐ Resource: ๋ด ์ฑ์ด ๋๊ตฌ์ธ์ง ์๋ ค์ฃผ๋ ๋ฉํ๋ฐ์ดํฐ
Resource๋ "์ด Trace๋ฅผ ๋ณด๋ธ ๊ฒ ์ด๋ค ์๋น์ค๋"๋ฅผ ๋ํ๋ด๋ ๋ฉํ๋ฐ์ดํฐ์ ๋๋ค.
service.name, service.version, os.name ๊ฐ์ ์์ฑ๋ค๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
๋ชจ๋ํฐ๋ง ํด์์ ํํฐ๋งํ ๋ ์ด Resource ์ ๋ณด๊ฐ ๊ธฐ์ค์ด ๋ฉ๋๋ค.
์ ๋๋ก ์ค์ ํด๋์ง ์์ผ๋ฉด ์ด๋ ์๋น์ค์์ ์จ Trace์ธ์ง ๊ตฌ๋ถ์ด ์ ๋ฉ๋๋ค.
๐ SpanProcessor์ Exporter์ ๊ด๊ณ
Exporter๋ Span ๋ฐ์ดํฐ๋ฅผ ์ด๋๋ก ๋ณด๋ผ์ง๋ฅผ ๋ด๋นํฉ๋๋ค.
์ฝ์์ ์ถ๋ ฅํ๊ฑฐ๋(ConsoleSpanExporter), HTTP๋ก OTLP ์๋ํฌ์ธํธ์ ์ ์กํฉ๋๋ค(OTLPTraceExporter).
SpanProcessor๋ Exporter๋ฅผ ๊ฐ์ธ๋ ๋ํผ์ ๋๋ค.
BatchSpanProcessor๋ Span์ ๋ชจ์์ ์ผ๊ด ์ ์กํ๊ณ , SimpleSpanProcessor๋ ์ฆ์ ์ ์กํฉ๋๋ค.
๐ Web SDK๋ก RN SDK ๋ง๋ค๊ธฐ
๐ ํจํค์ง ๊ตฌ์ฑ
pnpm add @opentelemetry/sdk-trace-web@2.0.0 \
@opentelemetry/resources@2.0.1 \
@opentelemetry/exporter-trace-otlp-http@0.200.0 \
@opentelemetry/instrumentation@0.203.0 \
@opentelemetry/instrumentation-fetch@0.200.0 \
@opentelemetry/otlp-exporter-base@0.200.0 \
@opentelemetry/api
sdk-trace-web์ RN์์ ์ด๋ค๋ ๊ฒ ํต์ฌ์
๋๋ค.
RN์ JS ๋ฐํ์์ Hermes ๋๋ JSC์ธ๋ฐ, ๋ธ๋ผ์ฐ์ ์ฒ๋ผ fetch๋ XMLHttpRequest๊ฐ ๊ธ๋ก๋ฒ๋ก ์์ด์ Web SDK๊ฐ ๋์ํฉ๋๋ค.
๋จ, RN์ fetch๋ ์ง์ง ๋ธ๋ผ์ฐ์ fetch๊ฐ ์๋๋ผ whatwg-fetch polyfill → XHR → Native(NSURLSession/OkHttp) ๋ก ์ด์ด์ง๋ ๊ตฌ์กฐ๋ผ, Web SDK ์ ์ฅ์์๋ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค์ ๋คํธ์ํฌ๋ ๊ฐ OS์ ๋คํธ์ํฌ ์คํ์ด ์ฒ๋ฆฌํฉ๋๋ค.
์น์ด๋ ์์ ํ ๋์ผํ์ง ์์ ์๋ฒฝํ์ง ์์ง๋ง, ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ ๋ณ๋ ์ค์ ์์ด ์๋ํฉ๋๋ค.
๐ OptlConfig ์ธํฐํ์ด์ค
์ค์ ์ธํฐํ์ด์ค๋ถํฐ ์ ์ํฉ๋๋ค.
interface OptlConfig {
url: string; // OTLP ์๋ํฌ์ธํธ URL
accessToken: string; // ์ธ์ฆ ํ ํฐ
serviceName: string; // ๋ชจ๋ํฐ๋ง ํด์์ ์๋ณํ ์๋น์ค๋ช
serviceVersion: string;
}
๐ SpanProcessor ์ด์ค ์ค์ (Console + OTLP)
๊ฐ๋ฐ ์ค์๋ ์ฝ์์์ ๋ฐ๋ก ํ์ธํ๊ณ , ํ๋ก๋์ ์์๋ OTLP ์๋ํฌ์ธํธ๋ก ์ ์กํด์ผ ํฉ๋๋ค.
spanProcessors ๋ฐฐ์ด์ ๋ ๋ค ๋ฑ๋กํ๋ฉด ๋ฉ๋๋ค.
const logSpanProcessor = new BatchSpanProcessor(new ConsoleSpanExporter());
const otlpSpanProcessor = new BatchSpanProcessor(
new OTLPTraceExporter({
url: config.url,
headers: {
"Content-Type": "application/json",
Authorization: config.accessToken,
},
}),
);
tracerProvider = new WebTracerProvider({
resource,
spanProcessors: [logSpanProcessor, otlpSpanProcessor],
});
๋ฐฐ์ด์ ์ฌ๋ฌ SpanProcessor๋ฅผ ๋ฃ์ผ๋ฉด Span์ด ๋๋ ๋ ๋ฑ๋ก๋ ์์๋๋ก ๋ชจ๋ ์คํ๋ฉ๋๋ค.
๐ FetchInstrumentation์ผ๋ก ์๋ ์ถ์
fetch ํธ์ถ์ ์ผ์ผ์ด Span์ผ๋ก ๊ฐ์ธ๋ ๊ฑด ํ์ค์ ์ด์ง ์์ต๋๋ค.
FetchInstrumentation์ ๋ฑ๋กํ๋ฉด ๋ชจ๋ fetch ํธ์ถ์ด ์๋์ผ๋ก Span์ผ๋ก ์ถ์ ๋ฉ๋๋ค.
registerInstrumentations({
instrumentations: [
new FetchInstrumentation({
propagateTraceHeaderCorsUrls: /.*/, // ๋ชจ๋ ๋๋ฉ์ธ์ tracecontext ํค๋ ์ ํ
clearTimingResources: false,
applyCustomAttributesOnSpan: (span, request, result) => {
if (!(result as Response).ok) {
span.setStatus({
code: api.SpanStatusCode.ERROR,
message: `${(result as Response).status} ${(result as Response).url}`,
});
}
},
}),
],
});
applyCustomAttributesOnSpan ์ฝ๋ฐฑ์์ HTTP ์๋ฌ๋ฅผ ์ก์ Span์ ์๋ฌ ์ํ๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด 4xx/5xx ์๋ต๋ Trace์ ๋จ์ต๋๋ค.
๐ Resource์ OS ์ ๋ณด ๋ด๊ธฐ
import { Platform } from "react-native";
import { defaultResource, resourceFromAttributes } from "@opentelemetry/resources";
const resource = defaultResource().merge(
resourceFromAttributes({
"service.name": config.serviceName,
"service.version": config.serviceVersion,
"deployment.environment.name": process.env.NODE_ENV,
"os.name": Platform.OS, // 'ios' | 'android'
"os.version": Platform.Version, // iOS: '17.0', Android: 33
}),
);
Platform.OS์ Platform.Version์ผ๋ก iOS/Android๋ฅผ ๊ตฌ๋ถํฉ๋๋ค.
๋ชจ๋ํฐ๋ง ํด์์ ํ๋ซํผ๋ณ๋ก ํํฐ๋งํ ๋ ์ด ๊ฐ์ด ๊ธฐ์ค์ด ๋ฉ๋๋ค.
defaultResource()๋ SDK๊ฐ ์๋์ผ๋ก ์ฑ์์ฃผ๋ ๊ธฐ๋ณธ ์์ฑ๋ค(SDK ๋ฒ์ ๋ฑ)์ด ๋ด๊ฒจ ์์ต๋๋ค.
.merge()๋ก ์ปค์คํ
์์ฑ์ ๋ง๋ถ์ด๋ ๋ฐฉ์์
๋๋ค.
๐ ๊ฐ์ฅ ๊ณ ํต์ค๋ฌ์ด ๋ถ๋ถ: metro.config.js
SDK ์ฝ๋ ์์ฒด๋ ์ด๋ ต์ง ์์์ต๋๋ค. ์ง์ง ๋ฌธ์ ๋ ๋ฒ๋ค๋ง์ด์์ต๋๋ค.
๐ ์ ๋ฒ๋ค๋ง์ด ๊นจ์ง๋๊ฐ
metro๋ฅผ ๊ธฐ๋ณธ ์ค์ ์ผ๋ก ๋๊ณ ์ฑ์ ์คํํ๋ฉด ๋ ๊ฐ์ง ์๋ฌ๊ฐ ์์๋๋ก ๋ฉ๋๋ค.
์ฒซ ๋ฒ์งธ
Unable to resolve module '@opentelemetry/otlp-exporter-base/browser-http'
None of these files exist:
* node_modules/@opentelemetry/otlp-exporter-base/browser-http(.ios.js|.native.js|.js|...)
@opentelemetry/otlp-exporter-base ํจํค์ง๋ package.json์ exports ํ๋๋ก ์๋ธํจ์ค๋ฅผ ์ ์ํฉ๋๋ค.
Metro๋ ๊ธฐ๋ณธ์ ์ผ๋ก exports ํ๋๋ฅผ ๋ฌด์ํ๊ณ ํ์ผ ์์คํ
์์ ์ง์ ๊ฒฝ๋ก๋ฅผ ์ฐพ์ผ๋ ค ํฉ๋๋ค. ๋น์ฐํ ์คํจํ์ฃ .
๋ ๋ฒ์งธ
Unable to resolve module './semconvStability' from
'node_modules/@opentelemetry/instrumentation/build/src/...'
@opentelemetry/instrumentation ํจํค์ง ๋ด๋ถ์์ ์๋ ๊ฒฝ๋ก๋ก ./semconvStability๋ฅผ importํฉ๋๋ค.
Metro๊ฐ ์ด ์๋ ๊ฒฝ๋ก๋ฅผ ํจํค์ง ๋ด๋ถ ์ปจํ ์คํธ์์ ์ฌ๋ฐ๋ฅด๊ฒ ํด์ํ์ง ๋ชปํ๋ ๋ฒ๊ทธ์ ๋๋ค.
๐ ํด๊ฒฐ์ฑ : unstable_enablePackageExports + alias + resolveRequest
// metro.config.js
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const config = {
resolver: {
// ๋ฌธ์ 2 ํด๊ฒฐ: ์๋ ๊ฒฝ๋ก๋ฅผ ์ ๋ ๊ฒฝ๋ก๋ก ๊ฐ์ ๋งคํ
alias: {
"./semconvStability": require.resolve("@opentelemetry/instrumentation/build/src/semconvStability.js"),
},
// ๋ฌธ์ 1 ํด๊ฒฐ: browser-http ๋ชจ๋๋ง exports ํ๋๋ฅผ ํ์ฑํํด ํด์
resolveRequest: (context, moduleImport, platform) => {
if (moduleImport === "@opentelemetry/otlp-exporter-base/browser-http") {
return context.resolveRequest({ ...context, unstable_enablePackageExports: true }, moduleImport, platform);
}
return context.resolveRequest(context, moduleImport, platform);
},
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
ํต์ฌ ํฌ์ธํธ๋ ๋ ๊ฐ์ง์ ๋๋ค.
์ฒซ์งธ, unstable_enablePackageExports๋ฅผ ์ ์ญ์ผ๋ก ์ผ์ง ์๊ณ ํด๋น ๋ชจ๋์๋ง ์ ์ฉํฉ๋๋ค.
์ ์ญ์ผ๋ก ์ผ๋ฉด ๋ค๋ฅธ RN ๋ด๋ถ ํจํค์ง๋ค์ด ๊นจ์ง ์ ์์ต๋๋ค.
๋์งธ, alias๋ Metro์ resolver ๋ ๋ฒจ์์ ๋์ํ๊ธฐ ๋๋ฌธ์ ํจํค์ง ๋ด๋ถ์ ์๋ ๊ฒฝ๋ก๋ ์ก์๋ผ ์ ์์ต๋๋ค.
๐ ์ค์ ์ฌ์ฉ ์์
๐ initialize() ํธ์ถ ์์
App.tsx ์ต์๋จ์์ ๋ชจ๋ ์ค์ฝํ๋ก ํธ์ถํฉ๋๋ค.
React lifecycle ๋ฐ์์ ์คํ๋์ด์ผ FetchInstrumentation์ด ์ฑ ์์๋ถํฐ ๋์ํฉ๋๋ค.
// App.tsx
import optl from "./optl";
optl.initialize({
url: "YOUR_OTLP_ENDPOINT",
accessToken: "YOUR_ACCESS_TOKEN",
serviceName: "my-rn-app",
serviceVersion: "1.0.0",
});
๐ startSpan / endSpan / recordError ์ฌ์ฉ๋ฒ
// ์๋ Span: ํน์ ๋ก์ง์ ์คํ ์๊ฐ ์ถ์
const handlePayment = async () => {
const span = optl.startSpan("payment.process", {
"payment.method": "card",
"cart.total": 15000,
});
try {
await processPayment();
optl.endSpan(span); // OK ์ํ๋ก ์ข
๋ฃ
} catch (error) {
optl.endSpan(span, error); // ERROR ์ํ๋ก ์ข
๋ฃ + ์์ธ ๊ธฐ๋ก
}
};
// ์๋ฌ ๋จ๋
๊ธฐ๋ก
const handleError = (error: Error) => {
optl.recordError(error);
};
๐ FetchInstrumentation ์๋ ์ถ์
๋ณ๋ ์ฝ๋ ์์ด fetch๋ฅผ ๊ทธ๋ฅ ์ฐ๋ฉด ๋ฉ๋๋ค.
// ์ด ํธ์ถ์ด ์๋์ผ๋ก Span์ผ๋ก ์ถ์ ๋ฉ๋๋ค
const response = await fetch("https://api.example.com/users");
๋ชจ๋ํฐ๋ง ํด์์๋ ์ด๋ ๊ฒ ์ฐํ๋๋ค.
GET https://api.example.com/users
duration: 234ms
http.status_code: 200
http.method: GET
service.name: my-rn-app
os.name: ios
404๋ 500 ์๋ต์ applyCustomAttributesOnSpan ์ฝ๋ฐฑ์์ ERROR ์ํ๋ก ๊ธฐ๋ก๋๋ ์๋ฌ Trace๋ ๋ฐ๋ก ์ถ์ ๋ฉ๋๋ค.
๐ ์ ์ญ JS ์๋ฌ ์๋ ํฌ์ฐฉ
HTTP ์์ฒญ ์ธ์ try/catch๋ก ์กํ์ง ์์ ์ ์ญ JS ์์ธ๋ Trace๋ก ๋จ๊ฒจ์ผ ํฉ๋๋ค.
RN์ ErrorUtils.setGlobalHandler()๋ก ์ด๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
const previousHandler = ErrorUtils.getGlobalHandler();
ErrorUtils.setGlobalHandler((error, isFatal) => {
this.recordError(error, { 'exception.is_fatal': String(isFatal) });
previousHandler?.(error, isFatal);
});
isFatal ํ๋๊ทธ๋ฅผ attribute๋ก ํจ๊ป ๊ธฐ๋กํ๋ฉด "์ฑ์ด ๊ฐ์ ์ข
๋ฃ๋ ์๋ฌ"์ "๊ทธ๋ฅ ๋์ด๊ฐ ์๋ฌ"๋ฅผ ๊ตฌ๋ถํ ์ ์์ต๋๋ค.
๊ธฐ์กด ํธ๋ค๋ฌ(previousHandler)๋ฅผ ์ฒด์ด๋ํ๋ ๊ฒ๋ ์ค์ํ๋ฐ, Sentry ๊ฐ์ ๋ค๋ฅธ ์๋ฌ ๋ฆฌํฌํ
๋๊ตฌ๊ฐ ์ด๋ฏธ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํด๋์ ์ ์๊ฑฐ๋ ์.
๋ฎ์ด์ฐ๋ฉด ๋ ์ค ํ๋๊ฐ ์๋ฌ๋ฅผ ๋ชป ๋ฐ์ต๋๋ค.
๐ ErrorBoundary๋ก React ๋ ๋ ์๋ฌ ํฌ์ฐฉ
๋ฌธ์ ๋ ErrorUtils๊ฐ ์ ๋ถ๊ฐ ์๋๋ผ๋ ์ ์
๋๋ค.
React ์ปดํฌ๋ํธ ๋ ๋ ์ค ๋ฐ์ํ ์๋ฌ๋ React๊ฐ ๋ด๋ถ์ ์ผ๋ก ๋จผ์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์ ErrorUtils ํธ๋ค๋ฌ๊น์ง ๋๋ฌํ์ง ์์ต๋๋ค.
๋ ๋์ ๊ฑด, ErrorBoundary ์์ด ๋ ๋ ์๋ฌ๊ฐ ๋๋ฉด ์ปดํฌ๋ํธ ํธ๋ฆฌ ์ ์ฒด๊ฐ ์ธ๋ง์ดํธ๋๊ณ JS ์ค๋ ๋๊ฐ ์ค๋จ๋ฉ๋๋ค.
์ดํ ErrorUtils๊ฐ ํธ์ถ๋๋๋ผ๋ ์ด๋ฏธ ๋ณต๊ตฌ ๋ถ๊ฐ ์ํ์ธ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ์ฃ .
componentDidCatch()์์ info.componentStack์ attribute๋ก ๊ธฐ๋กํ๋ฉด ์ด๋ ์ปดํฌ๋ํธ์์ ์๋ฌ๊ฐ ํฐ์ก๋์ง Trace๋ก ์ถ์ ํ ์ ์์ต๋๋ค.
import React from 'react';
import {Text, View} from 'react-native';
import optl from './optl';
interface Props {
children: React.ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends React.Component<Props, State> {
state: State = {hasError: false};
componentDidCatch(error: Error, info: React.ErrorInfo) {
optl.recordError(error, {
'exception.type': 'ReactRenderError',
'react.component_stack': info.componentStack ?? '',
});
}
static getDerivedStateFromError(): State {
return {hasError: true};
}
render() {
if (this.state.hasError) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Something went wrong.</Text>
</View>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
JS ๋ฐํ์ ์๋ฌ๋ ErrorUtils๊ฐ, React ๋ ๋ ์๋ฌ๋ ErrorBoundary๊ฐ ๊ฐ๊ฐ ๋ด๋นํฉ๋๋ค.
๋ ๋ ์ด์ด๋ฅผ ๋ชจ๋ ์ปค๋ฒํด์ผ ์ฑ ์๋ฌ๋ฅผ ๋น ์ง์์ด OTEL๋ก ๋ณด๋ผ ์ ์์ต๋๋ค.
์ฒ์์ "Web SDK๋ฅผ RN์์ ๊ทธ๋ฅ ๋๋ ค๋ ๋๋?" ๋ฐ์ ๋ฐ์ํ๋ฉฐ ์์ํ์ต๋๋ค.
๊ทธ๋ฐ๋ฐ ๋ชจ๋ํฐ๋ง ํด ํ๋ฉด์ Trace๊ฐ ์ฐํ๋ ์๊ฐ, ์ฒ์์ผ๋ก ์ฑ์ด "๋ณด์ด๋" ๋๋์ ๋ฐ์์ต๋๋ค.
API ์๋ต ์๊ฐ์ด ์ซ์๋ก ๋์ค๊ณ , ์ด๋ค ํ๋ฉด์์ ์ด๋ค ์์ฒญ์ด ์ผ๋ง๋ ๊ฑธ๋ ธ๋์ง๊ฐ ํ๋์ ๋ค์ด์ค๋ ๊ฒฝํ์ด์์ต๋๋ค.
๋์ ํ์ง ๋ช ๊ฐ์์ด ์ง๋ ์ง๊ธ, ์ด์๊ฐ ๋ฐ์ํ์ ๋ ์ด๋ ๊ตฌ๊ฐ์์ ๋ฌธ์ ๊ฐ ์๊ฒผ๋์ง ๋น ๋ฅด๊ฒ ํ์ ํ ์ ์๊ฒ ๋์๊ณ , ์๋ฌ ํจํด์ ๋ฏธ๋ฆฌ ๊ฐ์งํด ์ ์ ์ ์ผ๋ก ๋์ํ ์ ์๋ ํ๊ฒฝ์ด ๊ฐ์ถฐ์ก์ต๋๋ค.
ํด์์ ์ง์ ์ ๊ณตํ๋ SDK๋ฅผ ์ฌ์ฉํด๋ ์ข์ง๋ง ์ง์ ๊ตฌํํ๋ค๋ฉด ๋น์ฉ์ ์ธ ๋ฉด์์ ๋ฉ๋ฆฌํธ๊ฐ ์์ผ๋ ์กฐ์ฌํ ์ถ์ฒ ๋๋ ค๋ด ๋๋ค.
'React-Native' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| React Native Lazy Loading์ผ๋ก ์ฑ ์์ ์๋ ๊ฐ์ ํ๊ธฐ (0) | 2025.05.19 |
|---|---|
| react-native-performance๋ก ์ฑ ์์ ์๊ฐ ์ธก์ ํ๊ธฐ (0) | 2025.05.17 |
| react-native ๋ฒ์ ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋, v0.69 to v0.78 (2) | 2025.04.19 |
| 2025๋ React Native ํํฉ๊ณผ CLI vs Expo ๋น๊ต๋ถ์ (2) | 2025.01.15 |
| [React Native] Android Native-Module Event Listener ๋ง๋ค๊ธฐ (0) | 2025.01.10 |
์คํ ์ฑํ
๋๊ธ