콘텐츠로 이동

공통 스키마와 데이터 타입

이 페이지는 기준 커밋의 request/VO와 공통 response 클래스로 확인되는 와이어 필드만 정리합니다. 필드가 없는 곳에 편의를 위한 값을 추가하지 마십시오. 도메인 의미와 공유 DB 계약은 공유 데이터 계약, 인증 값은 로그인과 토큰 인증, credential 경계는 API key와 client-secret을 참고하십시오.

JSON과 시간 표기

  • JSON 필드명은 lower camelCase입니다.
  • UUID는 하이픈을 포함한 표준 문자열입니다.
  • LocalDateTime 응답은 offset 없는 ISO-8601 문자열입니다.
  • client-secret의 expiresAt만 UTC ISO-8601 instant 문자열(...Z)로 계약되어 있습니다.
  • 응답 직렬화에서 null 필드는 생략될 수 있습니다. 호출자는 명시적 null과 필드 부재를 동일한 nullable 상태로 처리해야 합니다.

공통 성공 envelope

데이터 단건

{
  "status": 200,
  "message": "success",
  "data": {}
}
필드 타입 의미
status integer 공통 성공 객체의 값은 200
message string "success"
data endpoint별 단건, 객체 배열 또는 map

실제 HTTP status가 201이어도 body의 status는 200일 수 있습니다.

데이터 없는 성공

{
  "status": 200,
  "message": "success"
}

페이지 없는 목록

{
  "status": 200,
  "message": "success",
  "count": 2,
  "list": [{}, {}]
}

count는 list의 현재 항목 수입니다. 빈 목록은 count: 0입니다.

페이지 요청과 응답

요청 query:

필드 타입 기본 정규화
page integer 1 1 미만이면 1
size integer 20 1 미만이면 1, 최대 10,000

사용자별 대화 통계는 추가로 size를 최대 1,000으로 제한합니다. 정렬은 endpoint가 고정하며 공개 sort query 계약이 아닙니다.

{
  "status": 200,
  "message": "success",
  "page": {
    "page": 1,
    "size": 20,
    "totalItems": 42,
    "totalPages": 3,
    "hasNext": true,
    "hasPrevious": false,
    "first": true,
    "last": false
  },
  "count": 20,
  "list": []
}
필드 타입 의미
page.page integer 1부터 시작하는 현재 페이지
page.size integer 적용된 페이지 크기
page.totalItems integer 전체 검색 결과 수
page.totalPages integer 전체 페이지 수
page.hasNext boolean 다음 페이지 존재
page.hasPrevious boolean 이전 페이지 존재
page.first boolean 첫 페이지
page.last boolean 마지막 페이지
count integer 현재 list의 항목 수
list array endpoint별 항목

오류 envelope

{
  "status": 409,
  "message": "요청을 처리할 수 없습니다.",
  "data": {
    "code": "VOICE_PRESET_INACTIVE",
    "voicePreset": "female_warm"
  }
}
필드 타입 필수 의미
status integer 예 HTTP 상태
message string 조건부 지역화된 오류 메시지
data any 아니요 기계 판독 코드나 관련 값

모든 오류가 data.code를 보장하지는 않습니다. 명시된 코드형 오류만 코드로 분기해야 합니다. DB 제약명, SQL, driver 원문과 민감한 값은 응답 계약이 아닙니다.

식별자

이름 타입 출처·범위
member ID UUID access JWT sub; 회원 응답에는 별도 ID 필드가 없음
workspaceId UUID X-Workspace-Id, API key 귀속 값, client-secret claim
scenario.id UUID 워크스페이스 일반 시나리오 PK
scenario.slug string 워크스페이스 안에서 활성 유일
template id UUID 템플릿 등록 응답에만 노출
template slug string 전역 활성 템플릿 조회 키
historyId UUID 시나리오 이력 PK
dialogId UUID 공유 DB 대화 PK
sessionId string Socket Backend 세션 식별자; UUID 타입으로 가정하지 않음
messageId UUID 대화 메시지 PK
groupId UUID 사용자 그룹 PK
그룹 memberId UUID 그룹 구성원 레코드 PK; 회원 PK나 externalUserId가 아님
externalUserId string 외부 시스템 사용자 식별자
API key id UUID API key 레코드 및 감사 주체 식별자
jti UUID 문자열 JWT별 랜덤 식별자

다른 워크스페이스의 UUID를 지정하면 많은 조회가 403이 아니라 404를 반환합니다. 이는 존재 여부를 숨기는 계약입니다.

Credential 스키마

로그인 토큰 응답

{
  "tokenType": "Bearer",
  "accessToken": "<access-jwt>",
  "refreshToken": "<refresh-jwt>",
  "expiresIn": 1800
}
필드 타입 의미
tokenType string "Bearer"
accessToken string App Backend REST용 HMAC access JWT
refreshToken string 토큰 회전 전용 HMAC refresh JWT
expiresIn integer access token의 초 단위 TTL

API key 발급·목록

발급 data:

필드 타입 의미
id UUID API key PK
name string 요청한 표시명
key string 원본 키, 발급 응답에서만 1회 노출
keyPrefix string 이후 식별용 표시 접두
keyType string LIVE 또는 TEST
createdAt LocalDateTime 발급 시각

목록 data[]:

필드 타입 nullable 의미
id UUID 아니요 API key PK
keyName string 아니요 표시명
keyPrefix string 아니요 표시 접두
keyType string 아니요 LIVE/TEST
keyStatus string 아니요 ACTIVE/REVOKED/ROTATED
lastUsedAt LocalDateTime 예 마지막 사용 시각
createdAt LocalDateTime 아니요 발급 시각

목록에는 key와 secretHash가 없습니다.

Ed25519 client-secret 발급 응답

{
  "clientSecret": {
    "value": "<ed25519-client-secret-jwt>",
    "expiresInSeconds": 1800,
    "expiresAt": "2026-09-23T01:30:00Z"
  }
}
필드 타입 의미
clientSecret.value string Ed25519/EdDSA 서명 JWT
clientSecret.expiresInSeconds integer clamp 후 실제 TTL
clientSecret.expiresAt string UTC ISO-8601 만료 instant

JOSE header:

{
  "alg": "EdDSA",
  "typ": "JWT"
}

Payload claim:

claim 타입 값·의미
iss string sohri-app-backend
aud string sohri-realtime; 배열이 아닌 단일 문자열
sub string 발급 회원 UUID
workspaceId string 대상 워크스페이스 UUID
jti string 랜덤 UUID
iat integer epoch seconds
exp integer epoch seconds, iat + 실제 TTL

apiKeyId claim은 없습니다. client-secret은 access JWT와 다른 키 체계이며 API key도 아닙니다. App Backend는 값을 저장하거나 사용 횟수를 추적하지 않으므로 만료 전 재사용 가능합니다.

시나리오 스키마

일반 시나리오 요청

{
  "slug": "health-check",
  "title": "건강 체크",
  "language": "ko",
  "content": "{\"metadata\":{},\"graph\":{}}",
  "isTemplate": false
}
필드 타입 nullable 의미
slug string 아니요 워크스페이스 조회 키
title string 예 표시 제목
language string 예 언어 코드
content string 아니요 시나리오 전체 JSON을 담은 문자열
isTemplate boolean 예 전역 템플릿 기반 여부의 Boolean provenance

content 자체가 객체가 아니라 JSON 문자열인 점에 유의하십시오. 일반 POST는 이 문자열을 Graph Builder에서 검증하지 않습니다.

일반 시나리오 상세

필드 타입 nullable
id UUID 아니요
slug string 아니요
title string 예
language string 예
status string 아니요
isTemplate boolean 아니요
scenarioVersion integer 아니요
content string 아니요
createdAt LocalDateTime 아니요
updatedAt LocalDateTime 아니요

목록 항목은 여기서 isTemplate, content, updatedAt을 제외합니다.

시나리오 이력

목록 항목:

필드 타입
id UUID
scenarioId UUID
historyType string (I/U/D)
scenarioStatus string
title string, nullable
language string, nullable
scenarioVersion integer
createdAt LocalDateTime
updatedAt LocalDateTime

상세는 위 필드에 content: string을 추가합니다. 현재 이력 응답에는 isTemplate 필드가 없습니다.

전역 템플릿 요청과 content

{
  "slug": "senior-care",
  "name": "시니어 케어",
  "description": null,
  "scenarioContent": {
    "metadata": {
      "slug": "senior-care",
      "name": "시니어 케어",
      "description": null
    },
    "prompts": {},
    "graph": {}
  }
}
필드 타입 필수
slug string 예
name string 예
description string 또는 null 아니요
scenarioContent object 예

scenarioContent에서 App Backend가 정의하는 최상위 구조:

필드 타입 필수 제약
metadata object 예 slug, name, 선택 description을 포함하며 상위 값과 일치
prompts object 예 하위 shape는 App Backend가 정의하지 않음
graph object 예 하위 shape는 App Backend가 정의하지 않음
slots object 또는 null 아니요 값이 있으면 객체

이 네 필드 외의 하위 graph/node/prompt 필드는 App Backend VO나 검증기가 계약하지 않으므로 이 문서에서 임의로 열거하지 않습니다. 전체 객체는 UTF-8 직렬화 기준 1 MiB 이하입니다.

등록 응답은 id, slug, name, description, scenarioContent, createdAt, updatedAt입니다. 목록 항목은 slug, name, description, updatedAt만 가집니다.

대화 목록 항목

필드 타입 nullable 의미
dialogId UUID 아니요 대화 PK
scenarioId UUID 예 연결 시나리오
sessionId string 아니요 Socket 세션 ID
turnCount integer 아니요 대화 턴 수
currentRiskLevel string 예 현재 위기 단계
sourceType string 아니요 대화 출처
sttProvider string 아니요 저장된 STT provider
startedAt LocalDateTime 예 시작 시각
endedAt LocalDateTime 예 종료 시각
createdAt LocalDateTime 아니요 생성 시각

대화 상세

최상위 data:

필드 타입 nullable
dialogId UUID 아니요
sessionId string 아니요
summary DialogSummary 예
biomarkerSummary BiomarkerSummary 예
messages DialogMessage[] 아니요

DialogSummary:

필드 타입
dialogSummary string
dialogKeyword string
dialogEmotion string

BiomarkerSummary:

필드 타입
summary string

대화 메시지와 분석

DialogMessage:

필드 타입 nullable 제공 조건
messageId UUID 아니요 항상
messageTurns integer 아니요 항상; 연속 번호 보장 없음
speakerType string 아니요 USER 또는 BOT
sttText string 아니요 항상
confidenceScore number 예 저장 값이 있을 때
audio Audio 예 아카이브 음성 행이 있을 때
createdAt LocalDateTime 아니요 항상
keywords Keyword[] 아니요 없으면 빈 배열
emotion Emotion 예 분석이 있을 때
dementia Dementia 예 detail=true이고 분석이 있을 때
depression Depression 예 detail=true이고 분석이 있을 때
biomarker Biomarker 예 detail=true이고 분석이 있을 때

Audio:

필드 타입 nullable
url string 아니요
format string 예
duration number 예
rate integer 예
size integer 예

url은 인증이 필요한 App Backend 프록시 절대 URL이며 서명 URL이 아닙니다.

Keyword:

필드 타입
keyword string
riskCategory string
detectedAt LocalDateTime

Emotion:

필드 타입 nullable 조건
bestEmotion string 아니요 감정 분석이 있을 때
detail EmotionDetail 예 detail=true

EmotionDetail은 sadness, neutral, happiness, angry integer 점수를 가집니다.

Dementia는 control, dementia integer 점수를 가집니다. Depression은 negative, positive integer 점수를 가집니다.

Biomarker:

필드 타입
metrics BiomarkerMetric[]

BiomarkerMetric:

필드 타입
key string
name string
unit string
level string
value decimal number

분석 점수의 범위나 riskCategory, level, status의 전체 enum은 현재 응답 VO가 제한하지 않으므로 이 문서에서 추가 enum을 만들지 않습니다.

통계 shape

전체 통계 data:

{
  "oneMinuteOrLongerCount": 42
}

사용자별 통계 목록 항목:

필드 타입 nullable
userId string 아니요
userName string 예
oneMinuteOrLongerCount integer 아니요