# reopt data — full documentation > Every page of the public documentation, concatenated. Generated from the same source as https://data.reopt.ai/docs. # 시작하기 Source: https://data.reopt.ai/docs 리옵트 데이터에서 조직을 만들고, 이벤트를 수집하고, 분석 루프를 운영하기까지의 전체 흐름을 안내합니다. 리옵트 데이터는 제품 분석 워크스페이스입니다. 이벤트 수집부터 분석, 에러와 세션 리플레이, 실시간 인사이트, 운영 알림까지 한 프로젝트 안에서 이어서 씁니다. 처음 시작한다면 이 문서에서 전체 구조를 먼저 보고, 필요한 세부 문서로 이동하세요. ## 먼저 이해할 구조 리옵트 데이터는 다음 계층으로 구성됩니다. | 계층 | 하는 일 | | --------- | ------------------------------------------ | | 조직 | 멤버, 플랜, 데이터 포인트 사용량을 관리합니다. | | 프로젝트 | 이벤트 데이터와 분석 화면이 속하는 작업 공간입니다. | | 추적 설정 | SDK 키를 발급하고 `POST /api/track`로 이벤트를 수집합니다. | | 분석 워크스페이스 | 카탈로그, 퍼널, 리텐션, 사용자, 쿼리로 행동을 해석합니다. | | 관측 | 에러, 세션 리플레이, AI 관측으로 문제가 생긴 순간을 확인합니다. | | 모니터링과 알림 | 실시간, 인사이트, 알림으로 이상 징후를 빠르게 확인합니다. | ## 문서 읽는 순서 1. [서비스 이해](/docs/overview)에서 제품 구조와 화면 지도를 확인하세요. 2. [워크스페이스](/docs/workspace)에서 조직, 프로젝트, 추적 설정을 준비하세요. 3. [CLI 사용하기](/docs/workspace/cli)에서 계정 확인과 CI·에이전트 자동화를 준비하세요. 4. [프로젝트 워크스페이스](/docs/project-workspace)에서 질문에 맞는 분석 화면을 고르세요. 5. [수집과 API](/docs/platform)에서 수집 요청의 형식과 공개 HTTP API를 확인하세요. ## 일반적인 시작 순서 1. `/login`에서 Reopt 계정으로 로그인합니다. 2. `/dashboard`에서 조직을 만들거나 기존 조직을 선택합니다. 3. 프로젝트 화면에서 조직을 선택해 프로젝트를 생성합니다. 4. [빠른 연동](/docs/workspace/quickstart)을 따라 브라우저 이벤트와 서버 이벤트를 하나씩 보냅니다. 5. 이벤트 스트림과 프로젝트 홈에서 수집 결과를 확인합니다. 6. 데이터 카탈로그에서 전환, 태그, 상태, 카테고리를 정리합니다. 7. 퍼널, 리텐션, 사용자, 세그먼트, 경로 분석, 쿼리로 행동을 분석합니다. 8. 에러, 세션 리플레이, AI 관측으로 문제가 생긴 지점을 확인합니다. 9. 실시간, 인사이트, 알림으로 운영 대응을 자동화합니다. ## 화면 지도 | 화면 | 라우트 | 하는 일 | | ------ | ---------------------------------- | ---------------------------------- | | 랜딩 | `/` | 제품 소개와 문서, 로그인 진입점을 확인합니다. | | 로그인 | `/login` | Reopt 계정으로 워크스페이스 세션을 시작합니다. | | 프로젝트 | `/dashboard` | 모든 조직의 프로젝트를 찾고 바로 이동합니다. | | 프로젝트 홈 | `/dashboard/[orgId]/[projectId]` | 대시보드, SDK snippet, 핵심 지표를 확인합니다. | | 분석 화면 | `/dashboard/[orgId]/[projectId]/*` | 카탈로그, 퍼널, 리텐션, 사용자, 쿼리, 알림을 사용합니다. | 화면 전체 목록과 단축키는 [서비스 맵](/docs/overview/service-map)에 있습니다. ## 다음 단계 * 제품 구조를 먼저 보고 싶다면 [서비스 이해](/docs/overview) * 프로젝트를 만들고 첫 이벤트를 보내려면 [빠른 연동](/docs/workspace/quickstart) * 터미널과 CI에서 운영하려면 [CLI 사용하기](/docs/workspace/cli) * 어떤 분석 화면을 써야 할지 고르려면 [프로젝트 워크스페이스](/docs/project-workspace) --- # 서비스 개요 Source: https://data.reopt.ai/docs/overview/service-overview 조직, 프로젝트, 분석 화면, 모니터링과 알림이 리옵트 데이터 안에서 어떻게 이어지는지 설명합니다. 리옵트 데이터는 팀이 추적 정의를 정리하고, 분석을 반복하고, 이상 징후에 바로 반응하도록 만든 제품 분석 워크스페이스입니다. ## 해결하는 문제 이벤트와 속성에 이름, 의미, 상태를 붙여 팀이 같은 정의로 분석합니다. 전환은 퍼널, 재방문은 리텐션, 유입은 획득 분석처럼 질문마다 맞는 화면이 따로 있습니다. 같은 프로젝트 안에서 [오류 이슈](/docs/project-workspace/error-tracking), [방문 화면](/docs/project-workspace/session-replay), [LLM 호출](/docs/project-workspace/ai-observability)을 다시 봅니다. 인사이트에서 이상 징후를 확인하고, 조건을 걸어 둔 알림으로 팀에 알립니다. SDK 키와 보존 정책은 [프로젝트마다](/docs/project-workspace/settings) 따로 두고, 사용량과 플랜은 [조직 단위](/docs/workspace/cost-and-retention)로 봅니다. ## 서비스 계층 조직이 프로젝트를 담고, 프로젝트가 이벤트 데이터와 네 종류의 자산을 담습니다. 이름, 전환, 태그, 상태, 카테고리로 공통 분석 언어를 만듭니다. 분석 결과를 저장하고 다시 씁니다. 문제가 생긴 순간을 다시 보고 원인을 좁힙니다. 이상 징후를 찾고 조건에 맞춰 반응합니다. ## 실제 사용 흐름 사용자는 보통 다음 순서로 제품을 사용합니다. 이름 옆 표시는 그 단계에서 주로 쓰는 사이드바 묶음입니다. 조직과 프로젝트를 만듭니다. SDK 키를 발급하고 이벤트 수집을 붙입니다. 데이터 카탈로그에서 추적 정의를 정리합니다. 프로젝트 워크스페이스에서 대시보드와 분석 화면을 사용합니다. 에러, 세션 리플레이, AI 관측으로 문제가 생긴 지점을 확인합니다. 실시간, 인사이트, 알림으로 이상 징후를 확인하고 팀에 알립니다. 사용량, 데이터 포인트, 보존 정책을 관리합니다. ## 사이드바의 네 묶음 프로젝트에 들어가면 사이드바가 화면을 네 묶음으로 나눠 보여줍니다. 데이터를 정의하고 수집 상태를 확인합니다. 수집한 데이터를 탐색하고 해석합니다. 조건 기반 알림과 웹훅을 설정합니다. 프로젝트 설정과 조직의 사용량을 관리합니다. 화면마다의 경로와 단축키는 [서비스 맵](/docs/overview/service-map)에서 확인하세요. ## 다음 단계 * 화면 기준 구조를 보려면 [서비스 맵](/docs/overview/service-map) * 조직과 프로젝트를 만들려면 [워크스페이스](/docs/workspace) * 분석 화면을 고르려면 [프로젝트 워크스페이스](/docs/project-workspace) --- # 빠른 연동 Source: https://data.reopt.ai/docs/workspace/quickstart 프로젝트 생성 후 브라우저와 서버리스 route handler에 첫 이벤트를 붙이고, 수집 성공을 확인합니다. 리옵트 데이터를 처음 붙이는 개발자를 위한 가이드입니다. 브라우저 이벤트 하나와 서버리스 이벤트 하나를 보내고, 프로젝트 화면에서 수집 결과를 확인합니다. ## 시작하기 전에 다음 네 가지를 준비하세요. | 준비물 | 확인하는 곳 | | ------------------------------ | ---------------------------------------------------------------------------------------------------------------- | | 조직 | `/dashboard`에서 만들거나 선택합니다. | | 프로젝트 | `/dashboard`의 **새 프로젝트**에서 만듭니다. 조직의 `owner` 또는 `admin`이어야 합니다. | | 브라우저용 `writeKey` | 프로젝트 홈의 **SDK Integration** snippet에서 확인합니다. | | 서버용 `clientId`, `serverSecret` | 콘솔에서 프로젝트를 만든 직후의 창에 한 번 표시됩니다. CLI로 만들면 `reopt-data project create` 응답의 `clients.server.id`와 `clientSecret`입니다. | 콘솔에서 프로젝트를 만들면 생성 직후 창에 Write Key, 서버 Client ID, 서버 Client Secret이 표시됩니다. 서버 Client Secret은 창을 닫으면 다시 볼 수 없으니 먼저 복사해 두세요. 키 발급의 자세한 내용은 [조직과 프로젝트](/docs/workspace/organizations-and-projects)에 있습니다. `serverSecret`은 서버 전용 비밀값입니다. 브라우저 번들, HTML, client component, 공개 저장소에는 넣지 마세요. SDK 옵션과 환경변수 예시에서는 같은 값을 `clientSecret`이라는 이름으로 넣습니다. 조직에 수집 가능한 데이터 포인트도 있어야 합니다. 콘솔에서 만든 조직은 Free로 시작하며, Starter나 Pro를 구독한 뒤부터 이벤트를 수집합니다. 구독하지 않은 조직, 한도를 다 쓴 조직, 할당량이 0인 조직(데모 워크스페이스 포함)은 이벤트를 보내도 429 `quota_exceeded`로 거절됩니다. [비용과 보존 정책](/docs/workspace/cost-and-retention)에서 플랜을 확인하세요. ## 1. 패키지 설치하기 ```bash pnpm add @reopt-ai/data-sdk-client @reopt-ai/data-sdk-server ``` 브라우저 코드와 서버 코드를 섞지 않도록 패키지를 둘로 나눴습니다. 브라우저에서 도는 코드는 `-client`를, 서버에서 도는 코드는 `-server`를 import합니다. `-server`는 `server-only`를 import하므로, 실수로 client component에서 가져다 쓰면 런타임이 아니라 빌드 단계에서 실패합니다. 그래서 `clientSecret`이 브라우저 번들에 실려 나가지 않습니다. 브라우저만 붙일 계획이라면 `-client` 하나로 충분합니다. 모노레포 안에서 특정 앱에만 설치한다면 해당 앱 디렉터리에서 실행하세요. | 패키지 | 진입점 | 함께 필요한 것 | | --------------------------- | ----------------------------------------------------------------- | ----------------------------------------------- | | `@reopt-ai/data-sdk-client` | `.`(프레임워크 없음), `./react`, `./next` | `./react`는 React 18 이상, `./next`는 Next.js 14 이상 | | `@reopt-ai/data-sdk-server` | `.`(Next.js 요청 스코프), `./proxy`, `./node`(워커·크론), `./ai`(AI 텔레메트리) | 요청 스코프와 proxy는 Next.js 15 이상 | ## 2. 환경변수 추가하기 브라우저에서 쓰는 값은 공개 환경변수로 둡니다. 서버에서 쓰는 값은 공개 prefix 없이 둡니다. ```bash # Browser NEXT_PUBLIC_REOPT_WRITE_KEY="wpk_..." NEXT_PUBLIC_REOPT_API_URL="https://data.reopt.ai" # Server REOPT_CLIENT_ID="<서버용 client ID>" REOPT_CLIENT_SECRET="sec_..." REOPT_BASE_URL="https://data.reopt.ai" ``` `writeKey`는 서버 쪽에도 필요합니다. 인증에는 쓰지 않습니다. 인증은 `clientId`/`clientSecret`이 맡고, 이 값은 어느 쿠키를 읽을지를 정합니다. 그래서 한 도메인에 두 프로젝트를 붙여도 서로의 방문자를 덮어쓰지 않습니다. `baseUrl`에는 기본값이 없습니다. 브라우저 SDK는 `writeKey`나 `baseUrl`이 비어 있으면 경고를 남기고 아무것도 보내지 않습니다. ## 3. 브라우저 이벤트 보내기 앱의 클라이언트 진입점에서 SDK를 초기화합니다. 초기화 뒤에는 `track`, `identify`, `pageView`를 쓸 수 있습니다. ```ts import { init, identify, track } from "@reopt-ai/data-sdk-client"; init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.NEXT_PUBLIC_REOPT_API_URL!, debug: true, }); track("signup_clicked", { location: "header", plan: "starter", }); identify("user_123", { plan: "starter", }); ``` `init()`은 첫 `$pageview`를 직접 보냅니다. 여기서 `pageView()`를 한 번 더 부르면 같은 페이지가 두 번 세어집니다. `pageView()`는 SPA에서 화면이 바뀔 때 호출하세요. Next.js App Router라면 [추적 설정](/docs/workspace/tracking-setup#nextjs-app-router)의 `ReoptPageView`가 이동마다 대신 보냅니다. `debug: true`는 로컬과 미리보기 환경에서 큐와 전송 상태를 확인할 때만 사용하세요. 운영 환경에서는 불필요한 로그가 남지 않도록 끄는 것이 좋습니다. ## 4. Next.js route handler에서 서버 이벤트 보내기 Next.js 안에서는 요청 스코프 클라이언트를 씁니다. `track()`만 부르면 응답 뒤의 전송은 SDK가 처리합니다. 먼저 요청 스코프 클라이언트를 모듈 스코프에서 한 번만 만들어 둡니다. 요청마다 다시 만들면 배칭과 세션 조회 캐시를 모두 잃습니다. ```ts // lib/reopt.ts import "server-only"; import { createReopt } from "@reopt-ai/data-sdk-server"; export const { getReopt, getBootstrap } = createReopt({ clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, // 쿠키 이름을 유도하는 값. 브라우저에서 쓰는 것과 반드시 같아야 합니다 writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.REOPT_BASE_URL!, }); ``` ```ts // app/api/checkout/route.ts import { getReopt } from "@/lib/reopt"; export async function POST(request: Request) { const body = await request.json(); const reopt = await getReopt(); const queued = reopt.track("checkout_completed", { orderId: body.orderId, amount: body.amount, currency: "KRW", }); return Response.json({ ok: queued.queued, eventId: queued.eventId }); } ``` `getReopt()`가 반환하는 클라이언트는 이 요청을 보낸 방문자에게 이미 묶여 있습니다. 브라우저가 심어 둔 device 쿠키를 읽어 이벤트에 실어 보냅니다. 그래서 결제·주문 완료처럼 서버에서 확정되는 전환도 그 방문자의 현재 세션과 퍼널에 이어집니다. `flush()`를 직접 부르지 않아도 됩니다. Vercel에서는 SDK가 요청의 `waitUntil`을 자동으로 찾아 전송을 넘깁니다. 따로 설치할 패키지는 없습니다. 그런 수단이 없는 상시 실행 서버에서는 SDK 자체 배칭 타이머가 처리합니다. 응답 직후 프로세스가 멈추는 다른 서버리스 호스트라면 `createReopt({ waitUntil })`로 호스트의 `waitUntil`을 넘기거나, 응답 전에 `await reopt.flush()`를 호출하세요. 방문자가 수집을 거부(opt-out)했다면 `getReopt()`는 아무것도 보내지 않는 클라이언트를 돌려줍니다. 호출부에 동의 분기를 넣을 필요가 없습니다. Next.js가 아닌 워커·크론·스크립트에서는 요청이 없으므로 다른 진입점을 씁니다. ```ts import { createReoptNode } from "@reopt-ai/data-sdk-server/node"; const reopt = createReoptNode({ clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, baseUrl: process.env.REOPT_BASE_URL!, }); await reopt.trackAndFlush("nightly_report_generated", { rows: 12_400 }); await reopt.close(); // 프로세스 종료 전 필수. 타이머를 멈추고 큐를 비웁니다 ``` ## 5. 수집 결과 확인하기 첫 이벤트를 보낸 뒤 아래 순서로 확인하세요. 1. 브라우저 Network 탭에서 `POST /api/track` 응답이 200인지 봅니다. 2. 응답의 `accepted`가 1 이상인지 봅니다. 3. 프로젝트의 [이벤트 스트림](/docs/project-workspace/event-stream)에서 방금 보낸 이벤트가 시간 역순 목록에 나타나는지 확인합니다. 4. 데이터 카탈로그에서 이벤트 이름이 보이는지 확인합니다. 5. 로그인 직후 `identify`를 보냈다면 사용자 프로필에 `profileId`가 연결되는지 확인합니다. 분석 화면 반영은 수집 뒤에 이어지므로, 응답이 200이어도 화면에는 잠시 뒤에 나타날 수 있습니다. 브라우저 SDK가 무엇을 보냈는지 페이지 안에서 보려면 `@reopt-ai/data-sdk-devtool` 패널을 붙이세요. 큐에 들어간 이벤트와 전송된 배치, 서버 응답을 민감 값이 가려진 상태로 보여줍니다. 붙이는 방법은 [추적 설정](/docs/workspace/tracking-setup#sdk-devtool로-전송-내용-확인하기)에 있습니다. 성공 응답은 보통 다음 형태입니다. ```json { "status": "ok", "requestId": "...", "mode": "browser", "accepted": 1, "duplicates": 0, "rejected": [], "queued": true } ``` `accepted`는 이번 요청에서 새로 저장된 이벤트 수입니다. `mode`는 어떤 자격증명으로 보냈는지에 따라 `browser`(write key) 또는 `server`(client ID + secret)입니다. `queued`가 `true`이면 분석 화면에 반영할 작업이 예약된 것입니다. 브라우저 요청에는 서버가 세션을 이어 붙일 때 쓰는 `session` 값이 함께 내려올 수 있습니다. SDK가 알아서 보관하므로 직접 다룰 필요는 없습니다. ## 자주 막히는 지점 | 증상 | 확인할 것 | | ------------------------- | ------------------------------------------------------------------------------------------------------------ | | 401 응답 | 브라우저에는 `writeKey`, 서버에는 `clientId + serverSecret`을 썼는지 확인하세요. `clientId`는 `writeKey`가 아니라 서버용 client의 ID입니다. | | 400 `validation_failed` | 이벤트 타입, `eventId`, `timestamp`, payload 구조가 수집 형식에 맞는지 확인하세요. | | 429 `rate_limited` | 프로젝트당 분당 요청 한도에 걸렸습니다. `Retry-After`만큼 기다리면 풀립니다. | | 429 `quota_exceeded` | 조직의 월간 데이터 포인트 한도입니다. 기다려도 풀리지 않으니 플랜과 한도를 확인하세요. | | 콘솔에 `[reopt] ... missing` | `writeKey` 또는 `baseUrl`이 비어 SDK가 꺼진 상태입니다. 환경변수가 빌드에 들어갔는지 확인하세요. | | 응답은 200인데 화면에 늦게 보임 | 분석 화면 반영은 수집 뒤에 이어집니다. 이벤트 스트림에서 먼저 확인하고, 집계 화면은 잠시 뒤에 다시 보세요. | | 페이지뷰가 두 배로 잡힘 | `init()` 뒤에 `pageView()`를 한 번 더 부르고 있지 않은지 확인하세요. | | 같은 이벤트가 duplicate 처리됨 | 같은 `eventId`를 재사용하고 있지 않은지 확인하세요. 브라우저에서는 5초 안에 같은 내용의 이벤트를 다시 보내도 duplicate로 처리됩니다. | | 사용자가 연결되지 않음 | 로그인 직후 `identify`를 보냈는지, 이후 이벤트에 `profileId`가 붙는지 확인하세요. | ## 다음 단계 * SDK 동작을 더 자세히 보려면 [추적 설정](/docs/workspace/tracking-setup) * 에러 수집, 세션 리플레이, AI 텔레메트리를 켜려면 [추적 설정의 에러 수집 켜기](/docs/workspace/tracking-setup#에러-수집-켜기)부터 이어지는 절 * 이벤트 의미를 정리하려면 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 수집 요청의 형식과 오류 코드는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) --- # 추적 설정 Source: https://data.reopt.ai/docs/workspace/tracking-setup 프로젝트를 만든 뒤 브라우저, React, 서버리스 환경에 이벤트 수집을 붙이는 방법을 안내합니다. 프로젝트를 만들었다면 다음 단계는 이벤트 수집을 붙이는 것입니다. 처음에는 이벤트를 많이 보내는 것보다 키를 올바른 환경에 배포하고 로그인 전후 사용자를 안정적으로 연결하는 것이 더 중요합니다. 처음 연동한다면 [빠른 연동](/docs/workspace/quickstart)을 먼저 따라 해보세요. 이 문서는 SDK 동작, HTTP 직접 호출 형식, 검증 기준을 더 자세히 다룹니다. ## 먼저 지켜야 할 원칙 1. 공개 환경에는 `writeKey`만 배포합니다. 2. 서버 환경에는 `clientId`와 `serverSecret`만 배포합니다. 3. 로그인 직후에는 `identify`로 익명 행동과 사용자 프로필을 연결합니다. 4. 서버 이벤트는 응답 뒤에도 전송이 끝나게 합니다. Next.js 요청 스코프 클라이언트는 SDK가 처리하고, 워커·크론·스크립트는 `trackAndFlush` 계열과 `close()`로 직접 비웁니다. 제품 화면에서는 서버 비밀 키를 `serverSecret`으로 보여줍니다. SDK 설정과 환경변수 예시에서는 같은 값을 `clientSecret`이라는 이름으로 넣습니다. ## 시작하기 전에 준비할 것 | 준비물 | 설명 | | -------------- | --------------------------------------------------------- | | `writeKey` | 브라우저와 client component에서 사용하는 공개 수집 키입니다. | | `clientId` | 서버용 client의 ID입니다. `writeKey`와 다른 값입니다. | | `serverSecret` | 서버 SDK 인증에 쓰는 비밀 키입니다. | | `baseUrl` | 리옵트 데이터의 주소입니다. 기본값이 없으므로 `https://data.reopt.ai`를 명시합니다. | | 테스트 이벤트 이름 | 예: `signup_clicked`, `checkout_completed` | 키를 확인한 뒤에는 브라우저용과 서버용 환경변수를 나눠 넣으세요. 공개 prefix가 붙은 값은 브라우저에 노출될 수 있다고 보고 관리하세요. 브라우저 SDK는 `writeKey`나 `baseUrl`이 비어 있으면 콘솔에 경고를 남기고 아무것도 보내지 않습니다. 페이지를 실패시키지는 않습니다. ## 핵심 객체 | 객체 | 역할 | | -------------- | ----------------------------------------- | | `Client` | 프로젝트의 추적 주체를 식별하는 API client | | `writeKey` | 브라우저 또는 공개 환경에서 쓰는 수집 키 | | `clientId` | 서버 SDK가 사용하는 서버용 client의 ID | | `serverSecret` | 서버 환경 전용 비밀 키 | | `deviceId` | 로그인 전 사용자를 묶는 익명 디바이스 ID | | `profileId` | 로그인 후 식별된 사용자 ID | | `eventId` | 클라이언트가 생성하는 UUID. 재전송 중복을 걸러내는 기준입니다. | | `timestamp` | 클라이언트가 이벤트를 만든 millisecond Unix timestamp | `serverSecret`은 브라우저 번들, 공개 저장소, client component, HTML snippet에 들어가면 안 됩니다. 노출되었다면 `reopt-data project rotate-secret`으로 새 secret을 발급하세요. 기존 값은 즉시 동작을 멈춥니다. ## 수집 진입점 * API route: `POST /api/track` * CORS preflight: `OPTIONS /api/track` SDK는 `${baseUrl}/api/track`로 이벤트를 보냅니다. HTTP로 직접 호출한다면 브라우저 키와 서버 키의 헤더를 구분하세요. | 환경 | 필요한 헤더 | | ------------- | ---------------------------------------- | | 브라우저 또는 공개 환경 | `reopt-write-key` | | 서버 환경 | `reopt-client-id`, `reopt-client-secret` | | 익명 사용자 연결 보강 | `reopt-device-id` | 수집 모드는 어떤 키를 보냈는지에 따라 정해집니다. 모드를 따로 지정하는 헤더는 없습니다. | 모드 | 자격증명 | 동작 | | --------- | ----------------------------------------- | ----------------------------------------------------------------------------- | | `browser` | `reopt-write-key` | 같은 device의 같은 내용 이벤트를 5초 안에 다시 보내면 하나로 합칩니다. 잘못된 이벤트가 하나라도 있으면 배치 전체가 400입니다. | | `server` | `reopt-client-id` + `reopt-client-secret` | 5초 중복 제거가 없습니다. 잘못된 이벤트는 응답의 `rejected`에 행별로 보고하고 나머지는 처리합니다. | 요청 한도는 다음과 같습니다. | 항목 | 한도 | | ----------- | ------------------------------------------------------------- | | 요청 본문 | 512,000 bytes. 넘으면 400 `payload_too_large`입니다. | | 요청 빈도 | 프로젝트당 분당 1,000건. 넘으면 429 `rate_limited`와 `Retry-After`를 받습니다. | | 이벤트 이름 | 1\~200자 | | `profileId` | 문자열이면 500자 이하 | ## 도입 순서 1. 통합 프로젝트 화면(`/dashboard`)에서 조직을 선택해 프로젝트를 생성합니다. 2. 브라우저용 client와 서버용 client가 따로 발급됩니다. 3. 브라우저/React/Next.js client component에는 browser client의 `writeKey`를 넣습니다. 4. 서버 route handler, server action, worker, cron job에는 server client의 `clientId + serverSecret`을 넣습니다. 5. 프로젝트 홈의 SDK snippet으로 첫 이벤트를 붙입니다. 6. 이벤트 스트림 또는 데이터 카탈로그에서 이벤트 이름, 속성, 프로필 연결을 확인합니다. 7. 카탈로그에서 전환, 태그, 카테고리, 상태를 정리합니다. ## 브라우저에 붙이기 ```ts import { init, identify, track } from "@reopt-ai/data-sdk-client"; init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.NEXT_PUBLIC_REOPT_API_URL!, }); track("signup_clicked", { source: "landing", plan: "starter", }); identify("user_123", { plan: "starter", }); ``` `init()`은 `writeKey`마다 클라이언트를 하나만 만들고, 생성할 때 첫 `$pageview`를 직접 보냅니다. 같은 `writeKey`로 다시 호출하면 이미 만든 클라이언트를 돌려주므로 두 번 초기화해도 device나 큐가 둘이 되지 않습니다. SPA에서 화면이 바뀔 때는 `pageView()`를 호출하세요. `history`를 가로채는 자동 감지는 없습니다. 첫 페이지뷰를 직접 보내고 싶다면 `capture: { pageview: false }`로 끕니다. 브라우저 SDK는 이벤트를 바로 네트워크로 보내지 않고 내부 큐에 넣습니다. 기본값으로는 이벤트가 100개 모이거나 1초가 지나면 flush하고, 한 요청이 400,000 bytes를 넘지 않게 나눕니다. 페이지가 숨겨지거나 언로드될 때는 `keepalive` 전송을 시도합니다. 이 방식으로 페이지 이동 중의 이벤트 손실을 줄입니다. `track()`, `identify()` 같은 호출은 큐잉 결과를 바로 돌려줍니다. `queued`가 `false`이면 `reason`에 이유가 들어 있습니다. 값은 `tracking_paused`, `consent_denied`, `validation_failed`, `payload_too_large` 중 하나입니다. `init()` 전에 부른 호출은 콘솔에 경고를 남기고 `undefined`를 돌려줍니다. ### 자주 쓰는 옵션 | 옵션 | 기본값 | 설명 | | ----------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------- | | `capture.pageview` | `true` | 생성 시 첫 `$pageview`를 보냅니다. Next.js 진입점은 `ReoptPageView`가 맡으므로 `false`로 둡니다. | | `capture.pageleave` | `true` | 페이지를 떠나거나 숨길 때 체류 시간이 담긴 `$pageleave`를 보냅니다. | | `capture.scrollDepth` | `true` | `$pageleave`에 최대 스크롤 깊이를 붙입니다. | | `capture.exceptions` | `false` | 잡히지 않은 에러와 unhandled rejection을 `$exception`으로 보냅니다. | | `release` | 없음 | `track` 이벤트(자동 이벤트 포함)에 `$release_id`로 붙는 빌드 식별자입니다. 없으면 번들러 `define`이 주입한 `__REOPT_RELEASE__`를 읽습니다. | | `properties` | 없음 | 첫 이벤트부터 모든 `track` 이벤트(자동 이벤트 포함)에 붙는 속성입니다. 이후에는 `register()`로 갱신합니다. | | `normalizePath` | 없음 | SDK가 스스로 붙이는 경로를 바꿉니다. `/orders/123` 같은 경로를 `/orders/:id`로 접을 때 씁니다. | | `consent` | `analytics`만, 기본 허용, 저장함 | `categories`, `defaultConsent`, `persist`로 조정합니다. | | `identity.storage` | `auto` | device ID 저장 위치. 쿠키, `localStorage`, 메모리 순으로 시도합니다. | | `identity.cookieDomain` | 호스트 전용 | `.example.com`처럼 지정하면 서브도메인끼리 같은 방문자로 봅니다. `reoptProxy`를 쓴다면 같은 값을 `cookieDomain`으로 넘기세요. | | `batch` | `size` 100, `intervalMs` 1000 | 한 요청의 이벤트 수와 대기 시간. `maxBytes` 기본값은 400,000입니다. | | `sessionReplay` | 꺼짐 | 아래 [세션 리플레이 켜기](#세션-리플레이-켜기)를 확인하세요. | | `debug` | `false` | 큐잉과 전송 과정을 콘솔에 남깁니다. | `normalizePath`는 `pageView()`에 직접 넘긴 `path`에는 적용되지 않습니다. 같은 함수를 호출부에서도 쓰세요. ### React Provider ```tsx "use client"; import { ReoptProvider, useTrack } from "@reopt-ai/data-sdk-client/react"; export function AnalyticsProvider({ children }: { children: React.ReactNode }) { return ( {children} ); } function SignupButton() { const track = useTrack(); return ; } ``` Provider는 클라이언트를 렌더 중에 만듭니다. React는 effect를 자식부터 실행하므로, effect에서 만들면 동의 배너나 `useTrackOnMount` 같은 자식이 클라이언트가 생기기 전에 실행되기 때문입니다. 생성은 `writeKey` 단위로 멱등이라 StrictMode의 이중 렌더나 remount에서도 같은 인스턴스를 씁니다. Provider도 첫 `$pageview`를 직접 보냅니다. 끄려면 `initialPageView={false}`를 주세요. 라우트가 바뀔 때는 `usePageView()`가 돌려주는 함수를 호출합니다. 쓸 수 있는 hook은 `useReopt`, `useReoptClient`, `useTrack`, `useIdentify`, `usePageView`, `useConsent`, `useTrackOnMount`, `usePageViewOnMount`입니다. Provider가 unmount돼도 클라이언트는 닫히지 않습니다. 클라이언트는 페이지와 수명이 같고, StrictMode 시뮬레이션이나 layout 교체 때 큐를 잃으면 안 되기 때문입니다. 닫아야 한다면 `client.close()`를 직접 호출하세요. ### Next.js App Router App Router에서는 라우터가 페이지뷰를 알려줘야 하므로 전용 진입점을 씁니다. `ReoptPageView`는 children을 감싸지 말고 형제로 두세요. 감싸면 이동할 때마다 트리 전체가 다시 렌더됩니다. ```tsx // app/layout.tsx (서버 컴포넌트) import { getBootstrap } from "@/lib/reopt"; import { ReoptProvider, ReoptPageView, ReoptWebVitals } from "@reopt-ai/data-sdk-client/next"; export default async function RootLayout({ children }: { children: React.ReactNode }) { const bootstrap = await getBootstrap(); return ( {children} ); } ``` `bootstrap`은 서버가 읽은 device·동의 상태입니다. 이 값이 있으면 브라우저는 첫 페이지뷰부터 서버와 같은 device ID를 쓰고, 저장소를 읽는 경쟁이 사라집니다. 서버가 방문자를 모르면 `null`이며, 그때는 클라이언트가 자체 identity를 씁니다. `getBootstrap()`은 요청의 쿠키를 읽습니다. 결과에 방문자의 device ID가 들어 있으므로 여러 방문자가 공유하는 캐시 안에서 부르거나 그 값을 캐시하지 마세요. `baseUrl: "/ingest"`는 아래 proxy와 함께 쓰는 값입니다. `proxy.ts`를 쓰지 않는다면 리옵트 데이터 오리진을 그대로 넣으세요. `ReoptPageView`는 `usePathname`과 `useSearchParams`를 함께 구독하므로 쿼리스트링만 바뀌는 필터 변경도 페이지뷰로 셉니다. 자체 ``를 갖고 있어 상위 레이아웃의 정적 렌더를 깨지 않습니다. 이 진입점의 `ReoptProvider`는 첫 페이지뷰를 직접 보내지 않습니다. 첫 페이지뷰도 `ReoptPageView`가 보내므로 두 번 세지 않습니다. `ReoptWebVitals`는 Next.js가 보고하는 Core Web Vitals를 `$web_vitals` 이벤트로 보냅니다. ### proxy.ts 설정 ```ts // proxy.ts (Next 16에서 middleware.ts를 대체합니다) import { reoptProxy } from "@reopt-ai/data-sdk-server/proxy"; export const proxy = reoptProxy({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.REOPT_BASE_URL!, proxy: true, // /ingest/api/{track,replay/start,replay/chunk} → ${baseUrl}/api/… (SDK가 보내는 경로만) }); export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"] }; ``` * 첫 방문에 device 쿠키를 심습니다. 렌더 전에 심기 때문에 서버와 브라우저가 처음부터 같은 방문자를 봅니다. 동의 전에 쿠키를 쓸 수 없다면 `seedDeviceCookie: false`로 끄세요. * 방문자가 수집을 거부하면 쿠키를 더 쓰지 않는 데 그치지 않고 삭제합니다. * `/ingest/*`를 여러분의 도메인에서 받아 넘깁니다. 애드블로커와 서드파티 쿠키 차단은 1st-party 경로를 다르게 취급합니다. * 넘기는 대상은 `baseUrl` 오리진에서 SDK가 실제로 보내는 세 경로 — `/api/track`, `/api/replay/start`, `/api/replay/chunk` — 뿐입니다. 그 밖의 경로로 풀리는 요청은 400으로 거절합니다. * 사이트의 `Cookie`·`Authorization` 헤더는 넘기지 않습니다. 리옵트 데이터는 배치를 자체 헤더로 인증하므로 사이트의 세션 쿠키가 필요 없습니다. * 브라우저 SDK에 `identity.cookieDomain`을 지정했다면 `reoptProxy`에도 같은 값을 `cookieDomain`으로 넘기세요. 다르면 같은 이름의 쿠키가 두 개 생겨 브라우저와 서버가 서로 다른 방문자를 볼 수 있습니다. * `matcher`를 허용 목록으로 쓰는 앱은 `"/ingest/:path*"`를 목록에 넣어야 합니다. 빠지면 그 경로에서 proxy가 실행되지 않아 수집 요청이 리옵트 데이터로 전달되지 않습니다. * 이미 `proxy.ts`가 있다면 `reoptProxy(...)`가 돌려주는 함수에 요청과 여러분의 응답을 차례로 넘기세요. 예: `withReopt(request, response)`. ## 서버와 서버리스에 붙이기 서버에서는 `@reopt-ai/data-sdk-server`를 사용합니다. 브라우저 코드와 서버 코드를 섞지 않도록 패키지를 둘로 나눴습니다. 이 패키지는 `server-only`를 import하므로, client component에서 실수로 가져다 쓰면 빌드가 실패합니다. | 진입점 | 쓰는 곳 | | --------------------------------- | -------------------------------------------- | | `@reopt-ai/data-sdk-server` | 서버 컴포넌트, route handler, `instrumentation.ts` | | `@reopt-ai/data-sdk-server/proxy` | `proxy.ts` | | `@reopt-ai/data-sdk-server/node` | 워커, 크론, 스크립트 | | `@reopt-ai/data-sdk-server/ai` | Vercel AI SDK 텔레메트리 | ### Next.js 안에서 (요청 스코프) ```ts // lib/reopt.ts import "server-only"; import { createReopt } from "@reopt-ai/data-sdk-server"; import { auth } from "@/auth"; export const { getReopt, getBootstrap } = createReopt({ clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.REOPT_BASE_URL!, getProfileId: async () => (await auth())?.user?.id ?? null, }); ``` ```ts // app/api/checkout/route.ts import { getReopt } from "@/lib/reopt"; export async function POST(request: Request) { const body = await request.json(); const reopt = await getReopt(); const queued = reopt.track("checkout_completed", { orderId: body.orderId, amount: body.amount, }); return Response.json({ ok: queued.queued, eventId: queued.eventId }); } ``` 이 클라이언트는 요청을 보낸 방문자에게 이미 묶여 있습니다. * 브라우저가 심어 둔 device 쿠키를 읽어 이벤트에 실으므로, 서버에서 확정되는 전환이 그 방문자의 현재 세션에 들어갑니다. * `getProfileId`는 요청당 한 번만 실행되고, 그 결과가 쿠키의 profileId를 덮어씁니다. 쿠키는 브라우저가 쓰는 값이고 세션은 그렇지 않기 때문입니다. * 방문자가 수집을 거부한 상태면 아무것도 보내지 않는 클라이언트를 돌려줍니다. 호출부에 동의 분기가 필요 없습니다. * Vercel에서는 요청의 `waitUntil`을 자동으로 찾아 전송을 넘깁니다(따로 설치할 패키지는 없습니다). 이때는 `flush()`를 직접 부르지 않아도 이벤트가 유실되지 않습니다. * `waitUntil`이 없고 오래 실행되는 서버에서는 SDK의 배칭 타이머가 보냅니다. 응답 직후 멈추는 다른 서버리스 호스트라면 `createReopt({ waitUntil })`로 호스트의 `waitUntil`을 넘기거나 `await reopt.flush()`를 호출하세요. * `release` 옵션을 주면 `track()` 이벤트에 `$release_id`가 붙습니다. * `clientId`와 `clientSecret`이 없으면 경고를 한 번 남기고 서버 이벤트를 끕니다. 페이지는 실패하지 않습니다. 요청 스코프 클라이언트가 제공하는 것은 `track`, `identify`, `increment`, `decrement`, `flush`, 그리고 읽기 전용 `deviceId`·`profileId`입니다. `trackAndFlush` 계열은 아래 Node 진입점에만 있습니다. `createReopt()`는 모듈 스코프에서 한 번만 호출하세요. 요청마다 다시 만들면 배칭과 요청 단위 캐시를 잃습니다. 한 배포로 여러 프로젝트를 처리한다면 `writeKey`에 함수를 주고 `credentials` resolver를 함께 넘기세요. 둘 다 요청당 한 번만 실행되고, `null`을 돌려주면 그 요청은 아무것도 기록하지 않습니다. ### Next.js 밖에서 (워커·크론·스크립트) 읽을 요청이 없으므로 방문자에게 묶을 수 없습니다. flush는 프로세스가 직접 해야 합니다. ```ts import { createReoptNode } from "@reopt-ai/data-sdk-server/node"; const reopt = createReoptNode({ clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, baseUrl: process.env.REOPT_BASE_URL!, batch: { size: 20, intervalMs: 5_000 }, }); const delivery = await reopt.trackAndFlush("nightly_report_generated", { rows: 12_400 }); if (!delivery.queue.queued || delivery.flush?.status !== "success") { console.warn("이벤트가 전송되지 않았습니다", delivery); } await reopt.close(); // 프로세스 종료 전 필수. 타이머를 멈추고 큐를 비웁니다 ``` `trackAndFlush`, `identifyAndFlush`, `incrementAndFlush`, `decrementAndFlush`는 큐잉 결과와 전송 결과를 한 번에 반환합니다. ```ts type DeliveryResult = { queue: { eventId: string; queued: boolean; reason?: "tracking_paused" | "consent_denied" | "validation_failed" | "payload_too_large"; errors?: Array<{ field: string; message: string }>; }; flush: { status: "idle" | "success" | "failed" | "skipped"; sent: number; failed: number; pending: number; } | null; }; ``` `queue.queued`가 `false`이면 이벤트가 로컬 큐에 들어가지 않은 상태이므로 `flush`는 `null`입니다. `flush.status`가 `failed`이면 `pending`도 함께 확인하세요. 네트워크 오류, 5xx, rate limit은 재시도한 뒤에도 실패하면 배치를 큐에 남기고, quota 초과도 배치를 남깁니다. 401이나 400처럼 서버가 영구히 거절한 배치는 버리고 `failed`에 집계하며, 콘솔에 `[reopt] dropping events` 경고를 한 번 남깁니다. 서버 모드에서 일부 행만 거절된 경우에는 그 행 수가 `failed`에, 나머지가 `sent`에 들어갑니다. 특정 방문자의 전환을 나중에 보내야 한다면 이벤트에 그 방문자의 device를 실으세요. 요청 시점에 `readDeviceId()`로 쿠키에서 읽어 두었다가, 보낼 때 `identity`로 넘깁니다. 읽을 쿠키가 없으면 throw하지 않고 `null`을 돌려줍니다. ```ts // 요청 시점 (Next.js): 누구의 전환인지 기록해 둡니다 import { cookies } from "next/headers"; import { readDeviceId } from "@reopt-ai/data-sdk-server"; const deviceId = readDeviceId(await cookies(), { writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY! }); ``` ```ts // 나중에 (워커): 그 device로 보냅니다 reopt.track({ name: "order_confirmed", properties: { orderId }, identity: { deviceId } }); ``` ## 서버 SDK 응답 해석하기 `trackAndFlush` 계열의 응답은 두 단계로 나눠 읽습니다. | 위치 | 의미 | | --------------- | --------------------------- | | `queue.queued` | SDK 로컬 큐에 이벤트가 들어갔는지 나타냅니다. | | `queue.reason` | 큐에 들어가지 않았다면 이유를 알려줍니다. | | `flush.status` | 네트워크 전송이 성공했는지 나타냅니다. | | `flush.sent` | 이번 flush에서 전송된 이벤트 수입니다. | | `flush.pending` | 아직 SDK 큐에 남아 있는 이벤트 수입니다. | 워커와 스크립트에서는 `queue.queued === true`와 `flush.status === "success"`를 함께 확인하세요. 둘 중 하나라도 기대와 다르면 작업을 성공으로 끝내지 말고 재시도 정책을 세우는 편이 안전합니다. ## 로그인 사용자 연결하기 로그인 전에는 `deviceId`를 기준으로 익명 행동을 묶습니다. 로그인 직후 `identify(profileId, properties)`를 호출하면 이후 이벤트가 식별된 사용자 프로필과 연결됩니다. 로그아웃할 때는 `reset()`을 호출하세요. 현재 프로필 ID, 큐, `register()`로 붙인 속성을 비우고 새 device ID를 발급합니다. 공용 컴퓨터에서 다음 사용자가 이전 사용자의 기록을 이어받지 않게 하기 위해서입니다. ## 동의와 수집 중단 기본 동의 모델은 opt-out입니다. 아무 설정이 없으면 `analytics` 카테고리가 허용된 상태로 시작합니다. ```ts import { setConsent, pauseTracking, resumeTracking } from "@reopt-ai/data-sdk-client"; setConsent("analytics", false); // 수집 거부: 이후 이벤트는 큐에 들어가지 않습니다 setConsent("analytics", true); // 다시 허용 pauseTracking(); // 일시 중지. 동의 상태는 바꾸지 않습니다 resumeTracking(); ``` * `analytics`를 거부하면 SDK가 device 쿠키를 지웁니다. proxy도 이후 요청에서 남아 있는 device 쿠키를 삭제하고, 서버 SDK의 `getReopt()`는 그 방문자에게 아무것도 보내지 않는 클라이언트를 돌려줍니다. * 동의 결정은 기본으로 `reopt__consent` 쿠키에 저장되어 서버도 같은 값을 읽습니다. * 동의 전에는 수집하지 않으려면 `consent: { defaultConsent: false }`로 시작하고, 동의를 받은 뒤 `setConsent("analytics", true)`를 호출하세요. * 자체 동의 배너가 결정을 관리한다면 `consent: { persist: false }`로 SDK가 쿠키를 따로 두지 않게 하세요. ## SDK가 자동으로 붙이는 기본 메타데이터 SDK는 각 이벤트에 `eventId`와 `timestamp`를 붙이고, 요청 헤더에는 `reopt-device-id`를 보냅니다. * `eventId`는 프로젝트 안에서 이벤트를 구분하는 값입니다. 같은 프로젝트에 같은 `eventId`가 다시 들어오면 새로 저장하지 않고 duplicate로 처리합니다. * `timestamp`는 클라이언트 생성 시각입니다. 서버는 별도로 수신 시각도 저장합니다. * `deviceId`는 로그인 전 익명 행동을 세션/프로필 후보와 연결하는 기준입니다. * 브라우저는 device ID와 동의 상태를 쿠키(`reopt__device`, `reopt__consent`)에 저장합니다. 서버도 같은 값을 읽어야 첫 렌더부터 서버와 브라우저가 같은 방문자를 보기 때문입니다. offline queue는 `localStorage`에 둡니다. * 쿠키 이름이 `writeKey`에서 유도되므로 한 도메인에 두 프로젝트를 붙여도 서로의 방문자를 덮어쓰지 않습니다. * `localStorage`가 막힌 환경에서도 device ID는 쿠키에 남지만, offline queue의 영속성은 보장되지 않습니다. * 수집 응답이 내려준 서명된 세션 값은 SDK가 device 쿠키에 보관했다가 다음 요청의 `reopt-session-id` 헤더로 보냅니다. SDK가 스스로 보내는 이벤트는 다음과 같습니다. | 이벤트 | 보내는 시점 | 주요 속성 | | -------------- | ----------------------------------------------------- | ----------------------------------------------- | | `$pageview` | 초기화 시, 그리고 `pageView()`나 `ReoptPageView`가 호출될 때 | `path`, `origin`, `title`, `referrer`, `utm_*` | | `$pageleave` | 페이지를 떠나거나 숨길 때, 다음 페이지뷰가 들어올 때 | `path`, `origin`, `duration`(초), `scroll_depth` | | `$web_vitals` | `ReoptWebVitals`를 두었거나 `captureWebVital()`을 호출할 때 | `metric_name`, `value`, `rating`, `path` | | `$exception` | `capture.exceptions`를 켰거나 `captureException()`을 호출할 때 | `$exception_type`, `$exception_message`, `path` | | `$screen_view` | `screenView(name)`을 호출할 때 | `screen_name` | ## 에러 수집 켜기 브라우저에서는 옵션 하나로 켭니다. ```ts init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.NEXT_PUBLIC_REOPT_API_URL!, capture: { exceptions: true }, release: process.env.NEXT_PUBLIC_APP_VERSION, }); ``` * 잡히지 않은 에러와 unhandled rejection이 `$exception` 이벤트로 들어옵니다. 스택 파서는 별도 chunk라서 이 기능을 쓰지 않는 페이지의 초기 번들에는 들어가지 않습니다. * 자동 수집은 같은 타입의 예외가 몰릴 때 전송량을 제한합니다. 기본값은 예외 타입별로 10건까지 보내고 10초마다 1건씩 다시 허용하는 방식이며, `capture.exceptionRateLimit`으로 바꾸거나 `false`로 끌 수 있습니다. * 스택이 전부 브라우저 확장 프로그램 프레임인 에러는 자동 수집에서 제외합니다. * 직접 잡은 에러는 `captureException(error)`로 보냅니다. 이 호출은 조절 대상이 아닙니다. * `release`를 주면 이슈가 어느 빌드에서 처음 나타났는지, 고친 뒤 다시 나타났는지를 구분할 수 있습니다. Next.js 서버 에러는 `instrumentation.ts`에서 붙입니다. ```ts // instrumentation.ts import { createOnRequestError } from "@reopt-ai/data-sdk-server"; export const onRequestError = createOnRequestError({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, baseUrl: process.env.REOPT_BASE_URL!, beforeCapture: () => ({ $release_id: process.env.VERCEL_GIT_COMMIT_SHA }), }); ``` `beforeCapture`가 객체를 돌려주면 속성에 합치고, `false`를 돌려주거나 throw하면 그 에러는 보내지 않습니다. 압축된 스택을 원본 위치로 되돌리려면 배포 파이프라인에서 소스맵을 올리세요. 명령은 [CLI 사용하기](/docs/workspace/cli#소스맵을-ci에서-업로드하기)에 있습니다. 수집된 에러가 이슈로 묶이는 방식과 화면은 [에러 트래킹](/docs/project-workspace/error-tracking)에서 다룹니다. `$exception` 이벤트도 1건당 데이터 포인트 1개로 집계됩니다. ## 세션 리플레이 켜기 세션 리플레이는 다음 세 가지를 모두 갖춰야 녹화됩니다. 1. 프로젝트의 세션 리플레이 화면에서 녹화 설정을 켭니다. 수집 비율도 여기서 정합니다. 설정 변경은 `owner` 또는 `admin`만 할 수 있습니다. 2. SDK에서 `sessionReplay: { enabled: true }`를 줍니다. 3. 방문자가 녹화에 동의하면 `setConsent("replay", true)`를 호출합니다. `analytics` 동의도 함께 있어야 합니다. ```ts const client = init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: "/ingest", sessionReplay: { enabled: true }, }); // 동의 UI가 방문자의 녹화 결정을 받은 뒤에만 호출합니다 client.setConsent("replay", true); ``` * `replay` 동의는 기본으로 주어지지 않습니다. `setConsent("replay", true)`를 호출하기 전에는 녹화하지 않습니다. 기본 허용되는 `consent.categories` 목록에 `replay`를 넣지 마세요. * 입력값과 화면 텍스트는 가려서 기록합니다. `data-reopt-replay-block` 또는 `data-reopt-private` 속성을 단 영역은 통째로 제외하고, `sessionReplay.blockSelector`로 제외할 영역을 더할 수 있습니다. * 녹화 코드는 켰을 때만 별도 chunk로 로드됩니다. * 업로드는 `baseUrl` 아래 `/api/replay/start`와 `/api/replay/chunk`로 갑니다. `/ingest` proxy를 쓰고 있다면 그대로 전달됩니다. * `flushIntervalMs`는 전송 주기로, 기본 20초이며 1~~30초 사이에서 조정합니다. `idleTimeoutMs`는 유휴 중단 시간으로, 기본 60초이며 10초~~5분 사이에서 조정합니다. 녹화 목록과 재생 화면은 [세션 리플레이](/docs/project-workspace/session-replay)에서 다룹니다. ## AI 텔레메트리 켜기 Vercel AI SDK(`ai@7`)를 쓰는 서버라면 텔레메트리를 한 번 등록하세요. `generateText`, `streamText`, `embed`와 도구 실행이 호출부 수정 없이 기록됩니다. ```ts // instrumentation.ts (또는 에이전트 워커의 진입점) import { registerTelemetry } from "ai"; import { createReoptAiTelemetry } from "@reopt-ai/data-sdk-server/ai"; import { createReoptNode } from "@reopt-ai/data-sdk-server/node"; const reopt = createReoptNode({ clientId: process.env.REOPT_CLIENT_ID!, clientSecret: process.env.REOPT_CLIENT_SECRET!, baseUrl: process.env.REOPT_BASE_URL!, }); export const aiTelemetry = createReoptAiTelemetry({ reopt }); registerTelemetry(aiTelemetry); ``` * 프롬프트와 응답 본문은 기본으로 보내지 않습니다. 본문까지 보려면 SDK의 `captureContent: true`와 프로젝트 설정의 본문 수집을 모두 켜야 합니다. * `redact`로 본문과 에러 메시지를 내보내기 전에 가릴 수 있습니다. throw하면 그 필드는 보내지 않습니다. * `sampleRate`(0\~1)는 trace ID 기준이라 한 trace가 통째로 남거나 통째로 빠집니다. * 세션과 사용자 축은 `context` 옵션으로 매핑합니다. * AI SDK 밖의 호출은 `aiTelemetry.withAiTrace()`, `captureAiGeneration()`, `captureAiSpan()`, `captureAiFeedback()`으로 직접 기록합니다. * 스크립트를 끝내기 전에는 `await aiTelemetry.flush()` 뒤에 `await reopt.close()`를 호출하세요. AI 이벤트(`$ai_generation`, `$ai_trace`, `$ai_span`, `$ai_embedding`, `$ai_feedback`)도 각각 데이터 포인트 1개로 집계됩니다. 수집된 호출을 보는 화면은 [AI 관측](/docs/project-workspace/ai-observability)에서 다룹니다. ## SDK devtool로 전송 내용 확인하기 `@reopt-ai/data-sdk-devtool`은 브라우저 SDK가 큐에 넣은 이벤트, 보낸 배치, 서버 응답, identity 변화를 페이지 안의 패널로 보여줍니다. 여러분의 앱에 붙이는 패키지라서, 개발 중인 화면에서 SDK가 실제로 무엇을 보내는지 바로 확인할 수 있습니다. ```bash pnpm add -D @reopt-ai/data-sdk-devtool ``` ```ts // lib/devtools.ts: 인스턴스 하나를 SDK 설정과 패널이 함께 씁니다 import { createDevtools } from "@reopt-ai/data-sdk-devtool"; export const devtools = createDevtools(); ``` ```tsx "use client"; import { ReoptProvider } from "@reopt-ai/data-sdk-client/next"; import { ReoptDevtools } from "@reopt-ai/data-sdk-devtool/react"; import { devtools } from "@/lib/devtools"; export function Analytics({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` * 프로덕션에서는 기본으로 꺼져 있습니다. `NODE_ENV`가 `production`이면 아무것도 기록하지 않고 패널도 그리지 않습니다. 스테이징이나 데모에서 켜려면 `createDevtools({ enabled: true })`를 씁니다. * 자격증명 헤더와 흔한 개인정보 필드는 저장 전에 가립니다. 원본 값이 필요하면 `captureRaw: true`를 쓰되 신뢰할 수 있는 로컬 페이지에서만 켜세요. * Provider 없이 `init()`을 쓰는 앱에서도 같은 `fetch`와 `observe`를 넘기면 됩니다. ## 직접 HTTP로 호출하기 SDK를 통하지 않고 `POST /api/track`를 호출할 때도 같은 이벤트 형식을 지켜야 합니다. `track`, `identify`, `increment`, `decrement` 이벤트에는 UUID `eventId`와 millisecond `timestamp`가 필수입니다. `alias` 타입은 현재 수집 API에서 지원하지 않습니다. ```json [ { "type": "track", "eventId": "550e8400-e29b-41d4-a716-446655440000", "timestamp": 1716000000000, "payload": { "name": "signup_clicked", "properties": { "source": "landing" }, "profileId": "user_123" } } ] ``` 브라우저 키로 보낼 때는 `reopt-write-key`를, 서버 키로 보낼 때는 `reopt-client-id`와 `reopt-client-secret`을 헤더에 넣습니다. `reopt-device-id`는 익명 사용자와 세션 연결 품질을 높이므로 직접 호출에서도 보내는 것이 좋습니다. 서버 자격증명으로 호출하면서 이 헤더를 보내면, 그 이벤트는 해당 device의 활성 세션에 합류합니다. 몇 시간 뒤 도착한 웹훅처럼 활성 세션이 없을 때는 세션 없이 적재하고 새 세션을 만들지 않습니다. pageview가 0인 세션이 생기면 이탈률이 왜곡되기 때문입니다. 서버 자격증명으로 보내는 배치에서는 이벤트마다 `deviceId` 필드를 둘 수도 있습니다. 한 배치에 여러 방문자의 전환이 섞여 있을 때 쓰며, 요청 헤더보다 우선합니다. 브라우저 키로 보낸 요청에서는 이 필드를 무시합니다. `reopt-session-id`는 서버가 서명한 값만 인정합니다. 수집 응답의 `session`으로 내려온 `<세션 ID>.<토큰>`을 그대로 돌려보낸 경우에만 그 세션에 합류합니다. 서명이 없거나 맞지 않는 값은 조용히 무시하고 device 기준으로 세션을 정합니다. 누구나 임의 값을 넣을 수 있는 헤더를 그대로 믿으면 남의 세션에 이벤트를 주입할 수 있기 때문입니다. 직접 호출의 최소 예시는 다음과 같습니다. ```bash curl -X POST "$REOPT_BASE_URL/api/track" \ -H "content-type: application/json" \ -H "reopt-write-key: $NEXT_PUBLIC_REOPT_WRITE_KEY" \ -H "reopt-device-id: dev-device-1" \ --data '{ "type": "track", "eventId": "550e8400-e29b-41d4-a716-446655440000", "timestamp": 1716000000000, "payload": { "name": "signup_clicked", "properties": { "source": "docs" } } }' ``` server 모드의 2xx 응답은 `accepted + duplicates + rejected 개수 = 보낸 이벤트 수`를 만족합니다. 로그를 순서대로 다시 보내는 서버는 이 식으로 빠진 이벤트가 없는지 확인할 수 있습니다. ## 수집 요청 뒤에 이어지는 처리 1. 자격증명과 프로젝트의 분당 요청 한도를 확인합니다. 2. 이벤트 형식을 검증하고, 브라우저 수집이면 5초 중복 제거를 적용합니다. 3. 조직의 데이터 포인트 한도를 확인합니다. 배치 전체가 남은 한도에 들어와야 접수됩니다. 4. 원본 이벤트를 저장하고 응답을 돌려줍니다. 5. 그 뒤에 세션과 프로필을 정리하고 분석 화면과 실시간 스트림에 반영합니다. 분석 반영이 중간에 실패해도 저장된 원본을 기준으로 다시 처리하므로, 수집에 성공한 이벤트가 사라지지는 않습니다. 브라우저 요청은 서버가 사람, 의심 트래픽, 알려진 봇으로 분류합니다. 사람이 아닌 트래픽은 세션·프로필·기본 분석과 데이터 포인트 집계에 들어가지 않습니다. ## 이벤트 이름과 속성 정하기 * 이벤트 이름은 사용자 행동을 과거형 동사로 표현합니다. 예: `signup_clicked`, `checkout_completed` * `$`로 시작하는 이름은 SDK가 쓰는 이벤트입니다. `$pageview`, `$pageleave`, `$screen_view`, `$web_vitals`, `$exception`과 AI 텔레메트리의 `$ai_*`가 있습니다. * `session_start`, `session_end`, `screen_view`는 예약된 이름이라 직접 보내면 거절됩니다. * 속성 값은 문자열, 숫자, boolean, null, 배열, object처럼 JSON으로 직렬화 가능한 값만 사용합니다. * 결제 금액, 플랜, 실험군처럼 분석 축이 되는 값은 이벤트 속성에 명시적으로 넣습니다. * 이메일, 전화번호, 주소, access token 같은 민감 정보는 이벤트 속성에 넣지 않습니다. ## 주의할 점 * `serverSecret`은 서버 전용입니다. 공개 환경에 포함되면 안 됩니다. * 워커나 스크립트에서 `track()`만 호출하고 프로세스를 끝내면 이벤트 전송이 완료되지 않을 수 있습니다. `trackAndFlush` 계열을 쓰거나 종료 전에 `close()`를 호출하세요. * 같은 `eventId`를 재사용하면 새로 저장되지 않고 duplicate로 처리됩니다. * `accepted`가 0이어도 `duplicates`가 1 이상이면 요청 자체는 정상일 수 있습니다. * `init()`이나 Provider가 첫 `$pageview`를 이미 보냅니다. 같은 화면에서 `pageView()`를 다시 부르면 두 번 집계됩니다. * 이벤트 속성에는 개인식별정보와 인증 토큰을 넣지 마세요. ## 온보딩 검증 체크리스트 첫 배포 전에는 아래 순서로 확인하세요. 1. 개발 또는 preview 환경에서 `debug: true`로 큐잉 결과를 확인합니다. 2. 네트워크 탭에서 `POST /api/track` 응답이 200인지 확인합니다. 3. 응답의 `accepted`가 증가하고 `duplicates`가 비정상적으로 높지 않은지 봅니다. 4. 프로젝트의 이벤트 스트림 또는 데이터 카탈로그에서 이벤트 이름과 속성이 기대한 형태인지 확인합니다. 5. 로그인 직후 `identify`가 들어오고 이후 이벤트에 `profileId`가 붙는지 확인합니다. 6. 워커·크론 이벤트는 `trackAndFlush` 계열을 사용해 `flush.status === "success"`까지 확인합니다. 7. 데이터 카탈로그에서 전환, 태그, 카테고리, 상태를 정리합니다. ## 정상 응답 예시 수집 API가 정상 처리하면 다음과 같은 응답을 반환합니다. ```json { "status": "ok", "requestId": "...", "mode": "browser", "accepted": 1, "duplicates": 0, "rejected": [], "queued": true } ``` `accepted`는 이번 요청에서 새로 저장된 이벤트 수입니다. `duplicates`가 1 이상이면 같은 `eventId`가 이미 들어온 상태일 수 있습니다. 브라우저 모드에서는 5초 중복 제거로 합쳐진 이벤트도 여기에 들어갑니다. `rejected`는 서버 모드에서 행별로 거절된 이벤트 목록입니다. 각 항목의 `reason`은 `validation_failed`, `reserved_name`, `duplicate_in_batch` 중 하나입니다. 다시 보내도 결과가 같으므로 재시도하지 마세요. `queued`가 `true`이면 분석 화면에 반영할 작업이 예약된 것입니다. 브라우저 모드 응답에는 `session`(세션 ID와 서명 토큰)이 함께 올 수 있습니다. 세션의 첫 배치에는 없는 것이 정상입니다. 드물게 202와 `"status": "accepted"`가 돌아올 수 있습니다. 이벤트는 저장됐고 화면 반영만 조금 늦어지는 경우이므로 성공으로 처리하세요. 오류 응답은 `code`로 구분하세요. `error`와 `message`는 사람이 읽는 문장이라 바뀔 수 있습니다. | HTTP | `code` | 의미 | | ---- | ------------------- | ---------------------------------------- | | 400 | `invalid_json` | 본문이 JSON이 아닙니다. | | 400 | `payload_too_large` | 본문이 512,000 bytes를 넘습니다. 배치를 나누세요. | | 400 | `empty_batch` | 이벤트가 0건입니다. | | 400 | `alias_unsupported` | `type: "alias"` 이벤트가 들어 있습니다. | | 400 | `validation_failed` | 브라우저 모드에서 이벤트 하나가 검증에 실패했습니다. | | 401 | `unauthorized` | 자격증명이 없거나 맞지 않습니다. | | 410 | `project_purging` | 삭제 중인 프로젝트입니다. 더 보내지 마세요. | | 429 | `rate_limited` | 프로젝트 요청 한도입니다. `Retry-After` 뒤에 다시 보냅니다. | | 429 | `quota_exceeded` | 조직의 월간 데이터 포인트 한도입니다. 사람이 한도를 올려야 풀립니다. | | 500 | `internal_error` | 서버 오류입니다. 간격을 두고 재시도하세요. | ## 자주 보는 문제 | 증상 | 확인할 것 | 해결 방향 | | ----------------------------- | --------------------------------------------------------- | ------------------------------------------------- | | 401 응답 | 브라우저에는 `writeKey`, 서버에는 `clientId + serverSecret`을 썼는지 확인 | 환경변수 이름과 헤더를 분리하세요. `clientId`는 `writeKey`가 아닙니다. | | 400 `validation_failed` | `eventId`, `timestamp`, 이벤트 타입별 payload가 수집 형식에 맞는지 확인 | SDK를 쓰거나 직접 HTTP payload를 수집 형식에 맞추세요. | | 429 `rate_limited` | 프로젝트당 분당 요청 수 | 이벤트를 배치로 묶어 요청 수를 줄이세요. | | 429 `quota_exceeded` | 조직의 월간 데이터 포인트 사용량 | 사용량 및 요금 화면에서 플랜과 한도를 확인하세요. | | 콘솔에 `[reopt] dropping events` | 서버가 배치를 영구히 거절한 상태 | 자격증명과 `baseUrl`을 확인하세요. | | 이벤트가 큐에는 들어가지만 안 보임 | `flush.status`가 `success`인지, 응답의 `accepted`가 1 이상인지 확인 | 이벤트 스트림에서 먼저 확인하세요. 집계 화면 반영은 조금 늦습니다. | | 페이지뷰가 두 배로 잡힘 | 초기화 뒤 `pageView()`를 한 번 더 부르는지 확인 | 호출을 지우거나 `capture.pageview`를 `false`로 두세요. | | 같은 이벤트가 반복적으로 duplicate | 같은 `eventId`를 재사용하고 있지 않은지 확인 | 이벤트마다 새 UUID를 생성하세요. | | 사용자가 연결되지 않음 | 로그인 직후 `identify`와 이후 이벤트의 `profileId` 확인 | 로그인 성공 직후 `identify(profileId)`를 호출하세요. | | 리플레이가 쌓이지 않음 | 프로젝트 녹화 설정, SDK 옵션, `replay` 동의 세 가지 | 셋 중 빠진 것을 켜세요. 수집 비율에 따라 일부 세션만 녹화됩니다. | ## 다음 단계 * 이벤트 정의 정리는 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 수집된 이벤트 확인은 [이벤트 스트림](/docs/project-workspace/event-stream) * 에러·리플레이·AI 화면은 [에러 트래킹](/docs/project-workspace/error-tracking), [세션 리플레이](/docs/project-workspace/session-replay), [AI 관측](/docs/project-workspace/ai-observability) * 수집 요청의 형식과 오류 코드는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) * 비용과 보존 정책은 [비용과 보존 정책](/docs/workspace/cost-and-retention) --- # 분석 워크벤치 Source: https://data.reopt.ai/docs/project-workspace/analysis-workbench 분석 질문에 맞는 화면을 고르고, 탐색 결과를 리포트와 알림으로 이어 갑니다. 프로젝트 워크스페이스의 분석 화면은 질문 유형별로 나뉘어 있습니다. 먼저 질문을 정하고, 그 질문에 맞는 화면으로 이동하세요. ## 질문별 추천 화면 화면 이름은 사이드바에 보이는 이름입니다. ### 행동과 전환 | 질문 | 추천 화면 | 문서 | | ------------------------ | ------ | --------------------------------------------------------- | | 사용자는 어느 단계에서 이탈하는가 | 퍼널 분석 | [퍼널](/docs/project-workspace/funnel) | | 다시 돌아오는가 | 리텐션 | [리텐션](/docs/project-workspace/retention) | | 어디서 유입되었고 어떤 채널이 잘 되는가 | 획득 분석 | [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) | | 실제로 어떤 경로를 탔는가 | 경로 분석 | [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) | | 어떤 페이지가 방문·이탈·체류에서 달라졌는가 | 페이지 분석 | [페이지 분석](/docs/project-workspace/page-analytics) | ### 사용자와 방문 | 질문 | 추천 화면 | 문서 | | ------------------------ | ------- | ------------------------------------------------------- | | 이 사용자는 무엇을 했는가 | 사용자 분석 | [사용자와 세그먼트](/docs/project-workspace/users-and-segments) | | 한 방문 안에서 어떤 순서로 움직였는가 | 세션 분석 | [세션 탐색](/docs/project-workspace/sessions) | | 그 방문에서 화면이 실제로 어떻게 바뀌었는가 | 세션 리플레이 | [세션 리플레이](/docs/project-workspace/session-replay) | | 조건에 맞는 사용자 집합은 얼마나 되는가 | 세그먼트 | [사용자와 세그먼트](/docs/project-workspace/users-and-segments) | ### 비교와 분해 | 질문 | 추천 화면 | 문서 | | ---------------------- | ------ | --------------------------------------------------------------------- | | 어떤 사용자군이 다르게 행동하는가 | 코호트 비교 | [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) | | 어떤 속성에서 차이가 나는가 | 브레이크다운 | [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) | | 미리 준비되지 않은 질문을 어떻게 답할까 | 쿼리 | [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) | ### 관측과 모니터링 | 질문 | 추천 화면 | 문서 | | ------------------------------ | ----- | ----------------------------------------------------------------- | | 지금 어떤 이벤트가 들어오고 누가 활동 중인가 | 실시간 | [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) | | 평소와 다른 움직임이 있는가 | 인사이트 | [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) | | 어떤 오류가 발생하고 있고 어디까지 처리됐는가 | 에러 | [에러 트래킹](/docs/project-workspace/error-tracking) | | LLM 호출의 비용·토큰·지연·에러는 어디서 달라지는가 | AI 관측 | [AI 관측](/docs/project-workspace/ai-observability) | | 저장한 리포트와 핵심 지표를 한곳에서 보려면 | 대시보드 | [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) | ### 데이터 확인 분석 숫자가 이상할 때는 데이터 플랫폼 쪽 화면에서 원본부터 확인하세요. | 질문 | 추천 화면 | 문서 | | ----------------------------- | -------- | ------------------------------------------------- | | 이벤트가 실제로 어떤 값으로 들어왔는가 | 이벤트 스트림 | [이벤트 스트림](/docs/project-workspace/event-stream) | | 이 이벤트와 속성은 무슨 뜻이고 전환으로 잡혀 있는가 | 데이터 카탈로그 | [데이터 카탈로그](/docs/project-workspace/event-catalog) | | 시간별 롤업이 어떤 이벤트를 어떤 속성으로 집계하는가 | 메트릭 관리 | [메트릭 관리](/docs/project-workspace/metrics) | ## 화면 사이를 오가는 방법 * 사이드바, `⌘K` 빠른 이동, `G` 다음에 글자를 누르는 단축키로 이동합니다. 예: `G F` 퍼널 분석, `G R` 리텐션, `G U` 사용자 분석, `G S` 세그먼트, `G Q` 쿼리. * 경로 분석의 카드 메뉴에서 그 경로를 퍼널로 넘깁니다. * 퍼널의 이탈 사용자와 리텐션의 셀에서 대상 목록을 열고, 식별된 사용자는 프로필로 이동합니다. * 사용자 분석에서 속성 필터로 좁힌 목록을 세그먼트로 저장하고, 세그먼트에서 `비교로`를 눌러 코호트 비교로 넘어갑니다. ## 여러 화면의 공통 사항 * **조회 기간**: 퍼널 분석, 리텐션, 사용자 분석, 획득 분석, 경로 분석, 코호트 비교, 브레이크다운의 기본값은 최근 30일입니다. 오늘·어제·최근 7일·30일·90일은 바로 고를 수 있고, 조회 기간 옆에 프로젝트 시간대가 표시됩니다. * **세그먼트 필터**: 사용자 분석, 세션 분석, 퍼널 분석, 리텐션, 경로 분석에서 저장한 세그먼트로 결과를 좁힙니다. * **대시보드에 저장**: 퍼널 분석, 리텐션, 경로 분석의 조건을 리포트로 저장합니다. 퍼널과 경로 리포트는 대시보드 타일의 **분석 열기**로 같은 조건의 분석 화면을 다시 엽니다. * **사용자 수**: 퍼널, 리텐션, 세그먼트, 코호트 비교의 사용자 수는 디바이스 기준입니다. 프로필은 `identify()`가 호출된 사용자에게만 있습니다. ## 추천 해석 흐름 1. 데이터 카탈로그에서 전환과 태그를 정리합니다. 2. 대시보드에서 저장된 결과와 기본 상태를 봅니다. 3. 퍼널 분석, 리텐션, 획득 분석, 경로 분석으로 가설을 좁힙니다. 4. 사용자 분석, 세션 분석, 세그먼트로 내려가 원인을 좁힙니다. 5. 쿼리에서 예외 질문을 직접 검증합니다. 6. 의미 있는 결과는 리포트로 저장합니다. ## 세부 문서 * [퍼널](/docs/project-workspace/funnel) * [리텐션](/docs/project-workspace/retention) * [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * [세션 탐색](/docs/project-workspace/sessions) * [세션 리플레이](/docs/project-workspace/session-replay) * [페이지 분석](/docs/project-workspace/page-analytics) * [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) * [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) * [에러 트래킹](/docs/project-workspace/error-tracking) * [AI 관측](/docs/project-workspace/ai-observability) * [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) * [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) * [이벤트 스트림](/docs/project-workspace/event-stream) * [데이터 카탈로그](/docs/project-workspace/event-catalog) * [메트릭 관리](/docs/project-workspace/metrics) --- # 데이터가 처리되는 방식 Source: https://data.reopt.ai/docs/platform/architecture 수집한 이벤트가 저장되고 분석 화면에 나타나기까지의 흐름, 갱신 주기, 프로젝트 사이의 격리를 설명합니다. SDK가 보낸 이벤트가 어떤 단계를 거쳐 화면에 나타나는지 설명합니다. 방금 보낸 이벤트가 아직 보이지 않거나 화면마다 숫자가 조금 다를 때 보세요. ## 수집에서 화면까지 1. 수집: SDK가 `POST /api/track`으로 이벤트를 보냅니다. 자격증명 확인, 형식 검증, 중복 제거, 데이터 포인트 확인이 이 요청 안에서 끝납니다. 2. 원본 저장: 통과한 이벤트는 원본 그대로 먼저 저장되고, 응답은 이 시점에 돌아갑니다. 3. 분석 반영: 그 뒤에 세션과 프로필을 정리하고 분석 데이터에 기록합니다. 예외 이벤트는 에러 이슈로 묶이고, AI 이벤트는 AI 관측 기록이 됩니다. 이벤트에 담긴 속성 키는 데이터 카탈로그에 나타납니다. 4. 화면: 이벤트 스트림과 실시간 화면에 먼저 보이고, 집계가 필요한 화면은 아래 갱신 주기에 따라 반영됩니다. 분석 반영이 실패하면 자동으로 다시 시도합니다. 원본 이벤트는 그대로 남아 있으므로, 자동 재시도로 반영되지 않은 이벤트도 나중에 다시 반영할 수 있습니다. 수집 응답을 받은 이벤트가 오래 보이지 않으면 응답의 `requestId`와 함께 [문의 창구](/contact)로 알려 주세요. 이미 저장된 이벤트는 같은 `eventId`로 다시 보내도 중복으로 집계되고 두 번 저장되지 않습니다. ## 화면별 갱신 주기 | 대상 | 반영되는 시점 | | --------- | --------------------------------------------------------- | | 이벤트 스트림 | 분석 반영 직후. 방금 보낸 이벤트는 여기서 먼저 확인합니다 | | 실시간 | 연결해 둔 동안 들어오는 이벤트를 바로 보여줍니다 | | 시간별 롤업 | 약 5분마다 갱신합니다. 가장 최근 시간대는 원본 이벤트에서 읽어 합치므로 최신 숫자도 빠지지 않습니다 | | 대시보드 타일 | 결과를 5분 동안 캐시합니다 | | 동적 세그먼트 | 저장하면 바로 계산을 시도하고, 그 뒤로는 주기적으로 다시 계산합니다 | | 알림 | 5분마다 평가하고, 끝난 시간 구간만 봅니다 | | 분석 조회 API | `/api/v1/query/*`의 계산 결과를 5분 동안 캐시합니다 | 화면마다 숫자가 조금 다르게 보이는 것은 대개 이 주기 차이 때문입니다. 롤업에서 온 숫자인지 원본에서 읽은 숫자인지는 [메트릭 관리](/docs/project-workspace/metrics)에서 확인하세요. ## 집계에 들어가지 않는 것 * 봇과 의심 트래픽: 브라우저 수집에서 사람이 아닌 트래픽으로 판정된 이벤트는 세션·프로필·분석 화면에 반영하지 않고 데이터 포인트도 차감하지 않습니다. 건수와 사유는 데이터 카탈로그의 **트래픽 품질**에서 확인합니다. * 내부로 표시한 이벤트: 데이터 카탈로그에서 상태를 `내부`로 둔 이벤트는 이벤트 수 집계에서 빠집니다. * 중복 이벤트: 이미 받은 `eventId`와, 브라우저에서 5초 안에 똑같이 반복된 이벤트는 저장하지 않습니다. ## 프로젝트 사이의 격리 * 데이터는 프로젝트 단위로 나뉩니다. write key와 서버 자격증명은 프로젝트 하나에 묶여 있고, 그 프로젝트에만 쓰고 읽을 수 있습니다. * 분석 조회 API에 다른 프로젝트 id를 보내면 `403`으로 거절됩니다. * 쿼리 화면에서 직접 쓴 SQL은 지금 보고 있는 프로젝트의 `events`, `sessions`, `profiles`만 읽습니다. 다른 프로젝트의 행은 보이지 않습니다. * 콘솔 접근은 조직 멤버십을 따릅니다. 조직의 멤버만 그 조직의 프로젝트를 열 수 있습니다. 공개 데모 워크스페이스만 예외로, 로그인한 누구나 읽을 수 있습니다. ## 시간대 프로젝트 화면의 날짜와 조회 기간은 프로젝트 시간대를 기준으로 표시합니다. 사용량 월과 알림의 평가 구간처럼 UTC를 기준으로 하는 항목은 해당 문서에 따로 적혀 있습니다. ## 보존과 삭제 * 프로젝트의 보존 기간은 원본 이벤트와 분석 데이터에 함께 적용됩니다. 기간을 정해 두고 관리자가 프로젝트 설정에서 정리를 실행하면 그 이전 데이터가 지워집니다. * AI 호출 기록과 세션 리플레이 녹화는 각자의 보존 기간에 따라 자동으로 정리됩니다. * 지운 데이터는 되돌릴 수 없습니다. 자세한 내용은 [비용과 보존 정책](/docs/workspace/cost-and-retention)을 보세요. ## 다음 단계 * 수집 요청의 형식과 오류 코드는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) * 자격증명과 조회 API는 [HTTP API](/docs/platform/http-api) * 화면 전체 지도는 [서비스 맵](/docs/overview/service-map) --- # 서비스 이해 Source: https://data.reopt.ai/docs/overview 리옵트 데이터가 어떤 계층과 화면으로 구성되는지 먼저 살펴봅니다. 리옵트 데이터를 처음 본다면 이 섹션에서 서비스 전체 구조를 먼저 확인하세요. 조직, 프로젝트, 분석 화면, 모니터링과 알림이 어떻게 이어지는지 그림으로 설명합니다. ## 한눈에 보기 서비스에 SDK를 연결하면 이벤트가 프로젝트에 쌓입니다. 프로젝트의 화면에서 그 이벤트를 정리하고, 분석하고, 이상 징후에 대응합니다. 웹과 서버에 SDK를 붙여 이벤트를 보냅니다. 이벤트가 프로젝트에 쌓이면 데이터 카탈로그에서 이름과 의미를 정리합니다. 퍼널, 리텐션, 세션, 에러 같은 화면에서 행동과 문제를 확인합니다. 인사이트와 알림으로 이상 징후를 확인하고 팀에 알립니다. ## 이 섹션의 문서 서비스 개요를 먼저 읽고 서비스 맵으로 넘어가세요. 조직, 프로젝트, 분석 자산이 서로 어떻게 포함되는지 확인합니다. 어떤 화면이 어디에 있고 어떻게 이동하는지 확인합니다. --- # 서비스 맵 Source: https://data.reopt.ai/docs/overview/service-map 앱 라우트와 화면 기준으로 리옵트 데이터의 서비스 지도를 확인합니다. 앱에서 이동하는 순서대로 화면을 정리했습니다. 지금 보고 있는 화면의 이름이나 주소를 아래에서 찾으면 그 화면의 역할과 안내 문서를 확인할 수 있습니다. ## 최상위 화면 로그인하면 프로젝트 화면을 거쳐 프로젝트 하나로 들어갑니다. 제품 소개, 요금 안내, 문서, 로그인 진입점입니다. Reopt 계정으로 로그인합니다. 모든 조직의 프로젝트를 찾고 바로 이동합니다. 대시보드를 보고, 사이드바에서 다른 화면으로 이동합니다. ## 주소 읽는 법 프로젝트 화면의 주소는 조직, 프로젝트, 화면 순서로 이어집니다. ## 콘솔 화면 구성 프로젝트 안의 화면은 모두 같은 틀을 씁니다. 왼쪽 사이드바로 화면을 옮기고, 오른쪽에서 내용을 봅니다. 지금 열려 있는 프로젝트입니다. 누르면 접근할 수 있는 프로젝트를 검색해, 보던 화면 그대로 다른 프로젝트로 옮깁니다. 화면을 데이터 플랫폼, 애널리틱스, 오토메이션, 워크스페이스 운영 네 묶음으로 보여줍니다. 명령 팔레트와 키보드 단축키 안내를 엽니다. 화면에 적용할 기간을 고르고 새로 고칩니다. 날짜는 옆에 표시된 프로젝트 시간대를 기준으로 합니다. 사이드바에서 고른 화면의 내용입니다. 스크린샷은 프로젝트 홈인 대시보드입니다. ## 프로젝트 사이드바 프로젝트에 들어가면 사이드바가 화면을 네 묶음으로 보여줍니다. 카드를 누르면 그 화면의 안내 문서가 열립니다. 카드의 경로는 `/dashboard/[orgId]/[projectId]` 뒤에 붙습니다. 사용량 및 요금만 조직 단위 주소입니다. ### 데이터 플랫폼 데이터를 정의하고 수집 상태를 확인하는 화면입니다. 이벤트와 속성에 이름·의미·상태를 붙일 때 방금 보낸 이벤트가 도착했는지 확인할 때 롤업 상태를 확인하고 지표를 탐색할 때 ### 애널리틱스 수집한 데이터를 탐색하고 해석하는 화면입니다. 저장된 리포트와 핵심 지표를 한곳에서 볼 때 단계별 전환과 이탈을 볼 때 코호트별 재방문을 볼 때 사용자 목록과 개별 프로필을 볼 때 방문 단위 행동 흐름을 볼 때 동의한 방문의 화면 변화를 다시 볼 때 페이지별 방문·이탈·체류를 이전 기간과 비교할 때 라이브 이벤트와 활성 사용자를 모니터링할 때 예외가 묶인 이슈를 상태·심각도별로 추적할 때 LLM 호출의 비용·토큰·지연·에러를 볼 때 이상 징후와 신규·비활성 이벤트를 확인할 때 재사용할 사용자 집합을 정의할 때 채널과 캠페인 유입 성과를 볼 때 실제 행동 경로와 전이 구조를 볼 때 세그먼트 사이의 지표 차이를 볼 때 속성별 분포와 추이를 볼 때 준비된 화면으로 답하기 어려운 질문을 SQL로 볼 때 ### 오토메이션 조건에 맞춰 팀에 알리는 화면입니다. 조건 기반 알림과 웹훅을 설정할 때 ### 워크스페이스 운영 프로젝트 설정과 조직의 사용량을 관리하는 화면입니다. 프로젝트 설정과 운영 정책을 관리할 때 조직의 사용량과 요금을 확인할 때 ## 빠르게 이동하기 명령 팔레트가 열립니다. 화면 이름을 입력해 바로 이동합니다. 사이드바 아래쪽의 **빠른 이동**을 눌러도 열립니다. 프로젝트 안에서 `G`를 누른 뒤 곧바로 위 카드의 두 번째 키를 누릅니다. `G` `F`는 퍼널 분석입니다. 전체 키보드 단축키 안내가 열립니다. 사이드바 아래쪽의 **단축키**를 눌러도 열립니다. `G`로 시작하는 단축키는 입력 칸에 커서가 있을 때는 동작하지 않습니다. ## 보조 세부 화면 사이드바에는 없고, 목록이나 다른 화면에서 들어가는 화면입니다. | 화면 | 경로 | | ---------- | ----------------------------------------------------- | | 대시보드 라이브러리 | `/dashboards` | | 대시보드 상세 | `/dashboards/[dashboardId]` | | 리포트 생성 | `/dashboards/[dashboardId]/reports/new` | | 리포트 편집 | `/reports/[reportId]` | | 트래픽 품질 | `/events/traffic` | | 사용자 프로필 | `/users/[profileId]` | | 세션 상세 | `/sessions/[sessionId]` | | 세그먼트 만들기 | `/segments/new` | | 세그먼트 상세·편집 | `/segments/[segmentId]`, `/segments/[segmentId]/edit` | | 에러 이슈 상세 | `/errors/[issueId]` | | AI 트레이스 | `/ai/traces`, `/ai/traces/[traceId]` | | AI 대화 | `/ai/sessions`, `/ai/sessions/[sessionId]` | ## 프로젝트 밖의 진입 경로 | 경로 | 역할 | | --------------------------------------------- | ------------------------------------------------------- | | `/open/projects/[projectId]` | 프로젝트 ID만으로 그 프로젝트의 이벤트 스트림을 엽니다. | | `/open/projects/[projectId]/events/[eventId]` | 이벤트 스트림에서 해당 이벤트를 선택한 상태로 엽니다. | | `/cli-auth` | 터미널에서 시작한 [CLI](/docs/workspace/cli) 로그인을 브라우저에서 승인합니다. | 로그인하지 않은 상태로 `/open` 링크를 열면 로그인한 뒤 원래 목적지로 돌아옵니다. 접근 권한이 없는 프로젝트는 열리지 않습니다. ## 다음 단계 * 조직과 프로젝트 운영은 [워크스페이스](/docs/workspace) * 세부 분석 화면은 [프로젝트 워크스페이스](/docs/project-workspace) --- # 수집과 API Source: https://data.reopt.ai/docs/platform 이벤트 수집 API의 형식, 공개 HTTP API, 데이터가 처리되는 방식을 개발자 관점에서 안내합니다. 리옵트 데이터를 자기 서비스에 붙이는 개발자를 위한 기술 문서입니다. SDK가 대신 해 주는 일을 HTTP 수준에서 확인하거나, 수집한 데이터를 API로 읽어 갈 때 보세요. ## 먼저 볼 문서 1. [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance)에서 수집 요청의 형식, 응답, 오류 코드를 확인하세요. 2. [HTTP API](/docs/platform/http-api)에서 자격증명 종류와 공개 API 묶음, 요청 한도를 확인하세요. 3. [데이터가 처리되는 방식](/docs/platform/architecture)에서 수집한 이벤트가 화면에 나타나기까지의 흐름과 갱신 주기를 확인하세요. SDK를 처음 붙인다면 이 섹션보다 [빠른 연동](/docs/workspace/quickstart)과 [추적 설정](/docs/workspace/tracking-setup)을 먼저 보세요. ## 연동할 때 기억할 기준 * 수집에 성공해도 화면에는 바로 반영되지 않습니다. 수집 응답은 바로 돌아오고, 분석 화면 반영은 그 뒤에 이어집니다. * 이미 저장된 이벤트는 같은 `eventId`로 다시 보내도 중복으로 집계되고 두 번 저장되지 않습니다. * 데이터 포인트 한도는 수집 요청 안에서 확인합니다. 한도를 넘은 요청은 저장되지 않고 `429 quota_exceeded`로 거절됩니다. * 서버리스 환경에서는 응답이 나간 뒤에도 SDK 전송이 끝나도록 해야 합니다. 서버 SDK는 Vercel에서 요청의 `waitUntil`을 자동으로 찾아 쓰고(따로 설치할 패키지는 없습니다), 그런 수단이 없는 호스트에서는 `flush()`를 기다려야 합니다. * 브라우저에 넣는 write key와 서버에서만 쓰는 client secret은 권한이 다릅니다. secret을 브라우저 번들에 넣지 마세요. --- # 데이터 수집과 거버넌스 Source: https://data.reopt.ai/docs/platform/data-collection-and-governance 수집 API의 요청 형식, 수집 모드, 응답과 오류 코드, 중복 제거, 수집한 이벤트의 의미를 관리하는 방법을 설명합니다. SDK가 대신 해 주는 수집 요청을 HTTP 수준에서 설명합니다. SDK를 쓰지 않고 직접 보내거나, 응답과 오류를 해석해야 할 때 보세요. SDK 설치는 [추적 설정](/docs/workspace/tracking-setup)에 있습니다. ## 수집 진입점 * `POST /api/track` * `OPTIONS /api/track` SDK 수집과 직접 HTTP 수집 모두 이 주소 하나로 들어옵니다. 세션 리플레이 녹화만 별도 경로로 올라가며, SDK가 알아서 처리합니다. [세션 리플레이](/docs/project-workspace/session-replay)를 보세요. 수집 모드는 요청에 담긴 자격증명으로 정해집니다. 호출자가 따로 지정할 수 없습니다. | 헤더 | 모드 | 사용 위치 | | ---------------------------------------- | --------- | ------------------- | | `reopt-write-key` | `browser` | 브라우저. 공개되어도 되는 키 | | `reopt-client-id`, `reopt-client-secret` | `server` | 서버. 번들에 들어가면 안 되는 키 | client id와 secret 중 하나만 보내면 자격증명이 없는 것으로 처리합니다. 방문자 식별에는 두 헤더를 추가로 씁니다. * `reopt-device-id`: 방문자의 device id. browser 모드에서 없으면 서버가 새로 만듭니다. * `reopt-session-id`: 이전 응답으로 받은 `.` 값. 서버가 발급한 값일 때만 그 세션에 합류하고, 그렇지 않으면 오류 없이 무시하고 device 기준으로 처리합니다. `POST /api/track`의 응답에는 `x-request-id` 헤더가 붙습니다. 문의할 때 이 값을 함께 알려 주세요. ## 이벤트 형식 모든 이벤트에는 다음 공통 필드가 필요합니다. | 필드 | 설명 | | ----------- | ------------------------------------------------------- | | `type` | `track`, `identify`, `increment`, `decrement` 중 하나 | | `eventId` | UUID. 프로젝트 안에서 이 이벤트를 구분하는 값이고, 재전송 중복을 걸러내는 기준입니다 | | `timestamp` | 이벤트가 발생한 시각, millisecond timestamp | | `payload` | 이벤트 타입별 payload | | `deviceId` | 선택. server 모드에서만 읽으며, 이 이벤트가 속한 방문자 device를 행 단위로 지정합니다 | * `alias` 타입은 아직 지원하지 않으며, 요청에 포함되면 400으로 거절됩니다. * 이벤트 이름은 200자 이하이고, `session_start`, `session_end`, `screen_view`는 예약어라 쓸 수 없습니다. * 이벤트 하나(object)와 비어 있지 않은 이벤트 배열을 모두 받습니다. * 요청 body는 512,000바이트를 넘으면 거절됩니다. ## browser 모드와 server 모드 | 항목 | `browser` | `server` | | ----------------- | ------------------------------ | ------------------------------------------- | | 검증 실패 행 | 요청 전체를 400으로 거절 | 그 행만 `rejected`에 담고 나머지는 처리 | | 짧은 시간 중복 제거 | 적용 | 적용하지 않음. `eventId` 기준만 사용 | | 배치 안 `eventId` 중복 | 뒤의 행을 duplicate로 집계 | 뒤의 행을 `duplicate_in_batch`로 `rejected`에 담음 | | device | 요청 헤더만 사용. body의 `deviceId` 무시 | 이벤트의 `deviceId` → 요청 헤더 → `profileId` 파생 순서 | | 세션 | device의 세션을 만들고 이어 감 | 이미 있는 세션에 합류만 함. 없으면 세션 없이 기록 | | 봇 판정 | 서버가 요청 헤더로 판정 | 판정하지 않음 | | 응답의 `session` | 현재 세션이 있으면 내려줌 | 내려주지 않음 | server 모드는 잘못된 행만 거절하므로, 쌓아 둔 로그를 순서대로 다시 보내는 서버가 행 하나 때문에 멈추지 않습니다. ## 요청이 처리되는 순서 1. 자격증명을 확인하고 프로젝트의 분당 요청 한도를 확인합니다. 2. 이벤트 형식을 검증합니다. 3. browser 모드라면 사람이 보낸 트래픽인지 판정합니다. 4. 이미 받은 `eventId`인지 확인하고, browser 모드에서는 짧은 시간 중복 제거를 함께 적용합니다. 5. 새로 저장할 이벤트만큼 조직의 데이터 포인트를 확인합니다. 한도를 넘으면 요청 전체를 `429`로 거절하고 아무것도 저장하지 않습니다. 6. 원본 이벤트를 저장하고 응답을 돌려줍니다. 세션·프로필 정리와 분석 화면 반영은 응답이 나간 뒤에 이어집니다. 전체 흐름은 [데이터가 처리되는 방식](/docs/platform/architecture)을 보세요. ### 사람이 아닌 트래픽 서버가 요청 헤더만 보고 봇인지 판정합니다. 이벤트 속성은 보지 않으므로 호출자가 판정을 바꿀 수 없습니다. 봇이나 의심 트래픽으로 판정된 이벤트는 데이터 포인트를 차감하지 않고, 세션·프로필·분석 화면에 반영하지 않습니다. 건수와 사유는 데이터 카탈로그의 **트래픽 품질**에서 확인합니다. 이렇게 판정된 요청의 응답에는 `x-traffic-class` 헤더가 붙습니다. ## 중복 제거 중복 제거는 두 종류입니다. | 종류 | 기준 | 동작 | | ------------ | ------------------------------------ | ------------------------------------------ | | `eventId` 중복 | 프로젝트 + `eventId` | 이미 받은 이벤트는 새로 저장하지 않고 duplicate로 응답합니다. | | 짧은 시간 중복 제거 | 프로젝트, device, 타입, 이름, payload, 5초 구간 | 같은 방문자가 같은 5초 구간에 보낸 똑같은 이벤트를 저장 전에 제외합니다. | 짧은 시간 중복 제거는 브라우저의 중복 클릭을 걸러내는 규칙이며 server 모드에는 적용하지 않습니다. payload가 기준에 들어가므로 이름이 같아도 속성이 다른 이벤트는 모두 저장됩니다. 전송이 실패했는지 확신할 수 없다면 같은 `eventId`로 그대로 다시 보내세요. 이미 저장된 이벤트는 duplicate로 집계될 뿐 두 번 저장되지 않습니다. browser 모드에서는 한 가지를 주의하세요. 짧은 시간 중복 제거는 저장하기 전에 적용되므로, 실패한 요청을 5초 안에 똑같이 다시 보내면 중복으로 처리되어 저장되지 않을 수 있습니다. 실패한 요청은 5초가 지난 뒤에 다시 보내세요. ## 응답 읽는 법 ```json { "status": "ok", "requestId": "req_...", "mode": "server", "accepted": 1, "duplicates": 0, "rejected": [], "queued": true } ``` | 필드 | 의미 | | ------------ | ------------------------------------------------------------------------- | | `mode` | 이 요청이 처리된 수집 모드 (`browser` 또는 `server`) | | `accepted` | 이번 요청에서 새로 저장된 이벤트 수 | | `duplicates` | 중복 제거로 저장이 생략된 이벤트 수 | | `rejected` | 거절된 행 목록. 각 항목은 `eventId`, `index`, `reason`을 가집니다. browser 모드에서는 항상 빈 배열 | | `queued` | 분석 화면에 반영할 작업이 예약되었는지 여부 | | `requestId` | 이 요청을 추적하는 ID | | `session` | browser 모드 전용. 다음 요청의 `reopt-session-id`로 보낼 `id`와 `token` | * server 모드의 2xx 응답에서는 `accepted + duplicates + rejected 개수`가 보낸 이벤트 수와 같아야 합니다. * `rejected`의 `reason`은 `validation_failed`, `reserved_name`, `duplicate_in_batch` 중 하나입니다. `reserved_name`은 예약어일 때뿐 아니라 이벤트 이름이 없거나 200자를 넘을 때도 나옵니다. 다시 보내도 같은 결과가 나오므로 재시도하지 않습니다. * `accepted`가 0이어도 `duplicates`가 1 이상이면 요청은 정상 처리된 것입니다. * `queued`가 `false`이면 새로 반영할 이벤트가 없는 요청입니다. * `session`은 요청 시점에 이미 있던 세션을 가리킵니다. 세션의 첫 요청에는 없는 것이 정상입니다. 드물게 `202`와 `status: "accepted"`가 돌아올 수 있습니다. 이벤트는 저장됐고 화면 반영만 조금 늦어지는 경우이므로 성공으로 처리하세요. ### 오류 코드 오류 본문의 `code`로 분기하세요. `error`와 `message`는 문구가 바뀔 수 있습니다. | 상태 | `code` | 대응 | | --- | ----------------------------------------------------------------------- | ------------------------------------- | | 400 | `invalid_json`, `empty_batch`, `alias_unsupported`, `validation_failed` | 요청을 고칩니다. 재시도해도 같은 결과입니다. | | 400 | `payload_too_large` | 배치를 나눕니다. | | 401 | `unauthorized` | 자격증명을 확인합니다. 재시도하지 않습니다. | | 410 | `project_purging` | 삭제 중인 프로젝트입니다. 전송을 멈춥니다. | | 429 | `rate_limited` | 잠시 뒤 스스로 풀립니다. `Retry-After`만큼 기다립니다. | | 429 | `quota_exceeded` | 월 데이터 포인트 한도 소진입니다. 한도를 올려야 풀립니다. | | 500 | `internal_error` | 간격을 두고 재시도합니다. | 표에 없는 `code`가 오면 상태 코드로 판단하세요. 4xx는 요청을 고쳐야 하고, 5xx는 간격을 두고 재시도합니다. 두 `429`는 의미가 다릅니다. `rate_limited`는 프로젝트당 분당 요청 수 제한이고, `quota_exceeded`는 사람이 한도를 조정해야 하는 상태입니다. 한도와 사용량은 [비용과 보존 정책](/docs/workspace/cost-and-retention)에서 다룹니다. ## 직접 HTTP 호출 예시 SDK 없이 호출할 때는 `eventId`와 millisecond `timestamp`를 직접 넣어야 합니다. `eventId`는 이벤트마다 새 UUID로, `timestamp`는 이벤트가 발생한 시각으로 바꿔서 보내세요. ```bash curl -X POST "https://data.reopt.ai/api/track" \ -H "Content-Type: application/json" \ -H "reopt-client-id: $REOPT_CLIENT_ID" \ -H "reopt-client-secret: $REOPT_CLIENT_SECRET" \ -d '{ "type": "track", "eventId": "00000000-0000-4000-8000-000000000001", "timestamp": 1763769600000, "payload": { "name": "page_view", "properties": { "path": "/home" } } }' ``` 터미널이나 스크립트에서 보낼 때는 위처럼 서버 자격증명을 쓰세요. write key로 보낸 요청은 browser 모드로 처리되고, 브라우저가 아닌 곳에서 온 요청은 사람이 아닌 트래픽으로 분류됩니다. 이 경우 응답은 `200`이어도 이벤트 스트림과 분석 화면에는 나타나지 않습니다. ## 브라우저 SDK의 전송 방식 브라우저 SDK는 다음 기본값으로 동작합니다. * 이벤트는 큐에 들어간 뒤 1초(`batch.intervalMs`)마다 전송됩니다. * 큐 길이가 100개(`batch.size`)에 도달하면 즉시 전송을 시도합니다. * 한 요청은 직렬화 기준 400,000바이트(`batch.maxBytes`)를 넘지 않게 자릅니다. 서버 상한보다 작게 잡은 값입니다. * 페이지가 숨겨지거나 닫히면 큐를 먼저 저장한 뒤 `keepalive` 요청으로 나눠 보냅니다. * device id는 기본적으로 쿠키에 저장하고, 전송하지 못한 큐는 `localStorage`에 저장합니다. * `localStorage`가 막혀 있으면 큐는 메모리에만 남으므로 페이지를 떠날 때 못 보낸 이벤트는 사라질 수 있습니다. * 일시적인 전송 실패는 큐로 되돌려 재시도합니다. `429`가 아닌 4xx로 거절된 배치는 버리고 콘솔에 한 번 경고합니다. client는 write key당 페이지에 하나만 만들어집니다. `init()`을 다시 호출하거나 Provider가 다시 마운트되어도 같은 인스턴스를 재사용합니다. React Provider는 unmount 시 client를 닫지 않습니다. 정리가 필요하면 `close()`를 직접 호출하세요. ## 서버 SDK의 전송 방식 서버 SDK는 공개 키 대신 `clientId + clientSecret`을 사용합니다. * Next.js 요청 안에서는 `createReopt()`가 돌려주는 `getReopt()`를 씁니다. 방문자의 device 쿠키를 읽어 이벤트를 같은 방문자의 것으로 기록합니다. * 워커, 크론, 스크립트처럼 요청이 없는 Node 프로세스에서는 `@reopt-ai/data-sdk-server/node`의 client를 씁니다. * Node client는 `trackAndFlush`, `identifyAndFlush`, `incrementAndFlush`, `decrementAndFlush`를 제공합니다. 전송이 끝난 뒤에 resolve됩니다. * `flush()`는 큐가 빌 때까지 전송하고, `close()`는 flush한 뒤 타이머를 멈춥니다. 스크립트는 종료 전에 `close()`를 호출하세요. * 서버 SDK는 큐를 디스크에 저장하지 않습니다. 보내지 못한 이벤트는 프로세스와 함께 사라집니다. 서버리스 함수는 응답을 반환하는 순간 멈출 수 있어서, 큐에만 넣은 이벤트는 전송되지 않은 채 사라질 수 있습니다. Vercel에서는 SDK가 요청의 `waitUntil`을 자동으로 찾아 응답 뒤에도 전송이 끝나게 합니다. 따로 설치할 패키지는 없습니다. 다른 호스트에서는 `waitUntil` 옵션으로 호스트의 함수를 넘기거나, `await flush()` 또는 `*AndFlush` 계열로 요청 안에서 전송을 끝내세요. ## 수집한 이벤트의 의미 관리하기 수집이 끝나면 [데이터 카탈로그](/docs/project-workspace/event-catalog)에서 이벤트의 의미를 관리합니다. * 표시 이름, 설명, 아이콘, 색상 * 전환 여부, 태그, 카테고리 * 상태 (활성, 내부, 보관 등) 수집된 이벤트와 속성 키는 카탈로그에 자동으로 나타납니다. 표시 이름과 설명 같은 정의는 자동으로 채워지지 않으므로 직접 입력합니다. 속성에 대해 자동으로 채우는 것은 이름, 처음·마지막으로 본 시각, 비어 있는 타입뿐이고, 사람이 적은 설명과 숨김 여부는 건드리지 않습니다. 카탈로그는 콘솔뿐 아니라 저장소의 파일로도 관리할 수 있습니다. 파일에서 생략한 필드는 그대로 두는 것이 아니라 기본값으로 처리되니 주의하세요. 자세한 내용은 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)와 [CLI 사용하기](/docs/workspace/cli)를 보세요. ## 보존 기간 프로젝트의 보존 기간은 하나이고, 원본 이벤트와 분석 데이터에 함께 적용됩니다. 기간을 저장하는 것만으로는 아무것도 지워지지 않고, 관리자가 프로젝트 설정에서 정리를 실행할 때 지워집니다. AI 호출 기록과 세션 리플레이 녹화는 각자의 보존 기간에 따라 자동으로 정리됩니다. 화면에서 설정하는 방법은 [비용과 보존 정책](/docs/workspace/cost-and-retention)을 보세요. ## 주의할 점 * 수집된 이벤트가 분석 화면에 바로 보이지는 않습니다. 방금 보낸 이벤트는 [이벤트 스트림](/docs/project-workspace/event-stream)에서 먼저 확인하세요. * 서버리스에서는 `waitUntil` 또는 `*AndFlush` 계열로 요청 생명주기 안에서 전송을 끝내세요. * 직접 수집하는 서버는 2xx마다 `accepted + duplicates + rejected 개수`가 보낸 수와 같은지 확인하세요. * 문제를 추적할 때는 `requestId`와 `eventId`를 함께 기록해 두세요. ## 다음 단계 * SDK를 처음 붙이려면 [추적 설정](/docs/workspace/tracking-setup) * 이벤트 의미를 정리하려면 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 자격증명과 조회 API는 [HTTP API](/docs/platform/http-api) --- # HTTP API Source: https://data.reopt.ai/docs/platform/http-api 자격증명 종류, 공개 HTTP API 묶음, 요청 한도, OpenAPI 문서를 정리합니다. 리옵트 데이터의 기능은 SDK와 CLI 없이 HTTP로도 쓸 수 있습니다. 요청과 응답의 정확한 스키마는 [`/openapi.json`](/openapi.json)이 기준입니다. 이 문서는 어떤 자격증명으로 무엇을 할 수 있는지만 정리합니다. ## 자격증명 엔드포인트마다 받는 자격증명이 다릅니다. 자격증명 하나로 다른 일까지 할 수 없도록 나눠 두었습니다. | 자격증명 | 전달 방식 | 할 수 있는 일 | | ------------------------- | ------------------------------------------- | ----------------------------------- | | write key | `reopt-write-key` 헤더 | 브라우저 수집, 세션 리플레이 수집 | | client id + client secret | `reopt-client-id`, `reopt-client-secret` 헤더 | 서버 수집, 분석 조회 | | 조직 키 | `reopt-org-key` 헤더 | 조직 안의 프로젝트, 카탈로그, 에러 규칙, 알림, 소스맵 관리 | * write key는 브라우저 번들에 들어가는 공개 값입니다. 분석 조회에는 쓸 수 없습니다. * client secret과 조직 키는 서버와 CI에서만 쓰세요. 값은 발급될 때 한 번만 보이고, 잃어버리면 다시 발급(회전)해야 합니다. * 브라우저용 write key는 프로젝트 홈의 **SDK Integration**에서 확인합니다. * 서버용 client ID와 secret은 콘솔에서 프로젝트를 만든 직후의 창에 한 번 표시됩니다. 자세한 내용은 [조직과 프로젝트](/docs/workspace/organizations-and-projects)를 보세요. * 조직 키는 콘솔에서 발급하지 않습니다. 필요하면 [문의 창구](/contact)로 요청하세요. ## 수집 | 엔드포인트 | 자격증명 | 내용 | | ----------------- | ----------------------------- | ------ | | `POST /api/track` | write key 또는 client id+secret | 이벤트 수집 | 요청 형식, 응답, 오류 코드는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance)에 있습니다. ## 분석 조회 서버 자격증명(client id + secret)으로 분석 결과를 읽습니다. 자격증명이 속한 프로젝트만 읽을 수 있습니다. `/api/v1/query/*`와 `/api/v1/segments/preview`는 `POST`이고, 세그먼트 목록과 단건 조회는 `GET`입니다. | 엔드포인트 | 내용 | | ----------------------------------------------------------------------- | -------------------- | | `/api/v1/query/events/catalog` | 이벤트 목록과 수신 현황 | | `/api/v1/query/events/timeseries` | 이벤트 수 시계열 | | `/api/v1/query/traffic/overview`, `/api/v1/query/traffic/sources` | 방문·세션 요약과 유입 소스 | | `/api/v1/query/funnel` | 퍼널 전환 | | `/api/v1/query/retention` | 리텐션 | | `/api/v1/segments`, `/api/v1/segments/{id}`, `/api/v1/segments/preview` | 저장된 세그먼트 읽기와 인원 미리보기 | * 다른 프로젝트 id를 보내면 `403 project_scope_mismatch`가 돌아옵니다. * 삭제 중인 프로젝트는 `410 project_purging`으로 응답합니다. * `/api/v1/query/*`의 계산 결과는 5분 동안 캐시됩니다. 같은 요청을 반복해도 그동안은 같은 답이 돌아옵니다. 요청에 `Cache-Control: no-cache`를 보내면 캐시를 건너뜁니다. 세그먼트 API는 캐시하지 않습니다. * 세그먼트 API는 읽기 전용입니다. 세그먼트는 콘솔에서 만듭니다. CLI의 `reopt-data query`와 `reopt-data segment`가 같은 API를 씁니다. [CLI 사용하기](/docs/workspace/cli)를 보세요. ## 프로젝트 설정을 코드로 관리하기 조직 키로 프로젝트와 그 설정을 다룹니다. | 경로 | 내용 | | --------------------------------------------------------------- | ---------------------- | | `/api/v1/projects`, `/api/v1/projects/{id}` | 프로젝트 생성·목록·조회·설정 변경·삭제 | | `/api/v1/projects/{id}/clients/{clientId}/rotate` | 서버 client secret 회전 | | `/api/v1/projects/{id}/events`, `…/properties` | 이벤트·속성 카탈로그 읽기와 배치 쓰기 | | `/api/v1/projects/{id}/error-rules`, `…/alerts`, `…/definition` | 에러 규칙, 알림, 프로젝트 정의 | | `/api/v1/projects/{id}/symbol-sets` | 소스맵 조회, 업로드, 삭제 | * 카탈로그 배치 쓰기는 `expectedUpdatedAt`으로 동시 수정을 감지합니다. 한 건이라도 충돌하면 배치 전체가 `409`로 거절되고 아무것도 적용되지 않습니다. * 소스맵 엔드포인트는 조직 키를 `reopt-org-key`가 아니라 `Authorization: Bearer`로 받습니다. 업로드는 `action=start`로 올릴 대상을 정하고 `action=finish`로 마무리하는 두 단계입니다. 한 요청에 100개까지, 맵 하나는 50MB까지 받습니다. 이 API를 직접 부르기보다 CLI로 쓰는 편이 쉽습니다. [CLI 사용하기](/docs/workspace/cli)와 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)를 보세요. ## 요청 한도 모두 1분 단위로 셉니다. 한도를 넘으면 `429`와 `Retry-After` 헤더가 돌아옵니다. | 대상 | 기준 | 분당 한도 | | ----------------- | ------ | ----- | | `POST /api/track` | 프로젝트 | 1,000 | | 분석 조회, 세그먼트 | client | 60 | | 프로젝트·카탈로그·규칙 관리 | 조직 키 | 30 | | 소스맵 업로드 | 조직 키 | 300 | 분당 요청 한도는 월 데이터 포인트 한도와 별개입니다. 데이터 포인트는 [비용과 보존 정책](/docs/workspace/cost-and-retention)을 보세요. ## 공통 규칙 * 수집, 분석 조회, 프로젝트 관리 API의 오류 본문에는 `code`가 있습니다. `code`로 분기하세요. 사람이 읽는 문구는 바뀔 수 있습니다. * 소스맵 엔드포인트의 오류와, 일치하는 경로가 없을 때의 `404`에는 `code` 없이 `error` 문구만 있습니다. 이때는 상태 코드로 판단하세요. * `/api/*` 아래에서 일치하는 경로가 없으면 HTML이 아니라 JSON `404`를 돌려줍니다. * 수집 응답에는 `x-request-id` 헤더가 붙습니다. 문의할 때 함께 알려 주세요. ## 에이전트와 도구가 읽는 형식 * [`/openapi.json`](/openapi.json): 공개 HTTP API의 OpenAPI 3.1 문서 * [`/llms.txt`](/llms.txt), [`/llms-full.txt`](/llms-full.txt): 문서 목록과 전체 본문 * 문서 페이지는 `Accept: text/markdown` 요청이나 `.md` 접미사에 Markdown으로 응답합니다. ## 다음 단계 * 수집 요청의 형식과 오류는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) * 터미널과 CI에서 쓰려면 [CLI 사용하기](/docs/workspace/cli) * 수집한 데이터가 화면에 보이기까지의 흐름은 [데이터가 처리되는 방식](/docs/platform/architecture) --- # 프로젝트 워크스페이스 Source: https://data.reopt.ai/docs/project-workspace 프로젝트 홈에서 상태를 확인하고, 질문에 맞는 분석 화면을 고르는 방법을 안내합니다. 프로젝트 워크스페이스에서는 이벤트를 분석하고, 결과를 저장하고, 운영 대응으로 이어갑니다. 먼저 프로젝트 홈에서 현재 상태를 확인하고, 질문에 맞는 분석 화면으로 이동하세요. ## 화면을 고르는 기준 | 질문 | 먼저 볼 화면 | | ------------------------------- | --------------------------------------------------------------------- | | 지금 프로젝트 상태를 한눈에 보고 싶습니다. | [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) | | 어떤 이벤트가 전환인지 정리하고 싶습니다. | [데이터 카탈로그](/docs/project-workspace/event-catalog) | | 방금 보낸 이벤트가 도착했는지 보고 싶습니다. | [이벤트 스트림](/docs/project-workspace/event-stream) | | 롤업 상태를 확인하고 지표를 자유롭게 탐색하고 싶습니다. | [메트릭 관리](/docs/project-workspace/metrics) | | 어느 단계에서 이탈하는지 알고 싶습니다. | [퍼널](/docs/project-workspace/funnel) | | 사용자가 다시 돌아오는지 알고 싶습니다. | [리텐션](/docs/project-workspace/retention) | | 특정 사용자군을 따로 보고 싶습니다. | [사용자와 세그먼트](/docs/project-workspace/users-and-segments) | | 방문 한 번의 흐름을 자세히 보고 싶습니다. | [세션 탐색](/docs/project-workspace/sessions) | | 그 방문에서 화면이 어떻게 보였는지 확인하고 싶습니다. | [세션 리플레이](/docs/project-workspace/session-replay) | | 어떤 페이지가 방문을 받고 이탈시키는지 알고 싶습니다. | [페이지 분석](/docs/project-workspace/page-analytics) | | 유입 채널과 행동 경로를 연결하고 싶습니다. | [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) | | 준비된 화면으로 답하기 어려운 질문이 있습니다. | [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) | | 어떤 오류가 얼마나 자주 나는지 알고 싶습니다. | [에러 트래킹](/docs/project-workspace/error-tracking) | | LLM 호출의 비용과 지연을 추적하고 싶습니다. | [AI 관측](/docs/project-workspace/ai-observability) | | 이상 징후를 빨리 알고 싶습니다. | [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) | ## 추천 흐름 1. [데이터 카탈로그](/docs/project-workspace/event-catalog)에서 이벤트 정의를 정리하세요. 2. [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting)에서 반복해서 볼 지표를 저장하세요. 3. [분석 워크벤치](/docs/project-workspace/analysis-workbench)에서 질문별 화면을 고르세요. 4. [세션 탐색](/docs/project-workspace/sessions)과 [세션 리플레이](/docs/project-workspace/session-replay)에서 유입과 행동 순서를 검증하세요. 5. [에러 트래킹](/docs/project-workspace/error-tracking)에서 사용자가 겪은 오류를 이슈로 추적하세요. 6. [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts)으로 이상 징후를 확인하고 알림을 받으세요. 화면 사이를 빠르게 오가는 단축키는 [서비스 맵](/docs/overview/service-map)에 정리돼 있습니다. --- # 획득과 경로 분석 Source: https://data.reopt.ai/docs/project-workspace/acquisition-and-path 유입 채널 성과와 실제 사용자 경로를 함께 보며 유입 품질을 해석합니다. 획득 분석은 사용자가 어디서 왔는지, 경로 분석은 들어온 뒤 무엇을 했는지 보여줍니다. 두 화면을 함께 보면 채널 성과와 제품 안 행동을 연결할 수 있습니다. ## 진입 경로 * 획득 분석: `/dashboard/[orgId]/[projectId]/acquisition` * 경로 분석: `/dashboard/[orgId]/[projectId]/path` ## 획득 분석에서 답할 수 있는 질문 * 어떤 채널과 캠페인이 전환에 기여하나요? * referrer와 UTM 기준으로 성과가 어떻게 다른가요? * 첫 유입과 마지막 유입 관점에서 해석이 어떻게 달라지나요? * 지난 기간과 비교해 어느 채널과 캠페인이 늘거나 줄었나요? 획득 분석은 `단일 분석`과 `비교` 두 모드로 봅니다. 기본 조회 기간은 최근 30일입니다. ### 단일 분석 한 기간의 채널 성과를 봅니다. 위쪽에는 채널별 사용자 수와 전체 대비 비율이 요약됩니다. * `Overview`: 일별 사용자 추이 * `Channels`: 채널별 Users, Sessions, Pageviews, Avg Duration, Bounce Rate * `Campaigns`: Source · Medium · Campaign 조합별 성과, 상위 20개 * `Referrers`: 리퍼러별 성과, 상위 20개. 데이터가 있을 때만 표시됩니다. * `Attribution`: 채널별 전환 수와 비중. 전환 이벤트를 고르고 데이터가 있을 때만 표시됩니다. 채널은 Direct, Organic Search, Organic Social, Paid Search, Paid Social, Paid Display, Paid Other, Email, Referral, Affiliate로 나뉩니다. 필터는 두 가지입니다. * **전환 이벤트**: 고르면 Channels와 Campaigns에 Conversions와 Conv. Rate 열이 생깁니다. 데이터 카탈로그에서 전환으로 표시한 이벤트가 있으면 그 이벤트가 기본으로 선택됩니다. 후보는 조회 기간에 많이 발생한 이벤트 20개입니다. * **기여 방식**: `첫 유입` 또는 `마지막 유입`(기본값). Attribution 탭에 적용됩니다. 숫자를 읽는 법: * Users는 그 채널로 들어온 세션의 디바이스 수입니다. 채널이 비어 있는 세션은 집계에서 빠집니다. * Conversions는 전환 이벤트를 남긴 디바이스 수이고, Conv. Rate는 Conversions를 Users로 나눈 값입니다. * `마지막 유입`은 전환 이벤트가 발생했을 때의 채널로 전환을 셉니다. `첫 유입`은 조회 기간 안에서 그 디바이스의 첫 세션 채널로 셉니다. 조회 기간 이전의 유입은 보지 않습니다. ### 비교 두 구간(비교 A와 비교 B)을 나란히 놓고 무엇이 달라졌는지 봅니다. 처음 열면 비교 B는 최근 30일, 비교 A는 그 직전 기간입니다. * 기간 프리셋: `직전 기간`, `전주`, `전년 동기`, `직접 설정`. 프리셋을 쓰면 비교 B를 바꿀 때 비교 A가 같은 길이로 따라 바뀝니다. `A/B 교체`로 두 구간을 맞바꿉니다. * 구간별 유입 조건: 채널, UTM 소스, UTM 미디엄, 캠페인, 리퍼러 유형. A와 B에 서로 다른 조건을 걸 수 있습니다. * 지표: 사용자, 세션, 페이지뷰. 전환 이벤트를 고르면 전환과 전환율도 고를 수 있습니다. 결과 탭: * `추이`: 두 기간을 첫날부터 같은 일차에 맞춰 겹쳐 봅니다. `절대값`, `첫날=100`, `변화율`로 표시 방식을 바꾸고, 변화가 큰 구간을 따로 짚어줍니다. 전환 이벤트가 있으면 전환율 차이가 통계적으로 유의한지도 알려줍니다. * `채널 변화`, `캠페인 변화`: 변화가 큰 항목을 `절대 변화`, `상대 변화`, `점유율 이동` 기준으로 정렬합니다. `최소 변화`(5·10·20%)로 작은 변화를 숨깁니다. * `어트리뷰션`: 채널별 전환 기여도 비중과 그 변화. 전환 이벤트를 골랐을 때만 표시됩니다. * `전체 지표`: 사용자, 세션, 페이지뷰, 평균 세션 시간, 이탈률(전환 이벤트가 있으면 전환 사용자와 전환율)의 A, B, 차이, 증감률 채널 막대나 변화 목록, 추이의 날짜를 선택하면 비교 B 기간의 [세션 분석](/docs/project-workspace/sessions) 목록으로 이동합니다. 비교 조건은 주소에 담기므로 `링크 복사`로 그대로 공유할 수 있습니다. `비교 저장`은 이름을 붙여 다시 불러오는 기능이고, 저장한 비교는 이 브라우저에만 남습니다. 결과는 CSV나 JSON으로 내보냅니다. ## 경로 분석에서 답할 수 있는 질문 * 사용자는 실제로 어떤 순서로 이벤트와 페이지를 거치나요? * 어떤 경로가 가장 흔한가요? * 어디에서 이탈하나요? * 전환으로 이어지는 대표 경로는 무엇인가요? ### 여정 흐름 읽는 법 흐름 탭은 세션의 이벤트 순서를 단계별 컬럼으로 펼칩니다. 각 컬럼에는 그 단계에서 가장 많이 등장한 항목이 카드로 놓입니다. 상위에 들지 못한 항목은 **기타**로 묶이고, 그 단계에서 여정이 끝난 세션은 **여기서 종료**로 따로 셉니다. 마지막 컬럼의 **다음 단계로 계속**은 여정이 끝난 세션이 아니라 표시한 단계 수에서 잘린 세션입니다. 카드 사이의 리본은 인접한 단계 사이의 전환이고, 굵기가 세션 수입니다. * 카드에 마우스를 올리면 그 카드를 지나는 대표 경로만 밝아집니다. 툴팁에는 계속 진행·이탈 수, 이전 단계에서 걸린 평균 시간, 전체 여정 대비 비율이 나옵니다. 카드를 클릭하면 강조가 고정되고, 다시 클릭하거나 Esc를 누르면 풀립니다. * **그리드**는 카드 높이가 같아 항목 이름을 읽기 좋고, **생키**는 노드 높이가 세션 수에 비례해 어디로 흐름이 몰리는지 한눈에 보입니다. * 카드 메뉴에서 그 항목을 **시작점**이나 **종료점**으로 잡거나 **제외**합니다. **이 경로를 퍼널로 보기**를 고르면 그 카드를 지나는 대표 경로가 퍼널로 계산되어 아래에 나타납니다. **퍼널 페이지에서 열기**로 넘기면 단계 조건을 더 다듬을 수 있습니다. 퍼널 미리보기는 전환 기간 1일로 계산합니다. URL 그룹핑을 켠 동안에는 쓸 수 없습니다. 자리표시자가 들어간 경로(`/item/:id`)는 실제 페이지뷰 경로와 일치하지 않기 때문입니다. 핵심 입력(경로 설정 패널): * 단계 기준: 페이지뷰(기본값) / 이벤트 / 모두 * 시작점 · 종료점: 시작점만 잡으면 그 항목부터 앞으로, 종료점만 잡으면 그 항목까지 거꾸로, 둘 다 잡으면 그 사이 구간만 봅니다. 앵커가 없는 세션은 빠집니다. * 단계 수(2~~20, 기본 5) · 단계당 항목 수(1~~10, 기본 4) * 제외 항목: 빼고 난 뒤의 순서로 경로를 다시 만듭니다 (A → X → B가 A → B로). 100개까지 넣을 수 있습니다. * 반복 병합(기본 켜짐): A → A → B를 A → B로 읽습니다 * URL 그룹핑(기본 꺼짐): URL 속 ID를 자리표시자로 바꿉니다 * 리본 최소 세션 수: 가는 리본을 숨겨 그림을 정리합니다 * 세그먼트 위쪽 요약에는 여정에 포함된 세션, 페이지뷰 세션, 총 페이지뷰, 세션당 페이지뷰(평균과 중앙값)가 표시됩니다. 결과 탭: * `흐름`: 여정 흐름 (그리드 / 생키) * `Top 경로`: 가장 흔한 페이지 경로 상위 30개. 세션 수와 비율 * `이탈 지점`: 페이지를 둘 이상 본 세션이 마지막으로 본 페이지, 상위 10개 * `경로 패턴`: 루프 감지(같은 페이지를 연달아 다시 본 횟수)와 URL 패턴 그룹핑 * `진입/이탈`: 진입 페이지 TOP, 이탈 페이지 TOP(각 10개). 이 탭에서 **전환 이벤트**를 고르면 전환 경로 분석(전환·미전환 경로)이 함께 나옵니다. * `체류 시간`: 페이지별 평균·중앙값 체류와 페이지뷰 경로 패턴과 체류 시간 탭은 데이터가 있을 때만 표시됩니다. ### 저장하고 다시 열기 "대시보드에 저장"은 경로 설정(단계 기준, 시작·종료점, 단계 수, 단계당 항목 수, 제외 항목, 반복 병합, URL 그룹핑, 세그먼트)과 상대 기간을 리포트로 저장합니다. 대시보드 타일의 "분석 열기"를 누르면 그 설정 그대로 이 화면이 열립니다. 리본 최소 세션 수, 차트 종류, 전환 이벤트는 저장되지 않습니다. ## 함께 보는 이유 획득 분석은 유입 경로를, 경로 분석은 유입 뒤의 행동을 설명합니다. 두 화면을 같이 보면 유입 품질과 실제 사용 흐름을 연결할 수 있습니다. ## 주의할 점 * UTM과 referrer가 안정적으로 수집되어야 획득 분석의 품질이 좋아집니다. * 경로 분석은 이벤트 이름과 페이지 경로가 정리되어 있을수록 읽기 쉽습니다. * 경로 설정이 모든 탭에 적용되지는 않습니다. 단계 기준·제외 항목·반복 병합은 `흐름` 탭에만 적용됩니다. `Top 경로`는 시작점·종료점·URL 그룹핑·세그먼트를 따르고, `이탈 지점`과 `진입/이탈`의 페이지 표는 조회 기간만 따릅니다. * 획득 분석의 `비교 저장`은 브라우저에 남으므로 다른 사람이나 다른 기기에서는 보이지 않습니다. 공유하려면 `링크 복사`를 쓰세요. ## 다음 단계 * 채널별 전환 차이를 더 보려면 [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) * 개별 방문 흐름을 자세히 보려면 [세션 탐색](/docs/project-workspace/sessions) * 전환 단계 자체를 보려면 [퍼널](/docs/project-workspace/funnel) * 전환 이벤트를 지정하려면 [데이터 카탈로그](/docs/project-workspace/event-catalog) --- # AI 관측 Source: https://data.reopt.ai/docs/project-workspace/ai-observability LLM 호출의 비용·토큰·지연·에러를 추적하고, 문제 호출의 공통점을 비교해 트레이스와 대화로 내려갑니다. AI 관측에서는 비용·토큰·지연을 확인하고, 문제 호출이 정상 호출과 어떻게 다른지 비교합니다. 모델·프로바이더·기능·에이전트의 구성 차이를 단서로 삼아 실제 트레이스를 조사하세요. ## 진입 경로 * 개요: `/dashboard/[orgId]/[projectId]/ai` (사이드바의 **AI 관측**) * 트레이스: `/dashboard/[orgId]/[projectId]/ai/traces`, 상세는 `/ai/traces/[traceId]` * 대화: `/dashboard/[orgId]/[projectId]/ai/sessions`, 상세는 `/ai/sessions/[sessionId]` 화면 위의 **개요**, **트레이스**, **대화**로 오가도 기간과 필터가 그대로 유지됩니다. ## 시작하기 전에 * 서버에 `@reopt-ai/data-sdk-server/ai` 텔레메트리를 등록해야 모델 호출이 기록됩니다. 기록이 없는 프로젝트에서는 화면에 설치 코드가 표시됩니다. * 프로젝트를 볼 수 있는 조직 멤버라면 조회할 수 있습니다. 조회는 데이터 포인트를 쓰지 않습니다. * 기본 기간은 최근 7일입니다. 한 번에 조회할 수 있는 기간은 최대 400일입니다. * 프롬프트와 응답 본문은 기본으로 저장하지 않습니다. 비용·토큰·지연·에러 같은 호출 메타데이터만으로 이 문서의 화면을 모두 쓸 수 있습니다. ## 개요 상단 필터는 세 화면이 함께 씁니다. * **모델**은 여러 개를 고를 수 있습니다. 선택지는 고른 기간에 실제로 호출된 값입니다. * **결과**는 `전체`, `에러만`, `정상만` 중 하나입니다. * **추가 조건**을 펼치면 프로바이더, 기능, 에이전트, 종류(생성, 임베딩, 도구·구간, 트레이스)로 좁힙니다. * 분류 표의 값을 누르면 그 값이 필터로 걸립니다. 타일은 선택한 기간의 합계와, 길이가 같은 직전 기간 대비 증감을 보여줍니다. | 타일 | 내용 | | -------- | -------------------------------------------------- | | 비용 | 모델 호출 비용 합계(USD). 비용을 모르는 호출은 건수를 따로 표시 | | 모델 호출 | 생성·임베딩 호출 수 | | 에러율 | 실패한 모델 호출 비율과 건수 | | p95 지연 | 호출 지연의 95분위. p50을 함께 표시 | | p95 첫 토큰 | 첫 토큰까지 걸린 시간. 스트리밍 호출만 측정 | | 토큰 | 입력·출력 토큰 | | 트레이스 | 트레이스 수 | | 사용자 | `profile_id`, 없으면 `device_id` 기준. 둘 다 없는 호출은 세지 않음 | **추이** 차트는 `요청·에러`, `비용`, `토큰`, `지연`, `첫 토큰` 중 하나를 그립니다. 타일을 눌러도 차트 지표가 바뀝니다. * 기간이 2일 이하면 시간 단위, 그보다 길면 일 단위로 묶습니다. * 지연은 p50 선과 p50\~p95 범위를 함께 그립니다. * 차트를 드래그하면 그 구간으로 기간이 좁혀지고 주소에도 반영됩니다. * **이전 기간 비교**를 켜면 직전 기간을 겹쳐 그립니다. * **릴리스 첫 관측·알림 감지 표시**를 켜면 릴리스가 처음 관측된 시각과 알림이 발송된 시각을 표시합니다. 배포 시각이나 임계값을 넘은 시각이 아니라 기록된 시각입니다. **분류**는 모델, 프로바이더, 기능, 에이전트, 사용자, 종료 사유, 도구, 에러 유형 중 하나를 기준으로 호출, 에러율, 비용, 토큰, 지연, 트레이스 수를 나눠 보여줍니다. 최대 50개 값이 표시됩니다. `도구` 기준은 모델 호출이 아니라 도구 실행을 세므로 토큰과 비용이 비어 있습니다. 값을 누르면 그 도구를 실행한 트레이스로 좁힙니다. **이 조건의 트레이스 보기**를 누르면 같은 기간과 필터로 트레이스 목록이 열립니다. ## 트레이스 트레이스는 작업 하나를 이루는 모델 호출과 도구 실행의 묶음입니다. 목록은 트레이스 하나를 한 행으로 보여줍니다. * 열: 상태, 트레이스 이름과 ID, 시작, 소요, 비용, 토큰, 모델 호출 / 스팬, 모델, 기능, 에이전트, 대화, 사용자 * 정렬: `최근 순`, `비용 높은 순`, `오래 걸린 순` * 50건씩 불러오고 **더 보기**로 이어서 봅니다. * 비용 옆 `+?`는 비용을 모르는 호출이 섞여 실제보다 적게 표시됐음을 나타냅니다. * 대화 ID를 누르면 그 대화가 열립니다. 트레이스 이름을 누르면 상세가 열립니다. * 위쪽 타일은 상태, 소요, 비용, 토큰입니다. * 워터폴은 스팬을 같은 시간축에 그립니다. 생성, 임베딩, 도구 실행을 색으로 구분하고 실패한 스팬을 표시합니다. 아무 스팬도 실행되지 않은 긴 구간은 접어서 보여줍니다. * 한 트레이스에서 최대 2,000개 스팬을 불러옵니다. * 스팬을 고르면 모델, 프로바이더, 토큰, 비용과 비용 출처, 지연, 첫 토큰, 종료 사유, 에러 내용이 보입니다. * 본문을 저장한 호출이면 **입력**과 **출력**이 함께 보입니다. 호출에 붙인 사용자 정의 속성이 있으면 **속성**도 표시됩니다. * **재현**은 선택한 모델 호출을 Vercel AI SDK 코드로 보여줍니다. 실행은 각자의 프로바이더 키로 해야 합니다. * **피드백**은 이 트레이스에 남겨진 평가의 건수, 평균 점수, 긍정·부정 수와 개별 평가를 보여줍니다. 트레이스가 끝난 뒤 30일 안에 남겨진 평가까지 읽습니다. * **이전**(`P`), **다음**(`N`)은 들어온 목록의 순서대로 옆 트레이스를 엽니다. **목록으로**는 같은 필터의 목록으로 돌아갑니다. ## 대화 대화는 같은 `$ai_session_id`를 가진 트레이스의 묶음입니다. SDK의 `context`에서 `aiSessionId`를 넘겨야 대화로 묶입니다. 넘기지 않으면 트레이스는 보이지만 대화 목록은 비어 있습니다. * 목록의 열: 대화, 시작, 마지막 활동, 기간, 트레이스, 모델 호출, 에러, 토큰, 비용, 사용자 * 필터는 조건에 맞는 호출이 있는 대화를 고르는 데만 씁니다. 각 행의 숫자는 대화 전체의 합계입니다. 대화를 열면 주고받은 순서대로 표시됩니다. * 한 턴은 트레이스 하나입니다. 턴마다 모델 호출 수, 토큰, 비용, 소요 시간이 표시되고, **트레이스 열기**를 누르면 상세로 이동합니다. * 모델 호출은 보낸 내용과 받은 내용 두 메시지로 나뉩니다. 메시지마다 모델, 토큰, 비용, 지연이 표시됩니다. * 본문을 저장한 호출은 **보낸 내용 보기**, **받은 내용 보기**로 엽니다. 본문은 열 때 한 건씩 불러옵니다. * 대화는 목록에서 고른 기간과 관계없이 전체를 보여줍니다. 긴 대화는 **이어지는 턴 더 보기**로 이어서 불러옵니다. ## 문제 호출의 공통점 찾기 개요의 **문제 호출은 무엇이 다를까요?** 영역에서 두 집단을 비교합니다. * 비교 기간은 최대 31일입니다. 선택한 기간의 생성·임베딩 호출을 집계하며, 도구 스팬과 트레이스 요약 행은 호출 수에 더하지 않습니다. * 프롬프트·응답 본문 수집을 켤 필요는 없습니다. 비교는 호출 메타데이터만 사용합니다. ### 오류 호출의 공통점 찾기 1. 상단에서 기간을 고르고, 필요하면 기능이나 모델 범위를 좁히세요. 2. 추이의 **오류와 정상 비교**를 누르거나, 차트 아래 비교 영역에서 같은 항목을 선택하세요. 3. 상단 결과 필터가 `에러만` 또는 `정상만`이거나 호출 지연 필터가 걸려 있다면 **두 집단 모두 포함**을 누르세요. 나머지 조건은 유지됩니다. 4. 차이가 큰 속성부터 확인하세요. 각 값의 오류·정상 집단 비중과 실제 호출 건수가 함께 표시됩니다. 5. 해당 값의 **트레이스 보기**를 누르면 그 조건을 만족하는 모델 호출이 있는 트레이스로 이동합니다. 트레이스 목록은 선택된 트레이스 전체를 보여줍니다. 비교한 모델 호출 건수와 트레이스 행 수는 다를 수 있고, 한 트레이스에 다른 모델이나 도구 호출도 함께 있을 수 있습니다. ### 느린 호출의 공통점 찾기 **느린 호출 비교**를 선택하고 기준 시간을 밀리초로 정하세요. 기본값은 3,000ms입니다. 기준 이상인 호출과 미만인 호출을 비교하며, 지연을 측정하지 않은 호출은 양쪽에서 제외하고 그 건수를 따로 표시합니다. 예를 들어 3,000ms를 선택하면 정확히 3초인 호출도 느린 집단에 포함됩니다. 목록으로 이동한 뒤 **호출 지연** 필터를 지우면 다시 전체 범위가 보입니다. ### 결과 읽는 법 **차이(%p)** 수치는 두 집단에서 같은 값이 차지하는 비중의 차이입니다. 오류 호출의 60%, 정상 호출의 20%가 특정 모델을 사용했다면 차이는 +40%p입니다. 이 수치는 그 모델의 오류율이 아니며, 그 모델이 오류의 원인임을 뜻하지도 않습니다. * 전체 호출을 집계합니다. 화면에는 속성마다 차이가 큰 값 최대 8개를 표시하지만 비율의 분모에는 나머지 값도 포함됩니다. * **미기록**도 별도 값으로 비교합니다. 기능 이름이 빠진 호출이 있어도 그 호출을 분모에서 빼지 않습니다. * 한 집단이 비어 있으면 차이를 계산하지 않습니다. 필터·기간·지연 기준을 조정하세요. * 한 집단이 20건 미만이면 안내가 표시됩니다. 몇 건만 추가되어도 비율이 크게 바뀔 수 있습니다. 20건 이상이라고 해서 통계적 유의성이 보장되지는 않습니다. * 네 속성의 차이가 모두 0.1%p 미만이면 뚜렷한 구성 차이가 없다는 안내가 표시됩니다. * 다른 작업을 한 호출끼리 비교하면 작업 구성 차이가 먼저 보일 수 있습니다. 같은 기능으로 좁혀 모델이나 프로바이더 차이를 다시 살펴보세요. ### 조사 조건 공유하기 비교 방식·지연 기준·기간·필터는 주소에 남습니다. 상대 기간인 **최근 7일** 링크는 나중에 열면 그 시점의 최근 7일을 보여줍니다. 특정 조사 결과를 공유하려면 기간을 고정된 날짜와 시각으로 선택하세요. 비교 결과에서 **트레이스 보기**로 이동하는 링크는 방금 조사한 시작·종료 시각을 고정합니다. 목록은 생성·임베딩 호출이 있는 트레이스로 좁혀지고, 느린 호출 비교에서 넘어오면 오래 걸린 순으로 정렬됩니다. 뒤로 가면 비교 화면으로 돌아갑니다. ## 숫자를 세는 방식 * 호출, 에러, 토큰, 비용은 생성·임베딩 호출만 셉니다. 도구 스팬과 트레이스 요약 행은 더하지 않습니다. * 모델은 프로바이더가 응답에 보고한 모델을 쓰고, 없으면 요청한 모델을 씁니다. * 비용 출처는 `보고값`, `가격표`, `비용 모름` 중 하나입니다. 보고값도 가격표 항목도 없는 호출은 비용 합계에 $0으로 들어갑니다. 이런 호출이 있으면 개요에 안내가 표시되고, 실제 비용은 표시값보다 큽니다. * 지연 분위수는 지연을 측정한 호출만으로 계산합니다. 측정값이 없는 호출을 0으로 치지 않습니다. * 트레이스와 대화 목록에서 필터는 행을 고르기만 합니다. 각 행의 합계는 그 트레이스나 대화 전체입니다. * 시각은 프로젝트 타임존으로 표시합니다. ## 본문 수집과 보존 * 프롬프트·응답 본문은 SDK의 `captureContent`와 프로젝트 설정의 **프롬프트·응답 본문 수집**을 모두 켜야 저장됩니다. 프로젝트 설정은 조직 `owner` 또는 `admin`만 바꿀 수 있습니다. * 저장된 본문은 프로젝트 멤버 모두가 트레이스 상세와 대화에서 볼 수 있습니다. 개인정보나 비밀값이 섞일 수 있으면 SDK의 `redact`로 보내기 전에 지우세요. * 본문은 입력과 출력 각각 32,000자에서 잘립니다. * 본문 수집을 꺼도 이미 저장된 본문은 남습니다. 보존 기간이 지나면 정리됩니다. * AI 호출 기록은 프로젝트 보존 기간과 AI 관측의 **스팬 보존 기간**(기본 90일) 중 짧은 쪽이 지나면 매일 자동으로 정리됩니다. 정리된 트레이스와 대화는 열 수 없습니다. 설정 위치는 [프로젝트 설정](/docs/project-workspace/settings)을 보세요. ## 다음 단계 * 비용, 에러율, 지연이 기준을 넘을 때 알림을 받으려면 [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) * 다른 분석 도구의 사용 흐름은 [분석 워크벤치](/docs/project-workspace/analysis-workbench)에서 확인하세요. --- # 코호트 비교, 브레이크다운, 쿼리 Source: https://data.reopt.ai/docs/project-workspace/compare-breakdown-query 세그먼트 비교, 속성별 분해, SQL 쿼리로 준비된 화면을 넘어선 질문을 확인합니다. 이 세 화면은 한 단계 더 깊이 분석할 때 씁니다. 먼저 코호트 비교로 세그먼트 차이를 보고, 브레이크다운으로 어떤 속성에서 차이가 나는지 분해한 뒤, 필요하면 쿼리에서 SQL로 직접 검증하세요. ## 진입 경로 * 코호트 비교: `/dashboard/[orgId]/[projectId]/compare` * 브레이크다운: `/dashboard/[orgId]/[projectId]/breakdown` * 쿼리: `/dashboard/[orgId]/[projectId]/query` 코호트 비교와 브레이크다운의 기본 조회 기간은 최근 30일입니다. ## 코호트 비교 세그먼트를 같은 축에서 비교할 때 사용합니다. 위쪽에서 비교할 세그먼트를 고르면 결과가 나타납니다. * 세그먼트는 5개까지 고를 수 있습니다. `전체 사용자`도 비교 대상 하나로 고를 수 있습니다. * 선택은 주소(`?segments=`)에 담기므로 링크로 공유할 수 있습니다. 세그먼트 화면의 `비교로` 메뉴도 이 주소로 넘어옵니다. * `이벤트 필터`로 이벤트 추이에 그릴 이벤트를, `퍼널 단계`로 퍼널 비교의 단계를 고릅니다. 둘 다 조회 기간에 많이 발생한 이벤트 50개 중에서 선택합니다. 결과 탭: * `이벤트 추이`: 선택한 세그먼트별 일별 이벤트 수. 이벤트 필터를 고르면 세그먼트 × 이벤트마다 선이 하나씩 그려집니다. * 세그먼트 이름 탭: 첫 번째와 두 번째로 고른 세그먼트의 평균 리텐션(Day 0\~7) * `퍼널 비교`: 세그먼트를 둘 이상, 퍼널 단계를 2개 이상 골랐을 때 나타납니다. 첫 번째와 두 번째 세그먼트의 퍼널을 나란히 보여줍니다. * `지표 비교`: 선택한 모든 세그먼트의 이벤트 수, 고유 사용자, 인당 평균 이벤트 * `요약`: 첫 번째와 두 번째 세그먼트의 지표와 차이 숫자를 읽는 법: * 고유 사용자는 디바이스 수입니다. * 세그먼트는 그 세그먼트에 속한 디바이스 단위로 적용됩니다. 조건에 맞는 이벤트만 세지 않고, 세그먼트에 속한 디바이스의 모든 이벤트를 셉니다. * 리텐션 탭의 코호트는 조회 기간에 프로젝트에 처음 나타난 디바이스 중 그 세그먼트에 속한 것입니다. 일별, 7개 기간 고정입니다. * 퍼널 비교는 [퍼널](/docs/project-workspace/funnel) 화면과 같은 방식으로 세지만 전환 기간이 1일로 고정이고 단계 속성 조건이 없습니다. ## 브레이크다운 지표 변화를 속성 축으로 분해할 때 사용합니다. * **이벤트**: `전체 이벤트` 또는 조회 기간에 많이 발생한 이벤트 50개 중 하나 * **분석 대상**: `내장 컬럼` 또는 `커스텀 속성` * **지표**: `이벤트 수` 또는 `고유 사용자`(디바이스 수) 내장 컬럼: * Country * City * OS * Browser * Device * Path * Referrer * Referrer Type * Channel Type * UTM Source, UTM Medium, UTM Campaign 커스텀 속성을 고르면 이벤트 속성 키가 발생 수와 함께 50개까지 나옵니다. 데이터 카탈로그에서 "선택기에서 숨기기"를 켠 속성은 이 목록에 나오지 않습니다. 결과 탭: * `추이`: 상위 5개 값의 일별 이벤트 수. `패널`(기본), `겹쳐 보기`, `표`, `나눠 보기`로 전환합니다. 패널은 값마다 작은 차트를 하나씩 그립니다. * `바 차트`, `파이 차트`: 값별 분포 * `테이블`: 순위, 값, 이벤트 수(또는 사용자 수), 비율 분포는 상위 20개 값까지 보여주고, 값이 비어 있는 이벤트는 집계에서 빠집니다. 테이블의 비율은 표에 나온 값들의 합을 100%로 계산합니다. `추이` 탭은 지표 선택과 관계없이 이벤트 수로 그립니다. ## 쿼리 정형화된 화면으로 답이 나오지 않을 때 사용하는 SQL 워크스페이스입니다. 왼쪽에 사이드바, 위에 편집기, 아래에 결과가 놓이는 3단 구성입니다. 패널 크기와 사이드바 접힘은 브라우저에 기억됩니다. 사이드바는 `스키마`, `저장됨`, `이력`으로 전환합니다. * **스키마 트리**: 조회할 수 있는 `events`·`sessions`·`profiles` 테이블과 컬럼을 타입과 함께 보여줍니다. 테이블·컬럼을 검색하고, 이름을 편집기 커서에 삽입하거나 테이블을 바로 미리보기(`SELECT * … LIMIT 100`)할 수 있습니다. 편집기 자동완성도 같은 스키마를 씁니다. * **탭**: 쿼리마다 탭을 열어 두고 오갑니다. 탭의 SQL·필터·변수·차트 설정은 프로젝트별로 브라우저에 저장되고, 저장되지 않은 변경은 점으로 표시됩니다. * **실행**: `⌘↵`로 커서 위치의 문장만 실행합니다. 편집기에 문장이 여럿이면 분할 버튼에서 선택 영역이나 모든 문장을 차례로 실행합니다. 실행 버튼은 마지막 실행 이후 변경 여부를 색으로 알려주고, 실행 중에는 취소로 바뀝니다. * **필터**: 필터로 정한 기간과 속성 조건은 SQL의 `{filters}` 자리에 WHERE 조건으로 들어갑니다. 값 입력란은 해당 컬럼의 최빈값을 제안하되 직접 입력도 받습니다. 플레이스홀더가 없는 쿼리에 필터를 걸면 적용되지 않는다고 경고하고 삽입 버튼을 제공합니다. * **변수**: 실행마다 바뀌는 값을 `{variables.이름}`으로 참조합니다. 값은 실행 직전에 치환됩니다. * **결과**: 표는 헤더에 컬럼 타입을 표시하고 정렬·너비 조절·행 상세 패널을 지원합니다. 만 행도 스크롤로 다룹니다. 표·차트·함께 보기로 전환하고, 차트는 축과 값 컬럼을 골라 선·막대·영역·파이·숫자로 그립니다. 상태바에는 행 수·경과 시간·읽은 바이트·절단 여부가 남습니다. 결과는 CSV·JSON으로 내보냅니다. * **저장과 이력**: 이름·설명·공개 여부를 붙여 저장하고, 저장한 쿼리를 열어 수정하면 그 자리에서 갱신됩니다. `저장됨`에는 바로 열어 볼 수 있는 예제 쿼리도 함께 있습니다. 실행 이력(성공·실패, 행 수, 경과)은 본인이 실행한 최근 100건이 남고, 마지막 실행 후 30일이 지나면 지워집니다. 이력의 쿼리는 편집기로 되돌리거나 다시 실행할 수 있습니다. 쿼리는 읽기 전용이고 현재 프로젝트의 데이터만 읽습니다. `events`·`sessions`·`profiles` 세 테이블은 어떤 형태로 쓰든 이 프로젝트의 행만 돌려주므로 `project_id` 조건을 직접 쓸 필요가 없습니다. 쓰기·DDL 문장, 다른 테이블, 파일이나 URL을 읽는 테이블 함수는 실행 전에 거절됩니다. 입력 SQL은 최대 10,000자이며 한 번에 한 문장만 서버로 보냅니다. 공개 저장 쿼리는 프로젝트 멤버가 함께 볼 수 있고, 비공개 저장 쿼리는 작성자만 볼 수 있습니다. 저장 쿼리 수정과 삭제도 작성자만 할 수 있습니다. ## 추천 순서 1. 코호트 비교로 두 세그먼트의 지표가 얼마나 다른지 봅니다. 2. 브레이크다운으로 어떤 속성군에서 차이가 나는지 확인합니다. 3. 쿼리에서 예외 케이스나 추가 집계를 직접 검증합니다. ## 주의할 점 * 코호트 비교의 리텐션·퍼널 비교·요약 탭은 처음 고른 두 세그먼트만 보여줍니다. 세 개 이상을 한꺼번에 보려면 이벤트 추이와 지표 비교 탭을 쓰세요. * 코호트 비교의 세그먼트 선택 목록에는 최근에 갱신된 순으로 50개까지만 나옵니다. * 쿼리는 조직의 분석 저장소가 쓰는 SQL 문법을 따릅니다. 기본 저장소는 DuckDB 계열 문법입니다. * 쿼리는 조회 전용이며 현재 프로젝트 범위를 벗어난 데이터에 접근할 수 없습니다. * 쿼리 결과는 최대 10,000행으로 제한되고, 실행 시간은 30초를 넘길 수 없습니다. ## 다음 단계 * 비교할 세그먼트를 만들려면 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * 결과를 저장하려면 [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) --- # 대시보드와 리포트 Source: https://data.reopt.ai/docs/project-workspace/dashboards-and-reporting 프로젝트 홈에서 핵심 지표를 확인하고, 반복해서 볼 분석 결과를 리포트와 대시보드로 저장합니다. 대시보드는 팀이 반복해서 확인하는 지표를 모아 두는 화면입니다. 분석하다가 다시 볼 결과를 찾으면 리포트로 저장하고, 대시보드에서 함께 관리하세요. ## 진입 경로 * 프로젝트 홈: `/dashboard/[orgId]/[projectId]` (사이드바의 **대시보드**, 단축키 `G` `D`) * 대시보드 라이브러리: `/dashboard/[orgId]/[projectId]/dashboards` (대시보드 상세의 **대시보드 목록** 버튼) * 대시보드 상세: `/dashboard/[orgId]/[projectId]/dashboards/[dashboardId]` * 리포트 생성: `/dashboard/[orgId]/[projectId]/dashboards/[dashboardId]/reports/new` * 리포트 편집: `/dashboard/[orgId]/[projectId]/reports/[reportId]` ## 이 화면에서 답할 수 있는 질문 * 팀이 반복적으로 보는 핵심 지표는 무엇인가요? * 탐색 중 발견한 결과를 어떤 형태로 저장할까요? * 프로젝트의 현재 상태를 가장 빠르게 어디서 확인할 수 있나요? ## 프로젝트 홈에서 확인할 것 프로젝트 홈에서는 분석 화면으로 이동하기 전에 현재 상태를 확인합니다. 위에서부터 다음 순서로 놓여 있습니다. * **핵심 지표**: 방문자, 세션, 페이지뷰, 세션당 페이지, 이탈률, 평균 세션 시간. 각 값 아래에 바로 앞 같은 길이 기간 대비 증감이 붙고, 비교할 이전 값이 없으면 `신규`로 표시됩니다. * **웹 트래픽 추이**와 **이벤트별 카운트**: 방문자·세션·페이지뷰의 일별 추이와, 건수가 많은 이벤트 10개. * **빠른 작업**: `새 대시보드`(단축키 `N`), `템플릿`, `퍼널 분석`, `리텐션 분석`. * **에러 이슈**: 활성 이슈 수, 최근 7일 발생 수, 발생이 많은 이슈 3개. `전체 보기`로 에러 화면에 갑니다. * **운영 하이라이트**: Traffic Quality(사람·의심·봇·제외 건수)와 Top Sources, Top Entry Pages, Top Devices, Top Geo, Common Journeys 상위 5개. * **Top Events**와 **최근 이벤트**: 기간 내 상위 이벤트 10개와, 가장 최근 이벤트 20건. * **대시보드**: 저장된 대시보드 목록과 `+ 새 대시보드`. 행을 누르면 대시보드 상세로 갑니다. * **SDK Integration**: 브라우저용 클라이언트 키가 있으면 초기화 코드가 나옵니다. * **기능 구조**: 기능 영역별 화면 진입 카드. 기간 선택의 기본값은 최근 30일이고, 프로젝트 시간대의 날짜로 계산합니다. **에러 이슈**(최근 7일 고정)와 **최근 이벤트**(가장 최근 20건)는 기간 선택을 따르지 않습니다. ## 대시보드 만들기 대시보드는 리포트 여러 개를 한 화면에 묶어 보는 단위입니다. 프로젝트에 접근할 수 있는 조직 멤버는 대시보드와 리포트를 만들고 편집할 수 있습니다. 1. 프로젝트 홈의 `+ 새 대시보드`나 라이브러리의 **새 대시보드**를 누르고 이름을 입력합니다. 템플릿으로 시작하려면 홈의 `템플릿`이나 라이브러리의 **템플릿 적용**을 고르세요. 2. 대시보드 상세에서 **+ 리포트 추가**를 누릅니다. 3. 차트 유형, 집계 간격, 기간, 이벤트를 정하고 **만들기**를 누릅니다. 4. **레이아웃 편집**으로 타일 순서를 바꾸고 **레이아웃 저장**을 누릅니다. ### 템플릿 **템플릿으로 대시보드 만들기**에서 하나를 고르면 대시보드와 리포트가 한 번에 만들어지고 그 대시보드로 이동합니다. | 템플릿 | 분류 | 리포트 | | ---------- | ---- | --- | | SaaS 핵심 지표 | SaaS | 5개 | | 이커머스 전환 | 이커머스 | 6개 | | 온보딩 퍼널 | 그로스 | 6개 | 템플릿 리포트는 `sign_up`, `purchase`, `page_view`처럼 정해진 이벤트 이름을 봅니다. 프로젝트가 다른 이름으로 이벤트를 보내면 타일이 비어 보입니다. ### 대시보드 라이브러리 라이브러리는 프로젝트의 대시보드를 한 표로 보여 줍니다. 위쪽 카드는 `대시보드`, `저장 리포트`, `템플릿 기반` 수입니다. | 열 | 내용 | | ------- | ------------------------------------ | | `대시보드` | 이름 | | `리포트` | 담긴 리포트 수 | | `최근 변경` | 대시보드나 그 안의 리포트가 마지막으로 바뀐 시각 | | `생성 방식` | 템플릿으로 만들었으면 `Template`, 아니면 `Manual` | | `작업` | **열기**, **리포트 추가** | ## 대시보드 상세 헤더에는 **대시보드 목록**, **+ 리포트 추가**, **+ 에러 이슈 타일**, **레이아웃 편집**, **대시보드 삭제**가 있고, 그 아래 필터 줄에 기간과 세그먼트가 있습니다. 타일에 마우스를 올리면 **편집**과 **삭제**가 나타납니다. 퍼널과 경로 타일에는 **분석 열기**도 나옵니다. * **레이아웃 편집**: 타일을 드래그해 순서를 바꿉니다. 바꾼 뒤 **레이아웃 저장**을 눌러야 반영되고, **취소**를 누르면 되돌아갑니다. 바꿀 수 있는 것은 순서입니다. * **+ 에러 이슈 타일**: `에러 이슈 Top 5` 타일을 맨 아래에 추가합니다. 최근 7일 동안 발생이 많은 활성 이슈 5개를 보여 주고, 이슈를 누르면 이슈 상세로 갑니다. * 타일 하나가 실패하면 그 타일에만 "데이터를 불러오지 못했습니다."가 뜨고 나머지는 그대로 그려집니다. ### 필터가 타일에 적용되는 방식 필터는 대시보드에 저장됩니다. 한 사람이 바꾸면 그 대시보드를 여는 모든 멤버에게 같은 필터가 걸립니다. * **세그먼트**: 기본은 `전체 사용자`입니다. 세그먼트를 고르면 모든 타일에 적용되고, 타일이 저장할 때 갖고 있던 세그먼트보다 우선합니다. * **기간**: 한 번도 고르지 않은 대시보드에서는 각 타일이 자기 기간으로 그려집니다. 필터 줄에 `최근 30일`이라고 보여도 7일짜리 리포트는 7일을 그립니다. * 기간을 고르면 그때부터 모든 타일이 같은 날짜 범위를 그립니다. 범위는 **고른 날의 날짜로 고정 저장**됩니다. `최근 7일`을 골라도 다음 주에 열면 지난주 날짜 그대로이니, 최신 구간을 보려면 다시 고르세요. ## 리포트 만들기 **+ 리포트 추가**를 누르면 **새 리포트 만들기** 화면이 열립니다. 왼쪽에서 조건을 정하고 오른쪽에서 미리보기를 봅니다. | 입력 | 선택지 | | -------- | ----------------------------------------------------------- | | `리포트 이름` | 필수 | | `차트 유형` | 선형 그래프, 막대 그래프, 파이 차트, 메트릭, 영역 그래프, 게이지, 산점도, 테이블, 숫자+스파크라인 | | `집계 간격` | 분, 시간, 일, 주, 월 (기본 일) | | `기간` | 1일, 7일, 30일, 90일 (기본 30일) | | `이벤트` | 하나 이상. 건수가 많은 이벤트 20개가 제안되고, 이름을 직접 입력할 수도 있습니다 | **추천 템플릿** 8개(`DAU 추이`, `이벤트 Top 10`, `전환율 게이지` 등)는 위 입력을 한 번에 채워 줍니다. 템플릿의 이벤트 이름이 프로젝트와 다르면 고쳐서 쓰세요. 저장된 타일은 고른 이벤트의 기간별 건수를 그립니다. 이벤트를 여러 개 넣으면 이벤트마다 시리즈가 하나씩 생깁니다. | 차트 유형 | 타일에 그려지는 모양 | | ------------------ | ------------------ | | 선형 그래프, 산점도 | 선 | | 막대 그래프, 테이블 | 막대 | | 영역 그래프 | 영역 | | 파이 차트 | 파이 | | 메트릭, 게이지, 숫자+스파크라인 | 첫 번째 이벤트의 기간 합계 숫자 | * 기간은 프로젝트 시간대 기준으로 N일 전 날짜부터 오늘까지입니다. * 집계 간격 `분`은 타일에서 시간 단위로 그려집니다. * 미리보기는 참고용입니다. 막대·파이·메트릭·게이지·테이블 유형의 미리보기는 고른 이벤트가 아니라 프로젝트 전체 이벤트를 기준으로 그려져서, 저장한 타일과 숫자가 다를 수 있습니다. ## 분석 화면에서 저장하기 [퍼널](/docs/project-workspace/funnel), [리텐션](/docs/project-workspace/retention), [경로 분석](/docs/project-workspace/acquisition-and-path) 화면의 **대시보드에 저장**은 지금 보고 있는 분석 조건을 리포트로 저장합니다. 리포트 이름과 대시보드를 고르면 됩니다. 대시보드가 하나도 없으면 먼저 만들어야 합니다. | 저장한 분석 | 타일에 그려지는 것 | | ------ | ------------------------ | | 퍼널 | 단계별 사용자 수와 전체 전환율 | | 리텐션 | 평균 리텐션 곡선과 마지막 기간의 리텐션 | | 경로 | 가장 많이 일어난 이동(`A → B`) 순위 | 퍼널과 경로 타일의 **분석 열기**는 저장된 정의 그대로 원래 분석 화면을 엽니다. ## 리포트 편집과 삭제 타일의 **편집**을 누르면 **리포트 수정** 화면에 이름, 차트 유형, 집계 간격, 기간, 이벤트가 다시 채워집니다. 저장하면 원래 대시보드로 돌아갑니다. * 현재 타일은 **처음 저장한 정의**로 그려집니다. 편집에서 바뀌는 것은 리포트 이름이고, 차트 유형·집계 간격·기간·이벤트를 바꾸려면 리포트를 새로 추가한 뒤 기존 리포트를 삭제하세요. * 퍼널·리텐션·경로·에러 이슈 타일도 같습니다. 조건을 바꾸려면 분석 화면에서 다시 저장하세요. 삭제는 되돌릴 수 없습니다. * 타일의 **삭제**는 그 리포트 하나를 지웁니다. * **대시보드 삭제**는 포함된 리포트까지 함께 지웁니다. 남길 리포트가 있으면 다른 대시보드에 먼저 다시 만들어 두세요. ## 주의할 점 * 타일 결과는 5분 동안 캐시됩니다. 방금 들어온 이벤트가 바로 보이지 않을 수 있습니다. * 퍼널과 리텐션 타일도 데이터 포인트를 쓰지 않습니다. * 데이터 카탈로그에서 상태가 `내부`인 이벤트는 이벤트 건수 타일에 집계되지 않아 타일이 비어 보입니다. * 속성으로 나눈 타일(템플릿의 `카테고리별 조회`)은 세그먼트 필터와 함께 계산할 수 없습니다. 세그먼트를 고르면 그 타일에는 "데이터를 불러오지 못했습니다."가 뜹니다. ## 다음 단계 * 이벤트 정의를 먼저 정리해야 한다면 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 저장하지 않고 지표를 겹쳐 그려 보려면 [메트릭 관리](/docs/project-workspace/metrics) * 질문별 탐색은 [분석 워크벤치](/docs/project-workspace/analysis-workbench) * 세그먼트 필터에 쓸 사용자 집합은 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) --- # 에러 트래킹 Source: https://data.reopt.ai/docs/project-workspace/error-tracking 예외 이벤트를 이슈로 묶어 상태·심각도별로 추적하고, 스택트레이스와 발생 맥락으로 원인을 좁힙니다. 에러 화면은 SDK가 보낸 `$exception` 이벤트를 같은 원인끼리 이슈로 묶어 보여줍니다. 목록에서는 오류별 발생 빈도와 영향받은 사용자 수를 보고, 이슈 상세에서는 오류가 난 위치와 발생들의 공통점을 확인합니다. ## 진입 경로 * 이슈 목록: `/dashboard/[orgId]/[projectId]/errors` (사이드바의 **에러**, 단축키 `G O`) * 이슈 상세: `/dashboard/[orgId]/[projectId]/errors/[issueId]` * 규칙·릴리즈·소스맵 관리: **프로젝트 설정** (`/dashboard/[orgId]/[projectId]/settings`) ## 수집 켜기 예외 자동 수집은 기본으로 꺼져 있습니다. 브라우저 SDK에서 `capture.exceptions`를 켜면 잡히지 않은 오류와 처리되지 않은 Promise 거절이 `$exception`으로 전송됩니다. ```ts import { init } from "@reopt-ai/data-sdk-client"; const client = init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.NEXT_PUBLIC_REOPT_API_URL!, release: "1.4.0", // 빌드 식별자. 모든 이벤트에 $release_id로 붙습니다 capture: { exceptions: true }, }); // 직접 잡은 오류를 보고할 때 try { await submitOrder(); } catch (error) { client.captureException(error, { level: "warning", properties: { step: "checkout" } }); } ``` * 자동 수집은 같은 타입의 예외가 몰릴 때 전송량을 제한합니다. 기본은 예외 타입별로 10건까지 보내고 10초마다 1건씩 다시 허용합니다. `captureException()`으로 직접 보낸 오류는 조절하지 않습니다. * 브라우저 확장 프로그램 코드만으로 이루어진 스택은 자동 수집에서 제외합니다. * Next.js 서버 오류는 서버 SDK의 `createOnRequestError`를 `instrumentation.ts`에서 `onRequestError`로 내보내면 같은 방식으로 이슈가 됩니다. * `release`는 선택이지만 넣어 두세요. 이슈가 처음·마지막으로 발생한 빌드가 기록되고, 아래 회귀 판정에 쓰입니다. * minify된 스택을 원본 파일·줄·함수명으로 보려면 빌드 뒤에 소스맵을 올리세요. 명령은 [CLI](/docs/workspace/cli)의 "소스맵을 CI에서 업로드하기"에 있습니다. ## 이슈 목록 | 열 | 뜻 | | ------------- | ------------------------------------------- | | 심각도 | `낮음`·`보통`·`높음`·`심각` | | 이슈 | 이름(기본값은 예외 타입)과 메시지. 누르면 상세로 이동합니다 | | 추이 | 조회 기간의 발생량. 기간이 2일 이하면 시간별, 그보다 길면 일별 막대입니다 | | 발생 · 사용자 · 세션 | 조회 기간 안의 발생 수와, 그 오류를 겪은 사용자·세션 수 | | 최근 발생 | 마지막으로 발생한 시점 | | 상태 | 행에서 바로 바꿉니다 | | 담당자 | 없으면 `미배정` | * 처음 열면 **최근 7일**, 상태 **활성**, **최근 발생순**입니다. 정렬은 `최초 발생순`·`발생 횟수순`·`영향 사용자순`으로 바꿉니다. * 검색창은 이슈 이름과 메시지에서 찾습니다. Enter를 누르거나 입력란을 벗어나면 적용됩니다. * `상태`와 `심각도` 필터는 여러 값을 함께 고를 수 있습니다. * 기간·필터·정렬은 전부 URL에 들어갑니다. 좁혀 둔 목록을 링크로 그대로 넘길 수 있습니다. * 한 번에 50개씩 불러오고 `더 보기`로 이어집니다. * 행을 선택하면 `해결됨으로`, `보관`, `병합`(2개 이상 선택 시)을 한꺼번에 적용합니다. 조회 기간은 발생 수·사용자 수·추이를 그 구간으로 좁힐 뿐, 이슈를 목록에서 빼지는 않습니다. 그 기간에 발생이 없던 이슈는 0으로 남습니다. ## 상태와 재발 | 상태 | 쓰는 경우 | 다시 발생하면 | | -------- | ------------------ | ------------------------------------------------ | | `활성` | 아직 판단이 필요한 이슈 | 그대로 활성 | | `해결됨` | 고쳤다고 표시한 이슈 | 활성으로 돌아옵니다 | | `릴리즈 대기` | 고쳤지만 아직 배포되지 않은 이슈 | 수정된 릴리즈와 같은 릴리즈에서 난 발생은 그대로 두고, 그 밖에는 활성으로 돌아옵니다 | | `숨김` | 더는 주의를 끌지 않게 할 이슈 | 이벤트는 저장되고 집계되지만 상태는 바뀌지 않고, 이 이슈로는 알림이 가지 않습니다 | | `보관됨` | 목록의 `보관`으로 내려 둔 이슈 | 활성으로 돌아옵니다 | 이슈 상세에서 `해결됨`을 고르면, 이 프로젝트에서 관측된 릴리즈가 있을 때 **수정된 릴리즈**를 묻습니다. 릴리즈를 지정하면 이후 다른 릴리즈에서 같은 오류가 나올 때 `재발` 알림이 회귀(Regression)로 표시해 알려줍니다. 비워 두면 일반 재발로 처리합니다. ## 이슈 상세 상단에서 이름(눌러서 수정), 상태, 심각도, 담당자를 바꿉니다. 담당자는 조직 구성원 중에서만 고를 수 있습니다. 요약에는 `최초 발생`·`최근 발생`·`누적 발생`·`영향 사용자`·`fingerprint`·`담당자`가 보입니다. 릴리즈를 보내는 프로젝트라면 `릴리즈`(처음 → 마지막으로 발생한 빌드)가, 릴리즈를 지정해 해결한 이슈라면 `수정된 릴리즈`가 함께 나옵니다. `누적 발생`은 전체 기간의 합이고, `영향 사용자`와 아래 막대(`최근 30일 발생 추이`)는 최근 30일 기준입니다. | 탭 | 볼 수 있는 것 | | -------- | ---------------------------------------------------------------------------------------------------------- | | `스택트레이스` | 가장 최근 발생의 스택. `앱 코드만` / `전체 프레임` 전환, 원인 체인, `처리됨`·`미처리` 배지. 소스맵이 적용된 프레임은 `소스맵 해석됨`으로 표시되고 펼치면 주변 소스가 보입니다 | | `발생 이벤트` | 발생 한 건씩의 시각·경로·환경·국가. 행을 누르면 스택트레이스가 그 발생으로 바뀝니다. `세션 보기`, `리플레이`, `사용자 보기`로 이어집니다 | | `분포` | 브라우저, OS, 경로, 국가, 디바이스, 예외 타입, 발생 위치, 처리 여부, 릴리즈 중 하나를 골라 최근 7일의 상위 20개 값을 봅니다 | * `리플레이`는 그 발생 시점을 담은 녹화를 해당 위치에서 엽니다. 시점이 겹치는 녹화가 없으면 같은 세션에서 그 직전에 시작한 녹화를 열고, 그것도 없으면 직후 녹화를 엽니다. 재생할 녹화가 없으면 없다고 알려줍니다. * SDK가 직전 동작(`$exception_steps`)을 보내면 `직전 동작` 목록이 탭 위에 나타납니다. `capture.exceptionSteps`를 켜면 페이지 이동이 자동으로 기록되고, `client.addExceptionStep()`으로 직접 남길 수도 있습니다(최대 20개). * 프로젝트 설정의 `소스 저장소`를 채우면 해석된 프레임에 `소스 열기` 링크가 붙습니다. 릴리즈가 커밋 SHA 모양이면 그 커밋을, 아니면 `기본 브랜치`(비우면 `main`)를 엽니다. * `왜 이렇게 묶였나?`를 펼치면 이 발생이 어떤 조각으로 묶였는지 보여줍니다. * `알림 만들기`는 에러 이슈 조건이 미리 채워진 알림 화면으로 이동합니다. ### 묶음 고치기 * **병합**: 같은 원인이 여러 이슈로 갈라졌을 때 씁니다. 발생 이벤트는 그대로 두고 연결만 옮기므로 과거 이벤트를 모두 볼 수 있습니다. * **나누기**: 한 이슈에 다른 원인이 섞였을 때 fingerprint 단위로 새 이슈를 떼어냅니다. fingerprint가 2개 이상일 때만 가능하고, 최소 하나는 원래 이슈에 남아야 합니다. * 처음부터 직접 묶으려면 `captureException(error, { fingerprint: "..." })`로 지정하거나 아래의 `묶기` 규칙을 쓰세요. 우선순위는 SDK가 보낸 값, 묶기 규칙, 자동 묶음 순입니다. ## 프로젝트 설정의 에러 항목 | 패널 | 하는 일 | | -------- | ------------------------------------------------------------ | | `에러 규칙` | `숨김`·`배정`·`묶기`·`심각도` 규칙. 위에서부터 평가하고, 종류별로 처음 맞는 규칙 하나가 적용됩니다 | | `규칙 테스트` | 이벤트 properties를 붙여넣어 저장된 규칙을 돌려봅니다. 아무것도 기록하지 않습니다 | | `급증 감지` | 현재는 읽기 전용입니다. 기준은 평소의 3배, 최소 5분에 10건입니다 | | `릴리즈` | 이벤트의 `$release_id`로 관측된 빌드 목록. 미리 등록할 필요가 없습니다 | | `심볼셋` | 업로드한 소스맵과 마지막 사용 시점. 삭제해도 이미 해석된 프레임은 그대로 남습니다 | 규칙 조건은 예외 타입, 메시지, 발생 위치, 처리 여부, 레벨, 릴리즈, 경로로 겁니다. `숨김`·`배정`·`심각도` 규칙은 이슈가 처음 만들어질 때만 적용됩니다. 새로 만든 규칙이 사람이 이미 분류한 이슈를 뒤집지 않습니다. 이슈 상세의 `업무로 보내기`는 리옵트 스튜디오 브랜드에 연결된 프로젝트에서 쓸 수 있습니다. 보낸 뒤에는 스튜디오 업무 링크와 보드의 현재 단계가 보입니다. 이슈 상태를 바꾸면 업무에, 보드에서 단계를 바꾸면 이슈 상태에 반영됩니다. 연결되지 않은 프로젝트에서는 버튼이 비활성으로 보입니다. ## 다른 화면에서 이어 보기 * [알림](/docs/project-workspace/realtime-insights-alerts)에서 조건 종류를 `에러 이슈`로 고르면 `신규`·`재발`·`급증` 시점과 최소 심각도로 알림을 받습니다. * 대시보드 상세의 `+ 에러 이슈 타일`은 최근 7일 발생 횟수 기준 활성 이슈 상위 5개를 타일로 추가합니다. * 세션 타임라인과 사용자 타임라인의 예외 행에는 `이슈 보기`가 붙습니다. [이벤트 스트림](/docs/project-workspace/event-stream)에서는 예외 행의 상세 패널에 있는 `이슈 상세 열기`로 넘어옵니다. ## 주의할 점 * 예외 이벤트는 수집 1건당 데이터 포인트 1을 씁니다. `숨김` 규칙에 걸린 예외도 수집은 일어났으므로 똑같이 차감됩니다. 이슈 목록과 상세 조회는 차감하지 않습니다. * 사용자·세션 수는 fingerprint별 합계입니다. 병합으로 fingerprint가 여럿인 이슈에서는 같은 사람이 두 번 세어져 실제보다 크게 보일 수 있습니다. * 심각도는 SDK가 보낸 `level`을 먼저 따릅니다(`fatal`은 심각, `warning`은 보통, `info`는 낮음). 그 밖에는 미처리 오류가 `높음`, 처리된 오류가 `보통`으로 시작하고, `심각도` 규칙이나 수동 변경이 이를 덮어씁니다. * 소스맵은 이벤트가 저장될 때 한 번 적용됩니다. 지금 올린 소스맵은 그 이후에 발생한 이벤트부터 해석하고, 이미 저장된 이벤트는 그대로 남습니다. * 목록은 상태·심각도 조건에 맞는 이슈 중 최근에 발생한 500개까지를 대상으로 정렬하고 검색합니다. 이슈가 많은 프로젝트에서는 필터로 먼저 좁히세요. * 병합과 규칙 변경은 새 발생에 반영되기까지 최대 1분쯤 걸릴 수 있습니다. ## 다음 단계 * 소스맵 업로드를 CI에 붙이려면 [CLI](/docs/workspace/cli) * 오류가 난 순간의 화면을 보려면 [세션 리플레이](/docs/project-workspace/session-replay) * 신규·재발·급증 알림을 받으려면 [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) * 예외 한 건을 원본 이벤트로 확인하려면 [이벤트 스트림](/docs/project-workspace/event-stream) --- # 데이터 카탈로그 Source: https://data.reopt.ai/docs/project-workspace/event-catalog 수집된 이벤트와 속성에 이름·의미·상태를 붙여, 팀이 같은 기준으로 분석하도록 정리합니다. 데이터 카탈로그는 수집된 이벤트와 속성을 팀이 같은 의미로 읽도록 정리하는 화면입니다. 분석을 시작하기 전에 전환 이벤트와 상태를 먼저 정해 두세요. 퍼널·획득·알림·세그먼트가 모두 이 설정을 따릅니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/events`: 사이드바의 **데이터 카탈로그**, 단축키 `G` `E`. 상단에서 **이벤트 / 속성** 탭을 고릅니다(`?tab=properties`). * 헤더의 **이벤트 원본**은 [이벤트 스트림](/docs/project-workspace/event-stream)으로, **트래픽 품질**은 `/dashboard/[orgId]/[projectId]/events/traffic`으로 이동합니다. ## 링크로 화면 상태 공유하기 지금 보고 있는 화면 상태는 모두 URL에 들어갑니다. 주소를 복사해 보내면 받는 사람도 같은 화면을 봅니다. | 파라미터 | 뜻 | | ---------------------- | ------------------------------------- | | `?q=` | 검색어와 필터 (아래 문법) | | `?tab=properties` | 속성 탭 | | `?event=<이름>` | 그 이벤트를 인스펙터에 열기 | | `?property=<스코프>:<이름>` | 그 속성을 인스펙터에 열기 (`event:plan`) | | `?panel=` | 인스펙터 탭 또는 패널 (아래) | | `?scope=` | 속성 탭의 스코프 (`all`, `event`, `profile`) | | `?days=` | 관측 기간 (`7`, `30`, `90`. 기본 30) | | `?page=` | 목록 페이지 | `?panel=`은 두 가지로 쓰입니다. 인스펙터가 열려 있으면 그 안의 탭을 가리킵니다(`overview`·`properties`·`settings`·`history`·`code`, 속성 인스펙터는 `overview`·`settings`). 인스펙터가 닫혀 있으면 목록 옆 패널을 가리킵니다(`template`·`import`·`categories`·`triage`). 패널을 열면 열려 있던 인스펙터는 닫힙니다. `?days=`는 이벤트 탭의 볼륨 열과 추이선에 적용하는 기간입니다(목록 위 `7일`·`30일`·`90일`). **상태 판정은 이 값을 따르지 않고 항상 30일 기준입니다.** 7일로 판정하면 관측 기간이 짧아서 트래픽이 있는 이벤트가 모두 "신규"로 보이기 때문입니다. ## 검색창으로 필터 걸기 필터 버튼(`상태`·`카테고리`·`태그`·`전환`)과 검색창은 서로 연결돼 있습니다. 버튼으로 고른 필터는 검색창에 나타나고, 검색창에 입력한 필터는 버튼에 반영됩니다. ``` 결제 is:conversion status:deprecated category:checkout tag:pay ``` 속성 탭에서는 `type:number scope:profile is:hidden`을 씁니다. 한 필드에 값을 여러 개 줄 때는 괄호로 묶습니다. 괄호 안의 값 가운데 하나만 맞아도 걸립니다. ``` status:(new or never_seen) ``` 절끼리는 AND, 괄호 안은 OR입니다. 앞에 `-`를 붙이면 제외입니다(`-category:internal`). `status:new or status:never_seen`처럼 풀어 쓰면 안 됩니다. `or`는 괄호 안에서만 통하고, 풀어 쓴 두 절은 AND로 묶여 어떤 이벤트도 걸리지 않습니다. ## 이벤트 상태 7종 상태는 점과 단어로 함께 표시됩니다. 색만으로 구분하지 않습니다. | 상태 | 뜻 | 판정 기준 | | ----- | -------------- | ---------------- | | 활성 | 정상 수신 중 | 기본 | | 신규 | 7일 안에 처음 관측 | 첫 관측이 7일 이내 | | 미발생 | 선언만 되고 아직 안 옴 | 카탈로그에는 있고 수집이 없음 | | 비활성 | 7일 이상 안 옴 | 마지막 관측이 7일 이전 | | 지원 중단 | 새 코드가 쓰면 안 됨 | 직접 지정 | | 보관 | 목록에서 숨김(삭제 대체) | 직접 지정 | | 내부 | 이벤트 수 집계에서 제외 | 직접 지정 | 미발생과 비활성은 다릅니다. 미발생은 카탈로그에는 있지만 최근 30일 동안 한 번도 오지 않은 이벤트이고, 비활성은 오다가 끊긴 이벤트입니다. 둘을 구분하지 않으면 실제로는 없는 장애를 찾게 됩니다. 판정은 최근 30일 관측만 봅니다. 그래서 카탈로그에 등록돼 있지만 30일 넘게 오지 않은 이벤트는 `비활성`이 아니라 `미발생`으로 보입니다. 최근 30일 가운데 최근 7일에만 관측된 이벤트는 예전에 오던 이벤트라도 `신규`로 보입니다. 직접 지정한 상태(지원 중단·보관·내부)는 관측보다 우선합니다. **보관한 이벤트는 이벤트 목록에 나오지 않습니다.** `status:archived`로 걸러도 마찬가지입니다. 상태 필터의 `보관` 숫자로 개수만 확인할 수 있습니다. 되돌리려면 `?event=<이름>&panel=settings` 주소로 인스펙터를 열어 상태를 바꾸세요. 내보내기 파일에는 보관한 이벤트도 들어 있습니다. ## 정리 패널 목록 위 **정리 필요** 카드는 `신규`와 `미발생` 이벤트를 셉니다. 카드를 누르면 `status:(new or never_seen)` 필터가 걸립니다. 필터가 비어 있을 때 뜨는 **정리할 이벤트 N개** 안내에서 **모아 보기**는 같은 필터를 걸고, **한 번에 정리**는 정리 패널(`?panel=triage`)을 엽니다. 정리 패널에서는 인스펙터를 이벤트마다 열지 않고 여러 이벤트를 한 번에 정리합니다. 패널에서 하는 일은 두 가지입니다. * 행마다 다른 값: 표시 이름과 카테고리를 행에 직접 입력하고, 그 행들을 선택한 뒤 **카탈로그에 저장**으로 한 번에 저장합니다. * 여러 행에 같은 값: 고른 행 전체에 **카테고리 지정**·**보관**·**내부 이벤트로**를 적용합니다. 일부가 실패하면 실패한 행만 선택된 채로 남습니다. 같은 버튼을 다시 누르면 그 행만 재시도합니다. 정리 대상은 상태로 정해집니다. 이름과 카테고리를 붙여도 `신규`·`미발생` 상태는 그대로라서 목록과 **정리 필요** 숫자에 계속 남습니다. `미발생`은 이벤트가 들어오기 시작하면 `신규`로 바뀌고, `신규`는 첫 관측 후 7일이 지나야 빠집니다. 보관하거나 내부로 옮긴 이벤트는 바로 빠집니다. 카테고리는 목록에서 고르지 않고 직접 입력합니다. 이미 쓰는 이름은 자동완성으로 제안하고, 새 카테고리도 여기서 입력해 만듭니다. 이 패널의 판정 기간은 화면의 `?days=`와 상관없이 30일로 고정입니다. 이유는 위와 같습니다. 7일로 판정하면 트래픽이 있는 이벤트가 모두 정리 대상이 됩니다. 패널은 볼륨이 큰 순서로 최대 500개 이벤트 안에서 대상을 찾습니다. ## 이벤트 인스펙터 행을 클릭하면 목록 오른쪽에 인스펙터가 열립니다. 목록은 그대로 보입니다. * **개요**: 표시 이름·설명·아이콘·색상·태그. **저장**을 눌러야 반영되고, **되돌리기**는 저장 전 수정을 버립니다. * **속성**: 이 이벤트에 담겨 오는 속성과 고유 값 수, 빈 값 비율. 행을 누르면 설명·타입·숨김을 고치고 상위 값 10개를 봅니다. 옆의 **사용 현황**은 이 이벤트를 쓰는 리포트와 알림을 보여 줍니다. * **설정**: 전환 여부와 상태(활성·지원 중단·보관·내부). 누르는 즉시 저장됩니다. * **이력**: 필드 단위 변경 기록. * **코드**: 이 이벤트를 보내는 코드. JavaScript·TypeScript·React·Node 가운데 골라 복사합니다. 목록에서 바로 하는 일도 있습니다. `전환` 열의 스위치로 전환 여부를 바꾸고, 행 끝의 **코드**·**이력**으로 그 탭을 바로 엽니다. `$exception` 행에는 **에러 트래킹에서 보기**가 더 있습니다. ## 관리하는 메타데이터 | 필드 | 쓰는 곳 | | ------------------- | ---------------------------------- | | `displayName` | 모든 이벤트 선택기·리포트·알림 목록에 표시되는 이름 | | `description` | 목록 한 줄 설명, 인스펙터 | | `icon` / `color` | 선택기와 목록의 아이콘 | | `conversion` | 퍼널 마지막 단계 후보, 획득 분석 기본 전환 지표 | | `tags` / `category` | 필터와 묶어 보기 | | `status` | 위 7종 중 직접 지정하는 4종(활성·지원 중단·보관·내부) | | `rollupProperties` | 시간별 롤업이 이 이벤트를 나눠 세는 속성 키 (최대 20개) | `rollupProperties`는 인스펙터에 편집 칸이 없습니다. 카탈로그 파일로 관리하거나, [메트릭 관리](/docs/project-workspace/metrics) 탐색 탭의 `롤업에 추가` 버튼으로 추가합니다. 바꾸면 전체 이력 재계산이 예약됩니다. 상태를 `내부`로 바꾸면 그 이벤트는 **이벤트 수 집계에서 빠지고**(대시보드의 이벤트 추이 타일, 메트릭 관리의 이벤트 조회), 분석 화면의 이벤트 선택기에도 기본으로 나오지 않습니다. 퍼널·리텐션처럼 정의에 이벤트 이름이 이미 들어 있는 분석은 상태와 상관없이 그대로 계산합니다. 표시 이름은 이벤트 스트림 검색창에서도 쓸 수 있습니다. 스트림은 원본 키(`purchase_completed`)를 저장하지만, `name:"결제 완료"`처럼 화면에서 본 이름으로 검색하면 그 이름을 쓰는 원본 키까지 넓혀 찾습니다. 검색창에는 입력한 글자가 그대로 남으므로, 링크를 공유해도 같은 검색어가 전달됩니다. 한 표시 이름을 두 이벤트가 쓰고 있으면 둘 다 나옵니다. ## 속성 탭 속성 탭에는 프로젝트에서 관측된 속성 키가 모두 모여 있습니다. 이벤트를 하나씩 펼치지 않아도 됩니다. 열은 `속성`·`타입`·`스코프`·`관측 이벤트`·`마지막 발생`·`숨김`이고, 행을 누르면 속성 인스펙터(**개요**·**설정**)가 열립니다. * **타입**: 수집할 때 처음 관측한 값에서 기록한 타입이거나 사람이 고친 타입입니다. 한 번 정해지면 수집이 다시 덮어쓰지 않습니다. 정해진 것이 없으면 `미정`으로 보입니다. 이벤트 인스펙터의 속성 탭은 저장된 타입이 없을 때 표본에서 추정한 값을 `(추정)`으로 흐리게 보여 줍니다. * 타입을 바꾸면 필터 연산자와 쿼리 캐스팅이 바뀝니다. 이미 저장된 필터가 새 타입에서 허용되지 않는 연산자를 쓰고 있을 수 있습니다. `설정 안 됨`으로 되돌리면 표본에서 추론한 값을 다시 씁니다. * **숨김**: 선택기·브레이크다운 차원·롤업 후보·필터 제안에서 빠집니다. 필터로는 계속 쓸 수 있습니다. 숨긴 속성은 목록에서도 빠지므로, 다시 보려면 **숨긴 속성 포함**을 켜세요. * 스코프(`event` / `profile`)가 다르면 이름이 같아도 다른 속성입니다. 이벤트의 `plan`과 프로필의 `plan`은 서로 다릅니다. 목록 위 `전체`·`이벤트`·`프로필`로 좁힙니다. * 속성 탭의 검색과 필터는 **지금 보이는 페이지(25개) 안에서만** 걸러집니다. 찾는 속성이 안 보이면 스코프를 좁히거나 페이지를 넘겨 보세요. ## 파일로 주고받기 목록 위 **가져오기·내보내기**, **템플릿 적용**, **카테고리 제안** 버튼이 각각 패널을 엽니다. * **내보내기**: `reopt-data.events.json`으로 받습니다. CLI가 찾는 파일 이름이라 저장소에 두면 `reopt-data event push`가 바로 읽습니다. 보관한 이벤트와 롤업 속성도 들어 있습니다. * **가져오기**: **없는 것만 추가** 또는 **전부 덮어쓰기**를 고릅니다. 한 번에 이벤트 500개까지입니다. 덮어쓰기는 콘솔에서 쓴 값도 파일 내용으로 바꾸므로 확인을 한 번 더 받습니다. 파일의 이벤트에서 생략한 필드는 그대로 두지 않고 기본값으로 처리합니다. `rollupProperties`를 적지 않은 이벤트는 덮어쓸 때 롤업 속성이 비워집니다. * **템플릿 적용**: 템플릿의 이벤트 가운데 카탈로그에 아직 없는 것만 추가합니다. 이미 있는 이벤트의 표시 이름이나 설명은 바꾸지 않습니다. * **카테고리 제안**: `checkout_`처럼 이벤트 이름의 공통 접두사에서 카테고리를 제안합니다. **적용**을 누르면 그 묶음에 카테고리가 지정됩니다. 콘솔의 가져오기는 파일에 적힌 이벤트만 반영합니다. 파일에 없는 이벤트를 보관으로 넘기는 것은 CLI의 `reopt-data event push`입니다. ### 속성을 파일로 관리하기 파일 `version: 2`에는 `properties` 절이 있습니다. 여기에는 사람이 정한 타입·설명·숨김 세 가지만 담깁니다. ```jsonc { "version": 2, "projectId": "prj_123", "events": { "purchase": { "conversion": true } }, "properties": { "event": { "plan": { "propertyType": "string", "description": "요금제 코드" } }, "profile": { "signup_source": { "hidden": true } }, }, } ``` 이벤트와 다른 규칙이 하나 있습니다. `reopt-data event push`는 파일에서 빠진 이벤트를 `보관`으로 넘기지만, 파일에 없는 속성은 관측된 그대로 남습니다. 속성은 수집될 때 생기므로, 파일에서 빼도 다음 이벤트가 들어오면 다시 생기기 때문입니다. 파일에 선언한 속성에는 이벤트와 같은 규칙이 적용됩니다. `{}`는 "설명 없음, 숨김 아님"으로 처리하므로, 콘솔에서 쓴 설명을 파일에 적지 않으면 지워집니다. 내보내기는 사람이 손댄 속성(설명이 있거나 숨김)만 파일에 씁니다. 관측만 된 키까지 쓰면 파일이 트래픽에 따라 달라져 배포마다 어긋납니다. 손댄 속성이 하나도 없는 프로젝트는 `version: 1` 파일을 그대로 받습니다. 빈 `properties` 절은 "속성을 선언하지 않는다"는 뜻이 되므로 넣지 않습니다. ### CI에서 미선언 속성 검사하기 `reopt-data event verify`는 기본 설정에서 미선언 속성을 보고하지 않습니다. 더 엄격하게 검사하려면 아래 두 플래그 가운데 골라 켭니다. 두 플래그는 서로를 포함하지 않고, 보고 대상이 겹치지 않습니다. | 플래그 | 잡는 것 | | --------------------- | ------------------------ | | `--strict-properties` | 콘솔에서 설명·숨김을 지정했는데 파일에 없음 | | `--strict-observed` | 수집이 관측만 했는데 파일에 없음 | `--strict-observed`는 SDK가 자동으로 보내는 키(`$`로 시작하는 예약 키, `$pageview`의 `path` 같은 자동 이벤트 속성)를 제외합니다. 앱이 정한 키가 아니기 때문입니다. 다만 이름이 같아도 앱이 보낸 `purchase`에 담긴 `value`는 앱의 키이므로 잡힙니다. **주의: 속성을 추가한 PR에서는 잡히지 않습니다.** 그 시점에는 서버가 그 키를 아직 본 적이 없기 때문입니다. 배포 후 첫 이벤트가 들어와야 행이 생기므로, 검사는 그다음 PR에서 실패합니다. 이 점을 감수할 저장소에서만 켜세요. 명령과 종료 코드는 [CLI](/docs/workspace/cli)에 있습니다. ## 트래픽 품질 `/events/traffic`은 수집된 트래픽 가운데 사람으로 판정된 비중을 보는 화면입니다. 기간은 최근 30일로 고정이고 필터는 없습니다. | 항목 | 뜻 | | ---------- | ----------------------- | | `사람 트래픽` | 격리되지 않고 분석에 쓰이는 이벤트 수 | | `의심 트래픽` | 의심 트래픽으로 판정되어 격리된 이벤트 수 | | `확인된 봇` | 봇으로 판정되어 격리된 이벤트 수 | | `분석·과금 제외` | 의심 트래픽과 확인된 봇의 합 | 아래 **격리 사유** 표는 `판정 이유`별 이벤트 수입니다. 이유가 여러 개인 이벤트는 이유마다 한 번씩 세므로, 표의 합이 격리된 이벤트 수보다 클 수 있습니다. 격리된 이벤트는 따로 보관되어 이벤트 스트림과 이 카탈로그의 볼륨에 들어가지 않습니다. ## 주의할 점 * 여러 이벤트를 한 번에 바꾸는 곳은 정리 패널입니다. * 상태 필터의 숫자와 `이벤트`·`전환 이벤트`·`정리 필요` 카드는 필터를 걸기 전의 프로젝트 전체 개수입니다. 지금 화면에 보이는 개수가 아닙니다. * 이벤트 목록은 볼륨이 큰 순서로 한 페이지에 25개씩 나옵니다. * 목록이 비어 있을 때와 불러오지 못했을 때는 다르게 표시됩니다. 불러오지 못했을 때는 다시 시도 버튼이 있습니다. ## 다음 단계 * [이벤트 스트림](/docs/project-workspace/event-stream): 원본 이벤트 하나하나 보기 * [메트릭 관리](/docs/project-workspace/metrics): 롤업 속성과 제외가 집계에 반영된 결과 보기 * [퍼널 분석](/docs/project-workspace/funnel): 전환 이벤트로 단계 만들기 * [CLI](/docs/workspace/cli): 카탈로그 파일을 저장소에서 push·verify 하기 --- # 이벤트 스트림 Source: https://data.reopt.ai/docs/project-workspace/event-stream 수집된 원본 이벤트를 목록으로 보고, 쿼리로 걸러 한 건씩 열어 봅니다. 이벤트 스트림은 집계되기 전의 이벤트를 한 건씩 그대로 보는 화면입니다. 계측이 맞는지 확인할 때, 방금 보낸 이벤트가 도착했는지 볼 때, 이상한 이벤트 한 건을 자세히 볼 때 씁니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/stream`: 사이드바의 **이벤트 스트림**, 단축키 `G` `V` * 이벤트의 의미(표시명·상태·전환 여부)는 [데이터 카탈로그](/docs/project-workspace/event-catalog)에서 정합니다. * 봇·의심 트래픽으로 격리된 이벤트는 여기에 나오지 않습니다. 데이터 카탈로그의 **트래픽 품질**(`/events/traffic`)에서 건수와 사유를 봅니다. ## 링크로 화면 상태 공유하기 기간, 필터, 열, 펼친 행까지 모두 URL에 들어갑니다. 링크를 보내면 받는 사람도 같은 화면을 봅니다. | 파라미터 | 뜻 | | ------------ | ------------------------------------------------- | | `?query=` | 쿼리 (아래 문법) | | `?range=` | 기간 (`15m`, `1h`, `24h`, `7d`, `custom`. 기본 `24h`) | | `?from=` | 사용자 지정 기간 시작 (ISO 시각, `range=custom`일 때만) | | `?to=` | 사용자 지정 기간 끝 | | `?order=` | `oldest`면 오래된 순. 기본은 최신 순 | | `?cols=` | 표시할 열과 순서 (쉼표 구분, 최대 12개) | | `?selected=` | 상세 패널을 열어 둘 행의 id | | `?live=1` | 실시간 스트림 켜기 (기본 꺼짐. 아래 비용 참고) | | `?hist=0` | 상단 타임라인 숨기기 | 기간 선택에는 `최근 15분`·`최근 1시간`·`최근 24시간`·`최근 7일`이 있습니다. `custom`(사용자 지정)은 타임라인을 드래그해 구간을 좁혔을 때 생깁니다. 기간을 `custom`으로 잡으면 끝 시각이 고정되어 그 뒤로 도착할 이벤트가 없으므로 **실시간 스트림을 쓸 수 없습니다.** 타임라인을 드래그하면 스트림이 꺼지고 스위치가 비활성화됩니다. `range=custom&live=1`이 함께 든 링크로 들어오면 "고정된 기간에서는 실시간 스트림을 사용할 수 없습니다."라는 안내가 나옵니다. `from`과 `to`가 거꾸로 된 링크는 빈 결과를 보여 주지 않고 두 값을 바꿔서 읽습니다. 쿼리나 사용자 지정 기간이 걸려 있으면 **초기화**가 나타납니다. 누르면 쿼리를 비우고 기간을 최근 24시간으로 되돌립니다. ## 쿼리로 이벤트 거르기 검색창 하나에 자유 검색어와 필드 필터를 함께 씁니다. 아래는 검색창에 힌트로 나오는 예시입니다. `status`는 문법을 보여 주려고 넣은 이름이고, 실제 필드는 아래 목록에서 고릅니다. ``` path:/checkout -status:200 utm_source:* name:(signup or login) ``` | 쓰는 법 | 뜻 | | ----------------- | ---------------------- | | `결제` | 자유 검색 (아래 범위) | | `path:/checkout` | 값이 정확히 같음 | | `path:*checkout*` | 값에 포함됨 | | `utm_source:*` | 값이 있기만 하면 됨 | | `duration>1000` | 비교 (`>` `>=` `<` `<=`) | | `name:(a or b)` | 여럿 중 하나 | | `-country:KR` | 앞에 `-`를 붙이면 제외 | 절끼리는 AND, 괄호 안은 OR입니다. 공백이나 괄호가 든 값은 `"결제 완료"`처럼 따옴표로 묶습니다. 자유 검색은 이벤트 이름, 경로, 사용자·디바이스·세션 ID, 속성 값에서 찾습니다. 국가나 브라우저 같은 나머지 컬럼은 `country:KR`처럼 필드로 거르세요. 자유 검색어를 여러 개 쓰면 공백으로 이은 한 문구로 찾습니다. 떨어져 있는 두 단어를 함께 찾으려면 하나를 필드 절로 바꾸세요. 서버가 받는 한도는 필드 절 10개, 절마다 값 20개입니다(`name:`에는 이름 50개까지). 조건이 그보다 길어지면 저장된 세그먼트를 쓰세요. `/`를 누르면 검색창으로 바로 이동합니다. ### 컬럼과 속성 `path`처럼 모든 이벤트에 있는 컬럼과 SDK가 보낸 속성 키를 같은 문법으로 씁니다. `path:/checkout`과 `plan:pro`는 같은 종류의 절이고, 컬럼인지 속성인지는 화면이 구분해서 찾습니다. 컬럼은 `name`·`path`·`profile_id`·`session_id`·`device_id`·`country`·`city`·`region`· `os`·`browser`·`device`·`brand`·`model`·`origin`·`referrer`·`channel_type`·`utm_*`·`duration` 등이고, 필드 목록에서 검색해 고를 수도 있습니다. 처음 보이는 열은 **시각·이벤트·경로·사용자·환경·위치**입니다. ### 필드 사이드바와 열 툴바의 **필드**는 왼쪽 사이드바를 여닫습니다. `이벤트 필드`(컬럼)와 `속성`(SDK가 보낸 키)이 나뉘어 나옵니다. * 필드를 펼치면 지금 쿼리와 기간에서 그 필드의 상위 값 10개를 셉니다. 값 옆의 `… 만 보기`·`… 제외`를 누르면 쿼리에 절이 추가됩니다. * 필드를 열로 추가하거나 뺍니다. 열은 최대 12개까지 주소(`?cols=`)에 남고, 열 머리글에서 좌우로 옮깁니다. * 속성 목록은 쿼리가 이벤트 이름 하나로 좁혀져 있으면 그 이벤트의 키를, 아니면 기간 안 이벤트 표본에서 읽은 키를 보여 줍니다. 표본에 없던 키는 빠질 수 있습니다. * `시각` 열 머리글로 `최신 순`과 `오래된 순`을 바꿉니다(`?order=`). **타임라인** 버튼은 위쪽 막대 차트를 켜고 끕니다. **행 간격**(`촘촘하게`·`보통`·`여유롭게`)과 사이드바 열림 여부는 주소가 아니라 이 브라우저에 프로젝트별로 저장됩니다. ### `name:`으로 표시명 검색하기 스트림은 원본 키(`purchase_completed`)를 저장하지만, 카탈로그에서 "결제 완료"라고 이름을 붙였다면 `name:"결제 완료"`로도 찾을 수 있습니다. 그 표시명을 쓰는 원본 키까지 넓혀서 찾습니다. * 검색창에는 입력한 글자가 그대로 남습니다. 원본 키로 넓히는 일은 쿼리를 적용할 때 일어나므로, 링크를 공유해도 원본 키가 아니라 입력한 말이 그대로 전달됩니다. * 입력한 이름을 원본 키로 바꾸지 않고, 원본 키를 더해서 찾습니다. 어떤 이벤트의 원본 키가 다른 이벤트의 표시명과 같을 수 있기 때문입니다. 한 표시명을 두 이벤트가 쓰고 있으면 둘 다 나옵니다. ### 실시간 스트림의 필터 적용 목록과 타임라인은 서버가 걸러서 돌려줍니다. 실시간 스트림은 연결에 실어 보낼 수 있는 조건이 더 좁아서, 도착한 행을 브라우저가 같은 쿼리로 한 번 더 확인합니다. 사용할 때는 이 차이를 신경 쓰지 않아도 됩니다. 목록에 나오지 않을 행이 실시간으로 들어오는 일은 없습니다. ## 실시간 스트림 실시간 스트림은 기본으로 꺼져 있습니다. 툴바의 `스트림` 스위치로 켜고, 켜져 있지 않으면 `중지됨`이라고 표시됩니다. 실시간 스트림은 데이터 포인트를 쓰지 않습니다. 쿼리를 바꾸거나, 다시 연결하거나, 다른 탭에서 켜면 각각 새 연결입니다. 최신 행에서 벗어나 다른 곳을 읽고 있으면 새 행이 화면을 밀어내지 않고 기다립니다. 읽던 자리가 유지되고, 위에 `새 이벤트 N건`이 뜹니다. 누르면 최신 행까지 보여 줍니다. 기다리는 행이 1,000건을 넘으면 `1000+`로 표시하고, 한 건씩 끼워 넣는 대신 목록을 새로 불러옵니다. 타임라인은 서버 집계라 분 단위로 갱신되지만, 그 사이 도착한 실시간 행은 마지막 막대에 더해 셉니다. 목록과 타임라인의 건수가 서로 어긋나지 않게 하기 위해서입니다. ## 행 하나 열어보기 행을 클릭하면 오른쪽에 상세 패널이 열립니다. `?selected=`로 주소에 남으므로 그 행을 그대로 링크할 수 있습니다. 패널은 목록 위에 겹쳐 열리고, 목록은 그대로 스크롤하고 누를 수 있습니다. 패널의 화살표나 키보드 `↑`·`↓`(또는 `k`·`j`)로 이전/다음 행으로 이동하고, `Esc`로 닫습니다. * **이벤트**: 이벤트 이름(원본 키), 시각, 이벤트 ID, 경로, 도메인, 세션, 사용자, 디바이스 ID. * **속성**: 이 이벤트에 담겨 온 키와 값 전부. 키 옆의 타입 표시가 값의 종류를 알려 줍니다. 값마다 복사 버튼이 있고, 아래 **원본 JSON**에서 전체를 봅니다. * **환경**·**유입**: OS, 브라우저, 디바이스, 위치와 채널, 유입 경로, UTM. 값이 있는 항목만 나옵니다. * 링크: 세션은 세션 상세로, 사용자는 사용자 프로필로, 예외 행이면 해당 이슈로 바로 이동합니다. 목록의 이벤트 열에는 카탈로그에 표시 이름이 있으면 그 이름이 보이고, 누르면 카탈로그의 그 이벤트로 이동합니다. 상세 패널은 원본 키를 그대로 보여 줍니다. ### 속성 타입 표시 속성 옆의 타입 표시는 그 행의 값에서 추론합니다. 다만 값만으로는 판단할 수 없는 경우가 있습니다. `"3"`은 버전 코드일 수도 수량일 수도 있지만 둘 다 문자열로 보입니다. 그래서 카탈로그에 타입이 정해져 있으면 값에서 추론한 타입보다 카탈로그의 타입을 우선합니다. 카탈로그에 설명이 적혀 있으면 키에 마우스를 올렸을 때 보이므로, 키의 뜻을 이 화면에서 바로 확인할 수 있습니다. 이 조회는 행을 열었을 때만 일어납니다. 속성 키는 이벤트마다 달라서, 모든 이벤트의 정의를 한 번에 읽어 오면 지금 행에 없는 키의 설명까지 섞이기 때문입니다. ## 예외 행 `$exception` 이벤트는 목록에서 다르게 표시됩니다. 이름 자리에 배지와 함께 **예외 타입: 메시지**가 보여서, `$exception` 행이 여러 줄 이어져도 어떤 오류인지 구분됩니다. 배지는 두 가지입니다. `try/catch` 없이 올라온 예외는 **미처리**, 코드가 잡아서 보고한 예외는 **에러**입니다. 셀에서 `… 만 보기` / `… 제외`를 쓰면 메시지가 아니라 `$exception`으로 걸립니다. 메시지는 이벤트 이름이 아니기 때문입니다. 행에 색을 주는 기준도 같습니다. 메시지를 기준으로 하면 오류마다 색이 달라지기 때문입니다. 이슈로 묶인 예외는 상세 패널 맨 위 **에러** 구역의 `이슈 상세 열기`로 `/errors/[issueId]` 화면에서 이어 봅니다. 행에 이슈 정보가 아직 없으면 링크 대신 "이슈로 묶이는 중입니다."가 보입니다. ## 저장된 필터 자주 쓰는 조사 조건은 이름을 붙여 저장할 수 있습니다. 쿼리와 열 구성이 저장되고, 기간과 열어 둔 행은 저장되지 않습니다. **이 목록은 브라우저에, 프로젝트별로 저장됩니다.** 프로젝트 설정이 아니어서 다른 사람과 공유되지 않습니다. 다른 사람에게 화면을 건넬 때는 URL을 쓰세요. 12개까지 저장되고, 그보다 많아지면 오래된 것부터 빠집니다. 같은 이름으로 저장하면 덮어씁니다. ## 주의할 점 * 사용자 지정 기간에서는 실시간 스트림이 동작하지 않습니다. 이유는 위 설명을 참고하세요. * 한 번에 50건씩 불러옵니다. 기간 전체를 다 보여 준 뒤에는 끝까지 불러왔다는 안내가 나옵니다. 한 화면에 쌓아 두는 행은 5,000건까지이므로, 그보다 더 보려면 기간이나 쿼리를 좁히세요. * 쿼리, 기간, 정렬을 바꾸면 목록을 처음부터 다시 불러옵니다. ## 다음 단계 * [데이터 카탈로그](/docs/project-workspace/event-catalog): 이벤트에 이름·상태·전환 여부 붙이기 * [세션 분석](/docs/project-workspace/sessions): 방문 하나의 이벤트 흐름 보기 * [에러 트래킹](/docs/project-workspace/error-tracking): 예외 행이 묶인 이슈 보기 --- # 퍼널 Source: https://data.reopt.ai/docs/project-workspace/funnel 단계 이벤트를 연결해 전환율과 이탈 지점을 확인합니다. 퍼널은 사용자가 목표 행동까지 가는 과정에서 어디서 이탈하는지 볼 때 사용합니다. 전환 이벤트 정의가 정리되어 있을수록 결과를 더 정확하게 해석할 수 있습니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/funnel` ## 이 화면에서 답할 수 있는 질문 * 어떤 단계에서 사용자가 가장 많이 이탈하나요? * 특정 기간에 전환율이 좋아지거나 나빠졌나요? * 세그먼트별 또는 기간별 차이가 있나요? ## 핵심 입력 * 단계 이벤트 목록: 2개부터 10개까지 넣을 수 있고, 같은 이벤트를 여러 단계에 넣을 수 있습니다. 단계마다 속성 조건(예: `path = /pricing`)을 붙여 그 이벤트 가운데 조건에 맞는 것만 단계로 삼습니다. 조건 하나에 값을 여러 개 넣으면 OR, 조건을 여러 개 두면 AND입니다. * 조회 기간: 프로젝트 시간대 기준의 날짜 범위. 기본값은 최근 30일입니다. * 세그먼트 * 전환 기간: **1단계 이벤트부터 마지막 단계까지** 허용하는 전체 시간입니다. 단계 사이마다 따로 적용되는 값이 아닙니다. 각 단계는 이전 단계보다 뒤에 발생해야 합니다. 1·3·7·14·30일 중에서 고르고 기본값은 7일입니다. 단계 속성 조건은 단계마다 10개까지, 조건 하나의 값은 20개까지 넣을 수 있습니다. 연산자는 `=`, `≠`, `포함`, `미포함`, 크기 비교(`>`, `≥`, `<`, `≤`), `값 있음`, `값 없음`이며 속성 타입에 맞는 것만 표시됩니다. 값을 여러 개 넣을 수 있는 것은 `=`, `≠`, `포함`, `미포함`입니다. 사용자 수는 디바이스 기준으로 셉니다. 한 디바이스는 한 번만 세고, 1단계 이벤트를 여러 번 했다면 그중 가장 멀리 진행한 시도를 기준으로 합니다. 단계 이벤트는 모두 조회 기간 안에 있어야 합니다. 조회 기간이 끝난 뒤에 일어난 다음 단계는 전환으로 세지 않습니다. ## 보기 모드 * `퍼널`: 단계별 전환과 이탈을 봅니다. 위쪽에 진입 사용자, 완료 사용자, 전체 전환율, 가장 큰 이탈이 요약되고, 차트는 단계 흐름·목록·세로 막대·가로 막대 중에서 고릅니다. 단계별 요약의 **이탈 사용자**에서 그 단계에서 멈춘 사용자 목록을 엽니다. * `트렌드`: 1단계 이벤트가 발생한 날(또는 주) 기준으로 진입·전환 수와 전환율 변화를 봅니다. 프로젝트 시간대로 나눕니다. * `기간 비교`: 현재 조회 기간과, 바로 앞의 같은 길이 기간을 나란히 봅니다. * `이탈 분석`: 특정 단계에서 이탈한 사용자가 조회 기간 안에 대신 발생시킨 이벤트 상위 10개를 봅니다. 퍼널 단계에 쓴 이벤트는 제외합니다. ## 출력 * 단계별 전환율 * 단계별 이탈 수와 이탈률, 이탈 사용자 목록 * 기간 비교 결과 * 시계열 추세 * 이탈 후 행동 단계별 전환율과 이탈률은 직전 단계 대비 값이고, 전체 전환율은 1단계 대비 마지막 단계의 비율입니다. ## 저장하고 다시 열기 **대시보드에 저장**은 단계·속성 조건·전환 기간·세그먼트·상대 기간을 리포트로 저장합니다. 대시보드 타일의 **분석 열기**를 누르면 그 정의 그대로 이 화면이 열립니다. 기간은 저장된 상대 범위(예: 최근 30일)를 오늘 기준으로 다시 계산합니다. 차트 종류와 단계 색상은 읽는 사람의 보기 설정이라 리포트에 저장되지 않습니다. 경로 분석에서 **이 경로를 퍼널로 보기** → **퍼널 페이지에서 열기**를 누르면 그 경로가 단계로 채워진 채 이 화면이 열립니다. 페이지 단계는 해당 경로로 좁힌 페이지뷰로 들어오고, 전환 기간은 1일로 시작합니다. ## 주의할 점 * 퍼널은 전환 이벤트 정의가 잘 되어 있어야 의미가 있습니다. * 세그먼트를 같이 쓰면 전체 평균이 아니라 특정 사용자군의 전환 문제를 볼 수 있습니다. * **기간 비교** 탭은 배포 전후, 캠페인 전후, 온보딩 실험 전후를 비교할 때 씁니다. * 코호트 비교 화면의 퍼널도 같은 방식(순서 + 전환 기간)으로 세지만, 전환 기간이 1일로 고정이고 단계 속성 조건이 없습니다. 이 화면에서 전환 기간을 1일로 두고 조건 없이 볼 때 두 화면의 숫자가 일치합니다. * 조회는 데이터 포인트를 쓰지 않습니다. 같은 조건은 5분 동안 캐시되어 탭을 오가거나 목록을 넘겨도 다시 계산하지 않습니다. ## 다음 단계 * 코호트별 재방문을 보려면 [리텐션](/docs/project-workspace/retention) * 이탈 사용자를 좁히려면 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * 경로에서 퍼널 단계를 찾으려면 [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) * 데이터 포인트 기준은 [비용과 보존 정책](/docs/workspace/cost-and-retention) --- # 메트릭 관리 Source: https://data.reopt.ai/docs/project-workspace/metrics 시간별 롤업이 어떤 이벤트를 어떤 속성으로 집계하는지 확인하고, 롤업 상태와 결과를 살펴봅니다. 메트릭 관리는 시간별 롤업을 확인하는 화면입니다. 롤업은 원본 이벤트를 조회할 때마다 다시 세지 않도록 시간 단위로 미리 세어 둔 집계입니다. 어떤 이벤트가 어떤 속성으로 나뉘어 집계되는지, 집계가 얼마나 최신인지, 지금 보는 숫자가 롤업에서 왔는지 원본에서 왔는지를 여기서 확인합니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/metrics`: 사이드바의 **메트릭 관리**, 단축키 `G` `M` * 상단 탭은 **롤업 설정**(기본)과 **탐색**(`?tab=explore`) 두 개입니다. ## 이 화면에서 답할 수 있는 질문 * 이 이벤트는 총계만 집계되나요, 속성별로도 집계되나요? * 롤업이 어디까지 반영됐고, 재계산이 밀려 있지는 않나요? * 방금 본 숫자는 롤업에서 왔나요, 원본 이벤트를 직접 읽었나요? * 조회가 느린 이유가 무엇이고, 빠르게 만들 수 있나요? ## 시간별 롤업이 세어 두는 것 | 집계 | 내용 | | ------ | --------------------------------- | | 이벤트 총계 | 이벤트 이름별 시간당 건수 | | 속성별 건수 | 이벤트에 롤업 속성으로 등록한 키의 값별 시간당 건수 | | 트래픽 | 세션, 페이지뷰, 신규 방문, 이탈을 채널·UTM·리퍼러별로 | 롤업은 약 5분마다 갱신됩니다. 조회는 직전 시간대보다 앞선 구간만 롤업에서 읽고, 현재 시간대와 직전 시간대는 원본 이벤트에서 읽어 합칩니다. 그래서 오늘을 포함한 기간을 보면 출처가 `롤업+실시간`으로 표시됩니다. ## 롤업 속성과 제외 롤업 속성은 시간별 집계가 이벤트를 나눠 세는 속성 키입니다. `purchase`에 `plan`을 등록하면 요금제별 구매 수가 시간 단위로 미리 집계되고, `plan`으로 나눈 조회가 롤업에서 응답합니다. * 이벤트당 최대 20개까지 등록할 수 있습니다. * 등록한 키가 없는 이벤트는 표에 `총계만`으로 표시됩니다. 그 이벤트를 속성으로 나눠 보면 원본 이벤트를 읽습니다. * 키마다 하루(UTC)에 건수가 많은 값 1,000개까지만 따로 세고, 값은 256자에서 잘립니다. 사용자 ID처럼 값이 아주 다양한 속성은 롤업 속성으로 맞지 않습니다. * SDK가 자동으로 보내는 이벤트는 등록하지 않아도 정해진 키로 집계됩니다. `$pageview`는 `path`·`origin`·`referrer`·`utm_source`·`utm_medium`·`utm_campaign`입니다. 표의 롤업 속성 열에는 직접 등록한 키만 보입니다. 자동 이벤트에 키를 직접 등록하면 기본 키 대신 등록한 목록이 쓰입니다. 제외는 이벤트 상태를 `내부`로 두는 것입니다. 표에 `내부 (집계 제외)`로 표시되고, 이벤트 수를 세는 조회에서 빠집니다. ### 설정을 바꾸는 곳 롤업 설정 탭은 현재 설정을 보여 주기만 합니다. 바꾸는 곳은 아래와 같습니다. | 바꾸려는 것 | 바꾸는 곳 | | ------------- | ---------------------------------------------------------------------------------------------------------------- | | 제외(내부), 전환 여부 | 이벤트를 고르고 **카탈로그에서 설정 편집** → [데이터 카탈로그](/docs/project-workspace/event-catalog) 인스펙터의 **설정** 탭 | | 롤업 속성 추가 | **탐색** 탭에서 롤업에 없는 키를 썼을 때 나타나는 `<키> 롤업에 추가` 버튼 | | 롤업 속성 전체 관리 | 카탈로그 파일의 `rollupProperties`. [CLI](/docs/workspace/cli)의 `reopt-data event push`나 데이터 카탈로그의 **가져오기·내보내기**로 반영합니다 | 카탈로그 인스펙터의 설정 탭에는 전환 여부와 상태만 있습니다. 롤업 속성은 표의 나머지 두 경로로 바꿉니다. ## 롤업 설정 탭 ### 롤업 상태 화면 위쪽 네 칸이 롤업의 현재 상태입니다. 1분마다 다시 읽습니다. | 항목 | 뜻 | | ------------------ | ------------------------------------------------------- | | `events_hourly 행` | 이벤트 롤업에 쌓인 행 수와 가장 최근 집계 시각(UTC). `비어 있음`이면 아직 집계가 없습니다 | | `traffic_hourly 행` | 트래픽 롤업의 행 수와 가장 최근 집계 시각(UTC) | | `재계산 대기 시간대` | 새 이벤트가 들어와 다시 계산해야 하는 시간대 수. 다음 갱신 때 처리됩니다 | | `전체 재계산` | `정상`, `대기열`(재계산이 예약됨), `재계산 중` 가운데 하나 | 헤더의 **전체 재계산**은 프로젝트의 전체 이력을 다시 집계하도록 예약합니다. 이미 예약됐거나 진행 중이면 누를 수 없습니다. 재계산은 5분 주기 갱신이 한 번에 한 프로젝트씩 나눠 처리하므로, 이력이 길면 여러 주기에 걸쳐 끝납니다. ### 이벤트 표 최근 30일 안에 수집됐거나 카탈로그에 등록된 이벤트가 모두 나옵니다. * 열: `이벤트`, `30일 이벤트`, `상태`, `전환`, `롤업 속성` * 검색창은 이벤트 이름과 롤업 속성 키를 함께 찾습니다. 정렬은 `이벤트 수`와 `이름` 두 가지입니다. * `30일 이벤트`는 기간 선택과 상관없이 항상 최근 30일 건수입니다. 행을 누르면 오른쪽에 그 이벤트의 롤업 속성·상태·전환 여부와 **기간 내 이벤트** 건수가 나옵니다. 롤업 속성이 있으면 첫 번째 키의 상위 값 5개도 함께 보여 줍니다. ### 미리보기 선택한 기간(기본 최근 30일)의 트래픽 합계와 채널·리퍼러 상위 8개를 보여 줍니다. 날짜는 프로젝트 시간대 기준입니다. 숫자마다 붙은 배지는 **데이터 출처**입니다. | 배지 | 뜻 | | -------- | -------------------------------------- | | `롤업` | 기간 전체를 롤업에서 읽었습니다 | | `롤업+실시간` | 앞 구간은 롤업, 최근 구간은 원본에서 읽어 합쳤습니다. 정상입니다 | | `원본 스캔` | 롤업을 쓰지 못하고 원본 이벤트를 직접 읽었습니다. 느릴 수 있습니다 | ## 탐색 탭 탐색 탭은 트래픽 지표와 이벤트 수를 겹쳐 그려 보는 보드입니다. 패널마다 쿼리를 여러 개 넣고 한 차트에서 비교합니다. * **쿼리 소스**: `트래픽`은 세션·페이지뷰·신규 방문·재방문·이탈률 가운데 하나, `이벤트`는 이벤트 하나 또는 `전체 이벤트`입니다. * **이벤트 쿼리**: `breakdown 속성`과 상위 N(1–10, 기본 5)으로 나누고, 속성 필터를 5개까지 붙입니다. 연산자는 `=`·`≠`·`포함`·`미포함` 가운데 속성 타입에 맞는 것이 나옵니다. * **보드 전체 설정**: 기간(기본 최근 30일), 버킷 단위(`시간`·`일`·`주`·`월`), `이전 기간과 비교`(기본 켜짐), 시리즈 색상. * **패널 설정**: 차트 유형(`라인`·`영역`·`막대`·`누적 막대`·`표`·`스탯`·`스파크라인`), `normalize (최대값 대비 %)`, `반폭`, 임계선(패널당 5개). * 차트 위를 드래그하면 그 날짜 구간으로 좁혀집니다. 브라우저 뒤로 가기로 되돌립니다. 보드는 패널 12개, 패널당 쿼리 10개까지입니다. 한 패널의 시리즈가 40개를 넘으면 작은 값부터 빠지고 `시리즈 일부 생략`이 표시됩니다. ### 롤업을 쓰지 못했을 때 패널이 원본 이벤트를 읽으면 `원본 스캔` 배지와 함께 **롤업을 쓰지 못했습니다** 안내에 이유가 나옵니다. | 이유 | 할 수 있는 일 | | ----------------------- | ------------------------------------------------------ | | 롤업에 등록되지 않은 breakdown 키 | `<키> 롤업에 추가`를 누르고 재계산이 끝나길 기다립니다 | | `=` 이외의 연산자를 쓴 필터 | `=` 필터로 바꾸거나 느린 조회를 감수합니다 | | 롤업이 담고 있지 않은 속성 필터 | 속성 필터가 걸린 이벤트 조회는 원본을 읽습니다(아래 참고) | | 프로젝트 시간대가 UTC 정시가 아님 | UTC와 30분·45분 어긋나는 시간대(예: `Asia/Kolkata`)는 롤업을 쓸 수 없습니다 | | 롤업을 다시 만드는 중 | 재계산이 끝나면 풀립니다 | | 아직 집계되지 않은 최근 구간 | 안내에 표시된 시각부터 자동으로 빨라집니다 | | 롤업을 처음 검증하는 중, 갱신 지연 | 기다리면 풀립니다 | `롤업에 추가`는 그 키로 나누는 조회를 빠르게 합니다. 버튼은 필터에 쓴 키에도 나타나지만, 속성 필터가 걸린 이벤트 조회는 키를 등록해도 원본을 읽습니다. `$pageview`를 `surface`·`route_kind`·`page_id`·`publication_id`의 `=` 조건으로 거르는 경우만 롤업에서 응답합니다. 이벤트당 20개 한도를 넘게 되면 버튼이 비활성화되니, 쓰지 않는 키를 먼저 정리하세요. ### 보드 공유와 저장 보드 구성은 전부 주소에 담깁니다. **링크 복사**로 그대로 공유하세요. 기간이 `최근 30일` 같은 상대 표현이면 링크를 여는 날 기준으로 다시 계산됩니다. 보드가 커서 주소에 담기지 않으면 `URL 미포함` 배지가 붙고, 그 보드는 지금 브라우저 세션에서만 복원됩니다. **보드 저장**은 이 브라우저에 프로젝트별로 12개까지 저장합니다. 다른 사람과 공유할 때는 링크를 쓰세요. ## 설정을 바꾸면 달라지는 것 * 롤업 속성을 바꾸면 전체 이력 재계산이 예약됩니다. 상태가 `대기열` → `재계산 중` → `정상`으로 바뀝니다. 재계산 중에는 이 프로젝트의 속성별 조회가 원본 이벤트를 읽어 느려질 수 있고, 재계산이 끝나야 새 키가 롤업에서 응답합니다. 상태가 `정상`으로 돌아오기 전에는 새 키로 나눈 결과를 확정된 숫자로 보지 마세요. * 이벤트를 내부로 바꾸면 그 이벤트가 이벤트 수 집계에서 빠집니다. 이 화면의 `전체 이벤트` 건수, 탐색 탭의 이벤트 쿼리, 대시보드의 이벤트 추이 타일이 해당됩니다. 세션·페이지뷰 같은 트래픽 지표는 그대로입니다. * 대시보드 타일은 결과를 5분 동안 캐시하므로 변경이 그만큼 늦게 보일 수 있습니다. ## 주의할 점 * 롤업은 조회 속도를 높이기 위한 것입니다. 롤업을 쓰지 못한 조회는 같은 기간의 원본 이벤트를 직접 셉니다. 느려질 뿐 결과가 비지는 않습니다. * 쿼리 줄의 `원본 조회` 배지는 실행 전 예상이고, 패널 제목 옆 배지가 실제 출처입니다. * 기간 선택 옆의 `MotherDuck · 예약 집계` 같은 표시는 이 조직의 분석 저장소와 롤업을 만드는 방식입니다. ## 다음 단계 * 제외와 전환 여부를 정하려면 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 롤업 속성을 파일로 관리하려면 [CLI](/docs/workspace/cli) * 집계 전 이벤트를 한 건씩 확인하려면 [이벤트 스트림](/docs/project-workspace/event-stream) * 반복해서 볼 지표는 [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) --- # 페이지 분석 Source: https://data.reopt.ai/docs/project-workspace/page-analytics 페이지별 페이지뷰, 세션, 이탈률, 평균 체류를 이전 기간과 비교합니다. 페이지 분석은 어떤 페이지가 방문을 가장 많이 받고 어느 페이지에서 사람들이 떠나는지 보는 화면입니다. 페이지별 성과를 같은 기준으로 나란히 놓고 비교합니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/pages`: 사이드바의 **페이지 분석** ## 이 화면에서 답할 수 있는 질문 * 방문이 가장 많은 페이지는 어디인가요? * 지난 기간보다 방문이 늘거나 줄어든 페이지는 어디인가요? * 들어오자마자 떠나는 비율이 높은 페이지는 어디인가요? * 사람들이 오래 머무는 페이지와 금방 지나가는 페이지는 어디인가요? ## 입력 * **조회 기간**: 기본은 `최근 30일`입니다. `오늘`·`어제`·`최근 7일`·`최근 30일`·`최근 90일` 중에서 고르거나 날짜를 직접 지정합니다. 프로젝트 시간대의 날짜 단위로 조회하므로, 고른 날짜는 그날 전체가 포함됩니다. * **페이지 검색**: 제목, 경로, 도메인에 검색어가 포함된 페이지만 남깁니다. 대소문자는 구분하지 않습니다. ## 표 읽는 법 한 행은 도메인 + 경로 하나입니다. 같은 경로라도 도메인이 다르면 다른 행입니다. 세션이 많은 순서로(같으면 페이지뷰가 많은 순서로) 상위 100개 페이지를 보여줍니다. 열 머리글을 누르면 불러온 행 안에서 다시 정렬합니다. | 열 | 뜻 | | ---------- | ---------------------------------------------------------------------- | | `페이지` | 그 페이지에서 가장 최근에 수집된 제목과, 그 아래 도메인 + 경로. 제목이 없으면 경로를 보여줍니다 | | `페이지뷰` | 기간 안에 그 페이지에서 발생한 `$pageview` 수 | | `세션` | 그 페이지를 한 번이라도 본 세션 수 | | `이전 기간 대비` | 세션 수의 증감률. 비교 대상은 조회 기간 바로 앞의 같은 길이 기간입니다 | | `이탈률` | 그 페이지를 본 세션 중 이탈 세션의 비율. 이탈 세션은 한 페이지만 보고 10초 안에 떠난 방문입니다 | | `평균 체류` | 그 페이지뷰에서 같은 세션의 다음 페이지뷰까지 걸린 시간의 평균. 다음 페이지뷰가 30분 안에 있었던 경우만 계산에 들어갑니다 | `이전 기간 대비`가 `신규`로 보이는 페이지는 이전 기간의 결과에 없던 페이지입니다. ## 데이터가 보이려면 이 화면은 브라우저 SDK가 보내는 `$pageview` 이벤트로 만들어집니다. 경로가 비어 있는 페이지뷰는 제외됩니다. * Next.js App Router에서는 `ReoptPageView` 컴포넌트가 이동할 때마다 `$pageview`를 보냅니다. * 그 밖의 SPA에서는 초기화 때 첫 페이지뷰가 자동으로 나가고, 화면이 바뀔 때 `pageView()`(React에서는 `usePageView()`)를 직접 호출해야 합니다. * 이탈 판정에 쓰는 체류 시간은 `$pageleave`가 보고합니다. 기본으로 켜져 있으니 `capture.pageleave`를 끄지 마세요. 자세한 설치 방법은 [추적 설정](/docs/workspace/tracking-setup)에 있습니다. ## 주의할 점 * `/orders/8f3a...`처럼 ID가 들어간 경로는 주문마다 다른 행이 됩니다. SDK의 `normalizePath`로 `/orders/:id`처럼 묶어 보내세요. `pageView({ path })`로 직접 넘긴 경로에는 `normalizePath`가 적용되지 않으니 같은 함수를 직접 거쳐야 합니다. * `평균 체류`는 다음 페이지뷰가 있어야 잴 수 있습니다. 세션의 마지막 페이지에서 머문 시간은 들어가지 않고, 잴 수 있는 페이지뷰가 하나도 없으면 `0초`로 보입니다. * `평균 체류`는 검색어 없이 읽으세요. 검색 중에는 검색에 걸린 페이지뷰끼리의 간격으로 계산되어 값이 달라집니다. * `신규`는 이전 기간에 방문이 전혀 없었다는 뜻이 아닐 수 있습니다. 이전 기간도 같은 조건의 상위 100개 페이지만 비교 대상으로 삼기 때문에, 그때 순위 밖이었던 페이지도 `신규`로 표시됩니다. * `이탈률`이 높아도 그 페이지만의 문제가 아닐 수 있습니다. 이탈 세션은 한 페이지만 본 방문이므로, 주로 진입 페이지로 쓰이는 페이지에서 높게 나옵니다. * 조회 기간과 검색어는 URL에 저장되지 않습니다. 새로고침하면 기본값으로 돌아갑니다. * 이 화면의 조회는 데이터 포인트를 차감하지 않습니다. ## 다음 단계 * 한 방문이 페이지를 어떤 순서로 지났는지 보려면 [세션 탐색](/docs/project-workspace/sessions) * 페이지 사이의 이동 흐름과 유입 채널을 보려면 [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) * 특정 페이지를 단계로 넣어 전환을 보려면 [퍼널](/docs/project-workspace/funnel) --- # 실시간, 인사이트, 알림 Source: https://data.reopt.ai/docs/project-workspace/realtime-insights-alerts 실시간 이벤트를 확인하고, 이상 징후를 탐지하고, 조건에 맞으면 알림을 받는 방법을 안내합니다. 실시간, 인사이트, 알림은 데이터의 변화를 확인하고 팀에 알리는 화면입니다. 실시간으로 상태를 보고, 인사이트로 이상 유형을 확인하고, 알림으로 팀에 전달하세요. ## 진입 경로 * 실시간: `/dashboard/[orgId]/[projectId]/realtime` * 인사이트: `/dashboard/[orgId]/[projectId]/insights` * 알림: `/dashboard/[orgId]/[projectId]/alerts` ## 실시간 SSE 연결로 이벤트를 실시간으로 보는 화면입니다. * 활성 사용자 (5분): 최근 5분 동안 이벤트를 보낸 디바이스 수 * 지난 1분, 지난 1시간, 지난 24시간 이벤트 수 * 인기 이벤트 (1시간): 최근 1시간 동안 많이 발생한 이벤트 상위 5개 * 실시간 이벤트 피드: 가장 최근 100건. 이벤트 이름과 경로·국가·브라우저를 보여주고, 예외 이벤트는 에러로 구분합니다 * 연결 상태: `Connected` 또는 `Disconnected` 숫자는 30초마다 서버 집계로 갱신되고, 그 사이에 도착한 이벤트는 화면에서 바로 더합니다. 피드는 이벤트가 수집된 뒤 분석 저장소에 반영되는 시점에 채워집니다. 실시간 화면은 데이터 포인트를 쓰지 않습니다. 다 봤으면 **실시간 일시 중지**를 누르세요. 연결을 끊습니다. 다시 보려면 **실시간 다시 연결**을 누르세요. 재연결과 다른 탭은 각각 새 연결로 계산합니다. 같은 프로젝트에 1분에 30번 넘게 연결하면 잠시 거절됩니다. ## 인사이트 이벤트와 세션의 일별 추이에서 이상 징후를 찾아 카드로 보여줍니다. 탐지는 자동으로 돌지 않습니다. 유형과 값을 고른 뒤 **이상 탐지 실행**을 눌러야 새 결과가 쌓입니다. 탐지 유형: | 유형 | 찾는 것 | | ---------- | ---------------------------------------------------------------------------------------------------- | | 볼륨 이상 탐지 | 이벤트별 일별 발생 수의 급증·급감. 이벤트마다 7일 이상의 데이터가 있어야 합니다 | | 신규 이벤트 감지 | 분석 기간 이전에는 기록이 없고 최근 3일 안에 처음 나타난 이벤트 | | 비활성 이벤트 감지 | 이전에는 발생하던 이벤트가 최근 3일 동안 한 건도 없는 경우 | | 전환율 이상 탐지 | `signup_completed`, `purchase_completed`, `trial_started`, `demo_requested`가 전체 이벤트에서 차지하는 일별 비율의 변동 | | 세션 이상 탐지 | 일별 세션 수와 평균 세션 시간의 변동 | 실행 값: * 분석 기간 (일): 기본 30일. 7\~90일만 실행됩니다. * Z-Score 임계값: 기본 2. 1\~5만 실행됩니다. 가장 최근 날의 값이 평균에서 표준편차의 이 배수 이상 벗어나면 이상으로 봅니다. 범위를 벗어난 값으로 실행하면 탐지가 실패하고 오류가 표시됩니다. 심각도는 Z-Score의 크기로 정합니다. 3 이상이면 Critical, 2.5 이상이면 Warning, 그 밖은 Info입니다. 신규 이벤트는 항상 Info이고, 비활성 이벤트는 사라지기 전 하루 평균이 10건 이상이면 Critical, 3건 이상이면 Warning입니다. 운영 기능: * 심각도 카드(전체, Critical, Warning, Info)를 누르면 그 심각도만 봅니다. 건수는 숨긴 인사이트를 빼고 셉니다. * **숨기기**로 확인한 인사이트를 목록에서 내립니다. **숨긴 인사이트 표시**를 켜면 다시 보입니다. * 같은 이벤트의 같은 종류 인사이트는 24시간 안에 다시 추가하지 않습니다. 그래서 실행 결과의 "새로 추가" 건수가 감지 건수보다 적을 수 있습니다. * 목록에는 가장 최근 100건이 표시됩니다. ## 알림 조건이 맞으면 이메일, Slack, webhook으로 통지를 보냅니다. 프로젝트에 접근할 수 있는 조직 멤버는 알림을 만들고 수정할 수 있습니다. 알림을 저장소의 `reopt-data/alerts.json`으로 선언하고 웹훅 주소를 `${env:NAME}`으로 두는 방법은 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)에 있습니다. 저장소가 관리하는 알림(`key`가 있는 항목)은 목록의 **관리 방식**에 `코드 관리`로 표시됩니다. 콘솔에서 고치면 다음 `verify`가 어긋남으로 잡습니다. ### 만들기와 수정 **+ 새 알림**을 누르면 알림을 받을 이메일 주소나 웹훅 URL을 먼저 입력합니다. **알림 만들기**를 누르면 알림이 바로 만들어지고 오른쪽에 **알림 수정** 패널이 열립니다. 처음 조건은 이름 `New Alert`, 1시간에 100건 이상입니다. 저장 버튼은 없습니다. 패널에서 값을 바꾸면 그때마다 저장되고 다음 평가부터 적용됩니다. **알림 채널**은 입력을 마치고 다른 곳을 누르거나 Enter를 누를 때 저장됩니다. 웹훅 URL은 https로 시작하는 공개 주소여야 하고, `example.com` 같은 예시용 주소는 받지 않습니다. **조건 종류**에서 무엇을 감시할지 고릅니다. | 조건 종류 | 감시 대상 | 설정 | | ----- | -------------------------------- | ------------------------------ | | 이벤트 수 | 이벤트 하나의 발생 건수 | 이벤트, 비교, 임계값, 확인 주기 | | 에러 이슈 | 에러 이슈의 상태 변화 | 발생 시점, 최소 심각도 | | AI 관측 | 모델 호출의 비용, 에러율, p95 지연, 비용 미상 비율 | 지표, 임계값, 비교, 측정 기간, 모델·기능·에이전트 | 이벤트 수: * 확인 주기 `Real-time`, `Hourly`, `Daily`는 각각 5분, 1시간, 24시간 윈도의 건수를 봅니다. 목록에는 `실시간`, `1시간마다`, `하루 한 번`으로 표시됩니다. * 알림 하나는 조건 하나만 봅니다. 조건 줄을 더 추가해도 첫 번째 조건만 저장됩니다. * 고를 수 있는 이벤트가 아직 없으면 양식 대신 안내 문구만 보입니다. * 이 양식에는 전달 주소 입력란이 없습니다. 요약의 **알림** 줄이 실제 받을 주소인지 확인하세요. 주소는 `reopt-data/alerts.json`의 `destination`으로 지정할 수 있습니다. 에러 이슈: * 발생 시점은 `신규`(처음 보는 에러), `재발`(닫아 두었던 이슈가 다시 활성으로 돌아옴), `급증`(발생량이 평소의 3배를 넘음) 중 하나입니다. * 최소 심각도를 고르면 그 이상인 이슈만 알립니다. * `신규`와 `재발`은 상태가 바뀐 이슈마다 한 번씩 통지합니다. 한 번의 평가에서 이슈를 최대 50건까지 확인하고 나머지는 다음 평가로 넘깁니다. * 새로 만든 `신규` 알림은 첫 평가에서 이미 활성 상태인 기존 이슈도 통지 대상으로 봅니다. * 에러 이슈 상세의 **알림 만들기**로 들어오면 새 알림이 이 조건 종류로 시작합니다. * 이슈 묶음과 심각도 규칙은 [에러 트래킹](/docs/project-workspace/error-tracking)을 보세요. AI 관측: * 지표는 비용(기간 합계, USD), 에러율(%), p95 지연(ms), 비용 미상 비율(%)입니다. * 측정 기간은 최근 5분, 15분, 1시간, 6시간, 24시간 중에서 고릅니다. * 모델·기능·에이전트는 각각 한 값으로 좁힐 수 있습니다. 선택지는 최근 24시간에 실제로 호출된 값입니다. * 모델 호출이 한 건도 없는 윈도는 `이하`·`미만` 조건이어도 조건이 맞지 않은 것으로 봅니다. * 지표의 뜻은 [AI 관측](/docs/project-workspace/ai-observability)을 보세요. 전달 주소: * 에러 이슈와 AI 관측 양식의 **알림 채널**에 이메일 주소 또는 웹훅 URL 하나를 넣습니다. * 이메일 주소는 이메일로, `hooks.slack.com` 주소는 Slack으로, 그 밖의 URL은 webhook으로 보냅니다. * URL은 `https://`여야 합니다. 주소에 계정 정보(`user:pass@`)를 넣은 URL과 localhost·사설 IP 같은 내부 주소는 저장되지 않습니다. * 형식이 맞지 않는 주소는 저장되지 않고 오류가 표시됩니다. ### 평가 방식 * 켜져 있는 알림은 5분마다 평가합니다. * 이벤트 수와 AI 관측 알림은 끝난 윈도만 봅니다. 윈도는 UTC 기준으로 길이에 맞춰 정렬됩니다. 1시간 윈도라면 정시부터 다음 정시까지를 그 시간이 끝난 뒤 한 번 평가합니다. * 그래서 통지는 임계값을 넘은 순간이 아니라 윈도가 끝난 뒤의 다음 평가에서 나갑니다. 24시간 윈도는 UTC 자정(한국 시간 오전 9시)에 끝납니다. 프로젝트 타임존의 하루와 다를 수 있습니다. * 조건이 계속 맞는 동안은 한 번만 알립니다. 조건이 더 이상 맞지 않는 첫 윈도에 같은 채널로 해제 통지를 보냅니다. 패널의 **해제되면 알려주기**를 끄면 해제 통지를 보내지 않습니다. * 새 알림은 과거를 거슬러 평가하지 않습니다. 직전에 끝난 윈도 하나부터 시작합니다. * 평가가 밀리면 다음 평가가 놓친 윈도를 오래된 것부터 채웁니다. 한 번에 알림당 최대 24개 윈도, 최대 7일 전까지이고 그보다 오래된 윈도는 건너뜁니다. 통지는 가장 최근 윈도에서 상태가 바뀐 경우에만 나갑니다. * 측정에 실패한 윈도는 `평가 오류`로 남기고 다음 평가에서 다시 시도합니다. * 에러 이슈 알림은 윈도 없이, 평가할 때마다 그 사이 상태가 바뀐 이슈를 확인합니다. ### 상태, 음소거, 이력 목록의 **상태**는 마지막 평가 결과이고, **마지막 평가**는 평가한 윈도의 시작 시각입니다. 상태는 1분마다 새로 읽습니다. | 상태 | 뜻 | | ----- | --------------------------- | | 미평가 | 아직 한 번도 평가되지 않았습니다 | | 정상 | 마지막으로 평가한 윈도에서 조건이 맞지 않았습니다 | | 발생 중 | 마지막으로 평가한 윈도에서 조건이 맞았습니다 | | 평가 오류 | 가장 최근 평가가 실패했습니다 | | 음소거 | 통지를 멈춘 상태입니다. 평가는 계속합니다 | | 중지됨 | 꺼져 있어 평가하지 않습니다 | 콘솔에는 알림을 끄는 버튼이 없습니다. `중지됨`은 `reopt-data/alerts.json`에서 `status`를 `paused`로 선언한 알림에 표시됩니다. 음소거는 패널의 **30분**, **2시간**, **24시간** 버튼으로 겁니다. 그동안 평가와 이력은 계속 쌓이고 통지만 나가지 않습니다. **음소거 해제**로 바로 풉니다. * 음소거 중에 조건이 맞기 시작했다면, 음소거가 끝난 뒤에도 조건이 계속 맞는 동안은 통지하지 않습니다. 그 사건의 해제 통지도 보내지 않습니다. * 음소거는 이벤트 수와 AI 관측 알림의 통지를 멈춥니다. 에러 이슈 알림은 음소거해도 통지가 나갑니다. 패널의 **평가 이력**은 최근 48개 윈도를 보여줍니다. 윈도, 상태, 측정값과 임계값, 통지를 보냈는지, 몇 윈도 늦게 평가했는지, 오류 내용이 나옵니다. 평가 이력은 30일 동안 보관합니다. 화면 아래 **최근 발송 이력**은 가장 최근에 시도한 통지 10건의 성공과 실패를 보여줍니다. **조건 테스트**는 지금부터 윈도 길이만큼 거슬러 올라간 기간의 값을 계산해 조건이 맞는지만 알려줍니다. 통지를 보내지 않고, 데이터 포인트도 쓰지 않습니다. 실제 평가는 정렬된 윈도를 쓰므로 결과가 다를 수 있습니다. 에러 이슈 알림은 조건 테스트를 지원하지 않습니다. ### 통지 내용 통지 문구는 영어로 나갑니다. 이벤트 수 알림의 발생은 `Alert triggered: <알림 이름>`, 해제는 `Resolved: <알림 이름>`으로 시작합니다. 통지와 평가는 데이터 포인트를 쓰지 않습니다. webhook은 JSON을 `POST`합니다. ```json { "alertId": "…", "alert": "결제 급감", "event": "purchase_completed", "count": 3, "operator": "lte", "threshold": 5, "timeWindow": "1h", "triggeredAt": "2026-10-13T03:05:12.000Z", "resolved": false, "window": { "start": "2026-10-13T02:00:00.000Z", "end": "2026-10-13T03:00:00.000Z" } } ``` * `resolved`가 `true`이면 같은 알림의 앞선 통지에 대한 해제입니다. * 에러 이슈 알림은 `issue`(이슈 id, 이름, 심각도, 발생 횟수, 발생 시점 `created`·`reopened`·`spiking`, 콘솔 링크)가 붙습니다. `event`는 `$exception`이고 `threshold`·`operator`·`timeWindow`는 의미 없는 자리 채움 값입니다. `window`는 없습니다. * AI 관측 알림은 `ai`(지표, 측정값, 모델 호출 수, 필터, 비용 상위 모델, 콘솔 링크)가 붙습니다. `event`는 `$ai_generation`입니다. * 요청에는 `Idempotency-Key` 헤더가 있습니다. 재시도는 같은 값을 보내므로 중복 수신을 걸러낼 때 쓰세요. 서명 헤더는 없습니다. * 2xx 응답이면 성공입니다. 네트워크 오류, `429`, `5xx`는 합쳐서 3번까지 시도합니다. 10초 안에 응답이 없거나 그 밖의 `4xx`를 받으면 다시 시도하지 않습니다. * 리다이렉트는 따라가지 않고 실패로 기록합니다. 보내기 직전에 주소를 다시 확인해, 내부 주소로 풀리면 보내지 않습니다. Slack 주소로는 같은 내용을 Slack 메시지 형식으로 보냅니다. ### 알림이 오지 않을 때 * 상태가 `미평가`라면 첫 평가를 기다리는 중입니다. * 윈도가 아직 끝나지 않았을 수 있습니다. `Daily` 알림은 UTC 자정이 지나야 평가됩니다. * 상태가 이미 `발생 중`이면 조건이 계속 맞는 동안 다시 보내지 않습니다. * 평가가 밀린 사이에 조건이 맞기 시작해 지금까지 이어지고 있다면, 이력에는 `발생`으로 남지만 통지는 나가지 않습니다. 평가 이력의 **지연** 열을 확인하세요. * 음소거 중이었거나, 음소거 중에 시작된 사건일 수 있습니다. * 최근 발송 이력에 `실패`가 있으면 전달 주소와 수신 서버를 확인하세요. 실패한 통지는 다음 윈도에서 다시 보내지 않습니다. * 전달 주소가 `example.com` 같은 예시용 주소이면 보내지 않고 발송 이력에 `실패`로 남습니다. 예전에 만든 알림이 `https://example.com/webhook`으로 남아 있다면 **알림 채널**을 실제 주소로 바꾸세요. * 에러 이슈 알림은 활성 상태인 이슈만 알립니다. 최소 심각도보다 낮은 이슈는 건너뜁니다. ## 세 화면을 함께 쓰는 순서 1. 실시간에서 이상을 봅니다. 2. 인사이트에서 이상 유형을 확인합니다. 3. 알림으로 담당자에게 전달합니다. 4. 필요하면 사용자, 세그먼트, 쿼리로 내려가 원인을 좁힙니다. ## 주의할 점 * 실시간 피드에는 수집되어 처리가 끝난 이벤트가 나타납니다. 방금 보낸 이벤트가 조금 늦게 보일 수 있습니다. * 알림 임계값은 너무 낮게 잡으면 노이즈가 커집니다. * 전달 주소는 알림 목록의 **알림 채널** 열에 그대로 표시됩니다. 주소에 넣은 토큰도 프로젝트 멤버에게 보입니다. * webhook 요청에는 서명이 없습니다. 수신 주소를 추측하기 어렵게 관리하고, `Idempotency-Key`로 중복 수신을 처리하세요. * 알림을 만든 뒤 조건 테스트로 조건을 확인하고, 최근 발송 이력에서 실제 전달 결과를 확인하세요. ## 다음 단계 * 원인을 사용자군으로 좁히려면 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * 직접 집계하려면 [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) * 알림을 파일로 관리하려면 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code) * 데이터 포인트 기준은 [비용과 보존 정책](/docs/workspace/cost-and-retention) --- # 리텐션 Source: https://data.reopt.ai/docs/project-workspace/retention 코호트 히트맵으로 사용자가 다시 돌아오는지 확인하고, 돌아오지 않은 대상을 목록으로 엽니다. 리텐션은 사용자가 처음 행동한 뒤 다시 돌아오는지 확인하는 화면입니다. 코호트 정의, 시작 이벤트, 복귀 이벤트를 어떻게 고르느냐에 따라 결과 해석이 달라집니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/retention` ## 이 화면에서 답할 수 있는 질문 * 한 번 온 사용자가 다시 돌아오나요? * 어떤 코호트가 더 오래 유지되나요? * 시작 이벤트와 복귀 이벤트를 다르게 보면 어떤 차이가 생기나요? * 특정 코호트에서 돌아오지 않은 사람은 누구인가요? ## 핵심 입력 | 입력 | 선택지 | 기본값 | | ------ | ------------------------------------- | -------- | | 조회 기간 | 오늘, 어제, 최근 7·30·90일 또는 직접 지정 | 최근 30일 | | 시작 이벤트 | 모든 이벤트 또는 이벤트 하나 | 모든 이벤트 | | 복귀 이벤트 | 모든 이벤트 또는 이벤트 하나 | 모든 이벤트 | | 집계 단위 | 일별, 주별, 월별 | 일별 | | 코호트 정의 | 최초 방문, 기간 내 첫 방문 | 최초 방문 | | 기간 수 | 7, 14, 21, 30 (집계 단위에 따라 일·주·개월) | 7 | | 리텐션 타입 | 해당 기간 복귀 (Bounded), 누적 복귀 (Unbounded) | 해당 기간 복귀 | | 세그먼트 | 전체 사용자 또는 저장된 세그먼트 | 전체 사용자 | 조회 기간은 코호트가 만들어지는 기간입니다. 코호트와 복귀 날짜는 프로젝트 시간대로 나눕니다. ## 주요 출력 * 리텐션 히트맵: 행은 코호트(시작 날짜와 코호트 크기), 열은 `D0`부터 이어지는 기간입니다. 주별은 `W0`, 월별은 `M0`으로 표시됩니다. * 평균 리텐션: 기간별로 모든 코호트의 리텐션을 평균한 표 * 해석 가이드 코호트와 복귀는 디바이스 기준으로 셉니다. 로그인하지 않은 방문자도 포함됩니다. ### 돌아오지 않은 사용자 보기 히트맵의 셀을 누르면 그 코호트에서 해당 기간에 돌아오지 않은 대상 목록이 옆에 열립니다. 식별된 대상은 행을 눌러 프로필로 이동하고, 익명 디바이스는 목록에만 표시됩니다. 지금 보이는 페이지는 CSV나 JSON으로 내보낼 수 있습니다. ## 코호트 정의 * `최초 방문`: 디바이스가 프로젝트에 처음 나타난 시점이 조회 기간 안에 있을 때만 코호트에 넣습니다. 신규 유입만 보는 정의입니다. * `기간 내 첫 방문`: 조회 기간 안의 첫 활동을 코호트 기준으로 삼습니다. 기간 전에 이미 활동한 디바이스도 포함됩니다. `최초 방문`에서는 시작 이벤트와 세그먼트 선택이 결과에 반영되지 않습니다. 코호트가 처음 나타난 디바이스로만 정해지기 때문입니다. 특정 이벤트를 한 사용자나 특정 세그먼트로 코호트를 좁히려면 `기간 내 첫 방문`으로 바꾸세요. ## 해당 기간 복귀와 누적 복귀 * `해당 기간 복귀 (Bounded)`: 정확히 N번째 기간에 복귀 이벤트를 남긴 비율입니다. D3은 3일째에 온 사용자만 셉니다. * `누적 복귀 (Unbounded)`: N번째 기간까지 한 번이라도 복귀한 비율입니다. 한 번 복귀한 사용자는 그 뒤 기간에도 계속 포함되므로 값이 줄지 않습니다. 누적 복귀는 복귀 이벤트를 특정 행동으로 지정했을 때 의미가 있습니다. 복귀 이벤트가 `모든 이벤트`이면 코호트에 들어온 활동 자체가 0번째 기간의 복귀로 잡혀 모든 기간이 100%가 됩니다. ## 저장과 사용량 * **대시보드에 저장**은 지금의 조건(상대 기간, 집계 단위, 기간 수, 리텐션 타입, 코호트 정의, 시작·복귀 이벤트, 세그먼트)을 리포트로 저장합니다. * 조회는 데이터 포인트를 쓰지 않습니다. 같은 조건은 5분 동안 캐시되어 다시 계산하지 않습니다. * 셀의 사용자 목록도 조회로 셉니다. 같은 셀은 5분 안에 페이지를 넘겨도 한 번만 차감됩니다. ## 주의할 점 * 시작 이벤트가 너무 넓으면 코호트 의미가 흐려집니다. * 복귀 이벤트가 너무 좁으면 실제 재방문을 놓칠 수 있습니다. * 코호트 정의를 바꾸면 코호트 크기와 비율이 함께 달라집니다. 과거 차트와 비교할 때는 같은 정의인지 먼저 확인하세요. * `해당 기간 복귀`에서 최근 코호트는 아직 지나지 않은 기간이 0%로 보입니다. 이탈로 읽지 마세요. ## 다음 단계 * 사용자군별 원인 분석은 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * 저장한 리포트를 모아 보려면 [대시보드와 리포트](/docs/project-workspace/dashboards-and-reporting) * 데이터 포인트 기준은 [비용과 보존 정책](/docs/workspace/cost-and-retention) --- # 세션 리플레이 Source: https://data.reopt.ai/docs/project-workspace/session-replay 녹화에 동의한 방문의 화면 변화를 다시 재생하고, 같은 시간의 이벤트·오류와 나란히 확인합니다. 세션 리플레이는 방문자가 본 화면의 변화와 누른 곳을 다시 재생합니다. 이벤트 순서만으로 알기 어려운 상황을 확인할 때 씁니다. 녹화는 기본으로 꺼져 있고, 프로젝트 설정과 SDK 설정, 방문자의 녹화 동의가 모두 갖춰져야 쌓입니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/replays`: 사이드바의 **세션 리플레이** * 세션 상세의 `세션 리플레이` 버튼: 그 세션의 녹화만 모아 엽니다 * 에러 이슈 상세 `발생 이벤트` 탭의 `리플레이`: 오류가 난 시점에서 엽니다 ## 녹화 켜기 세 가지가 모두 필요합니다. 1. 프로젝트에서 허용: 화면 상단의 `녹화 설정`에서 `세션 녹화 허용`을 켭니다. 이 버튼은 조직 관리자 이상에게만 보입니다. 2. SDK에서 활성화: 브라우저 SDK 설정에 `sessionReplay: { enabled: true }`를 넣습니다. 3. 방문자의 녹화 동의: 동의 UI가 방문자의 결정을 받은 뒤 `setConsent("replay", true)`를 호출합니다. ```ts import { init } from "@reopt-ai/data-sdk-client"; const client = init({ writeKey: process.env.NEXT_PUBLIC_REOPT_WRITE_KEY!, baseUrl: process.env.NEXT_PUBLIC_REOPT_API_URL!, sessionReplay: { enabled: true }, }); // 방문자가 녹화에 동의한 뒤에만 호출하세요 client.setConsent("replay", true); // 동의를 철회하면 녹화가 멈춥니다 client.setConsent("replay", false); ``` 녹화 동의는 분석 동의와 별개이고, 둘 다 허용돼야 녹화합니다. `replay`는 호출하기 전까지 거부 상태이므로 기본 허용 카테고리에 넣지 마세요. ### 녹화 설정 | 항목 | 기본값 | 범위 | 뜻 | | ------------- | ---- | ------ | --------------------------------------------------------- | | `세션 녹화 허용` | 꺼짐 | - | 꺼져 있으면 SDK가 요청해도 녹화를 받지 않습니다 | | `수집 비율 (%)` | 10 | 0–100 | 세션 단위로 정해집니다. 뽑힌 세션은 탭을 새로 열거나 새로고침해도 계속 녹화 대상입니다 | | `보관 기간 (일)` | 7 | 1–30 | 기간을 줄이면 기존 녹화에도 바로 적용되고, 늘려도 이미 만료된 녹화는 돌아오지 않습니다 | | `저장 한도 (MiB)` | 1024 | 1–1907 | 한도가 차면 새 녹화를 받지 않습니다. 삭제했거나 보관 기간이 지난 녹화가 정리되면 사용량이 줄어듭니다 | 화면 상단에는 `수집 중` 또는 `수집 꺼짐`과 현재 사용량(MiB)이 함께 표시됩니다. ## 녹화되는 것과 가려지는 것 * 녹화하는 것: 화면 구조의 변화, 클릭, 포인터 이동, 스크롤, 입력 상태. * 입력값과 화면의 텍스트는 가려진 채로 기록됩니다. 레이아웃과 동작만 재현됩니다. * 비밀번호 입력란, `iframe`·`object`·`embed`, 그리고 `data-reopt-replay-block` 또는 `data-reopt-private` 속성이 붙은 영역은 통째로 제외합니다. `sessionReplay.blockSelector`로 제외할 영역을 더 지정할 수 있습니다. * 이미지와 폰트는 기본으로 담지 않습니다. 공개해도 되는 정적 이미지·폰트만 `sessionReplay.publicAssets`로 명시해 포함할 수 있고, 이렇게 넣은 픽셀과 폰트는 가려지지 않습니다. * canvas, 콘솔·네트워크 본문은 녹화하지 않습니다. * 재생 화면은 외부 리소스를 불러오지 않는 격리된 프레임에서 그려집니다. ## 녹화 찾기 녹화 하나는 창(페이지 로드) 하나의 기록입니다. 한 세션에 탭이 여럿이거나 새로고침이 있었다면 녹화도 여러 개입니다. | 조건 | 뜻 | | -------------- | -------------------------------------------------------- | | `조회 기간` | 녹화가 시작된 시각 기준. 기본은 최근 30일 | | `방문 경로 포함` | 녹화 시간 안에, 입력한 문자열이 경로에 들어간 페이지 조회가 있었던 녹화. `/`로 시작해야 합니다 | | `오류가 발생한 녹화만` | 녹화 시간 안에 예외 이벤트가 있었던 녹화 | | `아직 보지 않은 녹화만` | 내가 열어 본 적 없는 녹화 | | `상세 조건` | 세션 ID, 최소·최대 녹화 길이, 최소 활동 시간, 브라우저, 운영체제, 기기, 국가 코드 | | `정렬` | `최근순` · `활동 시간순` · `클릭순` | `조회 기간`은 바꾸는 즉시 반영되고, 나머지 조건과 정렬은 `검색 적용`을 눌러야 반영됩니다. 적용된 조건은 URL에 남아 링크로 공유할 수 있습니다. 목록의 각 행에는 시작 시점, 첫 페이지 경로, 녹화 길이, `활동` 시간, `클릭`·`입력` 횟수, 환경이 보입니다. 앞의 점은 아직 보지 않은 녹화를, `진행 중`은 방금까지 데이터가 들어온 녹화를 나타냅니다. `오류 발생` 표시는 `방문 경로`나 `오류가 발생한 녹화만` 조건으로 검색했을 때만 붙습니다. 조건이 걸린 검색은 한 번에 일정 범위만 훑습니다. 결과가 비어도 `계속 검색`이 보이면 더 이전 녹화가 남아 있습니다. ## 재생하기 * **타임라인 막대**: `활동`·`대기`·`데이터 없음` 구간과 이벤트 마커가 표시됩니다. 오류는 붉은 마커입니다. 막대나 마커를 누르면 그 시점으로 이동합니다. * **재생 속도**: 0.5배부터 16배까지. 기본으로 켜져 있는 `대기 건너뛰기`는 5초 넘게 아무 조작이 없던 구간을 건너뜁니다. * **시간 표시**: 경과 시간과 실제 시각 사이를 전환합니다. * **현재 위치 링크 복사**: 지금 보고 있는 시점이 `?stream=...&t=...`로 담긴 링크를 복사합니다. * **세부 정보 패널**: `이벤트`는 녹화 시간의 세션 이벤트입니다. `전체`·`오류`·`페이지 이동`으로 거르고, 누르면 그 시점으로 이동합니다. `세션`은 진입·이탈 경로, 유입, 환경, 위치를, `진단`은 활동 시간, 클릭·입력 수, 데이터 크기를 보여줍니다. * 위쪽 버튼으로 `세션 타임라인`(세션 상세로 이동)과 `이 세션의 다른 녹화`를 엽니다. `녹화 삭제`는 조직 관리자 이상에게만 보이고, 삭제하면 즉시 재생할 수 없게 됩니다. | 단축키 | 동작 | | -------------------- | ------------------------- | | `Space` / `K` | 재생 · 일시정지 | | `←` / `→` | 10초 이동 (재생 속도에 비례) | | `Alt` + `←` / `→` | 1초씩 이동 | | `⌘/Ctrl` + `←` / `→` | 처음 · 끝으로 | | `1`–`8` | 재생 속도 | | `S` · `F` · `I` | 대기 건너뛰기 · 전체화면 · 세부 정보 패널 | ## 주의할 점 * 녹화 1건은 데이터 포인트 10을 씁니다. 녹화를 시작할 때 차감하고, 조직의 수집 한도를 모두 쓰면 새 녹화를 시작하지 않습니다. 저장 공간은 그와 별개로 프로젝트별 `저장 한도`와 `보관 기간`으로 관리합니다. * 녹화는 분석 세션에 붙습니다. 그 세션이 아직 만들어지지 않았으면(첫 이벤트가 처리되기 전이거나, 수집 한도 초과로 이벤트가 거절되는 동안) 녹화도 시작되지 않고 SDK가 다시 시도합니다. * 녹화 하나는 최대 30분입니다. 같은 창을 더 오래 쓰면 새 녹화로 이어집니다. 60초 동안 조작이 없거나 탭이 가려지면 녹화를 멈추고, 다시 조작하면 새 녹화로 시작합니다. * 이벤트 패널과 `방문 경로`·`오류` 조건은 녹화 시간 안에 그 세션에서 난 이벤트를 기준으로 합니다. 같은 세션의 다른 탭에서 난 이벤트도 섞일 수 있습니다. * `방문 경로`는 대소문자를 구분하고, 쿼리 문자열과 `#` 이후는 넣을 수 없습니다. 브라우저·운영체제·기기·국가는 녹화 시작 시점의 값과 정확히 일치해야 합니다. * "아직 보지 않음" 표시는 콘솔 사용자마다 따로 기록됩니다. 녹화를 열면 내 계정에서만 본 것으로 바뀝니다. * 텍스트가 가려지고 외부 이미지·폰트가 빠지므로 재생 화면은 실제 페이지와 똑같아 보이지 않습니다. 의도된 동작입니다. ## 다음 단계 * 같은 방문의 이벤트 순서를 보려면 [세션 탐색](/docs/project-workspace/sessions) * 오류가 난 녹화에서 원인을 좁히려면 [에러 트래킹](/docs/project-workspace/error-tracking) * 브라우저 SDK를 처음 붙인다면 [추적 설정](/docs/workspace/tracking-setup) * 데이터 포인트와 보존 기준은 [비용과 보존 정책](/docs/workspace/cost-and-retention) --- # 세션 탐색 Source: https://data.reopt.ai/docs/project-workspace/sessions 방문 단위 행동 흐름을 확인하고, 익명/식별 세션과 개별 세션 타임라인을 분석합니다. 세션 탐색에서는 사용자가 한 번 방문해서 남긴 경로와 이벤트를 봅니다. 사용자 프로필로는 범위가 너무 넓을 때 세션 단위로 내려가면 유입, 환경, 이탈 맥락을 더 빠르게 확인할 수 있습니다. ## 진입 경로 * 세션 목록: `/dashboard/[orgId]/[projectId]/sessions` (사이드바의 **세션 분석**) * 세션 상세: `/dashboard/[orgId]/[projectId]/sessions/[sessionId]` ## 세션 목록에서 확인할 것 목록 화면에서 선택한 기간에 시작된 방문을 찾고 비교합니다. 요약 숫자: * `세션`: 기간 안의 전체 세션 수 * `식별 세션`: `identify()`로 프로필이 연결된 방문 수와 전체 대비 비율 * `익명 세션`: 프로필이 연결되지 않은 방문 수 * `평균 체류` * `이탈률`: 한 페이지만 보고 10초 안에 떠난 세션의 비율 좁히기: * 조회 기간: 기본은 `최근 30일`. `오늘`·`어제`·`최근 7일`·`최근 30일`·`최근 90일` 또는 직접 지정한 날짜 * `전체 세션` / `식별 세션` / `익명 세션` 탭 * 검색: 세션 ID·프로필 ID·디바이스 ID는 앞부분이 일치하면 찾습니다. 진입·이탈 경로, 유입(리퍼러·채널·UTM), 사용자 이름·이메일은 검색어가 포함되면 찾습니다. `/` 키를 누르면 검색창으로 이동합니다. * 정렬: `최신순` · `체류 시간순` · `이벤트순` · `페이지뷰순` * 이탈: `이탈 무관` · `이탈만` · `이탈 제외` * 세그먼트: 저장된 세그먼트에 속한 디바이스의 세션만 표의 열은 방문자, 진입 경로, 이탈 경로, 유입, 체류 시간, 이벤트, 페이지뷰, 이탈, 환경, 지역, 시작 시각입니다. 행을 누르면 오른쪽에 `세션 요약` 패널이 열립니다. 여기서 `세션 상세`로 넘어가거나, 식별된 방문이면 `이 사용자의 프로필 열기`로 사용자 프로필로 이동합니다. 표 아래에서는 페이지와 페이지당 행 수(25·50·100·200)를 바꾸고, `이 페이지 내보내기`로 지금 보이는 행을 CSV나 JSON으로 내려받습니다. 기간, 탭, 검색어, 정렬, 필터, 열어 둔 패널까지 전부 URL에 들어갑니다. 좁혀 둔 목록을 링크로 그대로 공유할 수 있습니다. ## 세션 상세에서 확인할 것 상세 화면은 한 방문 안에서 일어난 일을 시간 순서와 분포로 보여줍니다. * 요약: `세션 길이`, `이벤트`, `페이지뷰`, `에러`, `이탈` * `세션 정보`: 세션 ID, 프로필 ID, 디바이스 ID, 시작·종료 시각, 진입·이탈 경로, 식별 여부 * `획득 및 환경`: 유입 채널, 리퍼러, UTM Source·Medium·Campaign, 브라우저, 디바이스, 지역 * `이벤트 분포`와 `방문 경로`: 이 방문에서 많이 발생한 이벤트와, 이벤트가 많이 발생한 경로 * `세션 타임라인`: 이벤트를 발생한 순서대로, 앞 이벤트와의 간격과 함께 보여줍니다. 행을 펼치면 이벤트 속성이 보입니다. 타임라인에서 예외(`$exception`)는 붉게 표시되고 `미처리` 또는 `에러` 배지와 예외 타입·메시지가 함께 나옵니다. 이슈로 묶인 예외는 `이슈 보기`로 해당 에러 이슈로 넘어갑니다. 상단에는 다른 화면으로 가는 버튼이 있습니다. * `세션 목록`: 들어올 때 쓰던 필터가 그대로인 목록으로 돌아갑니다. * `세션 리플레이`: 이 세션에 녹화가 있을 때만 보입니다. 이 세션의 녹화만 모은 리플레이 화면이 열립니다. * `프로필 보기`: 식별된 방문일 때만 보입니다. ## 사용자 프로필과 나눠 보기 사용자 프로필은 한 사용자의 전체 이력을 보는 화면입니다. 세션은 그중 한 방문만 떼어 유입과 행동 순서를 보는 화면입니다. 퍼널에서 특정 단계 이탈이 보이면 세션 상세에서 실제 직전 경로와 이벤트를 확인하세요. 획득 분석에서 채널 품질이 애매하면 같은 채널의 세션을 열어 유입 뒤 행동이 이어졌는지 확인하세요. ## 주의할 점 * 같은 디바이스에서 마지막 이벤트 뒤 30분이 지나 들어온 이벤트는 새 세션이 됩니다. * 세션 지표는 프로젝트 시간대의 하루 단위로 조회합니다. 하루보다 짧은 구간을 넣어도 그날 전체가 조회됩니다. * 요약 숫자는 조회 기간과 세그먼트만 따릅니다. 탭, 검색어, 이탈 필터는 아래 표에만 적용됩니다. * 익명 세션은 프로필 없이 `deviceId`로만 묶인 방문이라 사용자 목록에 나타나지 않습니다. SDK에서 `identify()`를 호출하면 그 뒤의 방문부터 같은 사람으로 묶입니다. * 이탈은 `$pageleave`가 보고한 체류 시간으로 판정합니다. 페이지를 두 개 이상 본 방문은 이탈이 아니고, 아직 페이지를 떠나지 않은 진행 중인 방문은 잠시 이탈로 보이다가 정정됩니다. * `체류 시간`과 `세션 길이`는 첫 이벤트부터 마지막 이벤트까지의 간격입니다. 실제로 화면을 보고 있던 시간과는 다를 수 있습니다. * 세션 상세의 타임라인, 분포, `에러` 수는 그 세션의 처음 500개 이벤트까지를 기준으로 합니다. * 세션은 수집된 이벤트가 처리된 뒤에 만들어집니다. 수집 직후에는 목록 반영이 조금 늦을 수 있습니다. * UTM과 리퍼러가 비어 있는 세션은 어디서 유입됐는지 알 수 없습니다. ## 다음 단계 * 그 방문의 화면을 다시 보려면 [세션 리플레이](/docs/project-workspace/session-replay) * 사용자 전체 이력을 보려면 [사용자와 세그먼트](/docs/project-workspace/users-and-segments) * 채널별 성과를 보려면 [획득과 경로 분석](/docs/project-workspace/acquisition-and-path) * 수집 지연을 점검하려면 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) --- # 프로젝트 설정 Source: https://data.reopt.ai/docs/project-workspace/settings 프로젝트 설정 양식, 분석 기준값, 보존 정책, 에러·AI 수집 정책을 관리합니다. 프로젝트 설정에서는 프로젝트 하나의 운영 정책을 관리합니다. 분석 기준값, 데이터 보존, 에러와 AI 수집 정책을 여기서 정합니다. ## 진입 경로 * `/dashboard/[orgId]/[projectId]/settings`: 사이드바의 **프로젝트 설정**, 단축키 `G` `T` ## 설정 화면 설정 조회와 삭제 미리보기는 조직 멤버가 사용할 수 있습니다. 프로젝트 설정 저장, 보존 정책 변경, 수동 정리 실행, AI 관측 설정 변경은 조직 `owner` 또는 `admin` 권한이 필요합니다. 에러 규칙과 심볼셋은 프로젝트에 접근할 수 있는 멤버가 관리할 수 있습니다. 화면은 위에서부터 다음 순서입니다. * 프로젝트 설정 양식 * 데이터 보존 정책 * 삭제 미리보기와 수동 정리 * 저장소 사용량 * 에러 트래킹: 스튜디오 연결, 에러 규칙, 규칙 테스트, 급증 감지, 릴리즈, 심볼셋 * AI 관측 * 화면 모드 ### 프로젝트 설정 양식 **저장**을 눌러야 반영됩니다. 아래에서 고른 데이터 보존 정책도 이 버튼으로 함께 저장됩니다. | 항목 | 설명 | | -------------------- | ---------------------------------------------------------- | | 프로젝트 이름 | 비울 수 없습니다 | | 도메인 | `example.com` 형식. 프로토콜과 경로는 넣지 않습니다. 프로젝트를 찾을 때 검색됩니다 | | 파워 유저 기준 · 이벤트 수 | 비우면 기본값 25건 | | 파워 유저 기준 · 세션 수 | 비우면 기본값 5회 | | 활성 기준 · 최근 활동 며칠 이내 | 비우면 기본값 7일 | | 휴면 기준 · 이 일수를 넘기면 휴면 | 비우면 기본값 30일. 활성 기준을 함께 채웠다면 그보다 커야 합니다 | | 소스 저장소 | `http(s)://` 또는 `git@`로 시작하는 주소. 에러 스택에서 소스 코드로 가는 링크에 씁니다 | | 기본 브랜치 | 비우면 `main` | | 호스트 앱 | 여러 프로젝트의 화면을 한 앱이 함께 서빙할 때만 채웁니다 | 도메인은 프로젝트를 구분하는 정보입니다. 값을 넣어도 다른 도메인에서 온 이벤트 수집을 막지 않습니다. 기준값을 비워 두면 제품 기본값을 따릅니다. 파워 유저·활성·휴면 기준이 쓰이는 화면은 [사용자와 세그먼트](/docs/project-workspace/users-and-segments)를 보세요. 이 화면에 없는 항목: * 프로젝트 타임존은 여기서 바꿀 수 없습니다. 바꾸는 방법은 [조직과 프로젝트](/docs/workspace/organizations-and-projects)에 있습니다. * 수집 키는 [추적 설정](/docs/workspace/tracking-setup)을 보세요. * 세션 리플레이 녹화 설정은 [세션 리플레이](/docs/project-workspace/session-replay) 화면에 있습니다. 프로젝트 타임존은 화면의 시각 표시와 "오늘"·"최근 N일" 같은 조회 기간의 날짜 경계를 정합니다. 시각 옆에는 `KST`·`UTC`처럼 시간대가 함께 표시됩니다. 세션 상세와 사용자 프로필의 타임라인, 실시간 피드, 리플레이 타임라인은 예외로, 보고 있는 브라우저의 시간대로 표시합니다. 타임존을 정하지 않은 프로젝트는 UTC를 씁니다. ### 데이터 보존 정책과 수동 정리 보존 기간은 `무제한 (삭제 안 함)`, `30일`, `60일`, `90일`, `180일`, `365일` 중에서 고릅니다. 기간을 정해도 데이터가 자동으로 지워지지는 않습니다. 기간보다 오래된 데이터는 **수동 정리 실행**을 눌렀을 때 삭제됩니다. * **삭제 미리보기**는 기간을 고르면 나타나고, 그 기간으로 정리했을 때 테이블별로 지워질 행 수를 보여줍니다. * 미리보기는 화면에서 고른 기간으로 계산하지만, 실제 정리는 **저장된** 보존 기간으로 실행합니다. 기간을 바꿨다면 먼저 저장하세요. * 정리는 원본 이벤트와 분석 테이블에서 기준일 이전 행을 지웁니다. 원본 이벤트가 지워진 기간은 다시 반영할 수 없습니다. * 실행 전에 확인 창을 거치고, 실행한 뒤에는 되돌릴 수 없습니다. * **저장소 사용량**은 테이블별 행 수와 비율을 보여줍니다. AI 호출 기록은 예외입니다. 프로젝트 보존 기간과 AI 관측의 스팬 보존 기간 중 짧은 쪽으로 매일 자동 정리됩니다. ### 에러 트래킹 에러 이슈를 어떻게 묶고 분류할지 정하는 항목입니다. 동작은 [에러 트래킹](/docs/project-workspace/error-tracking)에서 설명합니다. * 스튜디오 연결: 에러 이슈를 스튜디오 업무로 보내는 연결의 상태를 보여줍니다. 읽기 전용입니다. * 에러 규칙: 위에서부터 평가하고, 종류별로 처음 맞는 규칙을 적용합니다. 추가, 수정, 켜고 끄기, 순서 변경, 삭제를 할 수 있습니다. * 규칙 테스트: 이벤트 properties를 붙여 넣어 저장된 규칙이 어떻게 적용되는지 확인합니다. 아무것도 저장하지 않습니다. * 급증 감지: 배수 ×3, 최소 발생 10건 / 5분. 지금은 읽기 전용입니다. * 릴리즈: 이벤트의 `$release_id`로 관측된 빌드 목록입니다. * 심볼셋: 업로드한 소스맵 목록입니다. 삭제할 수 있고, 이미 해석된 스택은 그대로 남습니다. ### AI 관측 모델 호출 기록의 수집 정책입니다. 조직 `owner` 또는 `admin`이 아니면 읽기 전용으로 보입니다. * 프롬프트·응답 본문 수집: 기본은 꺼짐입니다. 켜면 이후 호출의 프롬프트, 응답, 도구 인자와 결과가 저장되고 프로젝트 멤버 모두가 볼 수 있습니다. SDK에서도 `captureContent`를 켠 호출만 본문이 들어옵니다. * 스팬 보존 기간: 1\~365일, 기본 90일. 바꾼 뒤 이 영역의 **저장**을 눌러야 반영됩니다. 화면은 [AI 관측](/docs/project-workspace/ai-observability)을 보세요. ### 화면 모드 콘솔의 밝기를 `라이트`, `다크`, `시스템` 중에서 고릅니다. ### 코드로 관리하는 프로젝트 타임존, 보존 기간, 임계값 같은 설정은 저장소의 `reopt-data/project.json`으로도 관리할 수 있습니다. CI에서 `verify`로 어긋남을 잡고 배포와 함께 `push`하는 흐름은 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)를 보세요. 저장소에서 설정을 적용한 적이 있는 프로젝트는 화면 위에 마지막 코드 적용 시각과 적용한 주체가 표시됩니다. 저장소가 관리하는 에러 규칙에는 `코드 관리` 배지가 붙습니다. 콘솔에서 고칠 수는 있지만 저장소와 차이가 생기므로, 다음 적용 전에 저장소에도 반영하세요. ## 주의할 점 * 삭제 미리보기를 먼저 확인한 뒤 수동 정리를 실행하세요. 미리보기와 실제 정리가 같은 기간을 쓰는지도 확인하세요. * 본문 수집을 켜기 전에 개인정보나 비밀값이 섞이지 않는지 확인하세요. ## 다음 단계 * 비용과 보존 정책은 [비용과 보존 정책](/docs/workspace/cost-and-retention) * 수집 요청과 보존의 관계는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) * 설정을 파일로 관리하려면 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code) --- # 사용자와 세그먼트 Source: https://data.reopt.ai/docs/project-workspace/users-and-segments 개별 사용자 행동을 확인하고, 재사용 가능한 사용자 집합을 만들어 분석 화면에 적용합니다. 사용자 화면에서는 개별 사용자의 행동을 확인하고, 세그먼트로는 사용자를 집합으로 묶어 분석 화면에 적용합니다. 퍼널이나 리텐션에서 이상한 패턴을 발견했다면 사용자와 세그먼트로 원인을 좁혀보세요. ## 진입 경로 * 사용자 분석(목록): `/dashboard/[orgId]/[projectId]/users` * 사용자 프로필: `/dashboard/[orgId]/[projectId]/users/[profileId]` * 세그먼트 목록: `/dashboard/[orgId]/[projectId]/segments` * 새 세그먼트: `/dashboard/[orgId]/[projectId]/segments/new` * 세그먼트 상세: `/dashboard/[orgId]/[projectId]/segments/[segmentId]` * 세그먼트 편집: `/dashboard/[orgId]/[projectId]/segments/[segmentId]/edit` ## 사용자와 익명 방문자 프로필은 SDK에서 `identify()`를 호출해야 생깁니다. 그래서 사용자 목록에 있는 행은 모두 식별된 사용자입니다. `identify()` 없이 방문한 사람은 프로필이 없어 사용자 목록에 나오지 않고, 디바이스 단위로만 남습니다. * 사용자 분석의 **익명 방문자** 수는 프로필이 아니라 세션에서 셉니다. 누구인지는 [세션 분석](/docs/project-workspace/sessions)의 익명 세션 탭에서 확인하세요. * 세그먼트, 퍼널, 리텐션은 프로필이 아니라 디바이스를 셉니다. 익명 방문자도 포함됩니다. ## 사용자 분석 사용자 목록에서 조사할 프로필을 찾습니다. 기본 조회 기간은 최근 30일이고, 날짜 경계는 프로젝트 시간대를 따릅니다. 선택한 기간에 이벤트를 남긴 프로필만 통계와 목록에 들어갑니다. 요약 지표: * 활동 프로필: 선택 기간에 이벤트를 남긴 프로필 * 연락 가능: 그중 이름이나 이메일이 있는 프로필 * 익명 방문자: `identify()` 없이 방문한 디바이스 수. 사용자 목록에는 나타나지 않습니다. * 파워 유저: 기간 안의 이벤트 수 또는 세션 수가 기준 이상인 프로필 탭으로 목록을 나눠 봅니다. * `연락 가능`: 이름이나 이메일이 붙어 있는 사용자 * `ID만 있음`: `identify()`는 됐지만 이름·이메일이 전달되지 않은 사용자. SDK에서 traits를 함께 보내면 채워집니다. * `파워 유저`: 기준을 넘는 사용자. 이벤트 수가 많은 순으로 정렬됩니다. 파워 유저 기준의 기본값은 이벤트 25건 또는 세션 5회 이상이고, 프로젝트 설정의 "파워 유저 기준"에서 바꿀 수 있습니다. 목록을 좁히는 방법: * 검색: 이름, 이메일, ID. `/`를 누르면 검색창으로 이동합니다. * 세그먼트 필터: 저장된 세그먼트에 속한 사용자만 봅니다. * 속성 필터: 프로필 속성 키에 `=`, `≠`, `포함`, `미포함` 조건을 겁니다. 여러 개를 두면 모두 만족해야 합니다. 목록의 열은 사용자, 이벤트, 세션, 마지막 활동, 평균 세션, 최근 진입, 채널입니다. 이벤트·세션 수는 선택한 기간 안의 값입니다. 행을 누르면 옆에 요약이 열리고, "전체 프로필"로 프로필 화면에 들어갑니다. 지금 보이는 페이지는 CSV나 JSON으로 내보냅니다. 속성 필터를 하나 이상 걸었다면 **세그먼트로 저장**으로 그 조건을 세그먼트 편집 화면에 넘길 수 있습니다. 넘어가는 것은 속성 필터뿐입니다. 탭(연락 가능, 파워 유저 등)과 검색어는 세그먼트 조건으로 표현할 수 없어 빠집니다. ## 사용자 프로필 프로필 화면은 한 사용자의 이력을 보여줍니다. 위쪽의 분석 기간(최근 14일·30일·90일, 기본 30일)은 참여도와 활동 차트에 적용됩니다. * 요약: 총 이벤트, 총 세션, 평균 세션, 이탈 세션, 마지막 활동, 에러. 총 이벤트와 총 세션은 전체 기간 값이고, 에러는 지금 불러온 타임라인 안에서 센 수입니다. * 참여도: 0\~100점. 최근성 30% + 빈도 25% + 다양성 20% + 꾸준함 25%로 계산하고 축별 점수를 함께 보여줍니다. 빈도는 하루 평균 이벤트 5건, 다양성은 이벤트 종류 10개에서 만점입니다. 40점 미만은 낮음, 70점 이상은 높음으로 표시됩니다. * 프로필 정보: 프로필 ID, 첫 활동, 프로필 생성, 최초 유입, 이메일 * 속성: `identify()`에 함께 보낸 traits * 활동 차트: 선택한 기간의 일별 이벤트 수와 세션 수 * 상위 이벤트, 자주 본 경로: 각각 상위 8개 * 활동 타임라인: 이벤트를 세션 단위로 묶어 보여주고, 세션 제목에서 세션 상세로 이동합니다. 이벤트 이름으로 거를 수 있고 100건씩 더 불러옵니다. ### 라이프사이클과 자동 태그 프로필 이름 옆에는 라이프사이클 단계가 표시됩니다. 단계는 마지막 활동이 얼마나 최근인지로 정합니다. | 단계 | 기준 (기본값) | | ----- | ------------------------- | | 신규 | 최근 7일 안에 활동했고 첫 활동도 7일 이내 | | 활성 | 최근 7일 안에 활동 | | 이탈 위험 | 마지막 활동이 7일 초과 30일 이내 | | 휴면 | 마지막 활동이 30일 초과 | 7일과 30일은 프로젝트 설정의 "활성 기준"과 "휴면 기준"에서 바꿀 수 있습니다. 자동 태그는 두 가지가 붙습니다. * 첫 활동이 속한 주의 시작일 (예: `W2026-08-23`) * 최초 유입 채널 (예: `Channel: organic_search`) ## 세그먼트 세그먼트는 여러 분석 화면에서 재사용하는 사용자 집합입니다. 조건마다 그 조건에 맞는 디바이스의 집합이 정해지고, 이 집합들을 교집합·합집합·제외로 묶은 결과가 세그먼트입니다. * 크기는 **기기** 수와 **식별 프로필** 수 두 가지로 표시합니다. 기기 수가 세그먼트의 크기이고, 식별 프로필은 그중 `identify()`된 수입니다. * `동적` 세그먼트는 조건에 맞는 사용자를 주기적으로 다시 계산합니다. `정적` 세그먼트는 업로드한 목록을 그대로 씁니다. 종류는 만들 때 정하고 나중에 바꿀 수 없습니다. ### 세그먼트 목록 위쪽 요약은 세그먼트 수, 동적·정적 개수, 순 커버리지, 최근 갱신입니다. 순 커버리지는 크기가 큰 순으로 최대 3개 세그먼트가 닿는 기기 수이고, 겹치는 기기는 한 번만 셉니다. * 이름으로 검색하고, 종류(전체·동적·정적)로 거르고, 갱신·이름·생성일·크기로 정렬합니다. * 보기 방식은 표와 카드 중에서 고르고, 선택은 브라우저에 기억됩니다. * 각 행은 조건 요약, 기기 · 식별 프로필 수, 최근 14일 크기 추이, 상태, 최근 갱신, 생성일을 보여줍니다. * 행 메뉴에서 편집, 복제, 재계산(동적만), 멤버 보기, 비교로, 삭제를 실행합니다. 상태는 다음 중 하나입니다. | 상태 | 의미 | | ---- | -------------------------------- | | 최신 | 최근 24시간 안에 계산됨 | | 오래됨 | 마지막 계산이 24시간을 넘김 | | 계산 중 | 지금 계산하고 있음. 이전 결과가 있으면 그대로 보여줍니다 | | 큐 대기 | 저장 직후이거나 조건이 바뀌어 계산을 기다리는 중 | | 오류 | 마지막 계산이 실패함 | | 정적 | 정적 세그먼트. 계산 대상이 아닙니다 | ### 세그먼트 만들기 **새 세그먼트**를 누르면 전체 화면 편집기가 열립니다. 이름, 종류, 설명, 태그를 적고 아래에서 조건을 조합합니다. 이름은 필수이고 100자 이내, 태그는 10개까지입니다. * 조건은 그룹으로 묶습니다. 그룹 안에서는 `모두 충족` 또는 `하나 이상 충족`을, 그룹끼리는 `모든 그룹 일치` 또는 `하나 이상 일치`를 고릅니다. * 그룹은 10개, 한 그룹의 조건은 50개까지입니다. * 조건마다 `제외`를 켜면 그 조건에 맞는 디바이스를 뺍니다. * 조건이 비어 있는 새 세그먼트에는 템플릿이 표시됩니다. 활성 사용자, 신규 가입 후 미구매, 이탈 위험, 온보딩 시퀀스, Pro 플랜 중 프로젝트에 해당 이벤트나 속성이 있는 것만 나옵니다. * `⌘S`로 저장합니다. 저장하지 않고 나가려고 하면 확인을 묻습니다. 조건 종류: | 분류 | 조건 | 뜻 | | -- | -------- | ----------------------------------------------------- | | 행동 | 이벤트 수행 | 기간 안에 이벤트를 N회 수행. 횟수는 이상·이하·정확히·범위로 지정합니다 | | 행동 | 처음 수행 | 그 이벤트를 처음 수행한 시점이 기간 안 | | 행동 | 순서대로 수행 | 2\~10개 단계를 순서대로 수행. 첫 단계부터 정한 시간 안에 마지막 단계까지 가야 합니다 | | 속성 | 프로필 속성 | `identify()`로 붙은 사용자 속성의 현재 값 | | 속성 | 기기 속성 | 브라우저·OS·국가 등 그 디바이스의 가장 최근 값 | | 속성 | 이벤트 속성 | 그 속성이 붙은 이벤트를 보낸 디바이스 | | 고급 | 정기적으로 수행 | 일·주·월 주기마다 N회 이상 수행한 기간이, 최근 M개 기간 중 K개 이상 | | 고급 | 수행 중단 | 이전 관찰 기간에는 수행했지만 최근 기간에는 수행하지 않음 | | 고급 | 수행 재개 | 최근 기간에 다시 수행했고, 그 앞의 관찰 기간에는 쉬었으며, 이번이 첫 수행은 아님 | | 고급 | 다른 세그먼트 | 저장된 다른 세그먼트에 속함. 중첩은 3단계까지이고 자기 자신이나 순환 참조는 저장되지 않습니다 | | 고급 | 첫 방문 | 프로젝트에 처음 나타난 시점이 기간 안 | 이벤트를 하지 않은 사용자를 고르려면 조건 추가 메뉴에서 `수행하지 않음`을 고르거나, `이벤트 수행`에 `제외`를 켜세요. 조건 안의 값은 문장 속 칩을 눌러 바꿉니다. * 기간: `상대`, `절대`, `전체 기간` 중에서 고릅니다. `상대`는 최근 N시간·일·주·개월이고 빠른 선택은 7·14·30·90일입니다. `절대`는 날짜 범위이며 종료일을 비우면 시작일 이후 전체입니다. 기본값은 최근 30일입니다. * 속성 조건: 위치를 프로필, 기기(최신), 유입(최초), 세션, 이벤트 중에서 고릅니다. 기기(최신)는 지금 값이고 유입(최초)은 처음 들어왔을 때의 값이라, 같은 `utm_source`라도 뜻이 다릅니다. 타입(문자열·숫자·불리언·날짜)에 따라 쓸 수 있는 연산자가 달라집니다. * 이벤트 조건에는 이벤트 속성 조건을 10개까지 붙일 수 있습니다. 다음과 같은 정의는 저장되지 않고, 이유가 편집기에 표시됩니다. * 제외 조건만 있는 세그먼트나 그룹 * 같은 조건을 포함과 제외로 함께 둔 그룹 * 최소 횟수가 최대 횟수보다 큰 범위, 최근 기간이 관찰 기간보다 긴 `수행 중단`·`수행 재개` ### 미리보기 편집기 오른쪽의 미리보기는 지금 조건이 고르는 사용자를 바로 보여줍니다. * 기기 수와 전체 대비 비율, 식별된 프로필 수 * 조건별 인원: 조건 하나씩 따로 센 수. 어떤 조건이 결과를 0으로 만드는지 여기서 찾습니다. * 샘플 10명 조건을 바꾸면 잠시 뒤 다시 계산합니다. 미리보기는 분석 조회라서 데이터 포인트를 쓰지 않습니다. 같은 조건은 1분 동안 캐시되어 다시 차감되지 않고, 한 사람이 1분에 30번까지 요청할 수 있습니다. ### 계산과 갱신 * 저장하면 바로 계산합니다. 10초 안에 끝나지 않으면 `계산 중`으로 남고 백그라운드에서 마무리됩니다. 목록과 상세는 계산 중인 동안 5초마다 갱신됩니다. * 조건을 고쳐 저장하면 이전 숫자는 지워지고 새 조건으로 다시 계산합니다. * 동적 세그먼트는 마지막 계산 후 15분이 지나면 다시 계산 대상이 됩니다. 재계산 작업은 10분마다 돌고 한 번에 처리하는 수에 한도가 있어, 세그먼트가 많으면 더 늦어질 수 있습니다. * "최근 30일" 같은 상대 기간은 계산하는 시점을 기준으로 다시 잡힙니다. 그래서 조건이 같아도 계산할 때마다 크기가 달라집니다. * 계산이 실패하면 재시도 간격이 1시간부터 최대 16시간까지 늘어나고, 연속 20회를 넘기면 자동 재시도를 멈춥니다. 조건을 고쳐 저장하거나 `재계산`을 누르면 다시 시작합니다. * 지금 바로 최신 숫자가 필요하면 행 메뉴나 상세의 `재계산`을 쓰세요. ### 세그먼트 상세 목록에서 이름을 누르면 상세가 열립니다. 위쪽에 기기 수(전체 대비 비율), 식별 프로필 수, 최근 갱신, 상태가 있고 아래에 다섯 개 탭이 있습니다. * `개요`: 크기 추이(30일·90일), 일별 활성 기기, 구성(채널·국가·기기 종류·OS), 조건. 크기 추이는 날짜별 마지막 계산 결과이고 계산이 없던 날은 비어 있습니다. * `멤버`: 세그먼트에 속한 대상 목록. `익명 포함`과 `식별만`을 전환하고, 식별된 대상은 프로필로 이동할 수 있습니다. CSV 내보내기는 최대 50,000행입니다. * `겹침`: 다른 세그먼트 1\~2개와의 교집합·합집합을 벤 다이어그램과 쌍별 표로 봅니다. 기기 기준으로 셉니다. * `사용 위치`: 이 세그먼트를 필터로 쓰는 대시보드와, 조건으로 참조하는 다른 세그먼트. 삭제하기 전에 여기서 영향을 확인하세요. * `계산 이력`: 계산 시도마다 시작 시각, 소요, 기기·식별 프로필 수, 트리거(저장·수동·예약), 상태, 당시 조건을 남깁니다. 실패한 행을 누르면 오류 내용이 보입니다. ### 정적 세그먼트 정적 세그먼트는 조건이 아니라 목록으로 정해집니다. 종류를 `정적`으로 고르고 조건 추가 메뉴의 `정적 멤버`를 넣어 저장한 뒤, 상세의 `멤버` 탭에서 목록을 관리하세요. * **CSV 업로드**: 헤더가 `device_id`, `distinct_id`, `profile_id`, `email` 중 하나인 컬럼을 읽습니다. 파일은 1MB 이하여야 합니다. 컬럼이 하나뿐이고 헤더가 없으면 디바이스 ID 목록으로 읽습니다. * `profile_id`와 `email`은 그 프로필이 사용한 디바이스로 바꿔서 넣습니다. 프로젝트에서 찾지 못한 값은 미매칭으로 셉니다. * 업로드가 끝나면 총 행 수, 매칭, 미매칭, 추가 수를 알려줍니다. 이미 멤버인 행은 건너뜁니다. * 멤버는 행마다 `제거`할 수 있습니다. ### 다른 화면에서 쓰기 저장한 세그먼트는 사용자 분석, 세션 분석, 퍼널 분석, 리텐션, 경로 분석의 세그먼트 필터와 코호트 비교, 대시보드 필터에서 고를 수 있습니다. 세그먼트 조건을 고치면 이 세그먼트로 거른 분석 결과의 캐시도 함께 비워집니다. CLI에서는 조회만 할 수 있습니다. `reopt-data segment list`, `segment get`, `segment preview`를 쓰세요. 만들기와 수정은 콘솔에서만 합니다. ## 주의할 점 * 세그먼트는 다른 분석 화면에서 재사용되므로 이름을 명확하게 지으세요. * 분석 화면의 세그먼트 선택 목록에는 최근에 갱신된 순으로 50개까지만 나옵니다. * 사용자 분석의 세그먼트 필터는 세그먼트에 속한 디바이스 중 프로필이 있는 사용자만 보여줍니다. 익명 디바이스까지 보려면 세그먼트 상세의 `멤버` 탭을 쓰세요. * 정적 세그먼트의 멤버 목록은 그 세그먼트의 상세 화면과 코호트 비교에서 반영됩니다. 사용자·세션·퍼널·리텐션·경로 화면의 세그먼트 필터와 `겹침` 탭에서는 아직 반영되지 않아 0으로 나올 수 있습니다. * `다른 세그먼트` 조건은 세그먼트 화면(미리보기·멤버·겹침)과 코호트 비교에서 계산됩니다. 사용자·세션·퍼널·리텐션·경로 화면의 세그먼트 필터에서는 그 조건이 빈 집합으로 처리됩니다. * 익명 방문자는 디바이스 ID로만 이어집니다. 디바이스 ID가 바뀌면 다른 방문자로 셉니다. * 이메일, 전화번호 같은 개인식별정보는 이벤트 속성에 넣지 마세요. ## 다음 단계 * 방문 단위 흐름을 자세히 보려면 [세션 탐색](/docs/project-workspace/sessions) * 세그먼트 차이를 직접 비교하려면 [코호트 비교, 브레이크다운, 쿼리](/docs/project-workspace/compare-breakdown-query) * 데이터 포인트 기준은 [비용과 보존 정책](/docs/workspace/cost-and-retention) * CLI로 세그먼트를 조회하려면 [CLI 사용하기](/docs/workspace/cli) --- # 워크스페이스 Source: https://data.reopt.ai/docs/workspace 조직과 프로젝트를 만들고, 추적을 시작하고, 비용과 보존 정책을 운영하는 방법을 안내합니다. 워크스페이스에서는 분석을 시작하기 전에 필요한 운영 작업을 합니다. 조직과 프로젝트를 만들고, SDK 키를 배포하고, 비용과 보존 정책을 관리합니다. ## 이 섹션에서 할 수 있는 일 * 조직과 프로젝트 구조를 이해합니다. * 빠른 연동 가이드를 따라 첫 이벤트를 보냅니다. * CLI에서 개인 계정을 확인하고 배포 작업을 자동화합니다. * 프로젝트 설정·룰·알림을 저장소의 파일로 선언하고 CI에서 서버와 맞춥니다. * 브라우저, React, Next.js, 서버·워커 환경에 맞게 추적을 붙입니다. * 같은 SDK에서 에러 수집, 세션 리플레이, AI 텔레메트리를 켭니다. * 조직 단위 비용과 프로젝트 단위 보존 정책을 관리합니다. ## 먼저 결정할 것 * 어떤 조직 아래 프로젝트를 만들지 정하세요. * 브라우저에는 `writeKey`만, 서버에는 `clientId + serverSecret`만 배포하세요. * 서버 이벤트는 응답 뒤에도 전송이 끝나도록 하세요. Next.js 요청 스코프 클라이언트는 Vercel의 `waitUntil`을 자동으로 쓰고, 워커·크론·스크립트에서는 `trackAndFlush` 계열과 `close()`로 큐를 직접 비웁니다. * 비용은 조직 단위, 보존 정책은 프로젝트 단위로 관리하세요. ## 다음 단계 1. [조직과 프로젝트](/docs/workspace/organizations-and-projects)에서 작업 공간을 만드세요. 2. [빠른 연동](/docs/workspace/quickstart)에서 첫 브라우저 이벤트와 서버 이벤트를 보내세요. 3. [CLI 사용하기](/docs/workspace/cli)에서 계정 조회와 배포 자동화를 준비하세요. 4. [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)에서 설정·룰·알림을 파일로 옮기세요. 5. [추적 설정](/docs/workspace/tracking-setup)에서 SDK 동작과 운영 기준을 자세히 확인하세요. 6. [비용과 보존 정책](/docs/workspace/cost-and-retention)에서 운영 기준을 정하세요. --- # CLI 사용하기 Source: https://data.reopt.ai/docs/workspace/cli reopt-data CLI를 설치하고 브라우저 로그인, 계정 조회, 프로젝트 관리, 분석 조회, 카탈로그·프로젝트 정의·소스맵 자동화를 사용합니다. `reopt-data`는 사람, CI, 에이전트가 같은 방식으로 리옵트 데이터를 다루는 CLI입니다. 브라우저에서 개인 세션을 승인하면 내 계정을 조회할 수 있고, 배포 자격증명을 설정하면 프로젝트·카탈로그·쿼리·소스맵을 다룰 수 있습니다. ## 설치하고 로그인하기 Node.js 22 이상에서 전역으로 설치하거나 `npx`로 바로 실행하세요. ```bash npm install -g @reopt-ai/data-cli reopt-data --help # 설치 없이 확인 npx -y @reopt-ai/data-cli --help ``` 개인 계정으로는 다음 네 명령을 씁니다. ```bash reopt-data login reopt-data status --format text reopt-data account show --format table reopt-data logout ``` ## 목적별 자격증명 개인 로그인과 배포 자격증명은 권한도 보관 방식도 다릅니다. | 목적 | 자격증명 | 사용하는 명령 | | -------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | 내 계정과 접근 범위 확인 | 브라우저에서 승인한 사용자 세션 | `status`, `account show`, `logout` | | 프로젝트·카탈로그 운영 | `REOPT_DATA_ORG_KEY` | `project *`, `event *`(`init`·`types` 제외), `pull`·`push`·`diff`·`verify`·`import`·`unlink`, 소스맵 | | 분석 쿼리와 세그먼트 | `REOPT_DATA_CLIENT_ID` + `REOPT_DATA_CLIENT_SECRET` | `query *`, `segment *` | | 자격증명 없이 실행 | 없음 | `link`, `config show`, `event init`, `event types`, `sourcemap inject` | 개인 CLI 세션의 현재 scope는 `account:read`뿐입니다. 그래서 `reopt-data login`으로 얻은 세션은 프로젝트 관리나 분석 쿼리에 필요한 조직 키·클라이언트 자격증명을 대신하지 않습니다. 반대로 배포 키만 설정해서는 `account show`를 실행할 수 없습니다. 조직 키와 client secret은 처음 만들 때 한 번만 내려옵니다. * 조직 키는 콘솔에서 발급하지 않습니다. 필요하면 [문의 창구](/contact)로 요청하세요. 잃어버린 키는 다시 볼 수 없고 새로 발급받아야 합니다. * `project create`의 응답에는 서버용 client의 ID(`clients.server.id`)가 들어 있고, `clientSecret`은 처음 만들 때만 함께 내려옵니다. 잃어버린 secret은 `project rotate-secret`으로 다시 발급합니다. ## 브라우저 로그인 과정 1. CLI가 짧은 확인 코드와 브라우저 URL을 발급받습니다. 2. 브라우저가 열리면 로그인된 Reopt 계정으로, 표시된 코드가 같은지 확인하고 승인합니다. 3. CLI가 승인 상태를 기다렸다가 한 번만 세션으로 교환합니다. 4. 세션을 `~/.reopt/data/auth.json`에 원자적으로 저장합니다. 비밀번호와 브라우저 쿠키는 터미널로 전달되지 않습니다. 브라우저 URL에는 사람이 확인할 코드만 들어가고, 별도의 device secret은 CLI 메모리에만 둡니다. 브라우저 쿠키와 CLI bearer는 서버에서 audience가 분리되어 있어 서로 대신 사용할 수 없습니다. 원격 서버나 브라우저를 열 수 없는 환경에서는 URL만 출력하세요. ```bash reopt-data login --no-browser ``` 이미 유효한 로그인이 있으면 `login`은 그 세션을 재사용합니다. 새 세션으로 바꾸려면 `--force`를 사용하세요. 새 세션이 안전하게 저장된 뒤 이전 서버 세션을 해제합니다. ```bash reopt-data login --force ``` POSIX 환경에서 인증 디렉터리는 `0700`, 파일은 `0600`으로 유지됩니다. 테스트나 격리된 실행에서는 `REOPT_DATA_AUTH_FILE`로 경로를 바꿀 수 있습니다. ## 로그인 상태 확인하기 `status`는 로컬 파일이 있는지만 보지 않고 서버에서 세션을 검증합니다. 토큰 값은 출력하지 않으며, 서버가 만료 시각을 연장했다면 로컬 파일에도 새 시각을 저장합니다. ```bash reopt-data status reopt-data status --format text ``` `session` 필드는 다음처럼 해석하세요. | 값 | 의미 | 다음 행동 | | ------------- | -------------------------------- | -------------------------- | | `valid` | 서버 검증까지 통과했습니다. | 그대로 사용합니다. | | `missing` | 저장된 사용자 세션이 없습니다. | `reopt-data login`을 실행합니다. | | `invalid` | 서버가 만료되거나 해제된 세션으로 판정했습니다. | `login --force`로 교체합니다. | | `corrupt` | 인증 파일 형식이나 서버 origin이 올바르지 않습니다. | `login --force`로 교체합니다. | | `unreachable` | 세션은 있지만 서버에 연결할 수 없어 판정하지 못합니다. | URL과 네트워크를 먼저 확인합니다. | `status`는 조직 키와 클라이언트 자격증명의 설정 여부도 함께 보여줍니다. 값은 출력하지 않습니다. ## 계정과 접근 가능한 프로젝트 확인하기 ```bash reopt-data account show reopt-data account show --format table ``` 응답에는 로그인 사용자, CLI 세션의 scope·만료·최근 사용 시각, 사용자가 멤버인 조직과 활성 프로젝트가 들어갑니다. 조직 키, write key, client secret 같은 배포 자격증명은 반환하지 않습니다. `account show`는 먼저 서버에서 사용자 세션을 다시 검증하고 `account:read` scope를 확인합니다. 브라우저 세션 쿠키를 bearer로 보내거나 CLI 토큰을 브라우저 쿠키로 사용하면 모두 거절됩니다. ## 로그아웃과 세션 폐기 ```bash reopt-data logout ``` `logout`은 저장된 서버 세션을 폐기한 뒤 로컬 인증 파일을 지웁니다. 서버에 연결하지 못해 폐기를 확인하지 못해도 로컬 파일은 제거하고 경고를 출력합니다. `REOPT_DATA_USER_TOKEN`으로 주입한 세션은 실행 환경이 소유하므로 CLI가 폐기하거나 지우지 않습니다. 로그아웃 뒤에도 이 변수가 설정되어 있으면 계속 사용되므로 secret manager나 셸 환경에서 직접 제거하세요. ## 배포 자격증명 설정하기 반복 실행과 CI에서는 플래그보다 환경변수를 사용하세요. 플래그 값은 셸 히스토리와 프로세스 목록에 남을 수 있습니다. ```bash export REOPT_DATA_ORG_KEY="..." export REOPT_DATA_CLIENT_ID="..." export REOPT_DATA_CLIENT_SECRET="..." ``` 필요한 자격증명이 없으면 CLI는 네트워크 요청 전에 종료 코드 `2`로 실패하고 설정할 변수 이름을 알려줍니다. CI secret store에는 실제 값만 넣고 로그, 저장소, `reopt-data.config.*`에는 기록하지 마세요. ## 출력과 입력 형식 기본 출력은 JSON입니다. stdout에는 결과만 쓰고, 진행 상황과 오류 envelope는 stderr에 씁니다. ```bash reopt-data project list --format json reopt-data project list --format table reopt-data project list --format csv reopt-data account show --format yaml ``` `project list`는 `--organization-id`가 필요합니다. 이 문서의 예시는 아래 [저장소를 프로젝트에 연결하기](#저장소를-프로젝트에-연결하기)로 조직과 프로젝트를 연결해 둔 저장소를 가정합니다. 필드 선택과 cursor 전체 순회를 조합할 수 있습니다. ```bash reopt-data project list --fields id,name --page-all --page-limit 20 ``` JSON에서 `--page-all`을 쓰면 배열 하나가 아니라 레코드당 한 줄인 NDJSON을 출력합니다. `--page-limit`의 기본값은 10입니다. 다음 cursor가 남아 있는데 `--page-limit`에 도달하면 종료 코드 `6`으로 끝나 결과가 잘렸음을 알립니다. `login --format json`도 진행 중 결과를 전달해야 하므로 `verification_uri`, `polling`, `success` 이벤트를 NDJSON으로 출력합니다. 세션 토큰과 device secret은 포함하지 않습니다. 입력은 플래그, JSON 문자열, 파일, stdin으로 줍니다. `--input`과 플래그를 함께 주면 플래그의 값이 `--input`의 같은 필드를 덮어씁니다. ```bash reopt-data query funnel --input @funnel.json echo '{"projectId":"prj_123"}' | reopt-data project get --input @- ``` 모든 옵션은 실행 코드와 같은 schema에서 생성되는 help로 확인하세요. ```bash reopt-data project create --help reopt-data query funnel --help ``` ## 안전한 변경 명령 삭제와 secret 회전 같은 파괴적 명령은 `--yes`가 없으면 요청을 보내지 않고 종료 코드 `7`로 멈춥니다. ```bash reopt-data project delete prj_123 --external-id brand_9 --yes reopt-data project rotate-secret prj_123 --yes ``` * `project delete`는 프로젝트와 그 프로젝트의 모든 데이터를 지우며 되돌릴 수 없습니다. `--external-id`가 프로젝트에 기록된 값과 다르면 거절합니다. 콘솔에서 만든 프로젝트는 external-id가 없어서 이 명령으로 지울 수 없습니다. * `project rotate-secret`은 서버용 client의 ID를 두 번째 인자로 받습니다. 이전 secret은 즉시 동작을 멈춥니다. 입력 검증, 자격증명 설정 여부 확인, `--yes` 확인은 모두 네트워크 요청 전에 끝납니다. `--yes`가 없어서 `7`로 끝났다면 서버에는 아무것도 보내지 않은 것입니다. `7`은 서버가 파괴적 변경을 막았을 때도 나옵니다. 예를 들어 `project update`로 보존 기간을 줄이면서 `--allow-destructive`를 빼면, 요청이 나간 뒤 서버가 거절하고 `7`로 끝납니다. 서버가 돌려준 401·403은 `2`로, 그 밖의 400·404·409·422는 `3`으로 끝납니다. 이 두 코드는 요청 전과 후 양쪽에서 나올 수 있습니다. `sourcemap upload`도 같은 규칙을 따릅니다. 업로드를 시작하는 단계에서 400·404가 오면(프록시나 예전 배포가 답한 경우 포함) 다시 시도하지 않고 `3`으로 끝납니다. `sourcemap inject`와 `sourcemap upload`는 `--dry-run`으로 파일 탐색과 계획을 먼저 확인할 수 있습니다. 지원하지 않는 명령에 `--dry-run`을 주면 무시하지 않고 종료 코드 `3`으로 실패합니다. ## 저장소를 프로젝트에 연결하기 `reopt-data link`는 저장소에 `reopt-data.config.mjs`를 만들어 프로젝트와 조직 ID를 기록합니다. 연결해 두면 `--project-id`와 `--organization-id`를 매번 주지 않아도 됩니다. ```bash reopt-data link --project-id prj_123 --organization-id org_abc reopt-data config show ``` * 이 파일에는 ID와 경로만 넣습니다. 커밋되는 파일이라 키를 넣으면 종료 코드 `4`로 거절합니다. * 값이 정해지는 순서는 플래그, 환경변수, config 순입니다. * `config show`는 해석된 설정과 각 자격증명의 설정 여부만 보여주고 값은 출력하지 않습니다. * `--config `로 다른 위치의 config를 지정합니다. 기본은 현재 디렉터리에서 위로 올라가며 찾은 가장 가까운 `reopt-data.config.{mjs,js,json}`입니다. ## 프로젝트 만들고 관리하기 조직 키로 프로젝트를 만들고 설정을 바꿉니다. ```bash reopt-data project create --external-id brand_9 --name "Acme Shop" --domain shop.acme.com --timezone Asia/Seoul reopt-data project get prj_123 reopt-data project update prj_123 --symbol-host-app studio-web ``` * `--external-id`가 멱등성 키입니다. 같은 값으로 다시 실행하면 새로 만들지 않고 `created: false`로 답합니다. 이때 `writeKey`와 서버 client ID는 다시 내려오고 `clientSecret`만 빠집니다. * 같은 `--external-id`에 시간대나 보존 기간을 다르게 주면 `409 external_id_conflict`로 거절합니다. * `project create`의 응답에는 브라우저용 `writeKey`와 서버용 client ID·secret이 들어 있습니다. * `project update`에서 생략한 필드는 그대로 둡니다. 비울 수 있는 필드는 `null`을 주면 값을 지우고, 시간대처럼 비울 수 없는 필드에 `null`을 주면 거절합니다. 보존 기간을 줄일 수 있는 변경은 `--allow-destructive --yes`가 함께 필요합니다. ## 분석 쿼리와 세그먼트 조회하기 클라이언트 자격증명으로 계산된 분석 결과를 읽습니다. 모두 읽기 전용입니다. ```bash reopt-data query timeseries --start-date 2026-08-01 --end-date 2026-08-31 --event-name purchase reopt-data query funnel --start-date 2026-08-01 --end-date 2026-08-31 --steps view_item --steps add_to_cart --steps purchase reopt-data query retention --help reopt-data query traffic-overview --start-date 2026-08-01 --end-date 2026-08-31 reopt-data query traffic-sources --help reopt-data segment list --format table reopt-data segment preview --segment-id seg_123 ``` * 날짜는 `YYYY-MM-DD`이고 `--timezone`을 주지 않으면 프로젝트 시간대로 자릅니다. * `query funnel`의 단계는 순서대로 `--steps`를 반복해 2\~10개를 줍니다. * `query timeseries`에서 `--breakdown`과 `--segment-id`는 함께 쓸 수 없습니다. * 세그먼트는 조회만 합니다. `segment list`의 `deviceCount`가 `null`이면 0명이 아니라 아직 계산된 적이 없는 상태입니다. ## 이벤트 카탈로그를 코드로 관리하기 프로젝트 저장소의 `reopt-data.events.json`을 기준으로 서버 카탈로그를 동기화합니다. ```bash reopt-data event init --project-id prj_123 reopt-data event pull reopt-data event diff --format text reopt-data event push # 계획만 출력 reopt-data event push --apply --yes # 실제 반영 reopt-data event verify # drift가 있으면 exit 8 reopt-data event types --out src/reopt-events.d.ts ``` 파일에서 생략한 필드는 기존 값을 유지하지 않고 계약의 기본값이 됩니다. 파일에서 뺀 이벤트는 삭제되지 않고 `archived`가 됩니다. `event push`는 마지막 동기화 시각으로 전체 배치를 비교합니다. 콘솔 변경과 충돌하면 일부만 반영하지 않고 전부 거절합니다. 동기화 지점은 파일 옆의 `reopt-data.events.lock.json`에 기록되니 함께 커밋하세요. 변경에는 등급이 있습니다. 표시 필드만 바뀌는 `safe`는 `--apply`만으로 반영됩니다. `conversion`·`status`처럼 숫자가 달라지는 `metric-changing`과 롤업 재계산을 부르는 `expensive`(`rollupProperties`)는 `--yes`가 필요합니다. 파일 없이 이벤트 하나만 고치거나 이력을 볼 수도 있습니다. ```bash reopt-data event set-meta prj_123 purchase --conversion --rollup-properties plan --rollup-properties currency reopt-data event history purchase --limit 20 ``` `event types`는 파일만으로 오프라인에서 동작하고 속성 값의 타입은 `unknown`으로 둡니다. `--with-types`를 주면 프로젝트의 속성 카탈로그에서 타입을 읽어 오며, 이때는 조직 키가 필요합니다. `event pull`은 사람이 설명을 달았거나 숨긴 속성만 파일에 씁니다. 관측된 속성을 전부 쓰려면 `--include-discovered`를 주세요. 이때는 배포마다 파일이 달라질 수 있습니다. `version: 2` 파일은 `properties` 절에 속성의 의미(타입·설명·숨김)도 함께 씁니다. 이벤트와 달리 파일에 없는 속성은 관측된 그대로 둡니다. 속성은 수집으로 생기는 것이라 파일로는 없앨 수 없기 때문입니다. 그래서 `verify`는 기본으로 미선언 속성을 지적하지 않습니다. 저장소가 카탈로그를 소유한다면 아래 둘 중 골라 켜세요. ```bash reopt-data event verify --strict-properties # 콘솔에서 손댔는데 파일에 없는 속성 reopt-data event verify --strict-observed # 수집이 관측만 했는데 파일에 없는 속성 ``` 두 옵션은 서로를 포함하지 않고 보고 대상도 겹치지 않습니다. 둘 다 필요하면 함께 주세요. `--strict-observed`는 SDK 소유 키(`$` 예약 키와 자동 이벤트 속성)를 제외합니다. `--strict-observed`는 속성을 추가한 PR에서는 실패하지 않습니다. 그 시점에는 서버가 그 키를 본 적이 없기 때문입니다. 배포 뒤 첫 이벤트가 들어와야 행이 생기므로, 실패는 관계없는 다음 PR에서 납니다. 이 점을 감수할 저장소에서만 켜세요. ## 프로젝트 설정·룰·알림을 코드로 관리하기 프로젝트 설정, 에러 트래킹 룰, 알림도 카탈로그와 같은 규칙으로 저장소의 `reopt-data/` 디렉터리에 선언할 수 있습니다. 여섯 동사는 모두 최상위 명령입니다. 표를 출력하는 명령은 `diff`입니다. `status`는 로그인 상태를 보여줍니다. ```bash reopt-data pull # 서버 → reopt-data/*.json + reopt-data.lock.json reopt-data diff --format text # 종류 / key / 작업 / 등급 reopt-data verify # CI 게이트. 어긋나면 exit 8 reopt-data push --apply --yes # 반영. 보존 기간을 줄일 수 있는 변경은 --allow-destructive도 필요 reopt-data import error-rule --key ignore-vendor reopt-data unlink alert purchase-drop reopt-data --env staging pull # config `environments`로 프로젝트를 고르고 lock을 환경별로 ``` 파일은 `key`가 있는 룰·알림만 관리하고, 콘솔에서 만든 항목은 건드리지 않습니다. 웹훅 URL은 `"${env:SLACK_ALERTS_URL}"`처럼 환경 변수 이름으로 적으면 push 시점에 치환됩니다. 파일 형식, 등급, 충돌 해결, 환경 설정은 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)에서 자세히 다룹니다. ## 소스맵을 CI에서 업로드하기 ```bash reopt-data sourcemap inject --dir .next/static reopt-data sourcemap upload \ --dir .next/static \ --url-prefix https://shop.example.com/_next/static \ --project-id "$REOPT_DATA_PROJECT_ID" \ --release "$GIT_SHA" \ --delete-after-upload ``` 먼저 `--dry-run`으로 대상 파일과 업로드 계획을 확인하세요. dry run은 아무것도 바꾸지 않고 키도 필요 없습니다. `sourcemap inject`는 빌드된 `.js`마다 내용에서 유도한 chunk ID를 새겨, URL이 바뀌어도 맵을 찾을 수 있게 합니다. 여러 번 실행해도 결과가 같습니다. 업로드 대상은 두 가지입니다. | 대상 | 플래그 | 자격증명 | | ----------------- | ---------------------------------------------------- | ---- | | 프로젝트 하나 | `--url-prefix` + `--project-id` | 조직 키 | | 여러 프로젝트를 그리는 한 번들 | `--path-prefix` + `--host-app` + `--organization-id` | 조직 키 | 일부 맵이 실패하면 종료 코드 `6`으로 끝나고 결과에 실패한 항목이 나옵니다. `skipped`가 0보다 큰데 종료 코드가 `0`이면 서버가 이미 가진 맵을 건너뛴 것입니다. `--delete-after-upload`는 서버에 저장된 것이 확인된 `.map`(이번에 올렸거나 이미 같은 내용이 있는 것)을 지웁니다. 업로드에 실패했거나 너무 커서 건너뛴 `.map`은 남기고, dry run에서는 지우지 않습니다. 프로젝트와 조직 host app 범위의 symbol set은 `sourcemap list`와 `sourcemap delete`로 조회하고 폐기합니다. ```bash reopt-data sourcemap list --project-id prj_123 reopt-data sourcemap delete sym_123 --project-id prj_123 --yes reopt-data sourcemap list --scope host --organization-id org_abc --host-app studio-web ``` ## 셸 completion과 에이전트 연동 현재 셸에 completion을 적용합니다. ```bash # bash / zsh source <(reopt-data completion bash) source <(reopt-data completion zsh) # fish reopt-data completion fish | source ``` 에이전트가 명령 계약을 탐색해야 하면 실행 가능한 schema catalog를 사용하세요. ```bash reopt-data tools --json ``` MCP client에는 같은 도구 registry를 stdio 서버로 연결합니다. ```json { "mcpServers": { "reopt-data": { "command": "npx", "args": ["-y", "@reopt-ai/data-cli", "mcp"], "env": { "REOPT_DATA_ORG_KEY": "..." } } } } ``` 도구 이름은 `reopt_data__` 형식입니다. 예: `reopt_data_project_create`, `reopt_data_query_funnel`. `login`, `status`, `logout`은 MCP 도구로 노출되지 않습니다. `account show` MCP 도구는 저장된 사용자 로그인 또는 `REOPT_DATA_USER_TOKEN`을 사용합니다. 배포 도구는 MCP process에 주입한 환경변수와 repository config를 사용합니다. 환경은 `REOPT_DATA_ENV`로만 고를 수 있습니다. 파괴적 도구는 CLI의 `--yes` 대신 `confirmed: true`가 필요합니다. `event push`처럼 계획에 따라 위험해지는 도구는 계획이 확인을 요구할 때만 `confirmed: true`가 필요합니다. 0이 아닌 종료 코드는 모두 MCP 오류 결과로 돌아옵니다. ## 종료 코드 | 코드 | 의미 | 처리 방법 | | --- | ------------------- | ------------------------------ | | `0` | 성공 | 다음 단계로 진행합니다. | | `1` | API·네트워크 오류 | `retryAfterMs`와 서버 상태를 확인합니다. | | `2` | 인증 오류 | 필요한 자격증명을 설정하거나 회전합니다. | | `3` | 입력 검증 오류·없는 리소스·충돌 | 인자와 `details.issues`를 수정합니다. | | `4` | 환경·설정 오류 | URL, 파일, 선택 dependency를 확인합니다. | | `5` | CLI 내부 오류 | request ID와 함께 버그로 보고합니다. | | `6` | 부분 실패·잘린 pagination | stdout 결과와 stderr를 함께 확인합니다. | | `7` | 확인 필요 | 영향을 확인한 뒤 `--yes`를 명시합니다. | | `8` | 드리프트(카탈로그·프로젝트 정의) | `pull`, `diff`, `push`로 해소합니다. | ## 자주 막히는 지점 | 증상 | 확인할 것 | | ---------------------------- | ---------------------------------------------------- | | 브라우저가 열리지 않음 | `login --no-browser`의 URL을 복사하고 코드가 같은지 확인하세요. | | `session: invalid` | `login --force`로 서버 세션을 새로 발급하세요. | | `session: unreachable` | 네트워크 연결, DNS와 방화벽을 확인하세요. | | 로그인했지만 `project create`가 실패함 | 개인 세션은 조직 키를 대신하지 않습니다. `REOPT_DATA_ORG_KEY`를 설정하세요. | | `account show`가 인증 오류로 실패함 | 사용자 세션이 있고 `account:read` scope로 발급됐는지 확인하세요. | | JSON parser가 `login`에서 실패함 | 로그인 출력은 JSON 문서 하나가 아니라 NDJSON 이벤트 stream입니다. | | logout 뒤에도 로그인으로 표시됨 | `REOPT_DATA_USER_TOKEN`이 남았는지 확인하고 실행 환경에서 제거하세요. | ## 다음 단계 * 프로젝트에 첫 이벤트를 붙이려면 [빠른 연동](/docs/workspace/quickstart) * 카탈로그 의미와 변경 등급은 [데이터 카탈로그](/docs/project-workspace/event-catalog) * 설정·룰·알림을 파일로 두는 흐름은 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code) * 같은 기능의 HTTP API는 [HTTP API](/docs/platform/http-api) --- # 비용과 보존 정책 Source: https://data.reopt.ai/docs/workspace/cost-and-retention 조직 단위 사용량과 비용, 프로젝트 단위 보존 정책과 데이터 정리를 관리합니다. 비용은 조직 단위로 보고, 데이터 보존은 프로젝트 단위로 정합니다. 사용량을 읽는 방법과 보존 기간을 정할 때 확인할 기준을 설명합니다. ## 조직 단위 사용량 및 요금 조직의 사용량 및 요금 화면은 `/dashboard/[orgId]/billing`에서 엽니다. 조직 멤버는 사용량과 월별 이력을 조회할 수 있습니다. 워크스페이스 소유자는 이 화면에서 Starter나 Pro를 구독하고 자동 갱신을 해지할 수 있습니다. 구독을 시작한 뒤에는 구독한 그 소유자 계정만 해지할 수 있습니다. 화면 상단의 버튼은 플랜에 따라 **Starter 구독**, **Pro로 변경**, **한도 증설 문의** 중 하나입니다. 여기에서 다음 데이터를 확인합니다. * 이번 달 데이터 포인트 사용량과 월 한도 대비 사용 비율 * 지금 속도로 한도에 닿는 날짜, 한도를 넘어 받지 못한 이벤트 수(있을 때) * 현재 플랜, 남은 데이터 포인트, 월말 예상, 다음 초기화 시각 * 누적 사용량과 월 한도선, 지금 속도의 예상선, 일별 사용량 * 데이터 종류별(이벤트·예외·AI 관측·세션 리플레이)·프로젝트별 사용량 * 지급받은 데이터 포인트의 기간과 이번 달 쓸 수 있는 양(지급받았을 때) * 이번 달 분석 활동 횟수(차감 없음) * 월별 사용량과 그달의 한도 Free처럼 수집 한도가 없는 워크스페이스는 사용량 막대 자리에 **수집 한도 없음**이 보이고, 남은 양·누적 차트·종류별·프로젝트별 사용량은 나오지 않습니다. ## 사용량 및 요금에서 답할 수 있는 질문 * 이번 달에 얼마나 사용했나요? * 어떤 프로젝트가 사용량을 많이 만들었나요? * 이 속도라면 월말 전에 계약 한도를 넘기나요? * 데이터 포인트는 어떤 데이터에서 많이 쓰이나요? ## 플랜과 계약 콘솔에서 만든 조직은 Free로 시작합니다. Free는 결제 없이 워크스페이스와 프로젝트를 만들고 키를 준비할 수 있지만, 자기 데이터는 수집하지 않습니다. 기능은 모든 계정에 열려 있는 공개 데모 워크스페이스에서 예제 데이터로 먼저 둘러볼 수 있습니다. 더 많은 데이터를 수집하려는 팀은 Starter나 Pro를 구독합니다. Starter는 월 $49에 월 50만 데이터 포인트, Pro는 월 $119에 월 150만 데이터 포인트이고 기능은 같습니다. 두 플랜 모두 세금 별도이며 연간 약정 없이 월 단위로 이용합니다. 워크스페이스 소유자가 **사용량 및 요금**에서 **구독 플랜**을 고르고 **Starter 구독하기**나 **Pro 구독하기**로 결제하면 그 플랜의 한도가 적용됩니다. 이 한도는 수집한 이벤트·예외·AI 관측 이벤트와 세션 리플레이 녹화를 합한 양이며, 매월 1일 오전 9시(한국 시간)에 초기화됩니다. 화면에 구독 버튼 대신 `온라인 구독 준비 중`이 보이면 아직 온라인 결제가 열리지 않은 것이니 [요금 안내와 도입 문의](/#pricing)로 문의하세요. Starter와 Pro는 미국 달러로만 결제합니다. 세금은 구매자의 국가에 따라 결제할 때 더해지며, 한국에서 결제하면 부가가치세가 붙습니다. 자동 갱신도 같은 화면에서 해지할 수 있습니다. 해지하면 결제한 기간이 끝날 때까지 그 플랜의 한도를 유지하고, 기간이 끝나면 Free로 돌아가 새 데이터를 수집하지 않습니다. 구독 중에는 같은 화면의 **Pro로 변경**이나 **Starter로 변경**으로 플랜을 바꿉니다. 바꾸는 즉시 새 한도가 적용되고 다음 결제일은 그대로입니다. 다만 Starter로 내릴 때 이번 달에 이미 Starter 한도만큼 수집했다면, 이번 달은 Pro 한도로 계속 수집하고 다음 달부터 Starter 한도를 적용합니다. Pro로 올리면 남은 기간의 차액이 바로 결제되고, Starter로 내리면 남은 기간의 차액이 다음 청구에서 빠집니다. 자동 갱신을 해지한 구독은 플랜을 바꿀 수 없습니다. 해지한 뒤에도 이용 기간이 남아 있으면 같은 화면의 **구독 재개**로 되돌릴 수 있고, 다음 결제일은 그대로입니다. 이 기간에는 새로 결제할 수 없으니, 다른 플랜을 원하면 재개한 뒤 바꾸세요. 기존 데이터는 보존 정책 안에서 계속 조회할 수 있습니다. 자동 갱신 해지만으로는 이미 결제한 기간의 요금이 환불되지 않으며, 환불 기준은 [서비스 약관의 해지와 환불](/terms)을 따릅니다. 한 Reopt 계정은 한 워크스페이스의 구독을 관리합니다. 이용 중인 구독과 진행 중인 결제가 없다면 **결제 연결 해제**로 다른 워크스페이스를 선택할 수 있습니다. 결제 화면을 취소했더라도 유효한 결제가 진행 중이면 만료 또는 최종 결과 확인까지 기다려야 합니다. 큰 사용량과 조직별 운영 요구는 Enterprise 맞춤 견적으로 조율합니다. 기존 리옵트 엔터프라이즈 계약에 추가할 수도 있습니다. Starter는 문서와 비동기 문의를 지원합니다. 맞춤 연동·데이터 이관·전용 인프라·SLA는 별도 견적입니다. [요금 안내와 도입 문의](/#pricing)에서 시작하세요. 사용량 및 요금 화면의 **현재 플랜**은 조직에 적용된 수집 한도를 보여줍니다. Starter를 문의한 것만으로 한도가 생기거나 기존 조직의 요금이 바뀌지는 않습니다. | 현재 플랜 | 수집 | 한도가 정해지는 방식 | | ------- | ----------------------------------------- | ---------------------------- | | Free | 자기 데이터를 수집하지 않습니다 | 콘솔에서 만든 조직의 기본값, 구독 기간이 끝난 뒤 | | 플랜 없음 | 한도가 0이라 자기 데이터를 수집할 수 없습니다 | 계약 한도가 정해지기 전의 조직 | | 공개 데모 | 예제 데이터를 보는 공용 워크스페이스입니다. 내 데이터는 수집하지 않습니다 | 모든 계정에 공통 | | Starter | 월 50만 데이터 포인트까지 수집 | 구독 | | Pro | 월 150만 데이터 포인트까지 수집 | 구독 | | Growth | 계약한 월간 데이터 포인트 한도까지 수집 | Enterprise 계약 | | Scale | 한도 없이 수집 | Enterprise 계약 | Starter 구독 한도는 결제 확인 후 적용되고, Enterprise 계약의 한도는 계약 내용에 따라 리옵트가 설정합니다. 수집 한도는 조직의 월간 데이터 포인트 하나뿐입니다. 세션 리플레이 녹화도 이 한도에서 데이터 포인트를 쓰며, 프로젝트별 녹화 저장 한도는 그와 별개로 적용됩니다. Scale을 제외한 플랜은 한도에 도달하면 이벤트 수집이 거절됩니다. 수집 API는 이때 429와 `quota_exceeded` 코드를 돌려줍니다. 한도가 0인 조직은 수집이 차단됩니다. 남은 양보다 큰 요청이 오면 SDK가 보낸 요청은 남은 양만큼 저장하고, 나머지 이벤트는 SDK가 보관했다가 한도가 생기면 다시 보냅니다. 한도에 도달해도 분석 조회·실시간·알림은 계속 동작합니다. 막히는 것은 새 이벤트 수집뿐입니다. 사용량 월은 UTC 기준이며, 한국 시간으로 매월 1일 오전 9시에 초기화됩니다. 계약에 유예 한도가 포함된 조직은 그 비율만큼 더 수집되고, 초과분의 정산은 계약 조건을 따릅니다. 할당량의 80%에 이르렀을 때와 모두 사용했을 때 owner·admin에게 이메일이 가고 그 조직의 프로젝트 화면 위쪽에 배너가 뜹니다. 배너와 메일은 지금 속도로 한도에 닿는 날짜와 함께, 플랜에 따라 할 수 있는 일을 안내합니다. Starter는 Pro로 변경, 그 밖의 플랜은 한도 증설 문의입니다. Free 조직의 배너는 Starter 구독을 안내합니다. 한도에 막혀 받지 못한 이벤트 수는 **사용량 및 요금**에 따로 표시됩니다. SDK가 거절된 이벤트를 보관했다가 다시 보내도 같은 이벤트는 한 번만 셉니다. 다시 보낸 이벤트가 다음 달에 받아들여지면 그 달의 데이터 포인트로 셉니다. ## 분석 계산의 공정 사용 분석 조회는 데이터 포인트를 쓰지 않습니다. 대신 퍼널, 리텐션, 사용자 타임라인, 세그먼트 미리보기를 새로 계산하는 요청은 워크스페이스마다 1분에 받는 횟수가 정해져 있습니다. | 플랜 | 1분에 받는 새 계산 | | ------------- | ----------: | | Free | 30회 | | Starter | 60회 | | Pro | 120회 | | 공개 데모 | 60회 | | Enterprise 계약 | 제한 없음 | 같은 조건의 결과는 잠시 저장해 두므로 곧바로 다시 열거나 대시보드를 새로 고칠 때는 세지 않습니다. 횟수를 넘으면 차감 없이 몇 초 뒤 다시 시도하라는 안내가 나오고, 그 시간이 지나면 같은 요청이 정상으로 처리됩니다. ## 데이터 포인트 데이터 포인트는 리옵트 데이터가 받아서 보관하는 데이터의 단위입니다. 조직 안의 모든 프로젝트에서 수집한 양을 합산합니다. 예외와 AI 관측 이벤트는 일반 이벤트와 중복 차감하지 않습니다. | 데이터 | 데이터 포인트 | 적용 범위 | | ------- | ------: | ----------------------------- | | 일반 이벤트 | 1 | 정상 접수된 이벤트 1건 | | 예외 | 1 | 에러 이벤트 1건 | | AI 관측 | 1 | 생성·임베딩·스팬·트레이스·피드백 이벤트 각각 1건 | | 세션 리플레이 | 10 | 녹화 1건. 페이지 하나를 최대 30분까지 녹화합니다 | 분석 조회, 실시간 보기, 사용자 타임라인, 세그먼트 미리보기, 알림 발송은 데이터 포인트를 쓰지 않습니다. **사용량 및 요금**의 **이번 달 분석 활동**에서 횟수만 참고로 보여 줍니다. 예전에는 이 활동도 데이터 포인트로 셌습니다. 그때 쓴 양은 그달 합계에 남고 \*\*분석 조회 (이전 기준)\*\*으로 표시됩니다. 중복·유효하지 않은 수집 이벤트와 봇·의심 트래픽은 차감하지 않습니다. SDK가 보낸 요청은 남은 수집 한도만큼 접수하고, 나머지는 SDK가 보관합니다. 수집 API를 직접 호출한 요청은 배치 전체가 남은 수집 한도 안에 들어와야 접수됩니다. 일부 저장 실패·중복 건은 예약한 데이터 포인트를 돌려줍니다. 한도 때문에 받지 못한 이벤트는 저장되지 않고, 그 수가 **사용량 및 요금**에 표시됩니다. 기본 할당량을 썼어도 계약상 유예가 남아 있으면 수집할 수 있습니다. \*\*남은 데이터 포인트 (유예 포함)\*\*는 유예를 포함한 남은 양입니다. 초과분 정산은 계약 조건을 따르며 이 화면에서 자동 추가 청구하지 않습니다. 리옵트가 데이터 포인트를 지급하면(체험, 장애 보상 등) 정해진 기간 동안 플랜 한도에 더해집니다. 플랜과 구독은 그대로 유지됩니다. 매달 플랜 한도를 먼저 쓰고, 넘친 만큼을 먼저 끝나는 지급부터 씁니다. **사용량 및 요금**의 **지급받은 데이터 포인트**에서 기간과 이번 달 쓸 수 있는 양을 확인할 수 있습니다. 사용량과 구독 상태는 별도로 갱신됩니다. 월별 이력은 달마다 사용량과 그달의 한도를 함께 보여 줍니다. 한도가 없던 달과 한도가 기록되지 않은 예전 달은 한도 없이 사용량만 보입니다. 월말 예상은 지금까지의 일평균을 이번 달의 남은 날에 적용한 추정치입니다. 이 속도로 이번 달 안에 한도에 닿으면 그 날짜를 함께 보여 줍니다. ## 프로젝트 단위 보존 정책 프로젝트 설정 화면에서 보존 정책을 관리합니다. 보존 기간에 따라 저장소 비용과 장애 복구가 가능한 기간이 함께 달라집니다. 조직 멤버는 저장소 사용량과 삭제 미리보기를 볼 수 있습니다. 보존 정책 변경과 정리 실행은 `owner` 또는 `admin`만 할 수 있습니다. 지원 값: * 무제한 (삭제 안 함) * 30일 * 60일 * 90일 * 180일 * 365일 CLI의 `project update --retention-days`나 `reopt-data/project.json`으로는 1\~3650일 사이의 값을 지정할 수 있습니다. 이 경로로 보존 기간을 줄이거나, 새로 지정하거나, 해제하려면 `--allow-destructive --yes`가 필요합니다. [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)를 확인하세요. 운영 기능: * 저장소 사용량: 테이블별 행 수와 비율 * 삭제 미리보기: 화면에서 고른 보존 기간을 적용했을 때 테이블별로 지워질 행 수 * 수동 정리 실행: 저장된 보존 기간보다 오래된 행을 삭제 보존 기간을 저장하는 것만으로는 데이터가 지워지지 않습니다. 이벤트·세션·프로필과 원본 이벤트는 **수동 정리 실행**을 누를 때 삭제됩니다. 미리보기는 아직 저장하지 않은 선택값으로 계산하고, 정리는 저장된 값으로 실행합니다. 값을 바꿨다면 먼저 저장하세요. 정리는 한 번 더 확인을 받은 뒤 실행되며 되돌릴 수 없습니다. 다음 두 가지는 프로젝트 보존 정책과 별개로 자동 정리됩니다. * AI 관측 데이터: 프로젝트 설정의 AI 보존 기간(기본 90일, 1\~365일)과 프로젝트 보존 기간 중 짧은 쪽을 기준으로 매일 정리합니다. * 세션 리플레이 녹화: 세션 리플레이 화면의 녹화 설정에 있는 보관 기간(기본 7일, 1\~30일)이 지나면 만료됩니다. ## 주의할 점 * 정리하면 데이터가 삭제되므로 삭제 미리보기를 먼저 확인하세요. * 보존 기간은 원본 이벤트와 분석 테이블에 함께 적용됩니다. 기간을 줄여 정리하면 원본 이벤트를 다시 처리(replay)할 수 있는 기간도 짧아집니다. * 비용을 줄이려면 사용량이 큰 프로젝트부터 보존 기간을 점검하세요. * Free 조직은 이벤트를 보내도 수집하지 않습니다. 자기 데이터를 수집하려면 Starter나 Pro를 구독하세요. 할당량이 0인 계약 조직은 계약에 따라 할당량을 받아야 수집할 수 있습니다. ## 다음 단계 * 조직·프로젝트와 역할은 [조직과 프로젝트](/docs/workspace/organizations-and-projects) * 프로젝트 설정 상세는 [프로젝트 설정](/docs/project-workspace/settings) * 수집 요청과 보존의 관계는 [데이터 수집과 거버넌스](/docs/platform/data-collection-and-governance) --- # 조직과 프로젝트 Source: https://data.reopt.ai/docs/workspace/organizations-and-projects 통합 프로젝트 화면에서 조직을 필터링하고 프로젝트를 만들고 이동하는 흐름을 안내합니다. 조직은 비용과 멤버십을 관리하는 단위이고, 프로젝트는 이벤트 데이터와 분석 화면이 속하는 단위입니다. 조직과 프로젝트는 처음 시작할 때 만들고, 그 뒤에는 통합 프로젝트 화면에서 원하는 프로젝트를 바로 찾습니다. ## 진입 경로 * `/dashboard`: 통합 프로젝트 화면 * 기존 조직 홈 주소 `/dashboard/[orgId]`는 통합 화면의 해당 조직 필터(`/dashboard?organization=`)로 자동 이동합니다. ## 이 화면에서 확인할 것 * 내가 어떤 조직에서 작업 중인지 확인합니다. * 조직 안에 어떤 프로젝트가 있는지 확인합니다. * 새 프로젝트를 만들고 발급된 키를 확인합니다. ## 통합 프로젝트 화면 통합 프로젝트 화면은 사용자가 속한 모든 조직의 프로젝트를 한곳에 보여주는 첫 화면입니다. * 최근 프로젝트: 이 브라우저에서 최근에 연 프로젝트를 최대 3개까지 보여줍니다. 검색어가 없고 조직 필터가 "모든 조직"일 때만 나타납니다. * 프로젝트 이름, 조직 이름, 도메인 검색 * 조직 필터와 정렬(최근 사용 순, 이름 순) * **새 프로젝트**: 소속 조직, 프로젝트 이름, 도메인(선택)을 입력합니다. 소속 조직에는 내가 `owner`나 `admin`인 조직만 나옵니다. 단축키는 `N`입니다. * 조직 필터를 선택하면 목록 위에 그 조직의 **사용량 및 요금** 버튼이 나타납니다. * 워크스페이스 메뉴(⋯)에서 새 조직 생성과 로그아웃 자기 조직이 아직 없다면 화면의 **+ 새 조직**이나 워크스페이스 메뉴(⋯)에서 조직을 먼저 만드세요. **새 프로젝트**를 눌러도 조직 만들기부터 시작합니다. 조직을 만들면 첫 프로젝트 생성 창이 바로 이어서 열립니다. 공개 데모 워크스페이스는 목록 맨 위에 함께 보입니다. 목록에서는 조직 이름 옆에 `데모` 표시가 붙습니다. 데모 워크스페이스는 읽기 전용이라 그 안의 설정이나 데이터를 바꿀 수 없습니다. 데모 안의 화면마다 위쪽에 같은 안내가 나옵니다. 새로 만들거나 저장하는 버튼은 꺼져 있고, 버튼에 마우스를 올리면 이유가 나옵니다. 꺼져 있지 않은 버튼을 눌러 무언가를 바꾸려 하면 읽기 전용이라는 안내가 나옵니다. ## 역할별 권한 조직 멤버의 역할은 `owner`, `admin`, `member` 세 가지입니다. | 작업 | 필요한 역할 | | ------------------------------ | ------------------ | | 조직 생성 | 로그인한 사용자 누구나 | | 프로젝트 조회, 분석 화면 사용 | 조직 멤버 | | 프로젝트 생성과 설정 변경 | `owner` 또는 `admin` | | 보존 정책 변경과 데이터 정리 실행 | `owner` 또는 `admin` | | Starter 구독, 자동 갱신 해지, 결제 연결 해제 | `owner` | 조직을 만든 사람이 그 조직의 `owner`가 됩니다. ## 프로젝트를 만들면 함께 생성되는 것 * 프로젝트. 시간대는 `UTC`로 시작합니다. * 브라우저용 client `<프로젝트 이름> (browser)`: 수집 전용이고, 공개해도 되는 `writeKey`를 씁니다. * 서버용 client `<프로젝트 이름> (server)`: 수집과 조회를 모두 할 수 있고, `serverSecret`으로 인증합니다. * 서버용 1회성 `serverSecret` 브라우저 키와 서버 키는 권한이 달라서 따로 발급합니다. 브라우저 번들에 실리는 `writeKey`로는 이벤트를 보낼 수만 있고, 프로젝트의 분석 데이터는 읽을 수 없습니다. 콘솔에서 프로젝트를 만들면 생성 직후 창에 Write Key, 서버 Client ID, 서버 Client Secret이 표시되고 값마다 복사 버튼이 있습니다. 창을 닫으면 서버 Client Secret은 다시 볼 수 없으니 닫기 전에 안전한 곳에 옮겨 두세요. Free 조직이면 같은 창에 매월 수집할 수 있는 양이 함께 나옵니다. 수집이 막힌 조직이면 그 이유가 나옵니다. 프로젝트 홈의 **SDK Integration** 영역에서는 브라우저용 `writeKey`가 들어간 초기화 snippet을 언제든 확인할 수 있습니다. 서버 SDK에는 서버 Client Secret과 함께 서버용 client의 ID(`clientId`)가 필요합니다. 둘 다 프로젝트를 만든 직후의 창에서 복사합니다. 창을 닫은 뒤에는 콘솔에서 서버 Client ID와 Secret을 다시 볼 수 없습니다. CLI로 프로젝트를 만들 수도 있습니다. `reopt-data project create`는 브라우저 `writeKey`, 서버 client ID(`clients.server.id`), `clientSecret`을 함께 돌려줍니다. 잃어버린 secret은 `reopt-data project rotate-secret`으로 새로 발급합니다. 두 명령 모두 조직 키(`REOPT_DATA_ORG_KEY`)가 필요합니다. 자세한 내용은 [CLI 사용하기](/docs/workspace/cli)를 확인하세요. ## 프로젝트 시간대 조회 기간과 화면의 시각은 프로젝트 시간대를 따릅니다. 프로젝트 설정 화면에는 시간대 항목이 없습니다. 바꾸려면 조직 키로 CLI를 사용하세요. ```bash reopt-data project update prj_123 --timezone Asia/Seoul ``` 저장소에서 설정을 관리한다면 `reopt-data/project.json`의 `timezone`으로 선언해도 됩니다. 시간대를 바꾸면 대시보드 숫자가 달라지므로 [프로젝트를 코드로 관리하기](/docs/workspace/project-as-code)에서는 `metric-changing` 등급으로 다룹니다. ## 주의할 점 * `serverSecret`은 프로젝트를 만든 직후 한 번만 표시되는 서버 전용 값입니다. * 브라우저 번들, HTML, client component, 공개 저장소에는 `serverSecret`을 넣지 마세요. * 노출됐다면 `reopt-data project rotate-secret --yes`로 새 secret을 발급하세요. 이전 secret은 즉시 동작을 멈춥니다. ## 다음 단계 * 첫 이벤트를 보내려면 [빠른 연동](/docs/workspace/quickstart) * 이벤트 수집을 붙이려면 [추적 설정](/docs/workspace/tracking-setup) * 사용량과 구독은 [비용과 보존 정책](/docs/workspace/cost-and-retention) * 분석 화면을 고르려면 [프로젝트 워크스페이스](/docs/project-workspace) --- # 프로젝트를 코드로 관리하기 Source: https://data.reopt.ai/docs/workspace/project-as-code 프로젝트 설정, 에러 트래킹 룰, 알림을 저장소의 파일로 선언하고 reopt-data pull / diff / push / verify로 서버와 맞춥니다. Project as Code(PaC)는 프로젝트 설정·에러 트래킹 룰·알림을 저장소의 파일로 선언하고, 배포와 같은 커밋에서 서버에 맞추는 방법입니다. 콘솔에서 바꾼 설정은 어느 커밋에서 나온 것인지 알 수 없고, 스테이징과 프로덕션이 모르는 사이에 달라집니다. [이벤트 카탈로그를 코드로 관리하기](/docs/workspace/cli#이벤트-카탈로그를-코드로-관리하기)와 같은 규칙을 나머지 설정에도 적용합니다. ## 먼저 확인할 것 * `@reopt-ai/data-cli`가 설치되어 있어야 합니다. 설치와 자격증명은 [CLI 사용하기](/docs/workspace/cli)를 따르세요. * 조직 키(`REOPT_DATA_ORG_KEY`)가 필요합니다. 개인 로그인 세션이나 클라이언트 자격증명으로는 동작하지 않습니다. * 저장소를 프로젝트에 연결해 두면 `--project-id`를 매번 주지 않아도 됩니다. ```bash reopt-data link --project-id prj_123 # reopt-data.config.mjs를 만들거나 갱신합니다 ``` * 서버가 프로젝트 응답에 `updatedAt`을 주지 않으면 PaC 이전 배포입니다. 이때 `pull`·`verify`·`push`는 변경 없음으로 넘어가지 않고 종료 코드 `3`으로 멈춥니다. ## 다루는 파일 기본 위치는 저장소 루트의 `reopt-data/` 디렉터리입니다. `reopt-data.config.mjs`의 `definition.dir`로 바꿀 수 있습니다. | 파일 | 담는 것 | 비고 | | ----------------------------- | ------------------------------------------------------ | ---------------------------------- | | `reopt-data/project.json` | 타임존, 보존 기간, 네이밍 패턴, 파워 유저·라이프사이클 임계값, 저장소 링크, 심볼 호스트 앱 | 프로젝트당 하나. key가 없습니다 | | `reopt-data/error-rules.json` | 에러 트래킹 룰. 배열 순서가 평가 순서입니다 | 각 룰에 `key` | | `reopt-data/alerts.json` | 알림 조건, 채널, 목적지 | 각 알림에 `key`. 목적지는 `${env:NAME}` 가능 | | `reopt-data.lock.json` | 마지막 동기화 시점에 서버가 가지고 있던 `updatedAt`과 해석된 값의 해시 | 디렉터리 옆에 하나. 커밋하세요 | | `reopt-data/.schemas/*.json` | 위 세 파일의 JSON Schema. `pull`이 매번 다시 씁니다 | 에디터 검증용. 커밋하세요 | ```jsonc // reopt-data/project.json { "$schema": "./.schemas/project-settings.v1.json", "version": 1, "projectId": "prj_123", "settings": { "timezone": "Asia/Seoul", "retentionDays": 90, "lifecycleActiveDays": 7, "lifecycleAtRiskDays": 21, "repositoryUrl": "https://github.com/acme/shop", "repositoryBranch": "main", }, } ``` ```jsonc // reopt-data/error-rules.json: 위에 있는 룰이 먼저 평가됩니다 { "$schema": "./.schemas/error-rules.v1.json", "version": 1, "projectId": "prj_123", "rules": [ { "key": "ignore-vendor", "name": "벤더 번들 무시", "action": { "kind": "suppression" }, "filters": [{ "field": "$exception_source", "operator": "contains", "value": "node_modules" }], }, { "key": "checkout-critical", "name": "결제 경로는 critical", "action": { "kind": "severity", "severity": "critical" }, "filters": [{ "field": "path", "operator": "startsWith", "value": "/checkout" }], }, ], } ``` ```jsonc // reopt-data/alerts.json { "$schema": "./.schemas/alerts.v1.json", "version": 1, "projectId": "prj_123", "alerts": [ { "key": "purchase-drop", "name": "구매 급감", "eventName": "purchase", "condition": { "kind": "event_count", "operator": "lte", "threshold": 10, "timeWindow": "1h" }, "channel": "slack", "destination": "${env:SLACK_ALERTS_URL}", }, { "key": "new-critical-issues", "name": "새 critical 이슈", "condition": { "kind": "error_issue", "trigger": "created", "severityMin": "critical" }, "channel": "email", "destination": "oncall@example.com", }, ], } ``` `pull`은 파일과 함께 `reopt-data/.schemas/`에 JSON Schema 세 개를 씁니다. `$schema`가 그 사본을 가리키므로 VS Code 같은 에디터가 네트워크 없이도 오타 키, 잘못된 값, 빠진 필수값을 표시하고 자동완성합니다. 이 디렉터리도 커밋하세요. 같은 스키마를 `https://data.reopt.ai/schemas/<이름>.v1.json`에서도 제공하므로, 직접 만든 파일에는 그 주소를 써도 됩니다. CLI는 어느 쪽이든 읽습니다. 룰의 `field`·`operator`·`action`과 알림의 `condition`이 받는 값은 그 스키마에 정의되어 있습니다. 자주 쓰는 값은 다음과 같습니다. | 항목 | 받는 값 | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | 룰 `action.kind` | `suppression`, `grouping`(`fingerprint`), `assignment`(`assigneeUserId`), `severity`(`low`·`medium`·`high`·`critical`) | | 룰 `filters[].field` | `$exception_type`, `$exception_message`, `$exception_source`, `$exception_handled`, `$exception_level`, `$release_id`, `path`, `properties.` | | 룰 `filters[].operator` | `eq`, `neq`, `contains`, `not_contains`, `startsWith`, `in`, `not_in` | | 알림 `condition.kind` | `event_count`, `error_issue`(`created`·`reopened`·`spiking`), `ai_metric`(`cost_usd`·`error_rate`·`latency_p95_ms`·`unknown_cost_ratio`) | | 알림 `timeWindow` | `5m`, `15m`, `1h`, `6h`, `24h` | | 알림 `channel` | `email`, `slack`, `webhook` | 룰은 `"enabled": false`로, 알림은 `"status": "paused"`로 지우지 않고 꺼 둘 수 있습니다. 생략하면 켜진 상태입니다. 한 파일에 룰과 알림은 각각 200개까지, 룰 하나의 필터는 20개까지 둘 수 있습니다. 타입 검사는 에디터와 CLI 두 곳에서 합니다. 에디터는 스키마로 형태를 검사합니다. CLI는 `diff`·`verify`·`push`에서 같은 스키마에 더해 교차 규칙(`event_count`에는 `eventName`, `in` 연산자는 배열, lifecycle 대소, 채널별 목적지 형식, 중복 `key`)까지 검사하고, 어긋난 경로와 함께 종료 코드 `3`으로 거절합니다. ## 기본 규칙 1. 파일이 기준입니다. 파일에서 생략한 필드는 기존 값을 유지하지 않고 기본값이 됩니다. `"settings": {}`는 타임존 UTC, 보존 기간 무제한이라는 선언입니다. 그래서 `verify`가 파일과 서버가 같은지 판정할 수 있습니다. 2. lock이 있어야 파일이 바뀌었는지 콘솔이 바뀌었는지 구분합니다. lock이 없으면 둘이 다르다는 것만 알 수 있고, 기존 항목을 바꾸는 push는 `--force` 없이 거절됩니다. 3. 쓰기는 전부 적용되거나 전부 거절됩니다. push는 마지막 동기화의 `updatedAt`을 함께 보내고, 그 사이 콘솔에서 한 항목이라도 바뀌었으면 서버가 요청 전체를 `409`로 거절합니다. 일부만 적용된 상태는 생기지 않습니다. 4. `key`가 있는 룰과 알림만 저장소가 관리합니다. 파일에 따라 만들고 고치며, 파일에서 빠지면 삭제합니다. `key`가 없는 항목은 콘솔에서 만든 것이라 어떤 push도 건드리지 않고 개수만 보고합니다. ## 사용 흐름 ### 1. 서버 상태를 파일로 받기 ```bash reopt-data pull reopt-data pull --project-id prj_123 # 파일이 아직 없을 때 ``` 파일 세 개와 lock을 씁니다. 기본값은 생략하고 키 순서를 고정하므로, 바꾼 것이 없으면 두 번째 `pull`도 바이트 단위까지 같은 파일을 씁니다. `key`가 없는 룰·알림은 파일에 들어가지 않고 "console-owned"로 개수만 보입니다. 파일로 가져오려면 아래 `import`를 쓰세요. 마지막 동기화 뒤 파일 내용이나 룰 순서를 고쳤다면 `pull`은 덮어쓰지 않고 멈춥니다. 고친 내용을 push하거나, 버리려면 `--force`를 주세요. 룰 순서 기준이 없는 구형 lock을 쓰고 있고 룰이 여러 개라면, 먼저 로컬 순서를 확인한 뒤 `pull --force`로 기준을 갱신해야 합니다. ### 2. 파일을 고치고 차이를 보기 ```bash reopt-data diff --format text ``` ``` kind key operation grade project timezone update metric-changing error-rule ignore-vendor create metric-changing alert purchase-drop create safe timezone: "UTC" → "Asia/Seoul" console-owned rules: 1 (import them with `reopt-data import error-rule --key `) console-owned alerts: 0 last push: 2026-09-08T14:57:40.001Z by org-key:a6a0b13019f4 ``` `!`로 시작하는 줄은 마지막 동기화 뒤 콘솔에서 바뀐 항목입니다. 파일과 콘솔이 같은 항목을 함께 고쳤으면 `CONFLICT`로 표시됩니다. `diff`는 항상 종료 코드 `0`으로 끝납니다. 판정은 `verify`가 합니다. ### 3. 계획을 확인하고 반영하기 ```bash reopt-data push # 계획만 출력, 아무것도 쓰지 않습니다 reopt-data push --apply # safe 변경만 있을 때 반영 reopt-data push --apply --yes # metric-changing · expensive 변경 승인 reopt-data push --apply --yes --allow-destructive # 보존 기간 변경 승인 ``` `--yes`가 필요한 변경이 하나라도 있으면 첫 쓰기 요청을 보내기 전에 종료 코드 `7`로 멈춥니다. 프로젝트 설정만 반영되고 룰은 확인을 기다리는 상태는 생기지 않습니다. 설정·룰·알림은 한 요청, 한 트랜잭션으로 적용됩니다. lock은 그 트랜잭션이 반환한 스냅샷으로 갱신합니다. ### 4. CI에서 어긋남을 잡기 ```bash reopt-data verify # 어느 쪽이든 움직였으면 exit 8 ``` `verify`는 `diff`와 같은 보고를 출력하고, 파일과 서버가 다르면 `8`로 끝납니다. 서버에 연결하지 못하거나 서버가 PaC 이전 배포일 때도 드리프트 없음으로 넘어가지 않고 실패합니다. 기본 구성은 PR마다 `verify`를, 배포 직전에 `push --apply --yes`를 실행하는 것입니다. ```bash # PR 검사 reopt-data verify # 배포 파이프라인, 앱을 배포하기 직전 reopt-data push --apply --yes ``` lock 파일은 커밋해야 합니다. CI가 lock 없이 push하면 기존 항목을 바꾸는 쓰기에 비교 기준이 없어 거절됩니다. ## 변경 등급 | 등급 | 뜻 | 해당하는 변경 | 필요한 것 | | ----------------- | ------------------ | ---------------------------------------------------------------- | --------------------------- | | `safe` | 표시나 메타만 바뀜 | 저장소 링크, 심볼 호스트 앱, 네이밍 패턴, assignment·severity 룰, 알림 전부, 보존 기간 연장 | 없음 | | `metric-changing` | 대시보드 숫자나 이슈 목록이 바뀜 | 타임존, 라이프사이클·파워 유저 임계값, suppression·grouping 룰의 생성·수정·삭제·순서 변경 | `--yes` | | `expensive` | 서버가 재계산을 큐에 넣음 | (이벤트 카탈로그의 `rollupProperties`. 이 파일들에는 아직 없습니다) | `--yes` | | `destructive` | 데이터가 지워질 수 있음 | `retentionDays` 감소·신규 지정·해제 | `--yes --allow-destructive` | `destructive` 변경은 `--yes --allow-destructive`가 없으면 쓰기 요청을 보내기 전에 막힙니다. 서버도 `allowDestructive` 없이 보존 기간을 줄일 수 있는 REST 요청을 HTTP 409 `destructive_blocked`로 거절합니다. 기존 값을 해제하는 변경도 플랜 한도를 다시 적용하므로 승인 대상입니다. 보존 기간을 줄이면 기준일 이전 데이터가 정리 대상이 되므로, [비용과 보존 정책](/docs/workspace/cost-and-retention)의 삭제 미리보기를 먼저 확인하세요. ## 콘솔과 함께 쓰기 콘솔에서도 계속 편집할 수 있습니다. 다음 세 가지 상황만 알아 두세요. 콘솔에서 만든 룰을 파일로 가져오려면 `key`를 붙이세요. 그 순간부터 저장소 소유가 됩니다. id는 콘솔이 보여주는 값을 쓰세요. ```bash reopt-data import error-rule 054af108-… --key ignore-vendor reopt-data import alert 0bb6e8b3-… --key purchase-drop ``` `import`는 서버에 `key`를 쓰고, 파일에는 서버의 평가 순서에 맞는 자리에 항목을 넣고, lock에도 기록합니다. 그래서 바로 `verify`를 실행해도 `0`으로 끝납니다. 알림을 가져오면 목적지가 평문으로 파일에 들어갑니다. 웹훅 URL이면 `${env:NAME}`으로 바꾼 뒤 커밋하세요. 항목을 파일 관리에서 빼려면 `unlink`를 쓰세요. `key`만 떼고 항목은 서버에 남깁니다. 삭제가 아니므로 `--yes`가 필요 없습니다. ```bash reopt-data unlink error-rule ignore-vendor ``` 항목을 지우려면 파일에서 빼고 push하세요. 콘솔과 파일이 동시에 바뀌면 `verify`는 `8`로, `push`는 종료 코드 `3`으로 멈춥니다. 쓰기 도중에 경합이 나면 서버의 HTTP `409`가 같은 종료 코드로 바뀝니다. 다음 둘 중 하나를 고르세요. * 콘솔 쪽을 받아들이려면 `pull`을 실행합니다. 파일에 로컬 편집이 있으면 `pull --force`로 버려야 합니다. * 파일 쪽으로 덮으려면 `push --apply --yes --force`를 실행합니다. 비교를 건너뛰고 서버 값을 덮어씁니다. 다른 저장소나 동료가 그 사이 `import`한 `key` 항목이 있으면, push는 그 항목을 알지 못하므로 지우지 않고 종료 코드 `3`으로 멈춥니다. `pull`로 파일에 들여오거나, 지우려면 `--prune-imported`를 쓰세요. `--force`는 삭제를 승인하지 않습니다. 삭제도 등급 검사를 거치므로 suppression·grouping 룰에는 `--yes`가 필요합니다. diff 조회 이후 새로 import된 항목은 `--prune-imported`로도 지우지 않고 충돌로 거절합니다. ## 웹훅 URL 같은 값 넣기 웹훅 URL처럼 비밀로 다뤄야 하는 알림 `destination`은 커밋되는 파일에 그대로 넣지 말고 환경 변수 이름을 적으세요. ```jsonc "destination": "${env:SLACK_ALERTS_URL}" ``` * 이름은 CI secret store가 쓰는 대문자 형식(`[A-Z_][A-Z0-9_]*`)입니다. 잘못된 구문과 중첩 참조도 종료 코드 `3`으로 거절합니다. * `diff`·`verify`·`push`는 파일을 읽은 직후 치환합니다. 빠진 이름이 있으면 전부 나열하고 종료 코드 `3`으로 멈추며, 아무것도 보내지 않습니다. * 서버는 치환된 값만 봅니다. 출력에서는 서버 값을 ``로 가리고 템플릿만 보여줍니다. * `pull`은 파일의 기존 템플릿을 치환한 값이 서버 값과 정확히 같을 때만 템플릿을 보존합니다. 다르면 평문을 쓰고 `secretsWritten`으로 알려줍니다. 그때는 값을 secret store로 옮기고 파일은 `${env:NAME}`으로 되돌리세요. 이메일 목적지는 평문으로 두어도 됩니다. 채널별 검증(이메일 형식, `https` URL)은 서버가 합니다. ## 한 저장소에서 스테이징과 프로덕션 관리하기 파일은 하나로 두고 프로젝트만 환경마다 바꿉니다. ```js // reopt-data.config.mjs /** @type {import("@reopt-ai/data-cli").Config} */ export default { environments: { staging: { projectId: "prj_staging" }, production: { projectId: "prj_prod" }, }, }; ``` ```bash reopt-data --env staging pull reopt-data --env staging verify REOPT_DATA_ENV=production reopt-data push --apply --yes ``` * 환경을 지정한 상태에서는 `pull`이 파일에 `projectId`를 쓰지 않습니다. 파일을 환경끼리 공유하기 때문입니다. * lock은 환경별로 `reopt-data.staging.lock.json`처럼 따로 둡니다. 모두 커밋하세요. * 파일에 `projectId`가 고정되어 있는데 환경의 프로젝트와 다르면 종료 코드 `3`으로 멈춥니다. 파일에서 `projectId`를 빼거나 `--env`를 바로잡으세요. * 설정에 없는 환경 이름을 주면 종료 코드 `4`로 멈추고, 설정된 이름을 알려줍니다. 프로젝트가 정해지는 순서는 `--env`의 프로젝트 → 파일의 `projectId` → `--project-id` → `reopt-data.config`의 `projectId`입니다. ## 종료 코드 | 코드 | 언제 | 다음 행동 | | --- | ------------------------------------------------ | -------------------------------------- | | `0` | 성공. `verify`라면 파일과 서버가 같음 | 진행 | | `3` | 파일이 잘못됨, `${env:}` 미해결, 충돌, lock 없는 변경, 서버 `409` | 메시지의 항목을 고치거나 `pull` | | `4` | 프로젝트를 정할 수 없음, 모르는 `--env` | `link`, `--env`, `--project-id`를 확인 | | `7` | `--yes`가 필요한 등급 | 영향을 확인한 뒤 `--yes` | | `8` | `verify`가 어긋남을 발견 | `pull`(서버를 받아들임) 또는 `push --apply`(파일) | ## 주의할 점 * **보존 기간을 줄이면 데이터 삭제로 이어집니다.** `retentionDays`를 줄이는 push는 `destructive`이고 `--yes --allow-destructive`가 필요합니다. push 자체는 기간만 바꿉니다. 줄인 기간을 넘긴 AI 호출 기록은 다음 일일 정리에서 지워지고, 나머지 데이터는 프로젝트 설정에서 정리를 실행할 때 지워집니다. 지워진 데이터는 되돌릴 수 없습니다. * `--force`와 `--yes`는 서로 다른 플래그입니다. `--yes`는 등급을 승인하고, `--force`는 비교를 건너뛰고 콘솔 편집을 덮어씁니다. 가져온 `key` 항목을 삭제하려면 별도로 `--prune-imported`가 필요합니다. `--force`와 `--yes`를 함께 쓰는 실행은 CI에서 자동화하지 마세요. * lock을 커밋하지 않으면 CI의 push가 거절됩니다. 기존 항목을 바꾸는 쓰기에는 비교 기준이 필요합니다. * 평문 시크릿을 커밋하지 마세요. `pull`과 `import alert`는 평문을 쓸 때 반드시 알려줍니다. 그 자리에서 `${env:NAME}`으로 바꾸세요. * 콘솔 편집은 다음 push에 덮일 수 있습니다. `key`가 있는 항목을 콘솔에서 고치면 `verify`가 잡아내지만, `--force` push는 그 편집을 지웁니다. 어떤 항목을 저장소가 관리하는지 팀에 알려 두세요. ## 자주 막히는 지점 | 증상 | 확인할 것 | | --------------------------------------------------------------- | -------------------------------------------------------------------- | | `unresolved ${env:…} references … NAME` | 나열된 이름을 실행 환경에 설정하세요. 로컬에서는 `export NAME=…`, CI에서는 secret으로 넣습니다. | | `no sync point for N item(s)` | lock이 없거나 오래됐습니다. `pull`로 lock을 만들고 커밋하세요. 그래도 덮으려면 `--force`를 주세요. | | `The project changed since its settings were last read` | 콘솔에서 먼저 바뀌었습니다. `pull`로 받아들이거나 `--force`로 덮으세요. | | `keyed item(s) were imported on the server since the last pull` | 다른 곳에서 `import`했습니다. `pull`하면 파일에 들어옵니다. | | `this deployment sent no updatedAt` | 서버가 PaC 이전 배포입니다. 서버를 올려야 합니다. | | `names no project` | 파일에 `projectId`가 없고 환경도 없습니다. `--env`를 주거나 `reopt-data link`를 실행하세요. | | `import` 직후 `verify`가 reorder를 잡음 | 정상 동작이 아닙니다. `import`는 서버 순서대로 끼워 넣습니다. 파일을 손으로 정렬했는지 확인하세요. | ## 적용 중 연결이 끊겼을 때 같은 `push --apply` 명령을 다시 실행하세요. CLI는 쓰기 전에 private pending 파일에 작업 ID와 요청 해시를 기록해 두고, 다시 실행하면 서버에서 원래 적용 결과를 조회합니다. 결과가 있으면 새로 쓰지 않고 그 결과로 lock을 복구하며, 이후 콘솔 변경은 드리프트로 남습니다. 서버에 결과가 없으면 원래 파일·lock·환경 변수·대상·옵션이 같을 때만 같은 작업을 다시 보냅니다. 결과를 모르는 동안에는 `.pending.json`을 삭제하지 마세요. pending 파일과 `*.json.*.tmp` 파일은 Git에서 제외하고 lock은 커밋하세요. 서버의 적용 결과는 프로젝트를 삭제할 때 함께 삭제됩니다. 조직 키로만 조회할 수 있고 알림 수신처가 포함되므로 로그에 남기지 마세요. 복구 방법은 오류 응답의 `classification`, `retryable`, `resolution.action/message`에서 확인하세요. 전체 공개 컨트롤 API는 [OpenAPI](/openapi.json)에 있습니다. 설정·알림 화면에는 마지막 코드 적용 시각과 주체가, `key`가 있는 룰·알림에는 **코드 관리** 배지가 표시됩니다. 콘솔에서도 계속 편집할 수 있으며, 적용 전에 저장소에도 변경을 반영하라는 안내가 표시됩니다. ## 다음 단계 * 이벤트와 속성의 의미도 코드로 두려면 [CLI 사용하기의 이벤트 카탈로그 절](/docs/workspace/cli#이벤트-카탈로그를-코드로-관리하기) * 룰이 이슈를 어떻게 바꾸는지는 [에러 트래킹](/docs/project-workspace/error-tracking), 알림의 평가와 통지는 [실시간, 인사이트, 알림](/docs/project-workspace/realtime-insights-alerts) * 보존 기간과 삭제 미리보기는 [비용과 보존 정책](/docs/workspace/cost-and-retention) * 프로젝트 설정 화면은 [프로젝트 설정](/docs/project-workspace/settings)