feat(api-docs): 요청/응답 예시 블록 + 8종 결함 근본 수정 + audit error 승격

API 레퍼런스에 요청 예시(raw HTTP)·응답 예시(envelope 전문)를 표준 포맷에
편입하고, 전 대상(코어+전 모듈+전 플러그인)에 방출했다. 예시 결함 8종
(multipart·Host 마스킹·write 실측·path 치환·현실적 바디·query 반영·중복
제거·민감값 마스킹)을 스캐폴더/프로브 공용 수정으로 근본 해소했다.

추가로 검증에서 드러난 결함을 마감했다:
- 응답 예시 body 의 절대 URL(페이지네이터·콜백) 호스트를 placeholder 로
 마스킹(maskResponseHost) — 요청 Host 헤더만 마스킹되던 누락 보완, 확장
 39건 정정
- api:docgen 커맨드 통합 테스트 신설(라우트 수집·확장 라우팅·rules 추출·
 멱등 재생성·check drift)
- module-routing.md URL 스킴 오류 정정(/api/admin → /api/modules)
- README·cheatsheet·api-documentation 에 API 문서 커맨드/레퍼런스 진입점 반영

전 대상 3칸 완료로 api-doc-coverage 룰을 warn→error 승격.
This commit is contained in:
HeuJung
2026-07-08 18:22:34 +09:00
parent 70452745c4
commit 56a78e1d55
114 changed files with 60782 additions and 197 deletions
+2 -2
View File
@@ -13,7 +13,7 @@
| [activity-log-hooks.md](docs/backend/activity-log-hooks.md) | 활동 로그 훅 레퍼런스 (Activity Log Hooks Reference) | 코어 66훅 + 이커머스 92훅 + 게시판 32훅 + 페이지 8훅 = 총 198훅 |
| [activity-log.md](docs/backend/activity-log.md) | 활동 로그 시스템 (Activity Log System) | Monolog 기반: Service 훅 → Listener → Log::channel('activity... |
| [admin-settings-access.md](docs/backend/admin-settings-access.md) | Admin 환경설정 값 접근 (`g7_core_settings` vs `config()`) | 동기화 SSoT: storage/app/settings/*.json → SettingsServicePr... |
| [api-documentation.md](docs/backend/api-documentation.md) | API 레퍼런스 문서 규정 (API Documentation) | 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 전수 기재 |
| [api-documentation.md](docs/backend/api-documentation.md) | API 레퍼런스 문서 규정 (API Documentation) | 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 + 요청·응답 예시 ... |
| [api-resources.md](docs/backend/api-resources.md) | API 리소스 | Resource: BaseApiResource 상속 필수 / Collection: BaseApiColl... |
| [authentication.md](docs/backend/authentication.md) | 인증 및 세션 처리 | Laravel Sanctum 토큰 전용 인증 (Bearer 토큰만 사용) |
| [broadcasting.md](docs/backend/broadcasting.md) | Broadcasting (실시간 이벤트) | Laravel Reverb 사용 (WebSocket) |
@@ -117,7 +117,7 @@
| [module-i18n.md](docs/extension/module-i18n.md) | 모듈 다국어 시스템 | 백엔드: /lang/{locale}/*.php → __('vendor-module::key') |
| [module-identity-settings.md](docs/extension/module-identity-settings.md) | 모듈/플러그인 본인인증(IDV) 설정 통합 가이드 | 정책/목적/메시지: module.php::getIdentity{Policies,Purposes,Mess... |
| [module-layouts.md](docs/extension/module-layouts.md) | 모듈 레이아웃 시스템 | 위치: modules/_bundled/vendor-module/resources/layouts/admi... |
| [module-routing.md](docs/extension/module-routing.md) | 모듈 라우트 규칙 | URL prefix 자동: /api/admin/[vendor-module]/... |
| [module-routing.md](docs/extension/module-routing.md) | 모듈 라우트 규칙 | URL prefix 자동: /api/modules/[vendor-module]/... |
| [module-settings.md](docs/extension/module-settings.md) | 모듈 환경설정 시스템 개발 가이드 | - |
| [permissions.md](docs/extension/permissions.md) | 권한 시스템 | 구조: User → Role → Permission (기능 레벨) |
| [plugin-development.md](docs/extension/plugin-development.md) | 플러그인 개발 가이드 | 디렉토리: plugins/vendor-plugin (예: sirsoft-payment) |
+1 -1
View File
@@ -462,7 +462,7 @@ cp .env.example .env
| 플러그인 개발 | [docs/extension/plugin-development.md](docs/extension/plugin-development.md) |
| 템플릿 개발 | [docs/extension/template-basics.md](docs/extension/template-basics.md) |
| 테스트 | [docs/testing-guide.md](docs/testing-guide.md) |
| API 레퍼런스 | 준비 중 |
| API 레퍼런스 | [docs/backend/api-documentation.md](docs/backend/api-documentation.md) |
---
+295 -11
View File
@@ -38,6 +38,7 @@ class ApiDocgenCommand extends Command
{--user= : 실측 토큰 발급 대상 사용자 ID}
{--seed : 실측 전 완전 샘플 데이터를 시드 (개발 환경 전용)}
{--check : 생성하지 않고 누락/미실측만 리포트}
{--examples-only : 기존 문서에 요청/응답 예시 블록만 in-place 삽입 (표·서술 불가침, 재생성 금지)}
{--dry-run : 생성 대상만 출력}';
/**
@@ -123,16 +124,18 @@ class ApiDocgenCommand extends Command
$this->info('실측 기준 URL: '.$probe->baseUrl());
}
$stats = ['files' => 0, 'endpoints' => 0, 'probed' => 0, 'skipped' => 0];
$stats = ['files' => 0, 'endpoints' => 0, 'probed' => 0, 'skipped' => 0, 'examples' => 0];
$checkFindings = [];
$examplesOnly = (bool) $this->option('examples-only');
foreach ($grouped as $file => $items) {
$sections = [];
$sectionKeys = [];
$exampleBlocks = [];
foreach ($items as $route) {
$request = $introspector->introspect($route['controller'], $route['controller_method']);
[$schema, $probeMeta] = $this->probeEndpoint($probe, $inferrer, $route, $probed, $sampleMap);
[$schema, $probeMeta] = $this->probeEndpoint($probe, $inferrer, $route, $probed, $sampleMap, $request);
if ($probeMeta['skipped_reason'] === null) {
$stats['probed']++;
@@ -141,9 +144,28 @@ class ApiDocgenCommand extends Command
$checkFindings[] = "{$route['method']} {$route['uri']} — {$probeMeta['skipped_reason']}";
}
$key = $route['name'] ?: $route['uri'];
if ($examplesOnly) {
// 표·서술을 건드리지 않고 예시 2블록만 산출 (SSoT=스캐폴더 예시 메서드).
// 단, 응답 필드 표가 아직 실측 제외 마커(사람이 채운 셀 없음)인데 이번 실측으로
// 스키마가 확보됐으면 그 마커를 실측 필드 표로 갱신한다(쓰기 메서드 실측 도입분).
$commentMap = $this->columnComments($route, $commentResolver, $sampleMap);
$exampleBlocks[$key] = [
'request' => $scaffolder->requestExampleBlock($route, $request, $probeMeta),
'response' => $scaffolder->responseExampleBlock($schema, $probeMeta),
'response_fields' => $schema !== null
? $scaffolder->responseFieldTableBlock($schema, $probeMeta, $commentMap)
: null,
];
$stats['endpoints']++;
continue;
}
$commentMap = $this->columnComments($route, $commentResolver, $sampleMap);
$sections[] = $scaffolder->endpointSection($route, $request, $schema, $probeMeta, $commentMap);
$sectionKeys[] = $route['name'] ?: $route['uri'];
$sectionKeys[] = $key;
$stats['endpoints']++;
}
@@ -155,6 +177,28 @@ class ApiDocgenCommand extends Command
continue;
}
// examples-only: 재생성 없이 기존 문서에 예시 블록만 in-place 삽입.
// 전체 재생성은 @generated 블록 내부 표(파라미터/응답 필드)의 사람 서술을
// TODO 로 되돌리므로, 예시 방출을 위해 재생성을 쓰지 않는다.
if ($examplesOnly) {
if (! File::exists($file)) {
$this->warn("문서 없음(예시 삽입 건너뜀): {$file}");
continue;
}
$existing = File::get($file);
[$updated, $inserted] = $scaffolder->insertExampleBlocks($existing, $exampleBlocks);
if ($updated !== $existing) {
File::put($file, $updated);
$stats['files']++;
}
$stats['examples'] += $inserted;
continue;
}
$header = $this->documentHeader($file, $items[0]);
$existing = File::exists($file) ? File::get($file) : null;
$content = $scaffolder->mergeDocument($existing, $header, $sections, $sectionKeys);
@@ -182,6 +226,18 @@ class ApiDocgenCommand extends Command
}
$this->newLine();
if ($examplesOnly) {
$this->info(sprintf(
'예시 삽입 완료: 파일 %d개, 예시 블록 %d개 (실측 %d, 제외 %d)',
$stats['files'],
$stats['examples'],
$stats['probed'],
$stats['skipped']
));
return self::SUCCESS;
}
$this->info(sprintf(
'완료: 파일 %d개, 엔드포인트 %d개 (실측 %d, 제외 %d)',
$stats['files'],
@@ -277,22 +333,181 @@ class ApiDocgenCommand extends Command
* @param array<string, mixed> $route 라우트 메타데이터
* @param bool $probed 실측 가능 여부
* @param array<string, array{model: class-string, key: string, value: string}> $sampleMap 도메인별 대표 샘플 맵
* @param array<string, mixed> $request FormRequest 분석 결과 (쓰기 바디 구성용)
* @return array{0: array<string, mixed>|null, 1: array<string, mixed>} 스키마와 실측 메타
*/
private function probeEndpoint(ApiEndpointProbe $probe, ResponseSchemaInferrer $inferrer, array $route, bool $probed, array $sampleMap = []): array
private function probeEndpoint(ApiEndpointProbe $probe, ResponseSchemaInferrer $inferrer, array $route, bool $probed, array $sampleMap = [], array $request = []): array
{
$baseUrl = $probe->baseUrl();
if (! $probed) {
return [null, ['status' => null, 'skipped_reason' => 'no-token']];
return [null, ['status' => null, 'skipped_reason' => 'no-token', 'base_url' => $baseUrl, 'resolved_uri' => null, 'body' => null]];
}
$uri = $this->resolvePathParams($route, $sampleMap);
$result = $probe->probe($route['method'], $uri);
$isWrite = in_array($route['method'], ['POST', 'PUT', 'PATCH', 'DELETE'], true);
if (! $result['ok'] || $result['body'] === null) {
return [null, ['status' => $result['status'], 'skipped_reason' => $result['skipped_reason'] ?? ('http-'.$result['status'])]];
// 요청 예시에 노출할 정규 URI (실측용 per_page 보정 제외).
$exampleUri = $this->exampleUri($route, $sampleMap);
// 부수효과가 트랜잭션 롤백으로 되돌릴 수 없는 쓰기(확장 install/activate/update,
// 언어팩 설치, 코어 업데이트, 파일 업로드 등)는 in-process 실측에서 제외한다.
// 이들은 파일시스템·프로세스·외부 네트워크를 건드려 롤백 불가·hang 위험이 있다.
if ($isWrite && $this->isSideEffectfulWrite($route)) {
return [null, [
'status' => null,
'skipped_reason' => 'side-effectful-write',
'base_url' => $baseUrl,
'resolved_uri' => $exampleUri,
'body' => null,
]];
}
return [$inferrer->infer($result['body']), ['status' => $result['status'], 'skipped_reason' => null]];
// 쓰기 메서드는 FormRequest 규칙 기반 실측 바디를 만들어 in-process 롤백 실측에 사용한다.
$writeBody = in_array($route['method'], ['POST', 'PUT', 'PATCH'], true)
? $this->buildWriteBody($request, $sampleMap)
: [];
$result = $probe->probe($route['method'], $uri, $writeBody);
if (! $result['ok'] || $result['body'] === null) {
return [null, [
'status' => $result['status'],
'skipped_reason' => $result['skipped_reason'] ?? ('http-'.$result['status']),
'base_url' => $baseUrl,
'resolved_uri' => $exampleUri,
'body' => null,
]];
}
return [$inferrer->infer($result['body']), [
'status' => $result['status'],
'skipped_reason' => null,
'base_url' => $baseUrl,
'resolved_uri' => $exampleUri,
'body' => $result['body'],
]];
}
/**
* 부수효과가 트랜잭션 롤백으로 되돌릴 수 없는 쓰기 엔드포인트인지 판별합니다.
*
* 확장(모듈/플러그인/템플릿) 및 언어팩 설치·활성·업데이트, 코어 업데이트, 파일 업로드,
* 캐시/워밍업/생성 등 파일시스템·프로세스·외부 네트워크를 건드리는 쓰기는 in-process 롤백
* 실측이 불가능(hang·비가역 부수효과)하므로 실측에서 제외한다. URI 세그먼트로 판정한다.
*
* @param array<string, mixed> $route 라우트 메타데이터
* @return bool 부수효과 쓰기 여부
*/
private function isSideEffectfulWrite(array $route): bool
{
$uri = (string) $route['uri'];
// 확장/언어팩 관리 도메인의 쓰기는 전량 제외 (install/activate/... 은 실환경 변경).
if (preg_match('#/api/admin/(modules|plugins|templates|language-packs|core-update)(/|$)#', $uri)) {
return true;
}
// 동작성 세그먼트를 가진 쓰기 (도메인 무관).
$unsafeSegments = [
'install', 'install-from-file', 'install-from-github', 'install-from-url', 'install-from-bundled',
'uninstall', 'activate', 'deactivate', 'bulk-activate', 'update', 'check-updates',
'refresh-cache', 'clear', 'warmup', 'generate', 'sync', 'backup', 'restore',
'upload', 'import', 'export', 'manifest-preview', 'preview',
];
foreach ($unsafeSegments as $seg) {
if (str_contains($uri, '/'.$seg)) {
return true;
}
}
return false;
}
/**
* 요청 예시에 노출할 정규 URI 를 산출합니다.
*
* `resolvePathParams` 는 목록 GET 실측용으로 `?per_page=25` 를 덧붙이지만,
* 요청 예시에는 이 실측 보정을 제외한 순수 경로(치환된 path 파라미터 포함)를 노출한다.
* path 파라미터가 치환되지 않은(실측 제외) 경우 `{param}` placeholder 를 그대로 남긴다.
*
* @param array<string, mixed> $route 라우트 메타데이터
* @param array<string, array{model: class-string, key: string, value: string}> $sampleMap 도메인별 대표 샘플 맵
* @return string 요청 예시용 URI
*/
private function exampleUri(array $route, array $sampleMap): string
{
$resolved = $this->resolvePathParams($route, $sampleMap);
// 실측용 per_page 보정 쿼리를 제거해 정규 경로만 남긴다.
return preg_replace('/[?&]per_page=25$/', '', $resolved) ?? $resolved;
}
/**
* 쓰기 메서드 in-process 실측용 요청 바디를 FormRequest 규칙 근거로 구성합니다.
*
* 필수 파라미터를 우선하고, 검증을 통과할 수 있도록 이름·타입·허용값에 맞는 유효값을 채웁니다.
* 파일(image/file) 파라미터는 실측 바디에서 제외한다(in-process dispatch 로 파일 전송 불가 →
* 해당 엔드포인트는 http-422 로 스킵되고 정적 요청 예시[multipart]만 방출된다).
*
* @param array<string, mixed> $request FormRequest 분석 결과
* @param array<string, array{model: class-string, key: string, value: string}> $sampleMap 도메인별 대표 샘플 맵
* @return array<string, mixed> 실측 요청 바디
*/
private function buildWriteBody(array $request, array $sampleMap = []): array
{
$params = $request['params'] ?? [];
$body = [];
foreach ($params as $p) {
$name = (string) $p['name'];
$type = (string) ($p['type'] ?? 'string');
// 파일 타입은 in-process 바디로 전송 불가 → 생략.
if (in_array($type, ['file', 'image'], true)) {
continue;
}
$body[$name] = $this->writeValue($name, $type, (string) ($p['allowed'] ?? ''), $sampleMap);
}
return $body;
}
/**
* 쓰기 실측 바디의 파라미터 값을 검증 통과 가능하도록 생성합니다.
*
* @param string $name 파라미터명
* @param string $type 타입
* @param string $allowed 허용값 설명 (in: 열거 등)
* @param array<string, array{model: class-string, key: string, value: string}> $sampleMap 도메인 샘플 맵
* @return mixed 유효 예시값
*/
private function writeValue(string $name, string $type, string $allowed, array $sampleMap): mixed
{
$lower = strtolower($name);
// 허용값 in: 열거(백틱)가 있으면 첫 값을 채택(검증 통과 보장).
if (preg_match('/`([^`]+)`/', $allowed, $m)) {
return $m[1];
}
return match (true) {
$type === 'boolean' => true,
$type === 'integer', $type === 'number' => 1,
$type === 'array' => [],
$type === 'email' || str_contains($lower, 'email') => 'probe_'.uniqid().'@example.com',
$type === 'uuid' => (string) Str::uuid(),
$type === 'date' => '2026-01-01',
str_contains($lower, 'password') => 'Password123!',
str_contains($lower, 'url') || str_contains($lower, 'homepage') => 'https://example.com',
// identifier/slug 는 소문자/숫자/밑줄만 허용하는 규칙이 흔하므로 하이픈 없이 생성.
str_contains($lower, 'identifier') || str_contains($lower, 'slug') => 'probe_'.uniqid(),
str_contains($lower, 'locale') || $lower === 'language' => 'ko',
str_contains($lower, 'country') => 'KR',
str_contains($lower, 'timezone') => 'Asia/Seoul',
default => '실측 예시값',
};
}
/**
@@ -329,6 +544,17 @@ class ApiDocgenCommand extends Command
if ($explicit !== null) {
$uri = str_replace('{'.$param.'}', (string) $explicit, $uri);
continue;
}
// 코어 다수 라우트는 route-model binding 없이 `{id}`/`{identifier}`/`{key}` 문자열
// 파라미터를 쓰고 컨트롤러가 직접 조회한다. 도메인 대표 샘플 모델의 route key(또는
// 해당 컬럼)로 치환해 실측 가능하게 한다. (마지막 세그먼트 리소스 파라미터에 한함)
$generic = $this->resolveGenericPathParam($param, $domain, $sampleMap);
if ($generic !== null) {
$uri = str_replace('{'.$param.'}', (string) $generic, $uri);
}
continue;
@@ -351,6 +577,64 @@ class ApiDocgenCommand extends Command
return $uri;
}
/**
* route-model binding 없는 제네릭 path 파라미터를 도메인 샘플 모델의 실제 키로 치환합니다.
*
* 코어 다수 라우트는 `{id}`/`{identifier}`/`{key}`/`{slug}` 문자열 파라미터를 쓰고 컨트롤러가
* 직접 조회한다. 도메인 대표 샘플 모델을 찾아 파라미터명에 대응하는 컬럼값으로 치환한다:
* - `id` → 모델 기본키
* - `identifier`/`slug`/`key`/`code` → 같은 이름 컬럼이 있으면 그 값, 없으면 route key
* - 그 외 → 치환 안 함(null 반환 → 실측 제외 유지, 오치환 방지)
*
* @param string $param path 파라미터명
* @param string|null $domain 도메인 그룹명
* @param array<string, array{model: class-string, key: string, value: string}> $sampleMap 도메인 샘플 맵
* @return string|null 치환값 (없으면 null)
*/
private function resolveGenericPathParam(string $param, ?string $domain, array $sampleMap): ?string
{
$modelClass = ($domain !== null && isset($sampleMap[$domain]))
? $sampleMap[$domain]['model']
: $this->domainModelHint($domain);
if (! $modelClass) {
return null;
}
try {
/** @var Model $model */
$model = new $modelClass;
$record = $modelClass::query()->orderBy($model->getKeyName())->first();
if (! $record) {
return null;
}
$lower = strtolower($param);
// id 계열 → 기본키
if ($lower === 'id' || str_ends_with($lower, '_id')) {
return (string) $record->getKey();
}
// identifier/slug/key/code → 동일 컬럼이 있으면 그 값
if (in_array($lower, ['identifier', 'slug', 'key', 'code'], true)) {
$val = $record->getAttribute($lower);
return $val !== null ? (string) $val : (string) $record->getRouteKey();
}
// 도메인 단수 리소스명 파라미터 → route key
if ($domain !== null && $this->paramMatchesDomain($param, $domain)) {
return (string) $record->getRouteKey();
}
return null;
} catch (\Throwable) {
return null;
}
}
/**
* path 파라미터명이 도메인의 단수 리소스명과 일치하는지 판정합니다.
*
@@ -530,8 +814,8 @@ class ApiDocgenCommand extends Command
```text
1. 이 문서는 실제 API 호출로 실측한 {$domain} 엔드포인트 레퍼런스입니다
2. 각 엔드포인트: 메서드/URI/권한 + 요청 파라미터 표 + 실측 응답 필드 표
3. 응답 필드의 예시값은 실제 호출 응답에서 관측된 값입니다
2. 각 엔드포인트: 메서드/URI/권한 + 요청 파라미터 표 + 요청 예시(curl) + 실측 응답 필드 표 + 응답 예시(envelope)
3. 응답 필드의 예시값·응답 예시 JSON 은 실제 호출 응답에서 관측된 값입니다
4. 갱신: 코드 변경 후 php artisan api:docgen 재실행
5. 설명(TODO) 칸은 사람이 채웁니다
```
+747 -2
View File
@@ -22,6 +22,28 @@ class ApiDocScaffolder
*/
private const GEN_END = '<!-- @generated:end -->';
/**
* @var int 응답 예시의 목록(`data.data[]`) 최대 표시 항목 수
*
* 목록 응답 body 는 클 수 있으므로 대표 항목만 남기고 나머지는 절단 주석으로 대체한다.
*/
private const LIST_EXAMPLE_LIMIT = 2;
/**
* @var string 요청 예시의 토큰 마스킹 placeholder
*
* 실측 토큰은 임시 발급분이므로 문서에 평문 유출하지 않고 placeholder 로 마스킹한다.
*/
private const TOKEN_PLACEHOLDER = '{YOUR_TOKEN}';
/**
* @var string 요청 예시의 공개 Host placeholder
*
* 실측 기준 URL(로컬 개발 호스트 등)을 공개 문서에 노출하지 않고 중립 placeholder 로 마스킹한다.
* RFC 2606 예약 도메인(example.com) 기반이라 어느 환경에도 종속되지 않는다.
*/
private const DOC_HOST = 'api.example.com';
/**
* @param ResourceFieldDescriber $fieldDescriber accessor/computed 필드 설명기
* @param ParameterDescriber $paramDescriber 공통 요청 파라미터 설명기
@@ -37,7 +59,7 @@ class ApiDocScaffolder
* @param array<string, mixed> $route 라우트 메타데이터
* @param array<string, mixed> $request FormRequest 분석 결과
* @param array<string, mixed>|null $schema 실측 응답 스키마 (null=실측 안 됨)
* @param array<string, mixed> $probeMeta 실측 메타 (status, skipped_reason)
* @param array<string, mixed> $probeMeta 실측 메타 (status, skipped_reason, base_url, resolved_uri, body)
* @param array<string, string> $commentMap 컬럼명 => 주석 (필드 설명 기본값)
* @return string 마크다운 섹션
*/
@@ -67,11 +89,21 @@ class ApiDocScaffolder
}
$lines[] = '';
$lines[] = '**요청 예시**';
$lines[] = '';
$lines[] = $this->requestExampleBlock($route, $request, $probeMeta);
$lines[] = '';
$lines[] = '**응답 필드** (`data` 내부)';
$lines[] = '';
$lines[] = $this->responseFieldTable($schema, $probeMeta, $commentMap);
$lines[] = '';
$lines[] = '**응답 예시**';
$lines[] = '';
$lines[] = $this->responseExampleBlock($schema, $probeMeta);
$lines[] = '';
$lines[] = '**에러 응답**';
$lines[] = '';
$lines[] = $this->errorTable($route, $request);
@@ -141,8 +173,9 @@ class ApiDocScaffolder
private function requestParamTable(array $route, array $request): string
{
$rows = [];
$pathParams = $route['path_params'];
foreach ($route['path_params'] as $pathParam) {
foreach ($pathParams as $pathParam) {
$desc = $this->paramDescriber->describe($pathParam, 'path', 'string');
$descCell = $desc !== null ? $this->escapeCell($desc) : '<!-- TODO: 용도 -->';
$rows[] = "| {$pathParam} | path | string | 예 | — | {$descCell} |";
@@ -151,6 +184,12 @@ class ApiDocScaffolder
$location = in_array($route['method'], ['GET', 'DELETE'], true) ? 'query' : 'body';
foreach ($request['params'] as $p) {
// path 파라미터로 이미 출력한 이름은 FormRequest 규칙에서 중복 출력하지 않는다
// (라우트 바인딩 세그먼트가 FormRequest rules() 에도 있으면 path 행이 우선).
if (in_array($p['name'], $pathParams, true)) {
continue;
}
$required = $p['required'] ? '예' : '아니오';
$allowed = $p['allowed'] !== '' ? $p['allowed'] : '—';
$desc = $this->paramDescriber->describe($p['name'], $location, $p['type']);
@@ -165,6 +204,19 @@ class ApiDocScaffolder
return "| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |\n| --- | --- | --- | --- | --- | --- |\n".implode("\n", $rows);
}
/**
* 응답 필드 표 블록을 생성합니다 (examples-only 모드의 마커 갱신용 공개 래퍼).
*
* @param array<string, mixed>|null $schema 실측 응답 스키마
* @param array<string, mixed> $probeMeta 실측 메타
* @param array<string, string> $commentMap 컬럼명 => 주석 (필드 설명 기본값)
* @return string 마크다운 표 또는 실측 제외 사유
*/
public function responseFieldTableBlock(?array $schema, array $probeMeta, array $commentMap = []): string
{
return $this->responseFieldTable($schema, $probeMeta, $commentMap);
}
/**
* 응답 필드 표를 생성합니다.
*
@@ -263,6 +315,437 @@ class ApiDocScaffolder
return "| 상태코드 | 의미 | 발생 조건 |\n| --- | --- | --- |\n".implode("\n", $rows);
}
/**
* 실제 호출을 재현하는 raw HTTP 요청 예시 블록을 생성합니다.
*
* 요청 라인(`{METHOD} {path} HTTP/1.1`) + 헤더 + 바디로 raw HTTP 요청을 조립합니다
* (응답 예시의 `HTTP/1.1 {status}` 상태줄과 대칭). curl 은 사용하지 않습니다.
* - 인증 필요(`auth:sanctum`/`optional.sanctum`) 시 `Authorization: Bearer {YOUR_TOKEN}` 마스킹.
* - 바디 있는 메서드(POST/PUT/PATCH)는 요청 파라미터 표의 필수 파라미터 + 타입별 샘플값을
* 빈 줄 뒤 JSON 바디로 붙입니다.
*
* path 파라미터가 실측 치환값(`resolved_uri`)으로 채워졌으면 그 값을, 아니면 `{param}` placeholder 를 쓴다.
*
* @param array<string, mixed> $route 라우트 메타데이터
* @param array<string, mixed> $request FormRequest 분석 결과
* @param array<string, mixed> $probeMeta 실측 메타 (base_url, resolved_uri)
* @return string 마크다운 코드블록
*/
public function requestExampleBlock(array $route, array $request, array $probeMeta): string
{
$method = strtoupper((string) $route['method']);
$path = (string) ($probeMeta['resolved_uri'] ?? $route['uri']);
// GET/DELETE 의 query 파라미터는 URL 쿼리스트링으로 반영한다(바디가 아니라 URL).
if (in_array($method, ['GET', 'DELETE'], true)) {
$path = $this->appendQueryString($path, $request);
}
// 요청 라인 + 헤더로 raw HTTP 요청 예시를 조립한다 (응답 예시의 HTTP/1.1 상태줄과 대칭).
// Host 는 실측 기준 URL(로컬 호스트)을 노출하지 않고 공개 placeholder 로 마스킹한다.
$lines = ["{$method} {$path} HTTP/1.1"];
$lines[] = 'Host: '.self::DOC_HOST;
$lines[] = 'Accept: application/json';
// 인증 필요 시 Bearer 토큰 마스킹 (auth:sanctum 필수 / optional.sanctum 선택).
$mw = $route['middleware'] ?? [];
if ($this->hasMiddleware($mw, 'sanctum')) {
$optional = $this->hasMiddleware($mw, 'optional.sanctum');
$suffix = $optional ? ' (optional.sanctum: 비회원은 헤더 생략 가능)' : '';
$lines[] = 'Authorization: Bearer '.self::TOKEN_PLACEHOLDER.$suffix;
}
// 바디 있는 메서드(POST/PUT/PATCH)는 바디를 붙인다.
if (in_array($method, ['POST', 'PUT', 'PATCH'], true)) {
$bodyParams = $this->bodyParams($request);
if ($bodyParams !== []) {
// 파일 업로드(image/file 타입) 파라미터가 있으면 multipart/form-data 로 표기한다
// (application/json 으로는 파일을 전송할 수 없다).
if ($this->hasFileParam($bodyParams)) {
foreach ($this->multipartBodyLines($bodyParams) as $bl) {
$lines[] = $bl;
}
} else {
$lines[] = 'Content-Type: application/json';
$lines[] = '';
$body = $this->jsonBody($bodyParams);
$json = json_encode($body, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$lines[] = (string) $json;
}
}
}
return "```http\n".implode("\n", $lines)."\n```";
}
/**
* GET/DELETE 의 query 파라미터를 URL 쿼리스트링으로 붙입니다.
*
* 이미 쿼리(`?per_page=..`)가 있으면 `&` 로 잇고, 각 파라미터는 타입별 대표값으로 채웁니다.
* path 파라미터는 URL 세그먼트라 제외합니다.
*
* @param string $path 경로(치환된 path 파라미터 포함 가능)
* @param array<string, mixed> $request FormRequest 분석 결과
* @return string 쿼리스트링이 붙은 경로
*/
private function appendQueryString(string $path, array $request): string
{
$params = $request['params'] ?? [];
if ($params === []) {
return $path;
}
$pathParams = [];
preg_match_all('/\{([^}]+)\}/', $path, $m);
if (! empty($m[1])) {
$pathParams = $m[1];
}
$pairs = [];
foreach ($params as $p) {
$name = (string) $p['name'];
if (in_array($name, $pathParams, true)) {
continue;
}
// 허용값 in: 열거가 있으면 그 첫 값을, 없으면 이름·타입 기반 대표값을 쓴다.
$value = $this->exampleValue($name, (string) ($p['type'] ?? 'string'), (string) ($p['allowed'] ?? ''));
if (is_array($value)) {
$value = $value[0] ?? '';
}
$pairs[] = $name.'='.rawurlencode((string) $value);
}
if ($pairs === []) {
return $path;
}
return $path.(str_contains($path, '?') ? '&' : '?').implode('&', $pairs);
}
/**
* body 위치 파라미터 목록을 반환합니다 (전체 — 필수+선택).
*
* @param array<string, mixed> $request FormRequest 분석 결과
* @return array<int, array<string, mixed>> body 파라미터 목록
*/
private function bodyParams(array $request): array
{
return array_values($request['params'] ?? []);
}
/**
* 파라미터 목록에 파일(image/file) 타입이 있는지 판별합니다.
*
* @param array<int, array<string, mixed>> $params 파라미터 목록
* @return bool 파일 파라미터 존재 여부
*/
private function hasFileParam(array $params): bool
{
foreach ($params as $p) {
if (in_array((string) ($p['type'] ?? ''), ['file', 'image'], true)) {
return true;
}
}
return false;
}
/**
* multipart/form-data 요청 예시의 헤더 + 파트 라인들을 생성합니다.
*
* 파일 파라미터는 `filename=` + `Content-Type` 파트로, 그 외는 값 파트로 표현합니다.
*
* @param array<int, array<string, mixed>> $params body 파라미터 목록
* @return array<int, string> multipart 요청 라인들
*/
private function multipartBodyLines(array $params): array
{
$boundary = '----G7ExampleBoundary';
$lines = ["Content-Type: multipart/form-data; boundary={$boundary}", ''];
foreach ($params as $p) {
$name = (string) $p['name'];
$type = (string) ($p['type'] ?? 'string');
$lines[] = "--{$boundary}";
if (in_array($type, ['file', 'image'], true)) {
// 배열 파일(files[]) 은 name 뒤에 [] 를 붙인다.
$isArrayFile = str_ends_with($name, 's') || $name === 'files';
$field = $isArrayFile ? $name.'[]' : $name;
$fileName = $type === 'image' ? 'example.png' : 'example.pdf';
$mime = $type === 'image' ? 'image/png' : 'application/octet-stream';
$lines[] = "Content-Disposition: form-data; name=\"{$field}\"; filename=\"{$fileName}\"";
$lines[] = "Content-Type: {$mime}";
$lines[] = '';
$lines[] = '(바이너리 파일 내용)';
} else {
$lines[] = "Content-Disposition: form-data; name=\"{$name}\"";
$lines[] = '';
$lines[] = (string) $this->exampleScalar($name, $type);
}
}
$lines[] = "--{$boundary}--";
return $lines;
}
/**
* JSON 요청 바디 맵을 조립합니다 (전체 파라미터 — 필수+선택).
*
* 값은 이름·타입에 맞는 현실적인 예시값으로 채웁니다(placeholder `"string"` 남발 방지).
*
* @param array<int, array<string, mixed>> $params body 파라미터 목록
* @return array<string, mixed> 예시 바디 (이름 => 예시값)
*/
private function jsonBody(array $params): array
{
$body = [];
foreach ($params as $p) {
$name = (string) $p['name'];
$type = (string) ($p['type'] ?? 'string');
$body[$name] = $this->exampleValue($name, $type, (string) ($p['allowed'] ?? ''));
}
return $body;
}
/**
* 파라미터의 예시값을 이름·타입·허용값 근거로 생성합니다.
*
* `array` 타입은 빈 배열이 아니라 대표 원소 1개를, boolean/number 는 타입값을,
* 그 외는 이름 기반 현실적 문자열(email/password/url 등)을 반환합니다.
* 허용값에 `in:` 목록(백틱)이 있으면 그 첫 값을 채택합니다.
*
* @param string $name 파라미터명
* @param string $type 타입
* @param string $allowed 허용값 설명
* @return mixed 예시값
*/
private function exampleValue(string $name, string $type, string $allowed = ''): mixed
{
// 허용값에 in: 열거(백틱으로 감싼 첫 값)가 있으면 그것을 우선 채택한다.
if (preg_match('/`([^`]+)`/', $allowed, $m)) {
return $m[1];
}
return match ($type) {
'array' => [$this->exampleScalar($this->singularName($name), 'string')],
default => $this->exampleScalar($name, $type),
};
}
/**
* 스칼라 파라미터의 예시값을 이름·타입 근거로 생성합니다.
*
* @param string $name 파라미터명
* @param string $type 타입
* @return mixed 예시 스칼라값
*/
private function exampleScalar(string $name, string $type): mixed
{
$lower = strtolower($name);
// 타입 우선 판정
if ($type === 'boolean') {
return true;
}
if (in_array($type, ['integer', 'number'], true)) {
return match (true) {
str_contains($lower, 'page') => 1,
str_ends_with($lower, '_id'), $lower === 'id' => 1,
default => 1,
};
}
if ($type === 'email' || str_contains($lower, 'email')) {
return 'user@example.com';
}
if ($type === 'uuid') {
return '9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d';
}
if ($type === 'date') {
return '2026-01-01';
}
// 이름 기반 현실적 값
return match (true) {
str_contains($lower, 'password') => 'Password123!',
str_contains($lower, 'url') || str_contains($lower, 'homepage') => 'https://example.com',
str_contains($lower, 'identifier') || str_contains($lower, 'slug') => 'example-key',
str_contains($lower, 'name') => '예시 이름',
str_contains($lower, 'title') => '예시 제목',
str_contains($lower, 'mobile') || str_contains($lower, 'phone') => '010-1234-5678',
str_contains($lower, 'locale') || $lower === 'language' => 'ko',
str_contains($lower, 'country') => 'KR',
str_contains($lower, 'timezone') => 'Asia/Seoul',
str_contains($lower, 'zipcode') || str_contains($lower, 'postal') => '06234',
str_contains($lower, 'address') => '서울특별시 강남구 테헤란로 1',
str_contains($lower, 'content') || str_contains($lower, 'description') || str_contains($lower, 'bio') => '예시 내용입니다.',
str_contains($lower, 'token') => self::TOKEN_PLACEHOLDER,
str_contains($lower, 'color') => '#4F46E5',
default => '예시값',
};
}
/**
* 배열 파라미터명의 단수형 근사값을 반환합니다 (예시 원소 이름용).
*
* @param string $name 파라미터명 (복수형 가능)
* @return string 단수형 근사
*/
private function singularName(string $name): string
{
return Str::singular($name);
}
/**
* 실측 응답 body 전문(envelope 통짜)을 예시 블록으로 방출합니다.
*
* 실측 body 가 있으면 `{success, data, message, error}` envelope 를 pretty JSON 으로 출력합니다.
* 목록 응답의 `data.data[]` 는 대표 항목으로 절단하고 나머지는 절단 주석으로 대체합니다.
* 실측 제외(쓰기 메서드·바이너리·미치환 path)면 사람 보강 마커를 남깁니다.
*
* @param array<string, mixed>|null $schema 실측 응답 스키마 (null=실측 안 됨)
* @param array<string, mixed> $probeMeta 실측 메타 (status, body, skipped_reason)
* @return string 마크다운 코드블록 또는 실측 제외 마커
*/
public function responseExampleBlock(?array $schema, array $probeMeta): string
{
$body = $probeMeta['body'] ?? null;
if (! is_array($body)) {
$reason = $probeMeta['skipped_reason'] ?? 'not-probed';
return "<!-- 실측 제외: {$reason} — 응답 예시는 사람이 작성하세요. -->";
}
$status = $probeMeta['status'] ?? 200;
$truncated = $this->truncateListBody($body);
// 실측 응답에 섞여 나온 민감값(토큰·비밀번호·시크릿 등)을 마스킹한다.
$sanitized = $this->sanitizeSensitive($truncated);
// 응답 body 내부의 절대 URL(페이지네이터 링크·콜백 URL 등)에 실측 기준 호스트가
// 그대로 직렬화돼 들어올 수 있으므로 공개 문서용 placeholder 호스트로 마스킹한다.
$masked = $this->maskResponseHost($sanitized, $probeMeta['base_url'] ?? null);
$json = json_encode($masked, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return "```http\nHTTP/1.1 {$status}\n```\n\n```json\n".(string) $json."\n```";
}
/**
* 응답 body 의 절대 URL 에 포함된 실측 기준 호스트를 공개 placeholder 호스트로 마스킹합니다 (재귀).
*
* Laravel 페이지네이터 메타(`first_page_url`/`last_page_url`/`path`/`url` 등)와 콜백/통보 URL 은
* 실측 시 요청 base URL 의 호스트(로컬 개발 호스트 등)를 그대로 직렬화한다. 요청 예시의 `Host:`
* 헤더는 {@see self::DOC_HOST} 로 마스킹되지만 응답 body 문자열값은 별도 처리가 없으면 유출된다.
* base URL 의 scheme+host 를 `https://{DOC_HOST}` 로 치환해 응답 예시에서도 호스트를 노출하지 않는다.
*
* @param mixed $value 응답 값 (배열/스칼라)
* @param string|null $baseUrl 실측 요청 base URL (없으면 마스킹 생략)
* @return mixed 호스트가 마스킹된 값
*/
private function maskResponseHost(mixed $value, ?string $baseUrl): mixed
{
if ($baseUrl === null || $baseUrl === '') {
return $value;
}
$host = parse_url($baseUrl, PHP_URL_HOST);
if (! is_string($host) || $host === '' || $host === self::DOC_HOST) {
return $value;
}
if (is_string($value)) {
// scheme://host 조합을 placeholder 로 치환 (포트 유무·http/https 모두 대응).
return preg_replace(
'#https?://'.preg_quote($host, '#').'(:\d+)?#i',
'https://'.self::DOC_HOST,
$value
);
}
if (is_array($value)) {
$result = [];
foreach ($value as $k => $v) {
$result[$k] = $this->maskResponseHost($v, $baseUrl);
}
return $result;
}
return $value;
}
/**
* 응답 body 에서 민감한 필드 값을 placeholder 로 마스킹합니다 (재귀).
*
* 실측 응답에 실제 토큰·비밀번호 해시·시크릿·API 키가 섞여 나올 수 있으므로 공개 문서에
* 방출하기 전에 마스킹한다. 필드명(키)에 민감 키워드가 포함되면 값을 placeholder 로 대체.
*
* @param mixed $value 응답 값 (배열/스칼라)
* @return mixed 마스킹된 값
*/
private function sanitizeSensitive(mixed $value): mixed
{
if (! is_array($value)) {
return $value;
}
$sensitiveKeys = [
'token', 'access_token', 'refresh_token', 'plaintexttoken', 'plain_text_token',
'password', 'secret', 'api_key', 'apikey', 'private_key', 'client_secret',
'authorization', 'remember_token', 'app_key',
];
$result = [];
foreach ($value as $k => $v) {
$keyLower = is_string($k) ? strtolower($k) : '';
$isSensitive = false;
foreach ($sensitiveKeys as $needle) {
if ($keyLower !== '' && str_contains($keyLower, $needle)) {
$isSensitive = true;
break;
}
}
if ($isSensitive && ! is_array($v) && $v !== null) {
// 타입 표기용 접미(_type/_expires 등)는 마스킹 대상이 아니므로 스칼라만 마스킹.
$result[$k] = ($keyLower === 'token_type') ? $v : '{MASKED}';
} else {
$result[$k] = $this->sanitizeSensitive($v);
}
}
return $result;
}
/**
* 목록 응답 body 의 `data.data[]` 를 대표 항목으로 절단합니다.
*
* 목록 body 는 클 수 있으므로 최대 LIST_EXAMPLE_LIMIT 항목만 남기고
* 나머지는 `// ... (총 N건 중 K건 표시)` 절단 주석 항목으로 대체합니다.
*
* @param array<string, mixed> $body 실측 응답 body (envelope)
* @return array<string, mixed> 절단된 body
*/
private function truncateListBody(array $body): array
{
$data = $body['data'] ?? null;
if (is_array($data) && isset($data['data']) && is_array($data['data'])) {
$rows = $data['data'];
$total = count($rows);
if ($total > self::LIST_EXAMPLE_LIMIT) {
$kept = array_slice($rows, 0, self::LIST_EXAMPLE_LIMIT);
$kept[] = "... (총 {$total}건 중 ".self::LIST_EXAMPLE_LIMIT.'건 표시)';
$body['data']['data'] = $kept;
}
}
return $body;
}
/**
* 마크다운 표 셀 안에서 안전하도록 파이프/개행을 이스케이프합니다.
*
@@ -354,4 +837,266 @@ class ApiDocScaffolder
return str_replace($stub, $preserved, $section);
}
/**
* 기존 문서의 각 엔드포인트 `@generated` 블록에 요청/응답 예시 블록을 in-place 삽입합니다.
*
* 전체 재생성(`endpointSection` + `mergeDocument`)은 `@generated` 블록 내부의
* 파라미터/응답 필드 표를 통째로 다시 조립하므로, 정적 추출로 재현 불가능한 사람의
* 도메인 서술 셀이 TODO 로 되돌아갑니다. 이 메서드는 표를 건드리지 않고 예시 2블록만
* 표 뒤에 삽입/치환하여 그 퇴행 없이 예시를 방출합니다.
*
* 삽입 위치(스캐폴더 `endpointSection` 순서와 동일):
* - `**요청 예시**` → `**응답 필드**` 헤딩 바로 앞
* - `**응답 예시**` → `**에러 응답**` 헤딩 바로 앞
*
* 이미 예시 블록이 있으면(멱등) 그 블록만 새 내용으로 치환합니다.
*
* @param string $content 기존 문서 내용
* @param array<string, array{request: string, response: string}> $exampleBlocks 라우트명 키 => 예시 블록
* @return array{0: string, 1: int} [갱신된 문서, 삽입/치환한 예시 블록 수]
*/
public function insertExampleBlocks(string $content, array $exampleBlocks): array
{
$inserted = 0;
foreach ($exampleBlocks as $key => $blocks) {
$startMarker = self::GEN_START.$key.' -->';
$startPos = strpos($content, $startMarker);
if ($startPos === false) {
continue;
}
$endPos = strpos($content, self::GEN_END, $startPos);
if ($endPos === false) {
continue;
}
$block = substr($content, $startPos, $endPos - $startPos);
// 이 엔드포인트의 사람 서술 영역(@generated:end 뒤 ~ 다음 ### 전)에 이미 사람이 작성한
// `**응답 예시**` 가 있으면, 블록 안에 응답 예시 마커를 넣지 않는다(중복 헤딩 회피).
// 요청 예시는 항상 삽입한다.
$proseHasResponseExample = $this->proseHasResponseExample($content, $endPos);
// 응답 필드 표가 실측 제외 마커(사람이 채운 셀 없음)이고 이번에 실측 표가 확보됐으면
// 그 마커를 실측 필드 표로 갱신한다(쓰기 메서드 실측 도입분). 이미 필드 표(| 필드 | 타입 |)가
// 있으면 건드리지 않아 사람 서술 셀을 보존한다.
$updatedBlock = $block;
// 요청 파라미터 표에서 path 행과 이름이 겹치는 query 행(자동 생성 중복)을 제거한다.
// 라우트 바인딩 세그먼트가 FormRequest rules() 에도 있어 path/query 로 2회 출력되던 결함.
$updatedBlock = $this->dedupeParamRows($updatedBlock, $inserted);
if (! empty($blocks['response_fields'])) {
$updatedBlock = $this->refreshResponseFieldMarker($updatedBlock, $blocks['response_fields'], $inserted);
}
$updatedBlock = $this->applyExampleBlock($updatedBlock, '**요청 예시**', '**응답 필드**', $blocks['request'], $inserted);
if ($proseHasResponseExample) {
// 서술 영역에 수기 응답 예시가 있으면 블록 안 응답 예시는 두지 않는다(중복 헤딩 회피).
// 과거 run 이 삽입한 블록 내 응답 예시가 남아 있으면 제거한다.
$updatedBlock = $this->removeExampleBlock($updatedBlock, '**응답 예시**', '**에러 응답**', $inserted);
} else {
$updatedBlock = $this->applyExampleBlock($updatedBlock, '**응답 예시**', '**에러 응답**', $blocks['response'], $inserted);
}
if ($updatedBlock !== $block) {
$content = substr($content, 0, $startPos).$updatedBlock.substr($content, $endPos);
}
}
return [$content, $inserted];
}
/**
* 엔드포인트의 사람 서술 영역에 이미 사람이 작성한 `**응답 예시**` 가 있는지 확인합니다.
*
* 서술 영역 = `@generated:end` 뒤부터 다음 `### ` 헤딩(또는 문서 끝) 전까지. 여기에
* `**응답 예시**` 가 있으면 블록 안에 응답 예시를 삽입하지 않아 중복 헤딩을 방지한다.
* 파일 업로드(바이너리)·미설치 정적 예시처럼 실측 불가라 사람이 미리 채운 응답 예시가 대상.
*
* @param string $content 전체 문서 내용
* @param int $endPos 이 엔드포인트 `@generated:end` 마커의 시작 위치
* @return bool 서술 영역에 수기 응답 예시가 있으면 true
*/
private function proseHasResponseExample(string $content, int $endPos): bool
{
$afterGen = substr($content, $endPos + strlen(self::GEN_END));
// 다음 ### 헤딩 전까지가 이 엔드포인트의 사람 서술 영역
$nextHeading = preg_match('/\n### /', $afterGen, $m, PREG_OFFSET_CAPTURE)
? $m[0][1]
: strlen($afterGen);
$prose = substr($afterGen, 0, $nextHeading);
return Str::contains($prose, '**응답 예시**');
}
/**
* 요청 파라미터 표에서 path 행과 이름이 겹치는 query 행(자동 생성 중복)을 제거합니다.
*
* 라우트 바인딩 세그먼트가 FormRequest rules() 에도 존재하면 같은 이름이 `| 이름 | path |` 과
* `| 이름 | query |` 로 2회 출력되던 결함을 in-place 로 정정한다. path 행을 남기고 query 행만 제거.
*
* @param string $block `@generated` 블록 문자열
* @param int $inserted 정정 카운터 (참조 누적)
* @return string 정정된 블록
*/
private function dedupeParamRows(string $block, int &$inserted): string
{
$lines = explode("\n", $block);
// path 행의 파라미터명 수집 (| name | path | ...)
$pathNames = [];
foreach ($lines as $line) {
if (preg_match('/^\|\s*([a-zA-Z_0-9]+)\s*\|\s*path\s*\|/', $line, $m)) {
$pathNames[$m[1]] = true;
}
}
if ($pathNames === []) {
return $block;
}
$out = [];
$removed = false;
foreach ($lines as $line) {
// 같은 이름의 query 행이면 제거 (path 행이 우선).
if (preg_match('/^\|\s*([a-zA-Z_0-9]+)\s*\|\s*query\s*\|/', $line, $m) && isset($pathNames[$m[1]])) {
$removed = true;
continue;
}
$out[] = $line;
}
if ($removed) {
$inserted++;
}
return implode("\n", $out);
}
/**
* 응답 필드 표가 실측 제외 마커일 때만 실측 필드 표로 치환합니다.
*
* `**응답 필드** (\`data\` 내부)` 헤딩 ~ 다음 헤딩(`**응답 예시**`/`**에러 응답**`) 사이 본문이
* `<!-- 실측 제외: ... -->` 마커면 실측 표로 교체하고, 이미 표(`| 필드 | 타입 |`)가 있으면
* 사람 서술 셀 보존을 위해 건드리지 않는다.
*
* @param string $block `@generated` 블록 문자열
* @param string $fieldTable 실측 응답 필드 표
* @param int $inserted 치환 카운터 (참조 누적)
* @return string 갱신된 블록
*/
private function refreshResponseFieldMarker(string $block, string $fieldTable, int &$inserted): string
{
$label = '**응답 필드** (`data` 내부)';
$labelPos = strpos($block, $label);
if ($labelPos === false) {
return $block;
}
$bodyStart = $labelPos + strlen($label);
// 다음 헤딩(응답 예시 또는 에러 응답) 전까지가 응답 필드 본문
$nextRespExample = strpos($block, '**응답 예시**', $bodyStart);
$nextError = strpos($block, '**에러 응답**', $bodyStart);
$candidates = array_filter([$nextRespExample, $nextError], fn ($v) => $v !== false);
if ($candidates === []) {
return $block;
}
$bodyEnd = min($candidates);
$body = substr($block, $bodyStart, $bodyEnd - $bodyStart);
// 사람이 채운 표(| 필드 | 타입 |)가 이미 있으면 보존 — 마커일 때만 교체.
if (! str_contains($body, '실측 제외')) {
return $block;
}
$inserted++;
return substr($block, 0, $bodyStart)."\n\n".$fieldTable."\n\n".substr($block, $bodyEnd);
}
/**
* `@generated` 블록 문자열에 예시 블록 하나를 삽입하거나 치환합니다.
*
* `$label`(예: `**요청 예시**`) 섹션이 이미 있으면 그 본문을 `$body` 로 치환하고,
* 없으면 `$anchor`(예: `**응답 필드**`) 헤딩 바로 앞에 `label + body` 를 삽입합니다.
*
* @param string $block `@generated` 블록 문자열
* @param string $label 예시 섹션 헤딩 (`**요청 예시**` / `**응답 예시**`)
* @param string $anchor 삽입 기준 헤딩 (`**응답 필드**` / `**에러 응답**`)
* @param string $body 예시 블록 본문 (코드블록 또는 실측 제외 마커)
* @param int $inserted 삽입/치환 카운터 (참조 누적)
* @return string 갱신된 블록 문자열
*/
private function applyExampleBlock(string $block, string $label, string $anchor, string $body, int &$inserted): string
{
$section = $label."\n\n".$body."\n\n";
$labelLine = $label."\n";
// 이미 예시 섹션이 있으면 그 label ~ (다음 빈 줄 + 다음 헤딩) 까지를 새 섹션으로 치환한다.
$labelPos = strpos($block, $labelLine);
if ($labelPos !== false) {
$anchorPos = strpos($block, $anchor, $labelPos);
if ($anchorPos === false) {
return $block;
}
$replaced = substr($block, 0, $labelPos).$section.substr($block, $anchorPos);
if ($replaced !== $block) {
$inserted++;
}
return $replaced;
}
// 없으면 anchor 헤딩 바로 앞에 삽입.
$anchorPos = strpos($block, $anchor);
if ($anchorPos === false) {
return $block;
}
$inserted++;
return substr($block, 0, $anchorPos).$section.substr($block, $anchorPos);
}
/**
* `@generated` 블록 문자열에서 예시 섹션 하나를 제거합니다.
*
* 서술 영역에 수기 예시가 있어 블록 안 예시가 중복이 되는 경우, `$label` 섹션
* (`$label` ~ `$anchor` 직전)을 제거한다. 섹션이 없으면 그대로 반환한다.
*
* @param string $block `@generated` 블록 문자열
* @param string $label 제거할 예시 섹션 헤딩 (`**응답 예시**`)
* @param string $anchor 섹션 끝 기준 헤딩 (`**에러 응답**`)
* @param int $inserted 변경 카운터 (참조 누적)
* @return string 갱신된 블록 문자열
*/
private function removeExampleBlock(string $block, string $label, string $anchor, int &$inserted): string
{
$labelLine = $label."\n";
$labelPos = strpos($block, $labelLine);
if ($labelPos === false) {
return $block;
}
$anchorPos = strpos($block, $anchor, $labelPos);
if ($anchorPos === false) {
return $block;
}
$removed = substr($block, 0, $labelPos).substr($block, $anchorPos);
if ($removed !== $block) {
$inserted++;
}
return $removed;
}
}
+80 -7
View File
@@ -3,6 +3,9 @@
namespace App\Support\ApiDoc;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken;
@@ -31,6 +34,11 @@ class ApiEndpointProbe
*/
private string $tokenName = 'api-docgen-probe';
/**
* @var User|null 실측 인증 사용자 (in-process 쓰기 실측 시 로그인 대상)
*/
private ?User $user = null;
/**
* @param string|null $baseUrl 기준 URL (null 이면 .env 의 APP_URL 직접 사용)
*/
@@ -71,6 +79,7 @@ class ApiEndpointProbe
return false;
}
$this->user = $user;
$this->cleanupTokens();
$this->token = $user->createToken($this->tokenName)->plainTextToken;
@@ -78,21 +87,21 @@ class ApiEndpointProbe
}
/**
* GET 엔드포인트를 실호출하여 응답을 관측합니다.
* 엔드포인트를 실호출하여 응답을 관측합니다.
*
* GET/HEAD 는 외부 HTTP 로 read-only 실측하고, 쓰기 메서드(POST/PUT/PATCH/DELETE)는
* **DB 트랜잭션 안에서 in-process 로 dispatch 한 뒤 롤백**하여(영속 안 함) 응답 shape 만
* 관측한다. 외부 HTTP 는 별도 프로세스라 롤백이 불가하므로 쓰기는 in-process 경로를 쓴다.
*
* @param string $method HTTP 메서드
* @param string $uri 라우트 URI (path 파라미터 치환 완료된 실제 경로)
* @param array<string, mixed> $body 쓰기 요청 바디 (쓰기 메서드에서만 사용)
* @return array{ok: bool, status: int|null, body: array<string, mixed>|null, skipped_reason: string|null}
*/
public function probe(string $method, string $uri): array
public function probe(string $method, string $uri, array $body = []): array
{
$method = strtoupper($method);
// 쓰기 메서드는 부수효과 위험으로 실호출하지 않는다 (정적 문서화로 대체).
if (! in_array($method, ['GET', 'HEAD'], true)) {
return ['ok' => false, 'status' => null, 'body' => null, 'skipped_reason' => 'write-method'];
}
// path 파라미터가 남아 있으면(치환 실패) 실호출 불가.
if (Str::contains($uri, '{')) {
return ['ok' => false, 'status' => null, 'body' => null, 'skipped_reason' => 'unresolved-path-param'];
@@ -102,6 +111,22 @@ class ApiEndpointProbe
return ['ok' => false, 'status' => null, 'body' => null, 'skipped_reason' => 'no-token'];
}
if (in_array($method, ['GET', 'HEAD'], true)) {
return $this->probeRead($method, $uri);
}
return $this->probeWrite($method, $uri, $body);
}
/**
* GET/HEAD 를 외부 HTTP 로 read-only 실측합니다.
*
* @param string $method HTTP 메서드
* @param string $uri 치환된 URI
* @return array{ok: bool, status: int|null, body: array<string, mixed>|null, skipped_reason: string|null}
*/
private function probeRead(string $method, string $uri): array
{
try {
$response = Http::withoutVerifying()
->withToken($this->token)
@@ -122,6 +147,54 @@ class ApiEndpointProbe
}
}
/**
* 쓰기 메서드를 DB 트랜잭션 안에서 in-process dispatch 후 롤백하여 실측합니다.
*
* 응답 shape 만 관측하고 부수효과(레코드 생성/수정/삭제)는 롤백으로 영속시키지 않는다.
* 인증은 샘플 사용자로 로그인(guard)해 수행하며, DELETE 등 바디 없는 메서드도 처리한다.
*
* @param string $method HTTP 메서드
* @param string $uri 치환된 URI
* @param array<string, mixed> $body 요청 바디
* @return array{ok: bool, status: int|null, body: array<string, mixed>|null, skipped_reason: string|null}
*/
private function probeWrite(string $method, string $uri, array $body): array
{
if (! $this->user) {
return ['ok' => false, 'status' => null, 'body' => null, 'skipped_reason' => 'no-token'];
}
DB::beginTransaction();
try {
// in-process dispatch 는 실제 Bearer 토큰 헤더로 인증한다(guard 종류 무관).
// Auth::login() 은 API 기본 guard 가 RequestGuard(토큰 전용)라 사용할 수 없다.
$request = Request::create($uri, $method, $body);
$request->headers->set('Accept', 'application/json');
$request->headers->set('Authorization', 'Bearer '.$this->token);
$response = app()->handle($request);
$content = $response->getContent();
$json = json_decode(is_string($content) ? $content : '', true);
$status = $response->getStatusCode();
return [
'ok' => $status >= 200 && $status < 300 && is_array($json),
'status' => $status,
'body' => is_array($json) ? $json : null,
'skipped_reason' => (is_array($json) && $status >= 200 && $status < 300) ? null : 'http-'.$status,
];
} catch (\Throwable $e) {
return ['ok' => false, 'status' => null, 'body' => null, 'skipped_reason' => 'write-probe-failed: '.$e->getMessage()];
} finally {
// 쓰기 부수효과를 영속시키지 않는다 (문서 생성 목적).
DB::rollBack();
// 다음 실측에 인증 컨텍스트가 새지 않도록 해석된 guard 를 초기화한다
// (API guard 는 RequestGuard 라 logout() 이 없으므로 forgetGuards 사용).
Auth::forgetGuards();
}
}
/**
* 발급한 임시 토큰을 정리합니다.
*/
+12 -12
View File
@@ -143,13 +143,6 @@ class ApiRouteInventory
$urlPrefix = "api/{$dir}/{$id}";
$namePrefix = "api.{$dir}.{$id}.";
// 등록 전 스냅샷 — 이미 등록된(활성) 라우트와 이번 로드분을 구분한다.
$existing = [];
foreach (RouteFacade::getRoutes() as $route) {
/** @var Route $route */
$existing[spl_object_id($route)] = true;
}
try {
RouteFacade::prefix($urlPrefix)
->name($namePrefix)
@@ -160,14 +153,15 @@ class ApiRouteInventory
return [];
}
// 이 확장 소유로 확정된 라우트를 전역 라우트 테이블에서 prefix(uri/name) 로 직접 골라낸다.
// object-id 스냅샷 방식은 refreshNameLookups() 가 RouteCollection 을 재색인하며 객체를
// 다시 만들어 dedup 이 어긋나므로(활성 확장 로드 순서·중복 로드에 취약) 쓰지 않는다.
// 대상 확장은 uri 가 `api/{dir}/{id}/`, name 이 `api.{dir}.{id}.` 로 시작해 결정적으로 식별된다.
$seen = [];
$routes = [];
foreach (RouteFacade::getRoutes() as $route) {
/** @var Route $route */
if (isset($existing[spl_object_id($route)])) {
continue;
}
$uri = $route->uri();
if (! Str::startsWith($uri, 'api/')) {
@@ -177,12 +171,18 @@ class ApiRouteInventory
$name = $route->getName() ?? '';
$owner = $this->resolveOwner($name, $uri);
// 폴백 대상 확장 소유로 확정되지 않으면(코어 등) 제외
// 폴백 대상 확장 소유로 확정되지 않으면(코어·타 확장) 제외
if ($owner['key'] !== $scope) {
continue;
}
foreach ($this->normalizeRoute($route, $uri, $name, $owner) as $entry) {
// 파일이 이미 한 번 로드돼 있으면 같은 (method, uri) 가 중복될 수 있어 dedup 한다.
$dedupKey = $entry['method'].' '.$entry['uri'];
if (isset($seen[$dedupKey])) {
continue;
}
$seen[$dedupKey] = true;
$routes[] = $entry;
}
}
@@ -103,6 +103,38 @@ class ResourceFieldDescriber
'origin' => '출처 (설치/등록 원천 구분 값)',
'target_name' => '대상 확장의 표시 이름 (scope+target_identifier 로 해석)',
'install_blocked_reason' => '설치가 차단된 사유 (차단 없으면 null)',
// 감사/관계 공통 (도메인 무관하게 역할 고정 — 쓰기 응답에서 흔히 등장)
'updated_by' => '최종 수정한 사용자 정보 (uuid/name — updated_by 관계 파생, 없으면 null)',
'creator' => '생성자 정보 객체 (uuid/name/email — creator 관계 파생)',
'user' => '대상 사용자 정보 객체 (uuid/name/email 등 — user 관계 파생)',
'roles' => '보유 역할 목록 (각 원소 id/name/permissions — roles 관계 파생)',
'permissions' => '권한 목록 (각 원소 identifier/name — permissions 관계 파생)',
'avatar' => '아바타 이미지 URL (미등록 시 null)',
// 인증/토큰 (로그인/토큰 발급 응답)
'token' => '발급된 API 접근 토큰 평문 (Bearer 토큰으로 사용, 발급 시 1회만 노출)',
'token_type' => '토큰 타입 (일반적으로 Bearer)',
'trigger_type' => '동작을 유발한 방식/주체 구분 값',
// 스케줄/실행 이력 (Schedule 실행 결과)
'last_duration' => '마지막 실행 소요 시간 (초/밀리초 — 실행 이력 파생)',
'duration' => '실행 소요 시간 (초/밀리초)',
'exit_code' => '실행 종료 코드 (0=성공, 그 외=실패)',
'output' => '실행 표준 출력 내용',
'error_output' => '실행 표준 에러 출력 내용 (없으면 빈 문자열/null)',
'memory_usage' => '실행 중 최대 메모리 사용량 (바이트)',
// 캐시/파일/집계 (일회성 동작 응답)
'ttl' => '캐시 유효 시간 (Time To Live, 초)',
'size_bytes' => '크기 (바이트)',
'older_than_days' => '기준 경과 일수 (이 일수보다 오래된 대상 필터/집계)',
'entries' => '항목 목록 (각 원소는 대상 도메인 레코드)',
'changelog_entries' => '변경 이력 항목 목록 (버전별 변경 내용)',
'templates' => '템플릿 목록 (각 원소 identifier/name 등 — 템플릿 관계 파생)',
'spec' => '스펙 정의 객체 (편집기/컴포넌트 선언 스키마 등)',
'source_meta' => '원천 메타데이터 객체 (출처·경로·해석 정보)',
'validation_summary' => '검증 결과 요약 객체 (통과/실패 건수 등)',
];
/**
@@ -123,6 +155,13 @@ class ResourceFieldDescriber
: null;
}
// status 는 도메인마다 허용값 집합이 다르나(active/blocked, success/failed 등) 역할은
// 공통이다. 문자열 상태값이면 일반 설명을 주고, 구체 허용값은 status_label/변형 필드와
// 각 도메인 문서에서 보강한다. (동반 필드 status_label/status_variant 는 EXACT 로 설명됨)
if ($field === 'status' && in_array($type, ['string', 'integer'], true)) {
return '상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조)';
}
if (isset(self::EXACT[$field])) {
return self::EXACT[$field];
}
+1 -1
View File
@@ -23,7 +23,7 @@
| [activity-log-hooks.md](activity-log-hooks.md) | 활동 로그 훅 레퍼런스 (Activity Log Hooks Reference) | 코어 66훅 + 이커머스 92훅 + 게시판 32훅 + 페이지 8훅 = 총 198훅 |
| [activity-log.md](activity-log.md) | 활동 로그 시스템 (Activity Log System) | Monolog 기반: Service 훅 → Listener → Log::channel... |
| [admin-settings-access.md](admin-settings-access.md) | Admin 환경설정 값 접근 (`g7_core_settings` vs `config()`) | 동기화 SSoT: storage/app/settings/*.json → Setting... |
| [api-documentation.md](api-documentation.md) | API 레퍼런스 문서 규정 (API Documentation) | 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 전... |
| [api-documentation.md](api-documentation.md) | API 레퍼런스 문서 규정 (API Documentation) | 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 +... |
| [api-resources.md](api-resources.md) | API 리소스 | Resource: BaseApiResource 상속 필수 / Collection: B... |
| [authentication.md](authentication.md) | 인증 및 세션 처리 | Laravel Sanctum 토큰 전용 인증 (Bearer 토큰만 사용) |
| [broadcasting.md](broadcasting.md) | Broadcasting (실시간 이벤트) | Laravel Reverb 사용 (WebSocket) |
+97 -14
View File
@@ -7,7 +7,7 @@
## TL;DR (5초 요약)
```text
1. 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 전수 기재
1. 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 + 요청·응답 예시 전수 기재
2. 위치: 코어 = docs/backend/api/, 확장 = {modules|plugins}/_bundled/{id}/docs/api/
3. 생성: php artisan api:docgen — 코드에서 추출한 스캐폴딩 + 사람이 서술 보강 (순수 수기 금지)
4. 추출 불가분(훅 주입 파라미터·동적 응답)은 <!-- TODO --> 마커 남기고 사람이 채움
@@ -55,14 +55,48 @@ G7 의 REST API 는 라우트 `->name()` 규약은 있으나 엔드포인트별
## 표준 문서 포맷
엔드포인트 1개당 아래 4개 구성(헤더 · 요청 파라미터 · 응답 필드 · 에러 응답)을 따른다.
`<!-- @generated:start -->` ~ `<!-- @generated:end -->` 사이는 `api:docgen` 이 재생성하는 추출 블록이며,
그 바깥의 사람 서술은 재생성 시 보존된다.
엔드포인트 1개당 아래 6개 구성(헤더 · 요청 파라미터 · 요청 예시 · 응답 필드 · 응답 예시 · 에러 응답)을
따른다. `<!-- @generated:start -->` ~ `<!-- @generated:end -->` 사이는 `api:docgen` 이 재생성하는 추출
블록이며, 그 바깥의 사람 서술(`**설명**`)은 재생성 시 보존된다.
에러 응답 표는 라우트 메타에서 대표 상태코드를 자동 추론한다: 인증 필수(`auth:sanctum`)→401,
`admin`/`permission:` 요구→403, FormRequest 검증 규칙 존재→422, path 파라미터 존재→404.
`optional.sanctum`(선택 인증)은 401 을 유발하지 않는다. 도메인 특이 에러(409·429 등)는 사람이 보강한다.
**요청 예시**(raw HTTP 요청)와 **응답 예시**(envelope 전문 JSON)는 실제 호출을 재현할 수 있도록 실측 기반으로
방출된다(단계 6). 요청 예시는 curl 이 아니라 raw HTTP 요청(요청 라인 + 헤더)으로 표기해 응답 예시의
`HTTP/1.1 {status}` 상태줄과 대칭을 이룬다. 세부 규칙:
- **요청 예시**: 요청 라인(`{METHOD} {path} HTTP/1.1`) + `Host:` + `Accept: application/json`, 인증 필요 시
`Authorization: Bearer {YOUR_TOKEN}`(실측 토큰 평문 유출 방지 마스킹). `Host` 는 실측 기준 URL(로컬 개발
호스트 등)을 노출하지 않고 공개 placeholder(`api.example.com`, RFC 2606 예약 도메인)로 마스킹한다.
- **바디 메서드(POST/PUT/PATCH)**: `Content-Type: application/json` + 빈 줄 뒤 JSON 바디. 바디는 필수만이
아니라 **전체 파라미터**를 담고 값은 이름·타입 기반 현실적 예시값으로 채운다(`"string"` placeholder 남발
금지). 허용값 `in:` 열거가 있으면 그 첫 값을 채택한다.
- **파일 업로드**(요청 파라미터 타입이 `image`/`file`): `application/json` 이 아니라
`Content-Type: multipart/form-data; boundary=...` 로 표기하고 각 파일 파트를 `filename=`+`Content-Type`
으로 나타낸다(JSON 으로는 파일 전송 불가).
- **GET/DELETE**: query 파라미터는 URL 쿼리스트링(`?a=..&b=..`)으로 반영한다(바디 아님).
- `optional.sanctum`(선택 인증) 엔드포인트는 Authorization 헤더에 "비회원은 생략 가능" 주석을 붙인다.
- **응답 예시**: `HTTP/1.1 {status}` 상태줄 + 실측 응답 body 전문(`{success, data, message, error}` envelope).
목록 응답의 `data.data[]` 는 대표 **2항목**으로 절단하고 나머지는 `... (총 N건 중 2건 표시)` 항목으로 대체한다.
응답에 섞여 나온 **민감값(토큰·비밀번호·시크릿·API 키)은 `{MASKED}` 로 마스킹**해 방출한다.
- **쓰기 메서드 실측**: GET/HEAD 는 외부 HTTP 로 read-only 실측하고, 쓰기(POST/PUT/PATCH/DELETE)는 **DB 트랜잭션
안에서 in-process dispatch 후 롤백**하여 응답 shape 만 관측한다(부수효과 미영속). 단, 부수효과가 롤백으로
되돌릴 수 없는 쓰기(확장 install/activate/update, 언어팩 설치, 코어 업데이트, 파일 업로드, 캐시/워밍업/생성
등 파일시스템·프로세스·외부 네트워크 접촉)는 실측에서 제외(`side-effectful-write`)하고 요청 예시만 정적 방출한다.
- **실측 제외**(부수효과 쓰기·바이너리·미치환 path 파라미터 등) 엔드포인트: 요청 예시는 파라미터 표 기반으로 정적
조립(raw HTTP 요청 골격), 응답 예시는 `<!-- 실측 제외: {사유} — 응답 예시는 사람이 작성하세요. -->` 마커로 남긴다.
- 두 예시 블록은 전량 `@generated` 블록 **내부**에 있다. 다만 이미 파라미터/응답 필드 표의 사람 서술이
채워진 문서에 예시를 추가할 때는 전체 재생성(`api:docgen`)을 쓰지 않는다 — 전체 재생성은 표를 통째로
다시 조립해 정적 추출로 재현 불가능한 도메인 서술 셀을 TODO 로 되돌린다. 대신 `api:docgen --examples-only`
로 표·서술을 건드리지 않고 예시 2블록만 in-place 삽입/치환한다(측정은 재수행하되 표는 불가침). 백필
커맨드(`backfill-params`/`backfill-fields`)도 예시 블록을 건드리지 않는다.
- **`--examples-only` 모드**: 기존 문서의 각 엔드포인트 `@generated` 블록에 요청 예시(응답 필드 표 앞)와
응답 예시(에러 응답 표 앞)만 삽입한다. 이미 예시가 있으면 그 블록만 새 실측으로 치환(멱등, 실측 상대시간
필드는 재측정으로 값이 달라질 수 있음). 표·필드 설명·엔드포인트 서술은 전량 보존된다. 최초 스캐폴딩이
없는 신규 문서는 먼저 `api:docgen` 으로 생성한 뒤 서술을 채우고, 이후 예시는 `--examples-only` 로 유지한다.
```markdown
### GET /api/admin/users
<!-- @generated:start:api.admin.users.index -->
@@ -79,12 +113,43 @@ G7 의 REST API 는 라우트 `->name()` 규약은 있으나 엔드포인트별
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있음 (`core.user.search_validation_rules`).
**요청 예시**
​```http
GET /api/admin/users HTTP/1.1
Host: example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
​```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 용도/설명 |
|------|------|-----------|
| id | integer | <!-- TODO: 설명 --> |
| uuid | string | <!-- TODO: 설명 --> |
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
|------|------|-------------|-----------|
| id | integer | `1` | <!-- TODO: 설명 --> |
| uuid | string | `a231747f-...` | <!-- TODO: 설명 --> |
**응답 예시**
​```http
HTTP/1.1 200
​```
​```json
{
"success": true,
"data": {
"data": [
{ "id": 1, "uuid": "a231747f-..." },
{ "id": 2, "uuid": "b0f2..." },
"... (총 42건 중 2건 표시)"
],
"pagination": { "current_page": 1, "total": 42 }
},
"message": null,
"error": null
}
​```
**에러 응답**
@@ -96,12 +161,6 @@ G7 의 REST API 는 라우트 `->name()` 규약은 있으나 엔드포인트별
<!-- @generated:end -->
**설명** <!-- 사람이 작성: 이 엔드포인트의 용도, 주의사항, 예시 시나리오 -->
**응답 예시**
​```json
{ "success": true, "data": { }, "message": null, "error": null }
​```
```
### 응답 envelope 표준
@@ -141,6 +200,9 @@ php artisan api:docgen --scope=all
# 생성 없이 누락/drift 만 리포트 (하네스가 소비)
php artisan api:docgen --check
# 이미 서술이 채워진 문서에 요청/응답 예시 블록만 in-place 삽입 (표·서술 불가침, 재생성 금지)
php artisan api:docgen --scope=module:sirsoft-board --seed --base-url=https://example.dev --examples-only
# 생성될 대상만 미리보기
php artisan api:docgen --scope=core --dry-run
```
@@ -189,6 +251,25 @@ php artisan api:docgen --scope=core --dry-run
- 확장에 새 PHP 클래스를 추가했으므로 `_bundled` 작업 후 `{type}:update {id} --force` 로 활성 디렉토리에
반영해야 오토로드된다.
### 설명 백필 커맨드 (`api:docgen-backfill-*`)
파라미터/응답 필드 표의 `<!-- TODO: 설명 -->` 셀 중 **도메인 무관 공통 필드**(페이지네이션·정렬·검색·
식별자·기간·토글·공통 파생 필드 등)는 전체 재생성 없이 in-place 로 자동 서술한다. 전체 재생성
(`api:docgen`)은 `@generated` 블록 표를 통째로 재조립해 실측 예시값·사람 서술 셀을 되돌리므로, TODO
셀만 치환하는 별도 백필 커맨드를 쓴다(멱등 — 채워진 셀·실측 예시값 불가침).
```bash
# 요청 파라미터 표 TODO 셀 백필 (SSoT: App\Support\ApiDoc\ParameterDescriber)
php artisan api:docgen-backfill-params
# 응답 필드 표 TODO 셀 백필 (SSoT: App\Support\ApiDoc\ResourceFieldDescriber)
php artisan api:docgen-backfill-fields
```
- 도메인 종속 필드(`status`/`type`/`code`/`category`·훅 주입·조건부 필드)는 값 의미가 도메인마다 달라
자동 채우지 않고 TODO 로 남겨 사람이 컨트롤러/FormRequest/Resource 근거로 서술한다.
- 새 공통 필드 규칙을 Describer 사전에 추가한 뒤 백필을 재실행하면 그 필드가 전 문서에서 자동 채워진다.
---
## 문서 갱신 의무
@@ -212,7 +293,9 @@ php artisan api:docgen --scope=core --dry-run
```text
□ 엔드포인트가 대응 위치(코어 docs/backend/api/ 또는 확장 docs/api/)에 문서화되었는가?
□ 요청 파라미터 표에 위치/타입/필수/허용값/용도가 모두 기재되었는가?
□ 요청 예시(raw HTTP 요청) 블록이 방출되었는가? (curl 금지, 인증 필요 시 Bearer {YOUR_TOKEN} 마스킹)
□ 응답 필드 표가 envelope 의 data 내부 기준으로 작성되었는가?
□ 응답 예시(envelope 전문 JSON) 블록이 방출되었는가? (목록은 2항목 절단)
□ 훅 주입 파라미터가 있으면 주석 + 사람 보강이 되었는가?
□ TODO 마커가 모두 채워졌는가?
□ api:docgen --check 가 drift 0 인가?
+149 -1
View File
@@ -42,6 +42,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.activity_log.index_validation_rules`).
**요청 예시**
```http
GET /api/admin/activity-logs?log_type=%EC%98%88%EC%8B%9C%EA%B0%92&action=%EC%98%88%EC%8B%9C%EA%B0%92&user_id=1&loggable_type=%EC%98%88%EC%8B%9C%EA%B0%92&search=%EC%98%88%EC%8B%9C%EA%B0%92&search_type=%EC%98%88%EC%8B%9C%EA%B0%92&created_by=%EC%98%88%EC%8B%9C%EA%B0%92&date_from=2026-01-01&date_to=2026-01-01&per_page=1&sort_by=%EC%98%88%EC%8B%9C%EA%B0%92&sort_order=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -70,6 +79,100 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_read":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "활동 로그 정보를 성공적으로 가져왔습니다.",
"data": {
"data": [
{
"number": 22,
"id": 22,
"log_type": "admin",
"log_type_label": "관리자",
"loggable_type": null,
"loggable_type_display": null,
"loggable_id": null,
"action": "user.index",
"action_label": "목록 조회",
"localized_description": "사용자 목록 조회",
"description_key": "activity_log.description.user_index",
"properties": {
"result_count": 2
},
"changes": null,
"bulk_changes": null,
"has_changes": false,
"actor_name": "API 문서 샘플 사용자",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"ip_address": "127.0.0.1",
"created_at": "2026-07-08 12:12:12",
"is_owner": true,
"abilities": {
"can_read": true,
"can_delete": true
}
},
{
"number": 21,
"id": 21,
"log_type": "admin",
"log_type_label": "관리자",
"loggable_type": null,
"loggable_type_display": null,
"loggable_id": null,
"action": "user.index",
"action_label": "목록 조회",
"localized_description": "사용자 목록 조회",
"description_key": "activity_log.description.user_index",
"properties": {
"result_count": 2
},
"changes": null,
"bulk_changes": null,
"has_changes": false,
"actor_name": "API 문서 샘플 사용자",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"ip_address": "127.0.0.1",
"created_at": "2026-07-08 12:08:26",
"is_owner": true,
"abilities": {
"can_read": true,
"can_delete": true
}
},
"... (총 22건 중 2건 표시)"
],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 22,
"from": 1,
"to": 22,
"has_more_pages": false
},
"abilities": {
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -95,10 +198,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
**요청 예시**
```http
POST /api/admin/activity-logs/bulk-delete HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -124,9 +247,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| activityLog | path | string | 예 | — | 대상 activity log의 식별자 |
**요청 예시**
```http
DELETE /api/admin/activity-logs/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "활동 로그가 삭제되었습니다.",
"data": null
}
```
**에러 응답**
+12
View File
@@ -29,10 +29,22 @@
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/attachment/{hash} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+99
View File
@@ -36,10 +36,51 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.attachment.upload_validation_rules`).
**요청 예시**
```http
POST /api/admin/attachments HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary
Content-Disposition: form-data; name="attachmentable_type"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="attachmentable_id"
1
------G7ExampleBoundary
Content-Disposition: form-data; name="collection"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="source_type"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="source_identifier"
example-key
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -72,10 +113,35 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.attachment.upload_batch_validation_rules`).
**요청 예시**
```http
POST /api/admin/attachments/batch HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"files": [
"예시값"
],
"attachmentable_type": "예시값",
"attachmentable_id": 1,
"collection": "예시값",
"source_type": "예시값",
"source_identifier": "example-key"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -103,10 +169,30 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.attachment.reorder_validation_rules`).
**요청 예시**
```http
PATCH /api/admin/attachments/reorder HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"order": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -132,10 +218,23 @@
| --- | --- | --- | --- | --- | --- |
| attachment | path | string | 예 | — | 대상 attachment의 식별자 |
**요청 예시**
```http
DELETE /api/admin/attachments/{attachment} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+2429 -3
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -27,10 +27,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/me/avatar HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -56,10 +69,31 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.upload_avatar_rules`).
**요청 예시**
```http
POST /api/me/avatar HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="avatar"; filename="example.png"
Content-Type: image/png
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+13
View File
@@ -26,10 +26,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/broadcasting/auth HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-403 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+217
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/dashboard/activities HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -39,6 +48,66 @@ _요청 파라미터 없음._
| time | string | `4시간 전` | 상대 시각 표시 (예: "24초 전" — diffForHumans() 산물) |
| timestamp | string | `2026-07-07T10:00:47+09:00` | 활동 발생 절대 시각 (created_at 을 사용자 타임존으로 변환한 ISO 8601) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "최근 활동을 성공적으로 조회했습니다.",
"data": [
{
"type": "admin",
"icon": "circle-info",
"icon_color": "blue",
"title": "사용자 목록 조회",
"description": "API 문서 샘플 사용자",
"time": "2분 전",
"timestamp": "2026-07-08T12:12:12+09:00"
},
{
"type": "admin",
"icon": "circle-info",
"icon_color": "blue",
"title": "사용자 목록 조회",
"description": "API 문서 샘플 사용자",
"time": "6분 전",
"timestamp": "2026-07-08T12:08:26+09:00"
},
{
"type": "admin",
"icon": "circle-info",
"icon_color": "blue",
"title": "사용자 목록 조회",
"description": "API 문서 샘플 사용자",
"time": "9분 전",
"timestamp": "2026-07-08T12:04:38+09:00"
},
{
"type": "admin",
"icon": "circle-info",
"icon_color": "blue",
"title": "사용자 목록 조회",
"description": "API 문서 샘플 사용자",
"time": "43분 전",
"timestamp": "2026-07-08T11:31:03+09:00"
},
{
"type": "admin",
"icon": "circle-info",
"icon_color": "blue",
"title": "사용자 목록 조회",
"description": "API 문서 샘플 사용자",
"time": "45분 전",
"timestamp": "2026-07-08T11:29:30+09:00"
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -61,12 +130,35 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/dashboard/alerts HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "시스템 알림을 성공적으로 조회했습니다.",
"data": []
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -89,6 +181,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/dashboard/recent-notifications HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -102,6 +203,31 @@ _요청 파라미터 없음._
| time | string | `19시간 전` | 상대 시각 표시 (예: "24초 전" — diffForHumans() 산물) |
| timestamp | string | `2026-07-06T18:20:23+09:00` | 발송 절대 시각 (sent_at, 없으면 created_at 을 사용자 타임존으로 변환한 ISO 8601) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "최근 알림을 성공적으로 조회했습니다.",
"data": [
{
"id": 1,
"type": "apidoc.sample.event",
"channel": "mail",
"recipient": "API 문서 샘플 사용자",
"subject": "API 문서 샘플 알림",
"status": "sent",
"time": "2시간 전",
"timestamp": "2026-07-08T09:41:24+09:00"
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -124,6 +250,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/dashboard/resources HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -134,6 +269,37 @@ _단건 응답: `data` 객체의 필드._
| memory | object | `{"percentage":96,"used":"30.1 GB","total":"31.5 GB","colo…` | 메모리 사용량 정보 (percentage 사용률, used/total: 사용량·총량 형식화 문자열, color: 임계 색상). 수집 불가 시 percentage 0·"알 수 없음"·color gray 폴백 |
| disk | object | `{"percentage":76,"used":"360.2 GB","total":"474.7 GB","co…` | 디스크 사용량 정보 (percentage 사용률, used/total: 사용량·총량 형식화 문자열, color: 임계 색상). 수집 불가 시 percentage 0·"알 수 없음"·color gray 폴백 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "시스템 리소스 정보를 성공적으로 조회했습니다.",
"data": {
"cpu": {
"percentage": 52,
"color": "blue"
},
"memory": {
"percentage": 86,
"used": "27 GB",
"total": "31.5 GB",
"color": "yellow"
},
"disk": {
"percentage": 76,
"used": "362.6 GB",
"total": "474.7 GB",
"color": "yellow"
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -156,6 +322,15 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/dashboard/stats HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -169,6 +344,48 @@ _단건 응답: `data` 객체의 필드._
| language_packs | object | `{"total":20,"active":16}` | 언어팩 집계 객체 (active: 현재 활성 언어팩 수, total: 활성 + 미설치 번들 팩 수) |
| system_status | object | `{"status":"normal","label":"정상","all_services_running":true}` | 시스템 상태 객체 (status: normal 정상 / warning 경고, label: 상태 다국어 라벨, all_services_running: 전체 서비스 정상 동작 여부) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "대시보드 통계를 성공적으로 조회했습니다.",
"data": {
"total_users": {
"count": 2,
"change_percent": 0,
"change_display": "+2",
"trend": "up"
},
"installed_modules": {
"total": 3,
"active": 3
},
"active_plugins": {
"total": 9,
"active": 0
},
"installed_templates": {
"total": 2,
"active": 0
},
"language_packs": {
"total": 17,
"active": 1
},
"system_status": {
"status": "normal",
"label": "정상",
"all_services_running": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+56
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/extensions/auto-deactivated HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -36,6 +45,27 @@ _단건 응답: `data` 객체의 필드._
| items | object | `{"plugins":[],"modules":[],"templates":[]}` | 코어 비호환으로 자동 비활성화된 확장을 타입별(`plugins`/`modules`/`templates`)로 묶은 목록. 각 원소는 식별자(`identifier`), 비호환 요구 버전(`incompatible_required_version`), 비활성화 시각(`deactivated_at`)을 가지며, 사용자가 dismiss했거나 hidden(학습용 샘플) 확장은 제외됨 |
| current_core_version | string | `7.0.1` | 현재 설치된 코어 버전 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "자동 비활성화된 확장 목록입니다.",
"data": {
"items": {
"plugins": [],
"modules": [],
"templates": []
},
"current_core_version": "7.0.1"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -61,10 +91,23 @@ _단건 응답: `data` 객체의 필드._
| type | path | string | 예 | module, plugin, template | 대상 확장의 타입 (module: 모듈, plugin: 플러그인, template: 템플릿). 타입에 맞는 Repository/Manager를 해석하는 데 사용되며 그 외 값은 422 |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/admin/extensions/{type}/{identifier}/dismiss HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -91,10 +134,23 @@ _단건 응답: `data` 객체의 필드._
| type | path | string | 예 | module, plugin, template | 대상 확장의 타입 (module: 모듈, plugin: 플러그인, template: 템플릿). 타입에 맞는 Repository/Manager를 해석하는 데 사용되며 그 외 값은 422 |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/admin/extensions/{type}/{identifier}/recover HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
+481 -2
View File
@@ -37,6 +37,15 @@
| per_page | query | integer | 아니오 | min 1, max 100 | 페이지당 항목 수 |
| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/admin/language-packs?scope=%EC%98%88%EC%8B%9C%EA%B0%92&target_identifier=example-key&locale=ko&status=%EC%98%88%EC%8B%9C%EA%B0%92&vendor=%EC%98%88%EC%8B%9C%EA%B0%92&search=%EC%98%88%EC%8B%9C%EA%B0%92&exclude_protected=1&per_page=1&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -76,6 +85,120 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| has_update | boolean | `false` | update 여부 |
| abilities | object | `{"can_activate":true,"can_deactivate":true,"can_uninstall…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "언어팩 목록을 조회했습니다.",
"data": {
"data": [
{
"id": null,
"identifier": "g7-core-en",
"vendor": "g7",
"scope": "core",
"target_identifier": null,
"locale": "en",
"locale_name": "EN",
"locale_native_name": "English",
"text_direction": "ltr",
"version": "7.0.1",
"latest_version": null,
"target_version_constraint": null,
"target_version_mismatch": false,
"name": null,
"license": null,
"description": null,
"status": "active",
"is_protected": true,
"source_type": "built_in",
"origin": "built_in",
"source_url": "lang/en",
"github_url": null,
"github_changelog_url": null,
"bundled_identifier": null,
"install_blocked_reason": null,
"target_name": null,
"installed_at": null,
"activated_at": null,
"created_at": null,
"updated_at": null,
"has_update": false,
"abilities": {
"can_activate": true,
"can_deactivate": true,
"can_uninstall": true
}
},
{
"id": 1,
"identifier": "apidoc-sample-lang",
"vendor": "apidoc",
"scope": "core",
"target_identifier": null,
"locale": "fr",
"locale_name": "French",
"locale_native_name": "Français",
"text_direction": "ltr",
"version": "1.0.0",
"latest_version": "1.0.0",
"target_version_constraint": null,
"target_version_mismatch": false,
"name": "API 문서 샘플 언어팩",
"license": "MIT",
"description": "문서 실측용 언어팩",
"status": "active",
"is_protected": false,
"source_type": "bundled",
"origin": "bundled",
"source_url": null,
"github_url": null,
"github_changelog_url": null,
"bundled_identifier": null,
"install_blocked_reason": null,
"target_name": null,
"installed_at": "2026-07-05 10:41:24",
"activated_at": "2026-07-05 10:41:24",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"has_update": false,
"abilities": {
"can_activate": true,
"can_deactivate": true,
"can_uninstall": true
}
},
"... (총 25건 중 2건 표시)"
],
"meta": {
"total": 43,
"active": 15,
"installed": 0,
"inactive": 0,
"error": 0,
"uninstalled": 10,
"current_page": 1,
"last_page": 2,
"per_page": 25
},
"abilities": {
"can_install": true,
"can_activate": true,
"can_deactivate": true,
"can_uninstall": true,
"can_refresh_cache": true,
"can_check_updates": true,
"can_update": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -103,10 +226,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.language_packs.bulk_activate_validation_rules`).
**요청 예시**
```http
POST /api/admin/language-packs/bulk-activate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -130,10 +273,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/language-packs/check-updates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -159,10 +315,29 @@ _요청 파라미터 없음._
| identifier | body | string | 예 | max 200 | 대상 확장/리소스의 식별자 |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부 |
**요청 예시**
```http
POST /api/admin/language-packs/install-from-bundled HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"identifier": "example-key",
"auto_activate": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -189,10 +364,35 @@ _요청 파라미터 없음._
| file | body | file | 예 | max 10240 | 업로드 파일 |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부 |
**요청 예시**
```http
POST /api/admin/language-packs/install-from-file HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary
Content-Disposition: form-data; name="auto_activate"
1
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -219,10 +419,29 @@ _요청 파라미터 없음._
| github_url | body | string | 예 | — | GitHub 저장소 URL |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부 |
**요청 예시**
```http
POST /api/admin/language-packs/install-from-github HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"github_url": "https://example.com",
"auto_activate": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -250,10 +469,30 @@ _요청 파라미터 없음._
| checksum | body | string | 아니오 | — | 무결성 검증 체크섬 (SHA-256) |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부 |
**요청 예시**
```http
POST /api/admin/language-packs/install-from-url HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"url": "https://example.com",
"checksum": "예시값",
"auto_activate": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -279,10 +518,31 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| file | body | file | 예 | max 5120 | 업로드 파일 |
**요청 예시**
```http
POST /api/admin/language-packs/manifest-preview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -306,10 +566,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/language-packs/refresh-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -335,10 +608,23 @@ _요청 파라미터 없음._
| id | path | string | 예 | — | 대상 리소스의 식별자 |
| cascade | query | boolean | 아니오 | — | 연쇄 처리 여부 (의존 항목 함께 처리) |
**요청 예시**
```http
DELETE /api/admin/language-packs/1?cascade=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -365,9 +651,128 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/language-packs/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| identifier | string | `apidoc-sample-lang` | 언어팩 고유 식별자 ({vendor}-{scope}-{target?}-{locale}) |
| vendor | string | `apidoc` | 언어팩 제작자 식별자 |
| scope | string | `core` | 적용 대상 분류 |
| target_identifier | null | `null` | 대상 확장 식별자 (scope=core일 때 null) |
| locale | string | `fr` | IETF BCP-47 locale 태그 |
| locale_name | string | `French` | 영문 언어명 |
| locale_native_name | string | `Français` | 원어 언어명 |
| text_direction | string | `ltr` | 텍스트 방향 |
| version | string | `1.0.0` | 언어팩 버전 |
| latest_version | string | `1.0.0` | 감지된 최신 배포 버전 |
| target_version_constraint | null | `null` | 대상 확장 버전 제약 (semver) |
| target_version_mismatch | boolean | `false` | 대상 버전 불일치 경고 플래그 |
| name | string | `API 문서 샘플 언어팩` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| license | string | `MIT` | 라이선스 |
| description | string | `문서 실측용 언어팩` | 설명 (다국어 필드는 로케일별 값 객체) |
| status | string | `active` | 상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조) |
| is_protected | boolean | `false` | protected 여부 |
| source_type | string | `bundled` | 설치 소스 유형 (zip/github/url/bundled/bundled_with_extension) |
| origin | string | `bundled` | 출처 (설치/등록 원천 구분 값) |
| source_url | null | `null` | source URL |
| github_url | null | `null` | GitHub 저장소 URL (manifest 파생) |
| github_changelog_url | null | `null` | GitHub 변경 이력(CHANGELOG) URL (manifest 파생) |
| bundled_identifier | null | `null` | 대응하는 번들 확장 식별자 (번들 원본 매칭용) |
| install_blocked_reason | null | `null` | 설치가 차단된 사유 (차단 없으면 null) |
| target_name | null | `null` | 대상 확장의 표시 이름 (scope+target_identifier 로 해석) |
| installed_at | string | `2026-07-05 10:41:24` | installed 일시 |
| activated_at | string | `2026-07-05 10:41:24` | activated 일시 |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 10:41:24` | 최종 수정 일시 |
| has_update | boolean | `false` | update 여부 |
| abilities | object | `{"can_activate":true,"can_deactivate":true,"can_uninstall…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
| manifest | object | `{"name":{"en":"API Doc Sample Pack","ko":"API 문서 샘플 언어팩"}…` | language-pack.json 전체 스냅샷 |
| validation_summary | object | `{"target_version_mismatch":false,"depends_on_core_locale"…` | 검증 결과 요약 객체 (통과/실패 건수 등) |
| source_meta | object | `{"type":"bundled","url":null,"installed_by":6,"latest_ver…` | 원천 메타데이터 객체 (출처·경로·해석 정보) |
| changelog_entries | array | `[]` | 변경 이력 항목 목록 (버전별 변경 내용) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "언어팩 목록을 조회했습니다.",
"data": {
"id": 1,
"identifier": "apidoc-sample-lang",
"vendor": "apidoc",
"scope": "core",
"target_identifier": null,
"locale": "fr",
"locale_name": "French",
"locale_native_name": "Français",
"text_direction": "ltr",
"version": "1.0.0",
"latest_version": "1.0.0",
"target_version_constraint": null,
"target_version_mismatch": false,
"name": "API 문서 샘플 언어팩",
"license": "MIT",
"description": "문서 실측용 언어팩",
"status": "active",
"is_protected": false,
"source_type": "bundled",
"origin": "bundled",
"source_url": null,
"github_url": null,
"github_changelog_url": null,
"bundled_identifier": null,
"install_blocked_reason": null,
"target_name": null,
"installed_at": "2026-07-05 10:41:24",
"activated_at": "2026-07-05 10:41:24",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"has_update": false,
"abilities": {
"can_activate": true,
"can_deactivate": true,
"can_uninstall": true
},
"manifest": {
"name": {
"en": "API Doc Sample Pack",
"ko": "API 문서 샘플 언어팩"
},
"locale": "fr",
"version": "1.0.0"
},
"validation_summary": {
"target_version_mismatch": false,
"depends_on_core_locale": null
},
"source_meta": {
"type": "bundled",
"url": null,
"installed_by": 6,
"latest_version": "1.0.0",
"directory_path": "lang-packs/apidoc-sample-lang"
},
"changelog_entries": []
}
}
```
**에러 응답**
@@ -394,10 +799,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/admin/language-packs/1/activate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -423,9 +841,44 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/language-packs/1/changelog HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| identifier | string | `apidoc-sample-lang` | 언어팩 고유 식별자 ({vendor}-{scope}-{target?}-{locale}) |
| entries | array | `[]` | 항목 목록 (각 원소는 대상 도메인 레코드) |
| changelog | string | `` | 변경 이력 텍스트 (원격/파일 CHANGELOG 본문) |
| has_changelog | boolean | `false` | changelog 여부 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "언어팩 목록을 조회했습니다.",
"data": {
"identifier": "apidoc-sample-lang",
"entries": [],
"changelog": "",
"has_changelog": false
}
}
```
**에러 응답**
@@ -452,10 +905,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/admin/language-packs/1/deactivate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -481,10 +947,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/admin/language-packs/1/update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+26
View File
@@ -29,10 +29,23 @@
| --- | --- | --- | --- | --- | --- |
| token | path | string | 예 | — | 인증/검증 토큰 |
**요청 예시**
```http
GET /api/layouts/preview/{token}.json HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -57,10 +70,23 @@
| templateIdentifier | path | string | 예 | — | 대상 template의 식별자 |
| layoutName | path | string | 예 | — | 대상 layout의 이름 (식별자) |
**요청 예시**
```http
GET /api/layouts/{templateIdentifier}/{layoutName}.json HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+25
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/license HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| content | string | `프로그램 명칭 : 그누보드7 (Gnuboard7) 저작자 : (주…` | 본문 내용 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "성공적으로 처리되었습니다.",
"data": {
"content": "프로그램 명칭 : 그누보드7 (Gnuboard7)\n\n저작자 : (주)에스아이알소프트\n\n라이선스 (License)\n\n번역문 아래에 원문이 있습니다.\n\n주의)\n1. 번역문과 원문의 내용상 차이가 있는 경우 원문의 내용을 우선으로 따릅니다.\n2. 이 라이선스 파일 및 내용은 저작자를 제외한 어느 누구도 추가, 수정, 삭제할 수 없습니다.\n\n----- MIT 라이선스 (한국어 번역) --------------------------------------------------------\n\nMIT 라이선스\n\nCopyright (c) 2026 (주)에스아이알소프트\n\n이 소프트웨어와 관련 문서 파일(이하 \"소프트웨어\")의 복사본을 취득하는 모든 사람에게\n소프트웨어를 제한 없이 사용, 복사, 수정, 병합, 출판, 배포, 서브라이선스 허여 및/또는\n판매할 수 있는 권리를 무상으로 부여합니다. 다만, 소프트웨어를 제공받은 사람은 다음\n조건을 따라야 합니다:\n\n위 저작권 고지와 본 허가 고지는 소프트웨어의 모든 복사본 또는 상당 부분에 포함되어야\n합니다.\n\n소프트웨어는 \"있는 그대로\" 제공되며, 명시적이든 묵시적이든 어떠한 종류의 보증도 하지\n않습니다. 여기에는 상품성, 특정 목적에의 적합성 및 비침해에 대한 보증이 포함되나 이에\n국한되지 않습니다. 어떠한 경우에도 저작자 또는 저작권자는 소프트웨어나 소프트웨어의\n사용 또는 기타 거래로 인해 발생하는 계약, 불법행위 또는 기타 청구, 손해 또는 기타\n책임에 대해 책임을 지지 않습니다.\n\n----- MIT License (English Original) --------------------------------------------------------\n\nThe MIT License (MIT)\n\nCopyright (c) 2026 SIRSOFT\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+33
View File
@@ -27,6 +27,14 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/locales/active HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -36,6 +44,31 @@ _단건 응답: `data` 객체의 필드._
| locales | array | `["ko","en","fr","ja"]` | 활성 로케일 코드 배열 |
| locale_names | object | `{"ko":"한국어","en":"English","ja":"日本語","fr":"Français"}` | 로케일 코드별 표시명 맵 (config app.locale_names) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "locales.fetched",
"data": {
"locales": [
"ko",
"en",
"fr"
],
"locale_names": {
"ko": "한국어",
"en": "English",
"fr": "Français"
}
}
}
```
**에러 응답**
_대표 에러 없음 (공개 조회). 인증·권한 미요구 엔드포인트로 도메인 특이 에러를 반환하지 않습니다._
+110
View File
@@ -27,10 +27,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/me HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-500 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -54,6 +67,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/me HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -117,6 +139,57 @@ _단건 응답: `data` 객체의 필드._
| ecommerce_preferred_shipping_country | null | `null` | 선호 배송 국가 코드 (이커머스 모듈 주입, 미설정 시 null) |
| ecommerce_preferred_shipping_country_name | null | `null` | 선호 배송 국가 이름 (코드 파생, 이커머스 모듈 주입, 미설정 시 null) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "프로필 정보를 성공적으로 가져왔습니다.",
"data": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"nickname": "song.hyunji",
"email": "apidoc-sample-user@example.com",
"avatar": null,
"language": "ko",
"timezone": "Asia/Seoul",
"country": "KR",
"status": "active",
"status_label": "활성",
"status_variant": "success",
"homepage": "https://example.com",
"mobile": "010-9595-2897",
"phone": "02-637-5618",
"zipcode": "16505",
"address": "경기도 안양시 봉은사로 2918",
"address_detail": "48동 718호",
"signature": "Fugit consequuntur repellendus sed.",
"bio": "Ut magni et sunt ducimus error adipisci. Pariatur corporis voluptatem ratione quo non saepe. Illo atque praesentium possimus dolores qui est fugit. Sint fugiat numquam voluptates.",
"is_super": false,
"is_admin": true,
"withdrawn_at": null,
"last_login_at": "2026-07-07 10:41:24",
"last_login_human": "1일 전",
"created_at": "2026-07-08 10:41:24",
"is_owner": true,
"notify_post_complete": false,
"notify_post_reply": false,
"notify_comment": false,
"notify_reply_comment": false,
"ecommerce_mileage": {
"enabled": false
},
"ecommerce_preferred_currency": null,
"ecommerce_preferred_shipping_country": null,
"ecommerce_preferred_shipping_country_name": null
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -167,10 +240,47 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.update_profile_validation_rules`).
**요청 예시**
```http
PUT /api/me HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"nickname": "예시 이름",
"email": "user@example.com",
"password": "Password123!",
"current_password": "Password123!",
"language": "ko",
"country": "KR",
"timezone": "Asia/Seoul",
"homepage": "https://example.com",
"mobile": "010-1234-5678",
"phone": "010-1234-5678",
"zipcode": "06234",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"signature": "예시값",
"bio": "예시 내용입니다.",
"notify_post_complete": true,
"notify_post_reply": true,
"notify_comment": true,
"notify_reply_comment": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+879 -2
View File
@@ -34,6 +34,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.menu.list_validation_rules`).
**요청 예시**
```http
GET /api/admin/menus?is_active=1&filters=%EC%98%88%EC%8B%9C%EA%B0%92&sort_by=created_at&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -58,6 +67,175 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_owner | boolean | `false` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "메뉴를 성공적으로 가져왔습니다.",
"data": {
"data": [
{
"id": 19,
"name": {
"ko": "페이지 관리",
"en": "Page Management"
},
"slug": "sirsoft-page",
"url": "/admin/pages",
"icon": "fas fa-file-alt",
"order": 25,
"is_active": true,
"parent_id": null,
"extension_type": "module",
"extension_identifier": "sirsoft-page",
"children": [],
"creator": null,
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
}
],
"created_at": "2026-07-08 10:44:43",
"updated_at": "2026-07-08 10:44:43",
"is_owner": false,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"id": 15,
"name": {
"ko": "게시판 관리",
"en": "Board Management"
},
"slug": "sirsoft-board",
"url": null,
"icon": "fas fa-clipboard-list",
"order": 30,
"is_active": true,
"parent_id": null,
"extension_type": "module",
"extension_identifier": "sirsoft-board",
"children": [
{
"id": 16,
"name": {
"ko": "환경설정",
"en": "Settings"
},
"slug": "sirsoft-board-settings",
"url": "/admin/boards/settings",
"icon": "fas fa-cog",
"order": 1,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board",
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
}
]
},
{
"id": 17,
"name": {
"ko": "게시판 목록",
"en": "Board List"
},
"slug": "sirsoft-board-list",
"url": "/admin/boards",
"icon": "fas fa-list",
"order": 2,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board",
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
}
]
},
{
"id": 18,
"name": {
"ko": "게시판 신고현황",
"en": "Board Reports"
},
"slug": "sirsoft-board-reports",
"url": "/admin/boards/reports",
"icon": "fas fa-flag",
"order": 3,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board",
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
}
]
}
],
"creator": null,
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
}
],
"created_at": "2026-07-08 10:44:35",
"updated_at": "2026-07-08 10:44:35",
"is_owner": false,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 3건 중 2건 표시)"
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -96,9 +274,121 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.menu.create_validation_rules`).
**요청 예시**
```http
POST /api/admin/menus HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"slug": "example-key",
"url": "https://example.com",
"icon": "예시값",
"parent_id": 1,
"order": 1,
"is_active": true,
"extension_type": "core",
"extension_identifier": "example-key",
"roles": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `26` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"실측 예시값","en":"실측 예시값","fr":"실측 예시값"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| slug | string | `probe_6a4dc09f5a6bd` | URL 친화 식별자 (slug) |
| url | string | `https://example.com` | 메뉴 URL |
| icon | string | `실측 예시값` | 아이콘 식별자 (아이콘 클래스/이름) |
| order | integer | `1` | 메뉴 순서 |
| is_active | boolean | `true` | active 여부 |
| parent_id | integer | `1` | parent 식별자 (연관 리소스 참조) |
| extension_type | string | `core` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | string | `probe_6a4dc09f5a6c3` | 이 리소스를 소유한 확장의 식별자 |
| parent | object | `{"id":1,"name":{"ko":"API 문서 샘플 메뉴","en":"API Doc Sample …` | 상위 항목 객체 (parent 관계 파생) |
| children | array | `[]` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
| creator | null | `null` | 생성자 정보 객체 (uuid/name/email — creator 관계 파생) |
| roles | array | `[{"id":1,"name":{"ko":"관리자","en":"Administrator"},"permis…` | 보유 역할 목록 (각 원소 id/name/permissions — roles 관계 파생) |
| created_at | string | `2026-07-08 12:14:39` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:39` | 최종 수정 일시 |
| is_owner | boolean | `false` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 201
```
```json
{
"success": true,
"message": "메뉴가 성공적으로 생성되었습니다.",
"data": {
"id": 26,
"name": {
"ko": "실측 예시값",
"en": "실측 예시값",
"fr": "실측 예시값"
},
"slug": "probe_6a4dc09f5a6bd",
"url": "https://example.com",
"icon": "실측 예시값",
"order": 1,
"is_active": true,
"parent_id": 1,
"extension_type": "core",
"extension_identifier": "probe_6a4dc09f5a6c3",
"parent": {
"id": 1,
"name": {
"ko": "API 문서 샘플 메뉴",
"en": "API Doc Sample Menu"
},
"url": "/admin/apidoc-sample",
"icon": "fas fa-book"
},
"children": [],
"creator": null,
"roles": [
{
"id": 1,
"name": {
"ko": "관리자",
"en": "Administrator"
},
"permission_type": "read"
},
{
"id": 4,
"name": {
"ko": "API 문서 샘플 역할",
"en": "API Doc Sample Role"
},
"permission_type": "read"
}
],
"created_at": "2026-07-08 12:14:39",
"updated_at": "2026-07-08 12:14:39",
"is_owner": false,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -125,6 +415,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/menus/active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -138,6 +437,272 @@ _요청 파라미터 없음._
| is_active | boolean | `true` | active 여부 |
| children | array | `[]` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "메뉴를 성공적으로 가져왔습니다.",
"data": [
{
"id": 19,
"name": {
"ko": "페이지 관리",
"en": "Page Management"
},
"slug": "sirsoft-page",
"url": "/admin/pages",
"icon": "fas fa-file-alt",
"order": 25,
"is_active": true,
"children": []
},
{
"id": 15,
"name": {
"ko": "게시판 관리",
"en": "Board Management"
},
"slug": "sirsoft-board",
"url": null,
"icon": "fas fa-clipboard-list",
"order": 30,
"is_active": true,
"children": [
{
"id": 16,
"name": {
"ko": "환경설정",
"en": "Settings"
},
"slug": "sirsoft-board-settings",
"url": "/admin/boards/settings",
"icon": "fas fa-cog",
"order": 1,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
},
{
"id": 17,
"name": {
"ko": "게시판 목록",
"en": "Board List"
},
"slug": "sirsoft-board-list",
"url": "/admin/boards",
"icon": "fas fa-list",
"order": 2,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
},
{
"id": 18,
"name": {
"ko": "게시판 신고현황",
"en": "Board Reports"
},
"slug": "sirsoft-board-reports",
"url": "/admin/boards/reports",
"icon": "fas fa-flag",
"order": 3,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
}
]
},
{
"id": 3,
"name": {
"ko": "이커머스",
"en": "Ecommerce"
},
"slug": "sirsoft-ecommerce",
"url": null,
"icon": "fas fa-shopping-cart",
"order": 40,
"is_active": true,
"children": [
{
"id": 4,
"name": {
"ko": "환경설정",
"en": "Settings"
},
"slug": "sirsoft-ecommerce-settings",
"url": "/admin/ecommerce/settings",
"icon": "fas fa-cog",
"order": 1,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 5,
"name": {
"ko": "상품 관리",
"en": "Products"
},
"slug": "sirsoft-ecommerce-products",
"url": "/admin/ecommerce/products",
"icon": "fas fa-box",
"order": 2,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 6,
"name": {
"ko": "카테고리 관리",
"en": "Categories"
},
"slug": "sirsoft-ecommerce-categories",
"url": "/admin/ecommerce/categories",
"icon": "fas fa-folder",
"order": 3,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 7,
"name": {
"ko": "브랜드 관리",
"en": "Brands"
},
"slug": "sirsoft-ecommerce-brands",
"url": "/admin/ecommerce/brands",
"icon": "fas fa-tag",
"order": 4,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 8,
"name": {
"ko": "상품정보제공고시",
"en": "Product Notice"
},
"slug": "sirsoft-ecommerce-product-notices",
"url": "/admin/ecommerce/product-notices",
"icon": "fas fa-file-alt",
"order": 5,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 9,
"name": {
"ko": "공통정보 관리",
"en": "Common Info"
},
"slug": "sirsoft-ecommerce-common-info",
"url": "/admin/ecommerce/common-info",
"icon": "fas fa-info-circle",
"order": 6,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 10,
"name": {
"ko": "주문 관리",
"en": "Orders"
},
"slug": "sirsoft-ecommerce-orders",
"url": "/admin/ecommerce/orders",
"icon": "fas fa-receipt",
"order": 7,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 11,
"name": {
"ko": "쿠폰 관리",
"en": "Coupons"
},
"slug": "sirsoft-ecommerce-promotion-coupons",
"url": "/admin/ecommerce/promotion-coupons",
"icon": "fas fa-ticket-alt",
"order": 8,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 12,
"name": {
"ko": "배송정책",
"en": "Shipping Policies"
},
"slug": "sirsoft-ecommerce-shipping-policies",
"url": "/admin/ecommerce/shipping-policies",
"icon": "fas fa-truck",
"order": 9,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 13,
"name": {
"ko": "리뷰 관리",
"en": "Reviews"
},
"slug": "sirsoft-ecommerce-reviews",
"url": "/admin/ecommerce/reviews",
"icon": "fas fa-star",
"order": 10,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
},
{
"id": 14,
"name": {
"ko": "마일리지 내역",
"en": "Mileage History"
},
"slug": "sirsoft-ecommerce-mileage-transactions",
"url": "/admin/ecommerce/mileage-transactions",
"icon": "fas fa-coins",
"order": 11,
"is_active": true,
"parent_id": 3,
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce"
}
]
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -165,10 +730,23 @@ _요청 파라미터 없음._
| type | path | string | 예 | — | 확장 소유 타입 (ExtensionOwnerType 으로 파싱 — module: 모듈, plugin: 플러그인. 미유효 값이면 422 menu.invalid_extension_type) |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/menus/extension/{type}/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -194,6 +772,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/menus/hierarchy HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -209,6 +796,96 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_active | boolean | `true` | active 여부 |
| children | array | `[]` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "메뉴를 성공적으로 가져왔습니다.",
"data": {
"data": [
{
"id": 19,
"name": {
"ko": "페이지 관리",
"en": "Page Management"
},
"slug": "sirsoft-page",
"url": "/admin/pages",
"icon": "fas fa-file-alt",
"order": 25,
"is_active": true,
"children": []
},
{
"id": 15,
"name": {
"ko": "게시판 관리",
"en": "Board Management"
},
"slug": "sirsoft-board",
"url": null,
"icon": "fas fa-clipboard-list",
"order": 30,
"is_active": true,
"children": [
{
"id": 16,
"name": {
"ko": "환경설정",
"en": "Settings"
},
"slug": "sirsoft-board-settings",
"url": "/admin/boards/settings",
"icon": "fas fa-cog",
"order": 1,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
},
{
"id": 17,
"name": {
"ko": "게시판 목록",
"en": "Board List"
},
"slug": "sirsoft-board-list",
"url": "/admin/boards",
"icon": "fas fa-list",
"order": 2,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
},
{
"id": 18,
"name": {
"ko": "게시판 신고현황",
"en": "Board Reports"
},
"slug": "sirsoft-board-reports",
"url": "/admin/boards/reports",
"icon": "fas fa-flag",
"order": 3,
"is_active": true,
"parent_id": 15,
"extension_type": "module",
"extension_identifier": "sirsoft-board"
}
]
},
"... (총 4건 중 2건 표시)"
]
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -239,10 +916,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.menu.update_order_validation_rules`).
**요청 예시**
```http
PUT /api/admin/menus/order HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"parent_menus": [
"예시값"
],
"child_menus": [
"예시값"
],
"moved_items": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -270,10 +973,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| menu | path | string | 예 | — | 대상 menu의 식별자 |
**요청 예시**
```http
DELETE /api/admin/menus/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -301,6 +1017,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| menu | path | string | 예 | — | 대상 menu의 식별자 |
**요청 예시**
```http
GET /api/admin/menus/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -326,6 +1051,67 @@ _단건 응답: `data` 객체의 필드._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "메뉴를 성공적으로 가져왔습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 메뉴",
"en": "API Doc Sample Menu"
},
"slug": "apidoc-sample-menu",
"url": "/admin/apidoc-sample",
"icon": "fas fa-book",
"order": 35,
"is_active": true,
"parent_id": null,
"extension_type": null,
"extension_identifier": null,
"parent": null,
"children": [
{
"id": 2,
"name": {
"ko": "하위 메뉴",
"en": "Child Menu"
},
"slug": "apidoc-sample-menu-child",
"url": "/admin/nihil-non-eos-doloribus-occaecati-optio",
"icon": "fas fa-chart",
"order": 38,
"is_active": true,
"parent_id": 1,
"extension_type": null,
"extension_identifier": null,
"roles": []
}
],
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"roles": [],
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"is_owner": true,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -365,10 +1151,39 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.menu.update_validation_rules`).
**요청 예시**
```http
PUT /api/admin/menus/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"slug": "example-key",
"url": "https://example.com",
"icon": "예시값",
"parent_id": 1,
"order": 1,
"is_active": true,
"extension_type": "core",
"extension_identifier": "example-key",
"roles": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -397,9 +1212,71 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| menu | path | string | 예 | — | 대상 menu의 식별자 |
**요청 예시**
```http
PATCH /api/admin/menus/1/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 메뉴","en":"API Doc Sample Menu"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| slug | string | `apidoc-sample-menu` | URL 친화 식별자 (slug) |
| url | string | `/admin/apidoc-sample` | 메뉴 URL |
| icon | string | `fas fa-book` | 아이콘 식별자 (아이콘 클래스/이름) |
| order | integer | `35` | 메뉴 순서 |
| is_active | boolean | `false` | active 여부 |
| parent_id | null | `null` | parent 식별자 (연관 리소스 참조) |
| extension_type | null | `null` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | null | `null` | 이 리소스를 소유한 확장의 식별자 |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:40` | 최종 수정 일시 |
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "메뉴가 성공적으로 업데이트되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 메뉴",
"en": "API Doc Sample Menu"
},
"slug": "apidoc-sample-menu",
"url": "/admin/apidoc-sample",
"icon": "fas fa-book",
"order": 35,
"is_active": false,
"parent_id": null,
"extension_type": null,
"extension_identifier": null,
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 12:14:40",
"is_owner": true,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
File diff suppressed because it is too large Load Diff
+58
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/notification-channels HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,55 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| channels | array | `[{"id":"mail","name_key":"notification.channels.mail.name…` | 사용 가능한 알림 채널 메타데이터 목록. 각 원소는 `id`(채널 식별자: mail, database 등), `name`/`name_key`·`description`/`description_key`(활성 locale 기준 해석된 라벨/설명과 원본 다국어 키), `icon`(Font Awesome 클래스), `source`(제공 주체: core/module/plugin)·`source_label`(출처 표시 라벨), `allow_guest`(비회원 발송 허용 여부), `readiness`(컨트롤러가 `ChannelReadinessCheckerInterface`로 붙인 채널 설정 완료 여부 정보)로 구성됩니다. config 기본 채널(mail, database)에 `core.notification.filter_available_channels` 훅으로 추가된 확장 채널이 병합됩니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 채널 목록을 조회했습니다.",
"data": {
"channels": [
{
"id": "mail",
"name_key": "notification.channels.mail.name",
"icon": "fas fa-envelope",
"description_key": "notification.channels.mail.description",
"source": "core",
"source_label_key": "notification.channels.core_default",
"allow_guest": true,
"name": "메일",
"description": "이메일로 알림 발송",
"source_label": "코어 기본 채널",
"readiness": {
"ready": false,
"reason": "notification.readiness.mail_smtp_host_empty"
}
},
{
"id": "database",
"name_key": "notification.channels.database.name",
"icon": "fas fa-bell",
"description_key": "notification.channels.database.description",
"source": "core",
"source_label_key": "notification.channels.core_default",
"allow_guest": false,
"name": "사이트내 알림",
"description": "사이트내 알림 센터에 표시",
"source_label": "코어 기본 채널",
"readiness": {
"ready": true,
"reason": null
}
}
]
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+497 -2
View File
@@ -38,6 +38,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification_definition.filter_index_rules`).
**요청 예시**
```http
GET /api/admin/notification-definitions?search=%EC%98%88%EC%8B%9C%EA%B0%92&extension_type=core&extension_identifier=example-key&channel=%EC%98%88%EC%8B%9C%EA%B0%92&is_active=1&per_page=1&sort_by=id&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -62,6 +71,222 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| updated_at | string | `2026-06-30 13:33:16` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 정의 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"type": "apidoc-sample.event",
"hook_prefix": "core",
"extension_type": "core",
"extension_identifier": "",
"name": {
"ko": "API 문서 샘플 알림",
"en": "API Doc Sample Notification"
},
"description": {
"ko": "문서 실측용 알림 정의",
"en": "Sample notification"
},
"variables": [],
"channels": [
"database",
"mail"
],
"hooks": [],
"is_active": true,
"is_default": false,
"templates": [
{
"id": 1,
"definition_id": 1,
"channel": "mail",
"subject": "API 문서 샘플 템플릿 제목",
"body": "안녕하세요 {{name}} 님, 문서 실측용 본문입니다.",
"click_url": "/admin/apidoc-sample",
"recipients": [
{
"type": "role",
"value": "admin",
"display_name": "관리자"
}
],
"is_active": true,
"is_default": false,
"user_overrides": null,
"updated_by": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_update": true,
"can_delete": true
}
}
],
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_update": true,
"can_delete": true
}
},
{
"number": 2,
"id": 2,
"type": "order_confirmed",
"hook_prefix": "sirsoft-ecommerce",
"extension_type": "module",
"extension_identifier": "sirsoft-ecommerce",
"name": {
"ko": "결제 완료",
"en": "Payment Completed"
},
"description": {
"ko": "결제 완료 시 고객에게 발송",
"en": "Sent to customer when payment is completed"
},
"variables": [
{
"key": "name",
"description": "수신자 이름"
},
{
"key": "app_name",
"description": "사이트 이름"
},
{
"key": "order_number",
"description": "주문번호"
},
{
"key": "total_amount",
"description": "결제 금액"
},
{
"key": "order_url",
"description": "주문 상세 URL"
},
{
"key": "site_url",
"description": "사이트 URL"
},
{
"key": "shipping_recipient_name",
"description": "수취인 이름"
},
{
"key": "shipping_country_name",
"description": "배송국가명"
},
{
"key": "shipping_address",
"description": "배송지 주소"
}
],
"channels": [
"mail",
"database"
],
"hooks": [
"sirsoft-ecommerce.order.after_confirm"
],
"is_active": true,
"is_default": true,
"templates": [
{
"id": 2,
"definition_id": 2,
"channel": "mail",
"subject": {
"ko": "[{app_name}] 결제가 완료되었습니다 (주문번호: {order_number})",
"en": "[{app_name}] Your payment has been completed (Order #{order_number})"
},
"body": {
"ko": "<div style=\"font-family:'Malgun Gothic',sans-serif;max-width:600px;margin:0 auto;padding:20px\"><h2 style=\"color:#333;border-bottom:2px solid #4F46E5;padding-bottom:10px\">결제 완료</h2><p style=\"color:#555;line-height:1.6\">{name}님, 결제가 완료되었습니다. 주문해 주셔서 감사합니다.</p><div style=\"background:#f8f9fa;padding:15px;border-radius:8px;margin:15px 0\"><p style=\"margin:5px 0\"><strong>주문번호:</strong> {order_number}</p><p style=\"margin:5px 0\"><strong>결제금액:</strong> {total_amount}</p><p style=\"margin:5px 0\"><strong>받는분:</strong> {shipping_recipient_name}</p><p style=\"margin:5px 0\"><strong>배송국가:</strong> {shipping_country_name}</p><p style=\"margin:5px 0\"><strong>배송지:</strong> {shipping_address}</p></div><p style=\"color:#555;line-height:1.6\">주문 상세 내용은 아래 버튼을 클릭하여 확인하실 수 있습니다.</p><div style=\"text-align:center;margin:25px 0\"><a href=\"{order_url}\" style=\"display:inline-block;padding:12px 30px;background-color:#4F46E5;color:#ffffff;text-decoration:none;border-radius:6px;font-weight:bold\">주문 상세 보기</a></div><hr style=\"border:none;border-top:1px solid #eee;margin:20px 0\"><p style=\"color:#999;font-size:12px\">본 메일은 {app_name}에서 발송되었습니다.</p></div>",
"en": "<div style=\"font-family:Arial,sans-serif;max-width:600px;margin:0 auto;padding:20px\"><h2 style=\"color:#333;border-bottom:2px solid #4F46E5;padding-bottom:10px\">Payment Completed</h2><p style=\"color:#555;line-height:1.6\">Dear {name}, your payment has been completed. Thank you for your order.</p><div style=\"background:#f8f9fa;padding:15px;border-radius:8px;margin:15px 0\"><p style=\"margin:5px 0\"><strong>Order Number:</strong> {order_number}</p><p style=\"margin:5px 0\"><strong>Total Amount:</strong> {total_amount}</p><p style=\"margin:5px 0\"><strong>Recipient:</strong> {shipping_recipient_name}</p><p style=\"margin:5px 0\"><strong>Shipping Country:</strong> {shipping_country_name}</p><p style=\"margin:5px 0\"><strong>Shipping Address:</strong> {shipping_address}</p></div><p style=\"color:#555;line-height:1.6\">Click the button below to view your order details.</p><div style=\"text-align:center;margin:25px 0\"><a href=\"{order_url}\" style=\"display:inline-block;padding:12px 30px;background-color:#4F46E5;color:#ffffff;text-decoration:none;border-radius:6px;font-weight:bold\">View Order</a></div><hr style=\"border:none;border-top:1px solid #eee;margin:20px 0\"><p style=\"color:#999;font-size:12px\">This email was sent from {app_name}.</p></div>"
},
"click_url": null,
"recipients": [
{
"type": "trigger_user"
}
],
"is_active": true,
"is_default": true,
"user_overrides": null,
"updated_by": null,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"abilities": {
"can_update": true,
"can_delete": true
}
},
{
"id": 3,
"definition_id": 2,
"channel": "database",
"subject": {
"ko": "결제가 완료되었습니다",
"en": "Your payment has been completed"
},
"body": {
"ko": "{name}님, 주문번호 {order_number}의 결제가 완료되었습니다.",
"en": "{name}, payment for your order {order_number} has been completed."
},
"click_url": "{order_url}",
"recipients": [
{
"type": "trigger_user"
}
],
"is_active": true,
"is_default": true,
"user_overrides": null,
"updated_by": null,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"abilities": {
"can_update": true,
"can_delete": true
}
}
],
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"abilities": {
"can_update": true,
"can_delete": true
}
},
"... (총 18건 중 2건 표시)"
],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 18,
"from": 1,
"to": 18,
"has_more_pages": false
},
"abilities": {
"can_update": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -87,6 +312,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| definition | path | string | 예 | — | 대상 definition의 식별자 |
**요청 예시**
```http
GET /api/admin/notification-definitions/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -110,6 +344,75 @@ _단건 응답: `data` 객체의 필드._
| updated_at | string | `2026-07-06 19:15:16` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 정의를 조회했습니다.",
"data": {
"id": 1,
"type": "apidoc-sample.event",
"hook_prefix": "core",
"extension_type": "core",
"extension_identifier": "",
"name": {
"ko": "API 문서 샘플 알림",
"en": "API Doc Sample Notification"
},
"description": {
"ko": "문서 실측용 알림 정의",
"en": "Sample notification"
},
"variables": [],
"channels": [
"database",
"mail"
],
"hooks": [],
"is_active": true,
"is_default": false,
"templates": [
{
"id": 1,
"definition_id": 1,
"channel": "mail",
"subject": "API 문서 샘플 템플릿 제목",
"body": "안녕하세요 {{name}} 님, 문서 실측용 본문입니다.",
"click_url": "/admin/apidoc-sample",
"recipients": [
{
"type": "role",
"value": "admin",
"display_name": "관리자"
}
],
"is_active": true,
"is_default": false,
"user_overrides": null,
"updated_by": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_update": true,
"can_delete": true
}
}
],
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -140,10 +443,34 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification_definition.filter_update_rules`).
**요청 예시**
```http
PUT /api/admin/notification-definitions/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"channels": [
"예시값"
],
"hooks": [
"예시값"
],
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -170,9 +497,106 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| definition | path | string | 예 | — | 대상 definition의 식별자 |
**요청 예시**
```http
POST /api/admin/notification-definitions/1/reset HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| type | string | `apidoc-sample.event` | 알림 타입 (welcome, order_confirmed 등) |
| hook_prefix | string | `core` | 훅 접두사 (core.auth, sirsoft-ecommerce 등) |
| extension_type | string | `core` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | string | `` | 이 리소스를 소유한 확장의 식별자 |
| name | object | `{"ko":"API 문서 샘플 알림","en":"API Doc Sample Notification"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | object | `{"ko":"문서 실측용 알림 정의","en":"Sample notification"}` | 설명 (다국어 필드는 로케일별 값 객체) |
| variables | array | `[]` | 사용 가능 변수 메타데이터 ([{key, description}]) |
| channels | array | `["database","mail"]` | 활성 채널 (["mail", "database"]) |
| hooks | array | `[]` | 트리거 훅 목록 (["core.auth.after_register"]) |
| is_active | boolean | `true` | active 여부 |
| is_default | boolean | `true` | default 여부 |
| templates | array | `[{"id":1,"definition_id":1,"channel":"mail","subject":"AP…` | 템플릿 목록 (각 원소 identifier/name 등 — 템플릿 관계 파생) |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:43` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 정의의 모든 템플릿이 기본값으로 복원되었습니다.",
"data": {
"id": 1,
"type": "apidoc-sample.event",
"hook_prefix": "core",
"extension_type": "core",
"extension_identifier": "",
"name": {
"ko": "API 문서 샘플 알림",
"en": "API Doc Sample Notification"
},
"description": {
"ko": "문서 실측용 알림 정의",
"en": "Sample notification"
},
"variables": [],
"channels": [
"database",
"mail"
],
"hooks": [],
"is_active": true,
"is_default": true,
"templates": [
{
"id": 1,
"definition_id": 1,
"channel": "mail",
"subject": "API 문서 샘플 템플릿 제목",
"body": "안녕하세요 {{name}} 님, 문서 실측용 본문입니다.",
"click_url": "/admin/apidoc-sample",
"recipients": [
{
"type": "role",
"value": "admin",
"display_name": "관리자"
}
],
"is_active": true,
"is_default": false,
"user_overrides": null,
"updated_by": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_update": true,
"can_delete": true
}
}
],
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 12:14:43",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -199,9 +623,80 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| definition | path | string | 예 | — | 대상 definition의 식별자 |
**요청 예시**
```http
PATCH /api/admin/notification-definitions/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| type | string | `apidoc-sample.event` | 알림 타입 (welcome, order_confirmed 등) |
| hook_prefix | string | `core` | 훅 접두사 (core.auth, sirsoft-ecommerce 등) |
| extension_type | string | `core` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | string | `` | 이 리소스를 소유한 확장의 식별자 |
| name | object | `{"ko":"API 문서 샘플 알림","en":"API Doc Sample Notification"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | object | `{"ko":"문서 실측용 알림 정의","en":"Sample notification"}` | 설명 (다국어 필드는 로케일별 값 객체) |
| variables | array | `[]` | 사용 가능 변수 메타데이터 ([{key, description}]) |
| channels | array | `["database","mail"]` | 활성 채널 (["mail", "database"]) |
| hooks | array | `[]` | 트리거 훅 목록 (["core.auth.after_register"]) |
| is_active | boolean | `false` | active 여부 |
| is_default | boolean | `false` | default 여부 |
| templates | null | `null` | 템플릿 목록 (각 원소 identifier/name 등 — 템플릿 관계 파생) |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:43` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 정의 활성 상태가 변경되었습니다.",
"data": {
"id": 1,
"type": "apidoc-sample.event",
"hook_prefix": "core",
"extension_type": "core",
"extension_identifier": "",
"name": {
"ko": "API 문서 샘플 알림",
"en": "API Doc Sample Notification"
},
"description": {
"ko": "문서 실측용 알림 정의",
"en": "Sample notification"
},
"variables": [],
"channels": [
"database",
"mail"
],
"hooks": [],
"is_active": false,
"is_default": false,
"templates": null,
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 12:14:43",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
+117 -1
View File
@@ -40,6 +40,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification_log.filter_index_rules`).
**요청 예시**
```http
GET /api/admin/notification-logs?sender_user_id=1&recipient_user_id=1&search=%EC%98%88%EC%8B%9C%EA%B0%92&channel=%EC%98%88%EC%8B%9C%EA%B0%92&notification_type=%EC%98%88%EC%8B%9C%EA%B0%92&extension_type=core&status=%EC%98%88%EC%8B%9C%EA%B0%92&per_page=1&sort_by=id&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -68,6 +77,68 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| updated_at | string | `2026-07-06 19:20:23` | 최종 수정 일시 |
| abilities | object | `{"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 발송 이력을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"channel": "mail",
"notification_type": "apidoc.sample.event",
"extension_type": "core",
"extension_identifier": "",
"recipient_user_id": 6,
"recipient_identifier": "apidoc-sample-user@example.com",
"recipient_name": "API 문서 샘플 사용자",
"sender_user_id": 6,
"sender": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"recipient": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"subject": "API 문서 샘플 알림",
"body": "문서 실측용 알림 본문입니다.",
"status": "sent",
"error_message": null,
"source": "apidoc",
"sent_at": "2026-07-08 09:41:24",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_delete": true
}
}
],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
},
"abilities": {
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -93,10 +164,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
**요청 예시**
```http
POST /api/admin/notification-logs/bulk-delete HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -122,9 +213,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| notificationLog | path | string | 예 | — | 대상 notification log의 식별자 |
**요청 예시**
```http
DELETE /api/admin/notification-logs/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 발송 이력이 삭제되었습니다.",
"data": null
}
```
**에러 응답**
+133 -1
View File
@@ -32,10 +32,35 @@
| body | body | array | 예 | — | 본문 |
| locale | body | string | 아니오 | max 10 | 로케일 코드 (표시 언어/지역) |
**요청 예시**
```http
POST /api/admin/notification-templates/preview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"definition_id": 1,
"subject": [
"예시값"
],
"body": [
"예시값"
],
"locale": "ko"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -68,10 +93,38 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification_template.filter_update_rules`).
**요청 예시**
```http
PUT /api/admin/notification-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"subject": [
"예시값"
],
"body": [
"예시값"
],
"click_url": "https://example.com",
"recipients": [
"예시값"
],
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -98,10 +151,23 @@
| --- | --- | --- | --- | --- | --- |
| template | path | string | 예 | — | 대상 template의 식별자 |
**요청 예시**
```http
POST /api/admin/notification-templates/1/reset HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -127,9 +193,75 @@
| --- | --- | --- | --- | --- | --- |
| template | path | string | 예 | — | 대상 template의 식별자 |
**요청 예시**
```http
PATCH /api/admin/notification-templates/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| definition_id | integer | `1` | definition 식별자 (연관 리소스 참조) |
| channel | string | `mail` | 채널: mail, database, fcm |
| subject | string | `API 문서 샘플 템플릿 제목` | 다국어 제목 ({"ko": "...", "en": "..."}) |
| body | string | `안녕하세요 {{name}} 님, 문서 실측용 본문입니다.` | 다국어 본문 ({"ko": "...", "en": "..."}) |
| click_url | string | `/admin/apidoc-sample` | click URL |
| recipients | array | `[{"type":"role","value":"admin","display_name":"관리자"}]` | 수신자 규칙 JSON ([{type, value, relation, exclude_trigger_user}]) |
| is_active | boolean | `false` | active 여부 |
| is_default | boolean | `false` | default 여부 |
| user_overrides | array | `["is_active"]` | 사용자가 수정한 필드명 목록 |
| updated_by | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | 최종 수정한 사용자 정보 (uuid/name — updated_by 관계 파생, 없으면 null) |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:43` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 템플릿 활성 상태가 변경되었습니다.",
"data": {
"id": 1,
"definition_id": 1,
"channel": "mail",
"subject": "API 문서 샘플 템플릿 제목",
"body": "안녕하세요 {{name}} 님, 문서 실측용 본문입니다.",
"click_url": "/admin/apidoc-sample",
"recipients": [
{
"type": "role",
"value": "admin",
"display_name": "관리자"
}
],
"is_active": false,
"is_default": false,
"user_overrides": [
"is_active"
],
"updated_by": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 12:14:43",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
+330 -4
View File
@@ -34,12 +34,46 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification.filter_index_rules`).
**요청 예시**
```http
GET /api/admin/notifications?read=unread&per_page=1&page=1&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 목록을 조회했습니다.",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -65,9 +99,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/admin/notifications/all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| deleted_count | integer | `0` | deleted 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "모든 알림이 삭제되었습니다.",
"data": {
"deleted_count": 0
}
}
```
**에러 응답**
@@ -93,9 +156,38 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/notifications/read-all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| marked_count | integer | `0` | marked 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "모든 알림을 읽음 처리했습니다.",
"data": {
"marked_count": 0
}
}
```
**에러 응답**
@@ -125,10 +217,30 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification.filter_batch_read_rules`).
**요청 예시**
```http
POST /api/admin/notifications/read-batch HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -154,6 +266,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/notifications/unread-count HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -162,6 +283,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| unread_count | integer | `0` | unread 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "미읽음 알림 수를 조회했습니다.",
"data": {
"unread_count": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -188,10 +325,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| notification | path | string | 예 | — | 대상 notification의 식별자 |
**요청 예시**
```http
DELETE /api/admin/notifications/{notification} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -219,10 +369,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| notification | path | string | 예 | — | 대상 notification의 식별자 |
**요청 예시**
```http
PATCH /api/admin/notifications/{notification}/read HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -255,12 +418,46 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification.filter_index_rules`).
**요청 예시**
```http
GET /api/user/notifications?read=unread&per_page=1&page=1&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "알림 목록을 조회했습니다.",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -285,9 +482,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/user/notifications/all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| deleted_count | integer | `0` | deleted 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "모든 알림이 삭제되었습니다.",
"data": {
"deleted_count": 0
}
}
```
**에러 응답**
@@ -312,9 +538,38 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/user/notifications/read-all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| marked_count | integer | `0` | marked 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "모든 알림을 읽음 처리했습니다.",
"data": {
"marked_count": 0
}
}
```
**에러 응답**
@@ -343,10 +598,30 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.notification.filter_batch_read_rules`).
**요청 예시**
```http
POST /api/user/notifications/read-batch HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -371,6 +646,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/user/notifications/unread-count HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -379,6 +663,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| unread_count | integer | `0` | unread 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "미읽음 알림 수를 조회했습니다.",
"data": {
"unread_count": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -404,10 +704,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| notification | path | string | 예 | — | 대상 notification의 식별자 |
**요청 예시**
```http
DELETE /api/user/notifications/{notification} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -434,10 +747,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| notification | path | string | 예 | — | 대상 notification의 식별자 |
**요청 예시**
```http
PATCH /api/user/notifications/{notification}/read HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+20
View File
@@ -33,10 +33,30 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.change_password_validation_rules`).
**요청 예시**
```http
PUT /api/me/password HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"current_password": "Password123!",
"password": "Password123!",
"password_confirmation": "Password123!"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
+530 -2
View File
@@ -36,6 +36,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.index_validation_rules`).
**요청 예시**
```http
GET /api/admin/plugins?search=%EC%98%88%EC%8B%9C%EA%B0%92&filters=%EC%98%88%EC%8B%9C%EA%B0%92&status=installed&per_page=1&page=1&include_hidden=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -71,6 +80,118 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| incompatible_required_version | null | `null` | 요구 코어 버전 미충족 시 필요한 버전 (호환되면 null) |
| abilities | object | `{"can_install":true,"can_activate":true,"can_uninstall":t…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "플러그인 목록을 성공적으로 가져왔습니다.",
"data": {
"data": [
{
"id": null,
"identifier": "sirsoft-ckeditor5",
"vendor": "sirsoft",
"name": "CKEditor 5 WYSIWYG 에디터",
"version": "1.0.0",
"description": "CKEditor 5를 이용한 WYSIWYG 에디터 플러그인입니다. 플러그인 설치만으로 기존 HtmlEditor가 교체됩니다.",
"dependencies": [],
"permissions": [],
"roles": [],
"config": [],
"hooks": [],
"status": "uninstalled",
"is_installed": false,
"has_settings": true,
"settings_route": "/admin/plugins/sirsoft-ckeditor5/settings",
"assets": {
"js": "/api/plugins/assets/sirsoft-ckeditor5/dist/js/plugin.iife.js",
"css": null,
"priority": 100
},
"update_available": false,
"update_source": null,
"latest_version": null,
"file_version": null,
"github_url": null,
"github_changelog_url": null,
"is_pending": false,
"is_bundled": false,
"deactivated_reason": null,
"deactivated_at": null,
"incompatible_required_version": null,
"abilities": {
"can_install": true,
"can_activate": true,
"can_uninstall": true
}
},
{
"id": null,
"identifier": "sirsoft-daum_postcode",
"vendor": "sirsoft",
"name": "Daum 우편번호",
"version": "1.0.0",
"description": "Daum 우편번호 서비스를 통한 주소 검색 기능을 제공하는 플러그인입니다. API 키 없이 무료로 사용할 수 있습니다.",
"dependencies": [],
"permissions": [],
"roles": [],
"config": [],
"hooks": [],
"status": "uninstalled",
"is_installed": false,
"has_settings": true,
"settings_route": "/admin/plugins/sirsoft-daum_postcode/settings",
"assets": {
"js": "/api/plugins/assets/sirsoft-daum_postcode/dist/js/plugin.iife.js",
"css": null,
"priority": 100
},
"update_available": false,
"update_source": null,
"latest_version": null,
"file_version": null,
"github_url": null,
"github_changelog_url": null,
"is_pending": false,
"is_bundled": false,
"deactivated_reason": null,
"deactivated_at": null,
"incompatible_required_version": null,
"abilities": {
"can_install": true,
"can_activate": true,
"can_uninstall": true
}
},
"... (총 9건 중 2건 표시)"
],
"pagination": {
"total": 9,
"current_page": 1,
"last_page": 1,
"per_page": 25
},
"meta": {
"total_plugins": 9,
"active_plugins": 0,
"inactive_plugins": 0,
"installed_plugins": 0,
"uninstalled_plugins": 9
},
"abilities": {
"can_install": true,
"can_activate": true,
"can_uninstall": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -99,10 +220,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.activate_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/activate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"plugin_name": "예시 이름",
"force": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -126,10 +266,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/plugins/check-updates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -157,10 +310,29 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.deactivate_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/deactivate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"plugin_name": "예시 이름",
"force": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -191,10 +363,35 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.install_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/install HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"plugin_name": "예시 이름",
"vendor_mode": "auto",
"dependencies": [
"예시값"
],
"language_packs": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -222,10 +419,31 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.install_from_file_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/install-from-file HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -253,10 +471,28 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.install_from_github_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/install-from-github HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"github_url": "https://example.com"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -280,6 +516,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/plugins/installed HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -315,6 +560,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| incompatible_required_version | null | `null` | 요구 코어 버전 미충족 시 필요한 버전 (호환되면 null) |
| abilities | object | `{"can_install":true,"can_activate":true,"can_uninstall":t…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "플러그인 목록을 성공적으로 가져왔습니다.",
"data": {
"data": [],
"meta": {
"total_plugins": 0,
"active_plugins": 0,
"inactive_plugins": 0,
"installed_plugins": 0,
"uninstalled_plugins": 0
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -338,10 +606,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| file | body | file | 예 | max 51200 | 업로드 파일 |
**요청 예시**
```http
POST /api/admin/plugins/manifest-preview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -369,10 +658,28 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.refresh_layouts_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/refresh-layouts HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"plugin_name": "예시 이름"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -401,10 +708,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.uninstall_validation_rules`).
**요청 예시**
```http
DELETE /api/admin/plugins/uninstall?plugin_name=%EC%98%88%EC%8B%9C%20%EC%9D%B4%EB%A6%84&delete_data=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -435,10 +755,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.extension.changelog_rules`).
**요청 예시**
```http
GET /api/admin/plugins/{identifier}/changelog?source=active&from_version=%EC%98%88%EC%8B%9C%EA%B0%92&to_version=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -465,10 +798,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/plugins/{identifier}/dependent-templates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -494,10 +840,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/plugins/{identifier}/license HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -523,10 +882,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/plugins/{identifier}/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -554,10 +926,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin_settings.update_rules`).
**요청 예시**
```http
PUT /api/admin/plugins/{identifier}/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -584,10 +969,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/admin/plugins/{identifier}/settings/layout HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -613,10 +1011,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| pluginName | path | string | 예 | — | 대상 plugin의 이름 (식별자) |
**요청 예시**
```http
GET /api/admin/plugins/{pluginName} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -642,10 +1053,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| pluginName | path | string | 예 | — | 대상 plugin의 이름 (식별자) |
**요청 예시**
```http
GET /api/admin/plugins/{pluginName}/check-modified-layouts HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -671,10 +1095,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| pluginName | path | string | 예 | — | 대상 plugin의 이름 (식별자) |
**요청 예시**
```http
GET /api/admin/plugins/{pluginName}/install-preview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -700,10 +1137,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| pluginName | path | string | 예 | — | 대상 plugin의 이름 (식별자) |
**요청 예시**
```http
GET /api/admin/plugins/{pluginName}/uninstall-info HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -734,10 +1184,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.plugin.perform_update_validation_rules`).
**요청 예시**
```http
POST /api/admin/plugins/{pluginName}/update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"layout_strategy": "overwrite",
"vendor_mode": "auto",
"force": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -764,13 +1234,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
| path | path | string | 예 | — | 경로 |
| identifier | query | string | 예 | — | 대상 확장/리소스의 식별자 |
| path | query | string | 예 | — | 경로 |
**요청 예시**
```http
GET /api/plugins/assets/{identifier}/{path} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -793,10 +1273,22 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/bundle.css HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-200 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-200 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회 — 활성 에셋이 없으면 빈 200 응답)._
@@ -816,10 +1308,22 @@ _대표 에러 없음 (공개 조회 — 활성 에셋이 없으면 빈 200 응
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/bundle.js HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-200 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-200 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회 — 활성 에셋이 없으면 빈 200 응답)._
@@ -841,10 +1345,22 @@ _대표 에러 없음 (공개 조회 — 활성 에셋이 없으면 빈 200 응
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/plugins/{identifier}/components.json HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -868,10 +1384,22 @@ _대표 에러 없음 (공개 조회 — 활성 에셋이 없으면 빈 200 응
| --- | --- | --- | --- | --- | --- |
| identifier | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/plugins/{identifier}/editor-spec HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+76
View File
@@ -27,10 +27,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/user/profile HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-403 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-403 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -81,10 +94,47 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.update_profile_validation_rules`).
**요청 예시**
```http
PUT /api/user/profile HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"name": "예시 이름",
"nickname": "예시 이름",
"email": "user@example.com",
"password": "Password123!",
"current_password": "Password123!",
"language": "ko",
"country": "KR",
"timezone": "Asia/Seoul",
"homepage": "https://example.com",
"mobile": "010-1234-5678",
"phone": "010-1234-5678",
"zipcode": "06234",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"signature": "예시값",
"bio": "예시 내용입니다.",
"notify_post_complete": true,
"notify_post_reply": true,
"notify_comment": true,
"notify_reply_comment": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-403 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -109,10 +159,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/user/profile/activity-log HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-403 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-403 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -136,10 +199,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/user/profile/update-language HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+9176 -4
View File
File diff suppressed because it is too large Load Diff
+598 -5
View File
@@ -45,6 +45,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.list_validation_rules`).
**요청 예시**
```http
GET /api/admin/schedules?page=1&per_page=1&filters=%EC%98%88%EC%8B%9C%EA%B0%92&type=artisan&frequency=everyMinute&status=active&last_result=success&without_overlapping=0&run_in_maintenance=0&extension_type=core&extension_identifier=example-key&created_from=2026-01-01&created_to=2026-01-01&sort_by=created_at&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -71,6 +80,70 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| created_at | string | `2026-07-06` | 생성 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true,"c…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"name": "API 문서 샘플 스케줄",
"type": "artisan",
"type_label": "Artisan 커맨드",
"command": "cache:clear",
"expression": "0 3 * * *",
"frequency": "daily",
"frequency_label": "매일",
"without_overlapping": true,
"run_in_maintenance": false,
"is_active": true,
"last_result": "success",
"last_result_label": "성공",
"last_run_at": "2026-07-07T10:41:24+09:00",
"last_duration": null,
"next_run_at": "2026-07-08T12:00:00+09:00",
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_run": true
}
}
],
"statistics": {
"total": 1,
"active": 1,
"inactive": 0,
"success": 1,
"failed": 0,
"running": 0,
"never_run": 0
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -109,9 +182,104 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.create_validation_rules`).
**요청 예시**
```http
POST /api/admin/schedules HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"description": "예시 내용입니다.",
"type": "artisan",
"command": "예시값",
"expression": "예시값",
"frequency": "everyMinute",
"without_overlapping": true,
"run_in_maintenance": true,
"timeout": 1,
"is_active": true,
"extension_type": "core",
"extension_identifier": "example-key"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `14` | 기본 키 (내부 식별자) |
| name | string | `실측 예시값` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | string | `실측 예시값` | 설명 (다국어 필드는 로케일별 값 객체) |
| type | string | `artisan` | 작업 유형: artisan(Artisan 커맨드), shell(쉘 명령), url(URL 호출) |
| type_label | string | `Artisan 커맨드` | `type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| command | string | `실측 예시값` | 명령어 또는 URL |
| expression | string | `실측 예시값` | Cron 표현식 |
| frequency | string | `everyMinute` | 실행 주기: everyMinute(매분), hourly(매시간), daily(매일), weekly(매주), monthly(매월), custom(사용자 정의) |
| frequency_label | string | `매분` | `frequency` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| without_overlapping | boolean | `true` | 중복 실행 방지 여부: 0(허용), 1(방지) |
| run_in_maintenance | boolean | `true` | 점검 모드 실행 여부: 0(비실행), 1(실행) |
| timeout | integer | `1` | 실행 제한 시간 (초) |
| is_active | boolean | `true` | active 여부 |
| last_result | string | `never` | 마지막 실행 결과: success(성공), failed(실패), running(실행중), never(미실행) |
| last_result_label | string | `미실행` | `last_result` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| last_duration | null | `null` | 마지막 실행 소요 시간 (초/밀리초 — 실행 이력 파생) |
| extension_type | string | `core` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | string | `probe_6a4dc0a862b69` | 이 리소스를 소유한 확장의 식별자 |
| creator | object | `{"uuid":"a234c2b1-cde8-437f-b28b-23323be2b98d","name":"AP…` | 생성자 정보 객체 (uuid/name/email — creator 관계 파생) |
| created_at | string | `2026-07-08 12:14:48` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:48` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true,"c…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 201
```
```json
{
"success": true,
"message": "스케줄이 생성되었습니다.",
"data": {
"id": 14,
"name": "실측 예시값",
"description": "실측 예시값",
"type": "artisan",
"type_label": "Artisan 커맨드",
"command": "실측 예시값",
"expression": "실측 예시값",
"frequency": "everyMinute",
"frequency_label": "매분",
"without_overlapping": true,
"run_in_maintenance": true,
"timeout": 1,
"is_active": true,
"last_result": "never",
"last_result_label": "미실행",
"last_duration": null,
"extension_type": "core",
"extension_identifier": "probe_6a4dc0a862b69",
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 12:14:48",
"updated_at": "2026-07-08 12:14:48",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_run": true
}
}
}
```
**에러 응답**
@@ -140,10 +308,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.bulk_delete_validation_rules`).
**요청 예시**
```http
DELETE /api/admin/schedules/bulk?ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -172,10 +353,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.bulk_update_status_validation_rules`).
**요청 예시**
```http
PATCH /api/admin/schedules/bulk-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -201,10 +403,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| historyId | path | string | 예 | — | 대상 history의 식별자 |
**요청 예시**
```http
DELETE /api/admin/schedules/history/{historyId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -228,6 +443,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/schedules/statistics HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -242,6 +466,28 @@ _단건 응답: `data` 객체의 필드._
| running | integer | `0` | 마지막 실행 결과가 실행중(running)인 스케줄 수 |
| never_run | integer | `0` | 아직 한 번도 실행되지 않은(last_result=never) 스케줄 수 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄 통계를 조회했습니다.",
"data": {
"total": 1,
"active": 1,
"inactive": 0,
"success": 1,
"failed": 0,
"running": 0,
"never_run": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -266,9 +512,34 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| schedule | path | string | 예 | — | 대상 schedule의 식별자 |
**요청 예시**
```http
DELETE /api/admin/schedules/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄이 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -295,6 +566,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| schedule | path | string | 예 | — | 대상 schedule의 식별자 |
**요청 예시**
```http
GET /api/admin/schedules/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -326,6 +606,53 @@ _단건 응답: `data` 객체의 필드._
| updated_at | string | `2026-07-06 19:20:23` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true,"c…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄 목록을 조회했습니다.",
"data": {
"id": 1,
"name": "API 문서 샘플 스케줄",
"description": "문서 실측용 스케줄",
"type": "artisan",
"type_label": "Artisan 커맨드",
"command": "cache:clear",
"expression": "0 3 * * *",
"frequency": "daily",
"frequency_label": "매일",
"without_overlapping": true,
"run_in_maintenance": false,
"timeout": 300,
"is_active": true,
"last_result": "success",
"last_result_label": "성공",
"last_run_at": "2026-07-07T10:41:24+09:00",
"last_duration": null,
"next_run_at": "2026-07-08T12:00:00+09:00",
"extension_type": null,
"extension_identifier": null,
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_run": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -365,9 +692,106 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.update_validation_rules`).
**요청 예시**
```http
PUT /api/admin/schedules/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"description": "예시 내용입니다.",
"type": "artisan",
"command": "예시값",
"expression": "예시값",
"frequency": "everyMinute",
"without_overlapping": true,
"run_in_maintenance": true,
"timeout": 1,
"is_active": true,
"extension_type": "core",
"extension_identifier": "example-key"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | string | `실측 예시값` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | string | `실측 예시값` | 설명 (다국어 필드는 로케일별 값 객체) |
| type | string | `artisan` | 작업 유형: artisan(Artisan 커맨드), shell(쉘 명령), url(URL 호출) |
| type_label | string | `Artisan 커맨드` | `type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| command | string | `실측 예시값` | 명령어 또는 URL |
| expression | string | `실측 예시값` | Cron 표현식 |
| frequency | string | `everyMinute` | 실행 주기: everyMinute(매분), hourly(매시간), daily(매일), weekly(매주), monthly(매월), custom(사용자 정의) |
| frequency_label | string | `매분` | `frequency` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| without_overlapping | boolean | `true` | 중복 실행 방지 여부: 0(허용), 1(방지) |
| run_in_maintenance | boolean | `true` | 점검 모드 실행 여부: 0(비실행), 1(실행) |
| timeout | integer | `1` | 실행 제한 시간 (초) |
| is_active | boolean | `true` | active 여부 |
| last_result | string | `success` | 마지막 실행 결과: success(성공), failed(실패), running(실행중), never(미실행) |
| last_result_label | string | `성공` | `last_result` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| last_run_at | string | `2026-07-07T10:41:24+09:00` | last run 일시 |
| last_duration | null | `null` | 마지막 실행 소요 시간 (초/밀리초 — 실행 이력 파생) |
| extension_type | string | `core` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | string | `probe_6a4dc0a8e44c0` | 이 리소스를 소유한 확장의 식별자 |
| creator | object | `{"uuid":"a234c2b1-cde8-437f-b28b-23323be2b98d","name":"AP…` | 생성자 정보 객체 (uuid/name/email — creator 관계 파생) |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:48` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true,"c…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄이 수정되었습니다.",
"data": {
"id": 1,
"name": "실측 예시값",
"description": "실측 예시값",
"type": "artisan",
"type_label": "Artisan 커맨드",
"command": "실측 예시값",
"expression": "실측 예시값",
"frequency": "everyMinute",
"frequency_label": "매분",
"without_overlapping": true,
"run_in_maintenance": true,
"timeout": 1,
"is_active": true,
"last_result": "success",
"last_result_label": "성공",
"last_run_at": "2026-07-07T10:41:24+09:00",
"last_duration": null,
"extension_type": "core",
"extension_identifier": "probe_6a4dc0a8e44c0",
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 12:14:48",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_run": true
}
}
}
```
**에러 응답**
@@ -395,9 +819,83 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| schedule | path | string | 예 | — | 대상 schedule의 식별자 |
**요청 예시**
```http
POST /api/admin/schedules/1/duplicate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `15` | 기본 키 (내부 식별자) |
| name | string | `API 문서 샘플 스케줄 (복사본)` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | string | `문서 실측용 스케줄` | 설명 (다국어 필드는 로케일별 값 객체) |
| type | string | `artisan` | 작업 유형: artisan(Artisan 커맨드), shell(쉘 명령), url(URL 호출) |
| type_label | string | `Artisan 커맨드` | `type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| command | string | `cache:clear` | 명령어 또는 URL |
| expression | string | `0 3 * * *` | Cron 표현식 |
| frequency | string | `daily` | 실행 주기: everyMinute(매분), hourly(매시간), daily(매일), weekly(매주), monthly(매월), custom(사용자 정의) |
| frequency_label | string | `매일` | `frequency` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| without_overlapping | boolean | `true` | 중복 실행 방지 여부: 0(허용), 1(방지) |
| run_in_maintenance | boolean | `false` | 점검 모드 실행 여부: 0(비실행), 1(실행) |
| timeout | integer | `300` | 실행 제한 시간 (초) |
| is_active | boolean | `false` | active 여부 |
| last_result | string | `never` | 마지막 실행 결과: success(성공), failed(실패), running(실행중), never(미실행) |
| last_result_label | string | `미실행` | `last_result` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| last_duration | null | `null` | 마지막 실행 소요 시간 (초/밀리초 — 실행 이력 파생) |
| extension_type | null | `null` | 이 리소스를 소유한 확장의 타입 (core/module/plugin/template) |
| extension_identifier | null | `null` | 이 리소스를 소유한 확장의 식별자 |
| created_at | string | `2026-07-08 12:14:48` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:48` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true,"c…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 201
```
```json
{
"success": true,
"message": "스케줄이 복제되었습니다.",
"data": {
"id": 15,
"name": "API 문서 샘플 스케줄 (복사본)",
"description": "문서 실측용 스케줄",
"type": "artisan",
"type_label": "Artisan 커맨드",
"command": "cache:clear",
"expression": "0 3 * * *",
"frequency": "daily",
"frequency_label": "매일",
"without_overlapping": true,
"run_in_maintenance": false,
"timeout": 300,
"is_active": false,
"last_result": "never",
"last_result_label": "미실행",
"last_duration": null,
"extension_type": null,
"extension_identifier": null,
"created_at": "2026-07-08 12:14:48",
"updated_at": "2026-07-08 12:14:48",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_run": true
}
}
}
```
**에러 응답**
@@ -434,12 +932,46 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.schedule.history_list_validation_rules`).
**요청 예시**
```http
GET /api/admin/schedules/1/history?page=1&per_page=1&status=success&trigger_type=scheduled&started_from=2026-01-01&started_to=2026-01-01&sort_by=started_at&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "실행 이력을 조회했습니다.",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -466,9 +998,70 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| schedule | path | string | 예 | — | 대상 schedule의 식별자 |
**요청 예시**
```http
POST /api/admin/schedules/1/run HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `7` | 기본 키 (내부 식별자) |
| schedule_id | integer | `1` | schedule 식별자 (연관 리소스 참조) |
| started_at | string | `2026-07-08T12:14:49+09:00` | started 일시 |
| ended_at | string | `2026-07-08T12:14:49+09:00` | ended 일시 |
| duration | integer | `0` | 실행 소요 시간 (초/밀리초) |
| duration_formatted | null | `null` | `duration` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| status | string | `success` | 상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조) |
| status_label | string | `성공` | 상태의 사람이 읽는 라벨 (상태 Enum label() 산물) |
| exit_code | integer | `0` | 실행 종료 코드 (0=성공, 그 외=실패) |
| memory_usage | integer | `427352` | 실행 중 최대 메모리 사용량 (바이트) |
| memory_usage_formatted | string | `417.34 KB` | `memory_usage` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| output | string | ` INFO Application cache cleared s…` | 실행 표준 출력 내용 |
| error_output | null | `null` | 실행 표준 에러 출력 내용 (없으면 빈 문자열/null) |
| trigger_type | string | `manual` | 동작을 유발한 방식/주체 구분 값 |
| trigger_type_label | string | `수동 실행` | `trigger_type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| created_at | string | `2026-07-08 12:14:49` | 생성 일시 |
| updated_at | string | `2026-07-08 12:14:49` | 최종 수정 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "스케줄이 실행되었습니다.",
"data": {
"id": 7,
"schedule_id": 1,
"started_at": "2026-07-08T12:14:49+09:00",
"ended_at": "2026-07-08T12:14:49+09:00",
"duration": 0,
"duration_formatted": null,
"status": "success",
"status_label": "성공",
"exit_code": 0,
"memory_usage": 427352,
"memory_usage_formatted": "417.34 KB",
"output": "\n INFO Application cache cleared successfully. \n\r\n",
"error_output": null,
"trigger_type": "manual",
"trigger_type_label": "수동 실행",
"created_at": "2026-07-08 12:14:49",
"updated_at": "2026-07-08 12:14:49"
}
}
```
**에러 응답**
+25
View File
@@ -37,6 +37,14 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.search.validation_rules`).
**요청 예시**
```http
GET /api/search?q=%EC%98%88%EC%8B%9C%EA%B0%92&type=%EC%98%88%EC%8B%9C%EA%B0%92&sort=relevance&page=1&per_page=1&board_slug=example-key&category_id=1 HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -46,6 +54,23 @@ _단건 응답: `data` 객체의 필드._
| q | string | `` | 실제 검색에 사용된 검색어 (요청 `q` 를 trim 하여 에코, 검색어가 비어 있으면 빈 문자열) |
| total | integer | `0` | 전체 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "검색어를 입력해주세요.",
"data": {
"q": "",
"total": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+125 -1
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/seo/cached-urls HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -36,6 +45,23 @@ _단건 응답: `data` 객체의 필드._
| urls | array | `[]` | 현재 사전 렌더 캐시에 남아 있는 봇 대상 페이지 URL 목록 (SeoCacheManager 인덱스에서 조회). |
| count | integer | `0` | 캐시된 URL 개수 (`urls` 배열 길이). |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"urls": [],
"count": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -63,10 +89,29 @@ SeoCacheManager 인덱스에서 현재 캐시된 SEO 페이지 URL 목록과 개
| layout | body | string | 아니오 | — | 무효화 대상 레이아웃명. 지정 시 해당 레이아웃의 SEO 캐시만 삭제하고 무효화된 항목 수를 반환하며, 미지정 시 전체 SEO 캐시를 삭제한다. |
| module | body | string | 아니오 | — | 모듈 식별자 필터. 검증 규칙에는 정의되어 있으나 현재 컨트롤러 로직에서는 사용되지 않는다(향후 모듈 단위 캐시 무효화 확장 예약 필드). |
**요청 예시**
```http
POST /api/admin/seo/clear-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"layout": "예시값",
"module": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -92,9 +137,42 @@ SEO 사전 렌더 캐시를 삭제합니다. `layout` 을 지정하면 해당
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/seo/sitemap/regenerate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| last_updated_at | string | `2026-07-08T03:14:49+00:00` | last updated 일시 |
| size_bytes | integer | `25489` | 크기 (바이트) |
| ttl | integer | `86400` | 캐시 유효 시간 (Time To Live, 초) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "Sitemap 재생성이 완료되었습니다.",
"data": {
"last_updated_at": "2026-07-08T03:14:49+00:00",
"size_bytes": 25489,
"ttl": 86400
}
}
```
**에러 응답**
@@ -120,6 +198,15 @@ sitemap.xml 을 즉시 재생성합니다. SitemapManager 에 위임하며 큐
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/seo/stats HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -130,6 +217,30 @@ _단건 응답: `data` 객체의 필드._
| by_layout | array | `[]` | 레이아웃별 통계 목록 (`layout_name` 으로 그룹핑). 각 원소는 `layout_name`·`total`·`hits`·`misses`·`hit_rate`·`avg_response_time_ms` 를 가지며, 레이아웃 단위로 캐시 효율을 비교하는 용도. |
| by_module | array | `[]` | 모듈별 통계 목록 (`module_identifier` 로 그룹핑). 각 원소는 `module_identifier`·`total`·`hits`·`misses`·`hit_rate`·`avg_response_time_ms` 를 가지며, 어느 확장 모듈의 SEO 페이지가 캐시로 재사용되는지 파악하는 용도. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"overall": {
"total_entries": 0,
"hits": 0,
"misses": 0,
"hit_rate": 0,
"avg_response_time_ms": null
},
"by_layout": [],
"by_module": []
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -154,10 +265,23 @@ SEO 캐시 적중 현황을 최근 7일 기준으로 전체·레이아웃별·
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/seo/warmup HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+744 -1
View File
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -49,6 +58,368 @@ _단건 응답: `data` 객체의 필드._
| available_drivers | object | `{"storage":[{"id":"local","label":{"ko":"로컬","en":"Local"…` | 드라이버 선택지 카탈로그 (DriverRegistryService 산물). 종류별(storage/cache/session/queue 등) 선택 가능한 드라이버 목록을 id/다국어 label 형태로 제공 |
| abilities | object | `{"can_update":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "설정을 성공적으로 가져왔습니다.",
"data": {
"general": {
"site_name": "Test Site",
"site_url": "https://test.example.com",
"site_description": "",
"admin_email": "admin@example.com",
"timezone": "Asia/Seoul",
"language": "ko",
"currency": "KRW",
"maintenance_mode": false,
"site_logo": []
},
"security": {
"force_https": true,
"login_attempt_enabled": true,
"auth_token_lifetime": "{MASKED}",
"max_login_attempts": 5,
"login_lockout_time": 30,
"two_factor_auth": false,
"password_min_length": "{MASKED}",
"require_password_special_char": "{MASKED}"
},
"mail": {
"mailer": "smtp",
"host": "",
"port": 587,
"username": "",
"password": "{MASKED}",
"encryption": "tls",
"mailgun_domain": "",
"mailgun_secret": "{MASKED}",
"mailgun_endpoint": "api.mailgun.net",
"ses_key": "",
"ses_secret": "{MASKED}",
"ses_region": "ap-northeast-2",
"from_address": "heuristing@gmail.com",
"from_name": "그누보드7"
},
"upload": {
"max_file_size": 10,
"allowed_extensions": [
"jpg",
"jpeg",
"png",
"gif",
"webp",
"pdf",
"doc",
"docx",
"xls",
"xlsx",
"zip"
],
"image_max_width": 2000,
"image_max_height": 2000,
"image_quality": 85
},
"seo": {
"meta_title_suffix": "",
"meta_description": "",
"meta_keywords": "",
"google_analytics_id": "",
"google_site_verification": "",
"naver_site_verification": "",
"bot_user_agents": [
"kakaotalk-scrap",
"Meta-ExternalAgent",
"ChatGPT-User"
],
"bot_detection_enabled": true,
"bot_detection_library_enabled": true,
"og_default_site_name": "",
"og_image_default_width": 1200,
"og_image_default_height": 630,
"twitter_default_card": "summary_large_image",
"twitter_default_site": "",
"cache_enabled": true,
"cache_ttl": 7200,
"sitemap_enabled": true,
"sitemap_cache_ttl": 86400,
"sitemap_schedule": "daily",
"sitemap_schedule_time": "02:00",
"sitemap_last_updated_at": "2026-07-08T03:14:49+00:00",
"generator_enabled": true,
"generator_content": ""
},
"advanced": {
"cache_enabled": true,
"cache_default_ttl": 86400,
"layout_cache_enabled": true,
"layout_cache_ttl": 3600,
"seo_cache_enabled": true,
"seo_cache_ttl": 7200,
"seo_sitemap_cache_ttl": 86400,
"stats_cache_enabled": true,
"stats_cache_ttl": 1800,
"notification_cache_ttl": 3600,
"extension_status_cache_ttl": 86400,
"geoip_cache_enabled": true,
"geoip_cache_ttl": 86400,
"version_check_cache_ttl": 3600,
"debug_mode": true,
"sql_query_log": false,
"log_level": "error",
"core_update_github_url": "https://github.com/custom/repo",
"core_update_github_token": "{MASKED}",
"geoip_enabled": false,
"geoip_license_key": null,
"geoip_auto_update_enabled": true,
"geoip_last_updated_at": ""
},
"cache": {
"cache_enabled": true,
"cache_default_ttl": 86400,
"layout_cache_enabled": true,
"layout_cache_ttl": 3600,
"seo_cache_enabled": true,
"seo_cache_ttl": 7200,
"seo_sitemap_cache_ttl": 86400,
"stats_cache_enabled": true,
"stats_cache_ttl": 1800,
"notification_cache_ttl": 3600,
"extension_status_cache_ttl": 86400,
"geoip_cache_enabled": true,
"geoip_cache_ttl": 86400,
"version_check_cache_ttl": 3600
},
"debug": {
"debug_mode": true,
"sql_query_log": false,
"log_level": "error"
},
"drivers": {
"storage_driver": "local",
"s3_bucket": null,
"s3_region": "ap-northeast-2",
"s3_access_key": null,
"s3_secret_key": null,
"s3_url": null,
"cache_driver": "file",
"redis_host": "127.0.0.1",
"redis_port": 6379,
"redis_password": null,
"redis_database": "1",
"memcached_host": "127.0.0.1",
"memcached_port": 11211,
"session_driver": "file",
"session_lifetime": 120,
"queue_driver": "sync",
"log_driver": "daily",
"log_level": "error",
"log_days": 14,
"websocket_enabled": true,
"websocket_app_id": "test-app-id",
"websocket_app_key": "{MASKED}",
"websocket_app_secret": "{MASKED}",
"websocket_host": "localhost",
"websocket_port": 8080,
"websocket_scheme": "https",
"websocket_verify_ssl": true,
"websocket_server_host": "127.0.0.1",
"websocket_server_port": 8080,
"websocket_server_scheme": "http",
"search_engine_driver": "mysql-fulltext"
},
"core_update": {
"core_update_github_url": "https://github.com/custom/repo",
"core_update_github_token": "{MASKED}"
},
"geoip": {
"geoip_enabled": false,
"geoip_license_key": null,
"geoip_auto_update_enabled": true,
"geoip_last_updated_at": ""
},
"notifications": {
"channels": [
{
"id": "mail",
"is_active": true,
"sort_order": 1
},
{
"id": "database",
"is_active": true,
"sort_order": 2
}
]
},
"identity": {
"default_provider": "g7:core.mail",
"purpose_providers": {
"signup": null,
"password_reset": null,
"inicis": {
"adult_verification": "inicis"
}
},
"challenge_ttl_minutes": 15,
"max_attempts": 5
},
"available_drivers": {
"storage": [
{
"id": "local",
"label": {
"ko": "로컬",
"en": "Local",
"fr": "로컬"
}
},
{
"id": "s3",
"label": {
"ko": "Amazon S3",
"en": "Amazon S3",
"fr": "Amazon S3"
}
}
],
"cache": [
{
"id": "file",
"label": {
"ko": "파일",
"en": "File",
"fr": "파일"
}
},
{
"id": "redis",
"label": {
"ko": "Redis",
"en": "Redis",
"fr": "Redis"
}
}
],
"session": [
{
"id": "file",
"label": {
"ko": "파일",
"en": "File",
"fr": "파일"
}
},
{
"id": "database",
"label": {
"ko": "데이터베이스",
"en": "Database",
"fr": "데이터베이스"
}
},
{
"id": "redis",
"label": {
"ko": "Redis",
"en": "Redis",
"fr": "Redis"
}
}
],
"queue": [
{
"id": "sync",
"label": {
"ko": "동기",
"en": "Sync",
"fr": "동기"
}
},
{
"id": "database",
"label": {
"ko": "데이터베이스",
"en": "Database",
"fr": "데이터베이스"
}
},
{
"id": "redis",
"label": {
"ko": "Redis",
"en": "Redis",
"fr": "Redis"
}
}
],
"log": [
{
"id": "single",
"label": {
"ko": "단일 파일",
"en": "Single File",
"fr": "단일 파일"
}
},
{
"id": "daily",
"label": {
"ko": "일별 파일",
"en": "Daily File",
"fr": "일별 파일"
}
}
],
"websocket": [
{
"id": "reverb",
"label": {
"ko": "Laravel Reverb",
"en": "Laravel Reverb",
"fr": "Laravel Reverb"
}
}
],
"mail": [
{
"id": "smtp",
"label": {
"ko": "SMTP",
"en": "SMTP",
"fr": "SMTP"
}
},
{
"id": "mailgun",
"label": {
"ko": "Mailgun",
"en": "Mailgun",
"fr": "Mailgun"
}
},
{
"id": "ses",
"label": {
"ko": "SES (Amazon)",
"en": "SES (Amazon)",
"fr": "SES (Amazon)"
}
}
]
},
"abilities": {
"can_update": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -86,10 +457,55 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.save_validation_rules`, `core.search.engine_drivers`).
**요청 예시**
```http
POST /api/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"_tab": "예시값",
"general": [
"예시값"
],
"mail": [
"예시값"
],
"upload": [
"예시값"
],
"seo": [
"예시값"
],
"security": [
"예시값"
],
"drivers": [
"예시값"
],
"advanced": [
"예시값"
],
"notifications": [
"예시값"
],
"identity": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -115,6 +531,15 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/settings/app-key HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -123,6 +548,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| app_key | string | `base64:97gZH*************************…` | 현재 애플리케이션 키(`APP_KEY`)를 마스킹한 문자열. 앞부분 일부만 노출하고 나머지는 별표로 가려 전체 원문은 반환하지 않음 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "성공적으로 처리되었습니다.",
"data": {
"app_key": "{MASKED}"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -147,10 +588,23 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/settings/backup HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -175,10 +629,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/settings/backup-database HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -203,10 +670,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/settings/clear-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -231,10 +711,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/settings/geoip/update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -259,9 +752,34 @@ MaxMind GeoLite2-City DB 를 즉시 재다운로드합니다. GeoIpDatabaseServi
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/admin/settings/optimize-system HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "시스템이 성공적으로 최적화되었습니다.",
"data": null
}
```
**에러 응답**
@@ -291,10 +809,28 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.regenerate_app_key_validation_rules`).
**요청 예시**
```http
POST /api/admin/settings/regenerate-app-key HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"password": "Password123!"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-403 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -324,10 +860,28 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.restore_validation_rules`).
**요청 예시**
```http
POST /api/admin/settings/restore HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"backup_path": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -353,6 +907,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/settings/system-info HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -382,6 +945,86 @@ _단건 응답: `data` 객체의 필드._
| timezone | string | `UTC` | 애플리케이션 기본 타임존 (`config('app.timezone')`) |
| server_time | string | `2026-07-07 05:08:58` | 서버 현재 시각 (Y-m-d H:i:s) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "성공적으로 처리되었습니다.",
"data": {
"os_info": "Windows NT 10.0",
"web_server": "Apache/2.4.62 (Win64) OpenSSL/3.0.16 PHP/8.3.26",
"php_version": "8.3.26",
"mysql_version": "Mysql 8.4.3",
"g7_version": "7.0.1",
"g7_release_year": "2026",
"laravel_version": "12.54.1",
"environment": "local",
"cpu_info": "Intel(R) Core(TM) Ultra 5 225H",
"memory_usage": {
"total": "31.49 GB",
"used": "26.92 GB",
"free": "4.57 GB",
"percentage": 85.49
},
"disk_usage": {
"total": "474.72 GB",
"used": "362.59 GB",
"free": "112.12 GB",
"percentage": 76.38
},
"php_memory_limit": "512M",
"max_execution_time": "36000초",
"upload_max_filesize": "2G",
"install_path": "C:\\Users\\HeuJung\\htdocs\\g7_2",
"config_path": "C:\\Users\\HeuJung\\htdocs\\g7_2\\storage\\app/settings",
"log_path": "C:\\Users\\HeuJung\\htdocs\\g7_2\\storage\\logs",
"upload_path": "C:\\Users\\HeuJung\\htdocs\\g7_2\\storage\\app/public",
"php_extensions": {
"required": {
"openssl": true,
"pdo": true,
"mbstring": true,
"tokenizer": "{MASKED}",
"xml": true,
"curl": true,
"json": true,
"zip": true,
"fileinfo": true,
"bcmath": true
},
"optional": {
"gd": true,
"imagick": false,
"redis": true,
"memcached": false,
"sodium": true,
"exif": true,
"intl": true,
"ldap": false,
"zlib": true
}
},
"database_config": {
"has_read_write_split": false,
"write": {
"host": "localhost",
"port": 3306,
"database": "g7_2",
"username": "g7_2"
},
"read": []
},
"timezone": "UTC",
"server_time": "2026-07-08 03:14:54"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -429,10 +1072,47 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.test_driver_connection_validation_rules`).
**요청 예시**
```http
POST /api/admin/settings/test-driver HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"storage_driver": "예시값",
"cache_driver": "예시값",
"session_driver": "예시값",
"queue_driver": "예시값",
"websocket_enabled": true,
"s3_bucket": "예시값",
"s3_region": "예시값",
"s3_access_key": "예시값",
"s3_secret_key": "예시값",
"s3_url": "https://example.com",
"redis_host": "예시값",
"redis_port": 1,
"redis_password": "Password123!",
"redis_database": 1,
"memcached_host": "예시값",
"memcached_port": 1,
"websocket_app_key": "예시값",
"websocket_host": "예시값",
"websocket_port": 1,
"websocket_scheme": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -476,10 +1156,42 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.test_mail_validation_rules`).
**요청 예시**
```http
POST /api/admin/settings/test-mail HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"to_email": "user@example.com",
"mailer": "smtp",
"from_address": "user@example.com",
"from_name": "예시 이름",
"host": "예시값",
"port": 1,
"username": "예시 이름",
"password": "Password123!",
"encryption": "tls",
"mailgun_domain": "예시값",
"mailgun_secret": "예시값",
"mailgun_endpoint": "예시값",
"ses_key": "예시값",
"ses_secret": "예시값",
"ses_region": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-500 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -507,10 +1219,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| key | path | string | 예 | — | 대상 설정/항목의 키 |
**요청 예시**
```http
GET /api/admin/settings/{key} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -541,10 +1266,28 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.settings.update_validation_rules`).
**요청 예시**
```http
PUT /api/admin/settings/{key} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"value": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
+638 -2
View File
@@ -38,6 +38,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.list_validation_rules`).
**요청 예시**
```http
GET /api/admin/users?page=1&per_page=1&filters=%EC%98%88%EC%8B%9C%EA%B0%92&start_date=2026-01-01&end_date=2026-01-01&date_filter=all&sort_by=created_at&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -65,6 +74,112 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_read":true,"can_create":true,"can_update":true,"can…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용자 정보를 성공적으로 가져왔습니다.",
"data": {
"data": [
{
"number": 2,
"uuid": "a234c3ea-a4f9-496d-a5e7-b44f0d53fc2f",
"name": "선지영",
"nickname": null,
"email": "jihee74@example.net",
"language": "ko",
"language_label": "한국어",
"country": null,
"country_flag": null,
"country_name": null,
"status": "active",
"status_label": "활성",
"status_variant": "success",
"mobile": null,
"roles": [],
"email_verified_at": "2026-07-08 10:44:49",
"last_login_at": null,
"created_at": "2026-07-08",
"is_owner": false,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
}
},
{
"number": 1,
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"nickname": "song.hyunji",
"email": "apidoc-sample-user@example.com",
"language": "ko",
"language_label": "한국어",
"country": "KR",
"country_flag": "🇰🇷",
"country_name": "한국",
"status": "active",
"status_label": "활성",
"status_variant": "success",
"mobile": "010-9595-2897",
"roles": [
{
"id": 1,
"identifier": "admin",
"name": "관리자"
},
{
"id": 4,
"identifier": "apidoc-sample-role",
"name": "API 문서 샘플 역할"
}
],
"email_verified_at": "2026-07-08 10:41:24",
"last_login_at": "2026-07-07 10:41:24",
"created_at": "2026-07-08",
"is_owner": true,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
}
}
],
"statistics": {
"total_users": 2,
"users_this_week": 2,
"users_this_month": 2,
"users_today": 2,
"active_users_this_week": 1
},
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 2,
"from": 1,
"to": 2,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -120,10 +235,54 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.create_validation_rules`).
**요청 예시**
```http
POST /api/admin/users HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"nickname": "예시 이름",
"email": "user@example.com",
"password": "Password123!",
"language": "ko",
"country": "KR",
"timezone": "Asia/Seoul",
"status": "active",
"homepage": "https://example.com",
"mobile": "010-1234-5678",
"phone": "010-1234-5678",
"zipcode": "06234",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"signature": "예시값",
"bio": "예시 내용입니다.",
"admin_memo": "예시값",
"roles": [
"예시값"
],
"role_ids": [
"예시값"
],
"notify_post_complete": true,
"notify_post_reply": true,
"notify_comment": true,
"notify_reply_comment": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -154,10 +313,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.bulk_update_status_validation_rules`).
**요청 예시**
```http
PATCH /api/admin/users/bulk-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"status": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -188,10 +368,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.check_email_validation_rules`).
**요청 예시**
```http
POST /api/admin/users/check-email HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"email": "user@example.com",
"exclude_user_id": "9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -221,9 +420,101 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.update_language_validation_rules`).
**요청 예시**
```http
PATCH /api/admin/users/me/language HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"language": "ko"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| uuid | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | 외부 노출용 UUID (URL/API 식별자, 내부 id 비노출) |
| name | string | `API 문서 샘플 사용자` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| nickname | string | `song.hyunji` | 닉네임 |
| email | string | `apidoc-sample-user@example.com` | 이메일 주소 |
| avatar | null | `null` | 아바타 이미지 URL (미등록 시 null) |
| language | string | `ko` | 사용자 언어 설정 (ko: 한국어, en: 영어) |
| language_label | string | `한국어` | 언어 코드의 현지화 라벨 (user.language.{code} 번역) |
| country | string | `KR` | 국가 코드 (ISO 3166-1 alpha-2) |
| status | string | `active` | 상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조) |
| status_label | string | `활성` | 상태의 사람이 읽는 라벨 (상태 Enum label() 산물) |
| status_variant | string | `success` | 상태 표시 색상/스타일 변형 키 (상태 Enum variant() 산물 — UI 배지용) |
| is_admin | boolean | `true` | 관리자 역할 보유 여부 (User::isAdmin() — 역할 관계 기반 파생) |
| homepage | string | `https://example.com` | 홈페이지 URL |
| mobile | string | `010-9595-2897` | 휴대폰 번호 |
| phone | string | `02-637-5618` | 전화번호 |
| zipcode | string | `16505` | 우편번호 |
| address | string | `경기도 안양시 봉은사로 2918` | 기본 주소 |
| address_detail | string | `48동 718호` | 상세 주소 |
| signature | string | `Fugit consequuntur repellendus sed.` | 서명 |
| bio | string | `Ut magni et sunt ducimus error adipis…` | 자기소개 |
| last_login_at | string | `2026-07-07 10:41:24` | last login 일시 |
| email_verified_at | string | `2026-07-08 10:41:24` | email verified 일시 |
| timezone | string | `Asia/Seoul` | 사용자 시간대 (예: Asia/Seoul, UTC) |
| created_at | string | `2026-07-08 10:41:24` | 생성 일시 |
| updated_at | string | `2026-07-08 10:41:24` | 최종 수정 일시 |
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_read":true,"can_create":true,"can_update":true,"can…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "언어 설정이 성공적으로 변경되었습니다.",
"data": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"nickname": "song.hyunji",
"email": "apidoc-sample-user@example.com",
"avatar": null,
"language": "ko",
"language_label": "한국어",
"country": "KR",
"status": "active",
"status_label": "활성",
"status_variant": "success",
"is_admin": true,
"homepage": "https://example.com",
"mobile": "010-9595-2897",
"phone": "02-637-5618",
"zipcode": "16505",
"address": "경기도 안양시 봉은사로 2918",
"address_detail": "48동 718호",
"signature": "Fugit consequuntur repellendus sed.",
"bio": "Ut magni et sunt ducimus error adipisci. Pariatur corporis voluptatem ratione quo non saepe. Illo atque praesentium possimus dolores qui est fugit. Sint fugiat numquam voluptates.",
"last_login_at": "2026-07-07 10:41:24",
"email_verified_at": "2026-07-08 10:41:24",
"timezone": "Asia/Seoul",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"is_owner": true,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
}
}
}
```
**에러 응답**
@@ -249,6 +540,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/users/recent HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -281,6 +581,91 @@ _요청 파라미터 없음._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_read":true,"can_create":true,"can_update":true,"can…` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용자 정보를 성공적으로 가져왔습니다.",
"data": [
{
"uuid": "a234c3ea-a4f9-496d-a5e7-b44f0d53fc2f",
"name": "선지영",
"nickname": null,
"email": "jihee74@example.net",
"avatar": null,
"language": "ko",
"language_label": "한국어",
"country": null,
"status": "active",
"status_label": "활성",
"status_variant": "success",
"is_admin": false,
"homepage": null,
"mobile": null,
"phone": null,
"zipcode": null,
"address": null,
"address_detail": null,
"signature": null,
"bio": null,
"last_login_at": null,
"email_verified_at": "2026-07-08 10:44:49",
"timezone": "Asia/Seoul",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"is_owner": false,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
}
},
{
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"nickname": "song.hyunji",
"email": "apidoc-sample-user@example.com",
"avatar": null,
"language": "ko",
"language_label": "한국어",
"country": "KR",
"status": "active",
"status_label": "활성",
"status_variant": "success",
"is_admin": true,
"homepage": "https://example.com",
"mobile": "010-9595-2897",
"phone": "02-637-5618",
"zipcode": "16505",
"address": "경기도 안양시 봉은사로 2918",
"address_detail": "48동 718호",
"signature": "Fugit consequuntur repellendus sed.",
"bio": "Ut magni et sunt ducimus error adipisci. Pariatur corporis voluptatem ratione quo non saepe. Illo atque praesentium possimus dolores qui est fugit. Sint fugiat numquam voluptates.",
"last_login_at": "2026-07-07 10:41:24",
"email_verified_at": "2026-07-08 10:41:24",
"timezone": "Asia/Seoul",
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"is_owner": true,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
}
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -310,10 +695,23 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.search_validation_rules`).
**요청 예시**
```http
GET /api/admin/users/search?keyword=%EC%98%88%EC%8B%9C%EA%B0%92&uuid=9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-422 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -339,6 +737,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/admin/users/statistics HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -352,6 +759,29 @@ _단건 응답: `data` 객체의 필드._
| active_users_this_week | integer | `2` | 이번 주 활동(로그인) 사용자 수 |
| language_distribution | object | `{"ko":92,"en":64}` | 언어별 사용자 분포 (언어 코드 => 사용자 수) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용자 통계를 성공적으로 가져왔습니다.",
"data": {
"total_users": 2,
"users_this_week": 2,
"users_this_month": 2,
"users_today": 2,
"active_users_this_week": 1,
"language_distribution": {
"ko": 2
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -378,9 +808,34 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.delete_validation_rules`).
**요청 예시**
```http
DELETE /api/admin/users/a234c2b1-cde8-437f-b28b-23323be2b98d HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용자가 성공적으로 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -410,6 +865,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| user | path | string | 예 | — | 대상 user의 식별자 |
**요청 예시**
```http
GET /api/admin/users/a234c2b1-cde8-437f-b28b-23323be2b98d HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -489,6 +953,103 @@ _단건 응답: `data` 객체의 필드._
| ecommerce_preferred_shipping_country | null | `null` | 선호 배송 국가 코드 (sirsoft-ecommerce 모듈 주입, 미설정 시 null) |
| ecommerce_preferred_shipping_country_name | null | `null` | 선호 배송 국가명 (배송 국가 코드에서 파생, sirsoft-ecommerce 모듈 주입) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용자 정보를 성공적으로 가져왔습니다.",
"data": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"nickname": "song.hyunji",
"email": "apidoc-sample-user@example.com",
"avatar": null,
"language": "ko",
"language_label": "한국어",
"country": "KR",
"status": "active",
"status_label": "활성",
"status_variant": "success",
"is_admin": true,
"homepage": "https://example.com",
"mobile": "010-9595-2897",
"phone": "02-637-5618",
"zipcode": "16505",
"address": "경기도 안양시 봉은사로 2918",
"address_detail": "48동 718호",
"signature": "Fugit consequuntur repellendus sed.",
"bio": "Ut magni et sunt ducimus error adipisci. Pariatur corporis voluptatem ratione quo non saepe. Illo atque praesentium possimus dolores qui est fugit. Sint fugiat numquam voluptates.",
"last_login_at": "2026-07-07 10:41:24",
"email_verified_at": "2026-07-08 10:41:24",
"timezone": "Asia/Seoul",
"modules_count": 0,
"plugins_count": 0,
"menus_count": 2,
"modules": [],
"plugins": [],
"menus": [
{
"id": 1,
"title": "API 문서 샘플 메뉴",
"url": "/admin/apidoc-sample",
"is_active": true
},
{
"id": 2,
"title": "하위 메뉴",
"url": "/admin/nihil-non-eos-doloribus-occaecati-optio",
"is_active": true
}
],
"roles": [
{
"id": 1,
"identifier": "admin",
"name": "관리자"
},
{
"id": 4,
"identifier": "apidoc-sample-role",
"name": "API 문서 샘플 역할"
}
],
"permissions": [],
"consents": [],
"terms_consent": null,
"privacy_consent": null,
"created_at": "2026-07-08 10:41:24",
"updated_at": "2026-07-08 10:41:24",
"is_owner": true,
"abilities": {
"can_read": true,
"can_create": true,
"can_update": true,
"can_delete": true,
"can_assign_roles": true
},
"admin_memo": "In officiis nemo quaerat debitis quia.",
"ip_address": "166.217.51.216",
"withdrawn_at": null,
"blocked_at": null,
"notify_post_complete": false,
"notify_post_reply": false,
"notify_comment": false,
"notify_reply_comment": false,
"ecommerce_mileage": {
"enabled": false
},
"ecommerce_preferred_currency": null,
"ecommerce_preferred_shipping_country": null,
"ecommerce_preferred_shipping_country_name": null
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -545,10 +1106,54 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.user.update_validation_rules`).
**요청 예시**
```http
PUT /api/admin/users/a234c2b1-cde8-437f-b28b-23323be2b98d HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"nickname": "예시 이름",
"email": "user@example.com",
"password": "Password123!",
"language": "ko",
"country": "KR",
"timezone": "Asia/Seoul",
"status": "active",
"homepage": "https://example.com",
"mobile": "010-1234-5678",
"phone": "010-1234-5678",
"zipcode": "06234",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"signature": "예시값",
"bio": "예시 내용입니다.",
"admin_memo": "예시값",
"roles": [
"예시값"
],
"role_ids": [
"예시값"
],
"notify_post_complete": true,
"notify_post_reply": true,
"notify_comment": true,
"notify_reply_comment": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -577,6 +1182,14 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| user | path | string | 예 | — | 대상 user의 식별자 |
**요청 예시**
```http
GET /api/users/a234c2b1-cde8-437f-b28b-23323be2b98d/profile HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -592,6 +1205,29 @@ _단건 응답: `data` 객체의 필드._
| created_at | string | `2026-07-06` | 생성 일시 |
| is_withdrawn | boolean | `false` | withdrawn 여부 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "프로필 정보를 성공적으로 가져왔습니다.",
"data": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"status": "active",
"status_label": "활성",
"avatar": null,
"bio": "Ut magni et sunt ducimus error adipisci. Pariatur corporis voluptatem ratione quo non saepe. Illo atque praesentium possimus dolores qui est fugit. Sint fugiat numquam voluptates.",
"created_at": "2026-07-08",
"is_withdrawn": false
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+18
View File
@@ -31,10 +31,28 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`core.auth.verify_password_rules`).
**요청 예시**
```http
POST /api/me/verify-password HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"password": "Password123!"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-401 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+16
View File
@@ -253,6 +253,22 @@ php artisan seo:generate-sitemap # Sitemap 생성 (큐 디스패치)
php artisan seo:generate-sitemap --sync # Sitemap 동기 생성
```
### API 문서 Artisan 커맨드
```bash
# API 레퍼런스 문서 생성/갱신 (실측 기반 스캐폴딩). 상세: docs/backend/api-documentation.md
php artisan api:docgen --scope=core # 코어 문서 생성 (범위: core|module:{id}|plugin:{id}|all)
php artisan api:docgen --scope=core --seed # 실측용 완전 샘플 시드 후 생성 (개발 환경 전용)
php artisan api:docgen --scope=core --base-url=https://example.com # 실측 기준 URL 지정
php artisan api:docgen --scope=core --examples-only # 표·서술 불가침, 요청/응답 예시 블록만 in-place 삽입
php artisan api:docgen --scope=core --check # 생성 없이 누락/drift 만 리포트 (하네스 소비)
php artisan api:docgen --scope=core --dry-run # 생성 대상 파일/엔드포인트 목록만 출력
# 파라미터/응답 필드 설명 TODO 셀 in-place 백필 (재생성 없이 공통 필드 자동 서술, 멱등)
php artisan api:docgen-backfill-params # 요청 파라미터 표 TODO 셀 백필
php artisan api:docgen-backfill-fields # 응답 필드 표 TODO 셀 백필
```
---
## 확장 업데이트 (CLI + API)
+2 -2
View File
@@ -7,8 +7,8 @@
## TL;DR (5초 요약)
```text
1. URL prefix 자동: /api/admin/[vendor-module]/...
2. name prefix 자동: api.[vendor-module]....
1. URL prefix 자동: /api/modules/[vendor-module]/...
2. name prefix 자동: api.modules.[vendor-module]....
3. 활성화된 모듈만 라우트 등록됨
4. 프론트엔드: routes/admin.json, routes/user.json으로 admin/user 분기
5. 레거시 routes.json은 admin으로 폴백 (경고 로그 출력)
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/gnuboard7-hello_module/memos HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-404 — 응답 필드는 사람이 작성하세요. -->
@@ -74,6 +83,15 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/gnuboard7-hello_module/memos/{id} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/me/activity-stats HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -37,6 +46,24 @@ _단건 응답: `data` 객체의 필드._
| total_comments | integer | `0` | 회원 본인이 작성한 게시글들의 댓글 수 합계(SUM of comments_count)입니다. 비활성 게시판 글은 제외됩니다. |
| total_views | integer | `42` | 회원 본인이 작성한 게시글들의 누적 조회수 합계(SUM of view_count)입니다. 비활성 게시판 글은 제외됩니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "활동 통계를 조회했습니다.",
"data": {
"total_posts": 1,
"total_comments": 0,
"total_views": 44
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/me/board-activities HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -47,6 +56,75 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| created_at_formatted | string | `4시간 전` | `created_at` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| content_plain | string | `API 레퍼런스 실측용 완전 샘플 게시글 본문입니다.` | 게시글 본문의 순수 텍스트입니다. HTML 모드 글은 태그를 제거한 평문으로 변환되며, 목록 미리보기용으로 사용합니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "내 활동 게시글을 조회했습니다.",
"data": {
"current_page": 1,
"data": [
{
"id": 1,
"board_slug": "apidoc-sample-board",
"board_name": "API 문서 샘플 게시판",
"activity_type": "authored",
"activity_count": 0,
"title": "API 문서 샘플 게시글",
"is_secret": "{MASKED}",
"status": "published",
"view_count": 44,
"comment_count": 0,
"created_at": "2026-07-08 10:41:34",
"created_at_formatted": "4시간 전",
"content_plain": "API 레퍼런스 실측용 완전 샘플 게시글 본문입니다."
}
],
"first_page_url": "https://api.example.com/api/modules/sirsoft-board/me/board-activities?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.example.com/api/modules/sirsoft-board/me/board-activities?page=1",
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-board/me/board-activities?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://api.example.com/api/modules/sirsoft-board/me/board-activities",
"per_page": 25,
"prev_page_url": null,
"to": 1,
"total": 1,
"query": {
"board_slug": "",
"search": "",
"sort": "latest",
"activity_type": ""
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/board-types HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -35,6 +44,45 @@ _요청 파라미터 없음._
| slug | string | `basic` | 유형 식별자 (basic, card, gallery 등) |
| name | object | `{"ko":"기본형","en":"Basic List","ja":"基本形"}` | 유형명 (다국어: {"ko": "기본형", "en": "Basic List"}) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "게시판 유형 목록을 조회했습니다.",
"data": [
{
"id": 2,
"slug": "basic",
"name": {
"ko": "기본형",
"en": "Basic List"
}
},
{
"id": 3,
"slug": "gallery",
"name": {
"ko": "갤러리형",
"en": "Gallery"
}
},
{
"id": 4,
"slug": "card",
"name": {
"ko": "카드형",
"en": "Card"
}
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -60,10 +108,29 @@ _요청 파라미터 없음._
| slug | body | string | 예 | max 50 | URL 친화 식별자 (slug) |
| name | body | string | 예 | — | 대상의 이름/명칭 |
**요청 예시**
```http
POST /api/modules/sirsoft-board/admin/board-types HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"slug": "example-key",
"name": "예시 이름"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -89,10 +156,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-board/admin/board-types/2 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -119,10 +199,28 @@ _요청 파라미터 없음._
| id | path | string | 예 | — | 대상 리소스의 식별자 |
| name | body | string | 예 | — | 대상의 이름/명칭 |
**요청 예시**
```http
PUT /api/modules/sirsoft-board/admin/board-types/2 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/dashboard/overview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -36,6 +45,23 @@ _단건 응답: `data` 객체의 필드._
| today_posts | integer | `0` | 오늘 등록된 새 게시글 수입니다. board_stats 집계 테이블의 오늘 행에서 읽으며, 행이 없으면 0 을 반환합니다(최대 1시간 지연). |
| today_comments | integer | `0` | 오늘 등록된 새 댓글 수입니다. board_stats 집계 테이블의 오늘 행에서 읽으며, 행이 없으면 0 을 반환합니다(최대 1시간 지연). |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.dashboard.fetch_success",
"data": {
"today_posts": 0,
"today_comments": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -59,6 +85,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| limit | query | integer | 아니오 | min 1, max 50 | 반환할 최대 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/dashboard/pending-reports?limit=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -68,6 +103,38 @@ _단건 응답: `data` 객체의 필드._
| items | array | `[{"id":7,"board_slug":"qna","board_name":"Q&A","target_ty…` | 전체 게시판을 가로질러 조회한 미처리 신고 항목 목록입니다. 각 항목은 신고 대상 게시판(board_slug/board_name), 대상 종류(target_type), 대상 제목/발췌(target_title/target_excerpt), 상태, 신고 시각을 포함합니다. `limit` 개까지 반환됩니다. |
| total | integer | `16` | 전체 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.dashboard.fetch_success",
"data": {
"items": [
{
"id": 1,
"board_slug": "apidoc-sample-board",
"board_name": "API 문서 샘플 게시판",
"target_type": "post",
"target_type_label": "게시글",
"target_post_id": 1,
"target_title": "API 문서 샘플 게시글",
"target_excerpt": null,
"status": "pending",
"status_label": "접수",
"author_name": "API 문서 샘플 사용자",
"last_reported_at": "4시간 전"
}
],
"total": 1
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -90,6 +157,15 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/dashboard/post-graph HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -104,6 +180,64 @@ _단건 응답: `data` 객체의 필드._
| updated_at | null | `null` | 최종 수정 일시 |
| updated_at_display | string | `` | 집계 행의 최종 갱신 시각(updated_at)을 모듈 날짜 표시 형식(display.date_display_format 설정)으로 포맷한 표시용 문자열입니다. 집계 행이 없으면 빈 문자열입니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.dashboard.fetch_success",
"data": {
"days": [
{
"date": "2026-07-02",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-03",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-04",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-05",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-06",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-07",
"post_count": 0,
"comment_count": 0
},
{
"date": "2026-07-08",
"post_count": 0,
"comment_count": 0
}
],
"total_posts": 0,
"total_comments": 0,
"posts_change": null,
"comments_change": null,
"updated_at": null,
"updated_at_display": ""
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -127,6 +261,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| limit | query | integer | 아니오 | min 1, max 50 | 반환할 최대 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/dashboard/recent-posts?limit=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -139,6 +282,30 @@ _단건 응답: `data` 객체의 필드._
| comments_count | integer | `0` | comments 개수 (집계) |
| created_at | string | `4시간 전` | 생성 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.dashboard.fetch_success",
"data": [
{
"id": 1,
"board_slug": "apidoc-sample-board",
"board_name": "API 문서 샘플 게시판",
"title": "API 문서 샘플 게시글",
"author_name": "API 문서 샘플 사용자",
"comments_count": 0,
"created_at": "4시간 전"
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/me/my-comments HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -42,6 +51,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| created_at | string | `2026-07-07 09:34:50` | 생성 일시 |
| created_at_formatted | string | `4시간 전` | `created_at` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "내 활동 게시글을 조회했습니다.",
"data": {
"current_page": 1,
"data": [
{
"id": 1,
"board_slug": "apidoc-sample-board",
"board_name": "API 문서 샘플 게시판",
"post_title": "API 문서 샘플 게시글",
"post_id_val": 1,
"content": "API 문서 샘플 댓글입니다.",
"created_at": "2026-07-08 10:41:34",
"created_at_formatted": "4시간 전"
}
],
"first_page_url": "https://api.example.com/api/modules/sirsoft-board/me/my-comments?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.example.com/api/modules/sirsoft-board/me/my-comments?page=1",
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-board/me/my-comments?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://api.example.com/api/modules/sirsoft-board/me/my-comments",
"per_page": 25,
"prev_page_url": null,
"to": 1,
"total": 1,
"query": {
"board_slug": "",
"search": "",
"sort": "latest"
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -38,6 +38,15 @@
| sort_order | query | string | 아니오 | `asc`, `desc` | 정렬 방향 (asc 오름차순 / desc 내림차순) |
| per_page | query | integer | 아니오 | — | 페이지당 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/reports?filters=%EC%98%88%EC%8B%9C%EA%B0%92&status=%EC%98%88%EC%8B%9C%EA%B0%92&target_type=%EC%98%88%EC%8B%9C%EA%B0%92&target_status=%EC%98%88%EC%8B%9C%EA%B0%92&board_id=1&reported_at_from=%EC%98%88%EC%8B%9C%EA%B0%92&reported_at_to=%EC%98%88%EC%8B%9C%EA%B0%92&sort_by=%EC%98%88%EC%8B%9C%EA%B0%92&sort_order=asc&per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -75,6 +84,92 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| is_owner | boolean | `false` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_view":true,"can_manage":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "신고 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"board_id": 1,
"board": {
"id": 1,
"name": "API 문서 샘플 게시판",
"slug": "apidoc-sample-board",
"title": null,
"current_status": null,
"deleted_at": null
},
"target_type": "post",
"target_type_label": "게시글",
"target_id": 1,
"post_id": 1,
"content": null,
"content_mode": "text",
"content_preview": "",
"author": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com",
"is_guest": false
},
"reporter": null,
"reason_type": null,
"reason_type_label": null,
"status": "pending",
"status_label": "접수",
"status_variant": "warning",
"processor": null,
"processed_at": null,
"metadata": null,
"report_count": 0,
"last_reported_at": "2026-07-08 10:41:34",
"is_reactivated": false,
"target_status": "published",
"target_trigger_type": "user",
"target_status_label": "게시중",
"created_at": "2026-07-08 10:41:34",
"updated_at": "2026-07-08 10:41:34",
"is_owner": false,
"abilities": {
"can_view": true,
"can_manage": true
}
}
],
"statistics": {
"by_status": {
"pending": 1
},
"by_type": {
"post": 1
},
"total": 1
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 20,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
},
"abilities": {
"can_view": true,
"can_manage": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -102,10 +197,32 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| status | body | string | 예 | — | 일괄 전환할 신고 상태 (`ReportStatus` 허용값: pending/review/rejected/suspended 등). 지정한 모든 신고를 이 상태로 변경합니다. |
| process_note | body | string | 아니오 | max 1000 | 처리 메모 (최대 1000자). 상태 변경 사유나 조치 내용을 처리 이력에 함께 기록합니다. |
**요청 예시**
```http
PATCH /api/modules/sirsoft-board/admin/reports/bulk-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"status": "예시값",
"process_note": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -132,10 +249,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
| target_status | body | string | 아니오 | — | 집계 기준이 되는 전환 대상 상태 (`ReportStatus` 허용값). 지정 시 해당 상태로의 일괄 전환을 가정한 상태별 건수 요약을 계산합니다. |
**요청 예시**
```http
POST /api/modules/sirsoft-board/admin/reports/status-counts HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"target_status": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -161,9 +299,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| report | path | string | 예 | — | 대상 report의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-board/admin/reports/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "신고가 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -190,6 +353,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| report | path | string | 예 | — | 대상 report의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/reports/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -220,6 +392,70 @@ _단건 응답: `data` 객체의 필드._
| created_at | string | `2026-06-04 09:35:42` | 생성 일시 |
| updated_at | string | `2026-06-04 09:35:42` | 최종 수정 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "신고 목록을 조회했습니다.",
"data": {
"id": 1,
"board_id": 1,
"board": {
"id": 1,
"name": "API 문서 샘플 게시판",
"slug": "apidoc-sample-board"
},
"target_type": "post",
"target_id": 1,
"post": {
"id": 1,
"title": "API 문서 샘플 게시글",
"content": "<p>API 레퍼런스 실측용 완전 샘플 게시글 본문입니다.</p>",
"content_mode": "html",
"created_at": "2026-07-08 10:41:34",
"author": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com",
"is_guest": false
}
},
"comment": null,
"target_status": "published",
"target_status_label": "게시중",
"blind_trigger_type": "user",
"blind_trigger_type_label": "사용자 직접 삭제",
"status": "pending",
"available_actions": [
"review",
"rejected",
"suspended",
"deleted"
],
"abilities": {
"can_view": true,
"can_manage": true
},
"reporters": [],
"report_count": 0,
"reason_summary": "-",
"first_reported_at": "2026-07-08 10:41:34",
"last_reported_at": "2026-07-08 10:41:34",
"histories": [],
"metadata": {
"reason": "spam"
},
"created_at": "2026-07-08 10:41:34",
"updated_at": "2026-07-08 10:41:34"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -247,6 +483,15 @@ _단건 응답: `data` 객체의 필드._
| per_page | query | integer | 아니오 | min 1 | 페이지당 항목 수 |
| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/reports/1/reporters?per_page=1&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -261,6 +506,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| snapshot | object | `{"board_name":"갤러리","title":"작업물 공유합니다","content":"최근에 작업…` | 신고 접수 시점의 대상 콘텐츠 스냅샷 (게시판명/제목/본문/작성자 등). 이후 대상이 수정·삭제되어도 신고 당시 내용을 보존합니다. |
| reported_at | string | `2026-06-04 09:35:42` | reported 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "신고 목록을 조회했습니다.",
"data": {
"data": [],
"pagination": {
"total": 0,
"from": 0,
"to": 0,
"per_page": 25,
"current_page": 1,
"last_page": 1
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -289,10 +558,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| status | body | string | 예 | — | 전환할 신고 상태 (`ReportStatus` 허용값). 현재 상태에서 전환 불가한 값(영구삭제 등)은 검증에서 422로 차단됩니다. |
| process_note | body | string | 아니오 | max 1000 | 처리 메모 (최대 1000자). 상태 변경 사유나 조치 내용을 처리 이력에 함께 기록합니다. |
**요청 예시**
```http
PATCH /api/modules/sirsoft-board/admin/reports/1/status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"status": "예시값",
"process_note": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -43,6 +52,291 @@ _단건 응답: `data` 객체의 필드._
| abilities | object | `{"can_update":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
| _meta | object | `{"limits":{"per_page_min":5,"per_page_max":100,"min_title…` | 편집 UI 보조 메타데이터. `limits`에 `config('sirsoft-board.limits')` 기반 입력 제한값(페이지당 글 수·답글/댓글 깊이의 최소·최대 등)이 담겨 프론트 입력 검증 범위로 사용됩니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.settings.fetch_success",
"data": {
"basic_defaults": {
"type": "basic",
"per_page": 20,
"per_page_mobile": 15,
"order_by": "created_at",
"order_direction": "DESC",
"secret_mode": "{MASKED}",
"use_comment": true,
"use_reply": true,
"max_reply_depth": 5,
"max_comment_depth": 10,
"comment_order": "ASC",
"show_view_count": true,
"use_report": false,
"min_title_length": 2,
"max_title_length": 200,
"min_content_length": 2,
"max_content_length": 10000,
"min_comment_length": 2,
"max_comment_length": 1000,
"use_file_upload": false,
"max_file_size": 10,
"max_file_count": 5,
"allowed_extensions": [
"jpg",
"jpeg",
"png",
"gif",
"webp",
"pdf",
"doc",
"docx",
"xls",
"xlsx",
"ppt",
"pptx",
"hwp",
"txt",
"zip"
],
"notify_admin_on_post": true,
"notify_author": true,
"new_display_hours": 24,
"blocked_keywords": [
"시발",
"씨발",
"ㅅㅂ",
"ㅆㅂ",
"좆",
"ㅈㄴ",
"존나",
"씹",
"지랄",
"ㅈㄹ",
"개새끼",
"새끼",
"ㅅㄲ",
"개자식",
"병신",
"ㅂㅅ",
"미친놈",
"미친년",
"느금마",
"니애미",
"느금",
"엠창",
"엠뒤",
"빡대가리",
"개같은",
"개년",
"개놈",
"개돼지",
"개씹",
"개쓰레기",
"닥쳐",
"꺼져",
"죽어",
"자살해",
"뒤져",
"저능아",
"정신병자",
"썅",
"쌍놈",
"쌍년",
"쉬발",
"쒸발",
"시팔",
"씨팔",
"한남",
"한녀",
"김치녀",
"된장녀",
"맘충",
"틀딱",
"느개비",
"장애인놈",
"병신새끼",
"흑형",
"짱깨",
"쪽바리",
"왜놈",
"튀기",
"똥남아",
"야동",
"포르노",
"섹스",
"야사",
"음란",
"성인사이트",
"조건만남",
"원나잇",
"섹파",
"오피",
"키스방",
"매춘",
"성매매",
"몸파는"
],
"default_board_permissions": {
"admin.posts.read": [
"admin"
],
"admin.posts.write": [
"admin"
],
"admin.posts.read-secret": [
"admin"
],
"admin.comments.read": [
"admin"
],
"admin.comments.write": [
"admin"
],
"admin.attachments.upload": [
"admin"
],
"admin.attachments.download": [
"admin"
],
"admin.manage": [
"admin"
],
"posts.read": [
"admin",
"user",
"guest"
],
"posts.write": [
"admin",
"user"
],
"posts.read-secret": [
"admin"
],
"comments.read": [
"admin",
"user",
"guest"
],
"comments.write": [
"admin",
"user"
],
"attachments.upload": [
"admin",
"user"
],
"attachments.download": [
"admin",
"user",
"guest"
],
"manager": [
"admin"
]
}
},
"report_policy": {
"auto_hide_threshold": 5,
"auto_hide_target": "both",
"daily_report_limit": 10,
"rejection_limit_count": 5,
"rejection_limit_days": 30,
"notify_admin_on_report": true,
"notify_admin_on_report_scope": "per_case",
"notify_admin_on_report_channels": [
"mail",
"database"
],
"notify_author_on_report_action": true,
"notify_author_on_report_action_channels": [
"mail",
"database"
]
},
"spam_security": {
"post_cooldown_seconds": 0,
"comment_cooldown_seconds": 0,
"report_cooldown_seconds": 60,
"view_count_cache_ttl": 86400
},
"display": {
"date_display_format": "standard"
},
"seo": {
"meta_boards_title": "{site_name}",
"meta_boards_description": "",
"meta_board_title": "{board_name}",
"meta_board_description": "{board_description}",
"meta_post_title": "{board_name} - {post_title}",
"meta_post_description": "",
"seo_boards": true,
"seo_board": true,
"seo_post_detail": true
},
"notifications": {
"channels": [
{
"id": "mail",
"is_active": true,
"sort_order": 0
},
{
"id": "database",
"is_active": true,
"sort_order": 1
}
]
},
"report_permissions": {
"view_roles": [
"admin",
"apidoc-sample-role"
],
"manage_roles": [
"admin",
"apidoc-sample-role"
]
},
"abilities": {
"can_update": true
},
"_meta": {
"limits": {
"per_page_min": 5,
"per_page_max": 100,
"min_title_length_min": 0,
"min_title_length_max": 200,
"max_title_length_min": 1,
"max_title_length_max": 1000,
"min_content_length_min": 0,
"min_content_length_max": 10000,
"max_content_length_min": 1,
"max_content_length_max": 100000,
"min_comment_length_min": 0,
"min_comment_length_max": 1000,
"max_comment_length_min": 1,
"max_comment_length_max": 10000,
"max_file_size_min": 1,
"max_file_size_max": 200,
"max_file_count_min": 1,
"max_file_count_max": 20,
"category_max": 50,
"max_reply_depth_min": 1,
"max_reply_depth_max": 10,
"max_comment_depth_min": 0,
"max_comment_depth_max": 10
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -74,9 +368,303 @@ _단건 응답: `data` 객체의 필드._
| spam_security | body | array | 아니오 | — | 스팸·보안 카테고리 값. 글·댓글·신고 작성 쿨다운 시간(초)과 조회수 캐시 TTL을 저장합니다. |
| seo | body | array | 아니오 | — | SEO 설정 카테고리 값. 게시판 목록/개별/글 상세 페이지의 메타 제목·설명 템플릿과 각 페이지 SEO 활성화 여부를 저장합니다. |
**요청 예시**
```http
PUT /api/modules/sirsoft-board/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"_tab": "basic_defaults",
"notifications": [
"예시값"
],
"basic_defaults": [
"예시값"
],
"report_policy": [
"예시값"
],
"report_permissions": [
"예시값"
],
"display": [
"예시값"
],
"spam_security": [
"예시값"
],
"seo": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| basic_defaults | object | `{"type":"basic","per_page":20,"per_page_mobile":15,"order…` | 저장 후 갱신된 기본 설정 카테고리. 게시판 타입, 페이지당 글 수(PC/모바일), 정렬 기준/방향, 댓글·답글 사용 여부와 깊이, 제목·내용·댓글 길이 제한, 파일 업로드 허용/용량/개수/확장자, 새 글 표시 시간, 게시판 기본 권한(default_board_permissions) 등을 포함합니다. |
| report_policy | object | `{"auto_hide_threshold":5,"auto_hide_target":"both","daily…` | 저장 후 갱신된 신고 정책 카테고리. 자동 숨김 임계치(auto_hide_threshold)와 대상(auto_hide_target: post/comment/both), 사용자별 일일 신고 한도, 신고 거부 누적 제한(횟수/기간), 관리자·작성자 신고 알림 발송 여부와 채널을 포함합니다. |
| spam_security | object | `{"post_cooldown_seconds":0,"comment_cooldown_seconds":0,"…` | 저장 후 갱신된 스팸·보안 카테고리. 글·댓글·신고 작성 사이의 도배 방지 쿨다운 시간(초)과 조회수 캐시 TTL을 포함합니다. |
| display | object | `{"date_display_format":"standard"}` | 저장 후 갱신된 표시 설정 카테고리. 날짜 표시 형식(date_display_format: standard 절대 표기 / relative 상대 표기)을 포함합니다. |
| seo | object | `{"meta_boards_title":"{site_name}","meta_boards_descripti…` | 저장 후 갱신된 SEO 메타 태그 설정 카테고리. 게시판 목록/개별 게시판/글 상세 페이지의 메타 제목·설명 템플릿과 각 페이지의 SEO 생성 활성화 여부(seo_boards, seo_board, seo_post_detail)를 포함합니다. |
| notifications | object | `{"channels":[{"id":"mail","is_active":true,"sort_order":0…` | 저장 후 갱신된 알림 채널 설정 카테고리. 각 채널(mail, database 등)의 식별자, 활성화 여부(is_active), 정렬 순서(sort_order)를 담은 channels 배열을 포함합니다. |
| report_permissions | object | `{"view_roles":[],"manage_roles":[]}` | 저장 후 동기화된 신고 관리 권한 역할. 신고 내역을 조회할 수 있는 역할(view_roles)과 신고를 처리·관리할 수 있는 역할(manage_roles)의 식별자 배열이며, 요청에 포함된 경우 DB 권한 역할로 동기화되어 반환됩니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-board::messages.settings.save_success",
"data": {
"basic_defaults": {
"type": "basic",
"per_page": 20,
"per_page_mobile": 15,
"order_by": "created_at",
"order_direction": "DESC",
"secret_mode": "{MASKED}",
"use_comment": true,
"use_reply": true,
"max_reply_depth": 5,
"max_comment_depth": 10,
"comment_order": "ASC",
"show_view_count": true,
"use_report": false,
"min_title_length": 2,
"max_title_length": 200,
"min_content_length": 2,
"max_content_length": 10000,
"min_comment_length": 2,
"max_comment_length": 1000,
"use_file_upload": false,
"max_file_size": 10,
"max_file_count": 5,
"allowed_extensions": [
"jpg",
"jpeg",
"png",
"gif",
"webp",
"pdf",
"doc",
"docx",
"xls",
"xlsx",
"ppt",
"pptx",
"hwp",
"txt",
"zip"
],
"notify_admin_on_post": true,
"notify_author": true,
"new_display_hours": 24,
"blocked_keywords": [
"시발",
"씨발",
"ㅅㅂ",
"ㅆㅂ",
"좆",
"ㅈㄴ",
"존나",
"씹",
"지랄",
"ㅈㄹ",
"개새끼",
"새끼",
"ㅅㄲ",
"개자식",
"병신",
"ㅂㅅ",
"미친놈",
"미친년",
"느금마",
"니애미",
"느금",
"엠창",
"엠뒤",
"빡대가리",
"개같은",
"개년",
"개놈",
"개돼지",
"개씹",
"개쓰레기",
"닥쳐",
"꺼져",
"죽어",
"자살해",
"뒤져",
"저능아",
"정신병자",
"썅",
"쌍놈",
"쌍년",
"쉬발",
"쒸발",
"시팔",
"씨팔",
"한남",
"한녀",
"김치녀",
"된장녀",
"맘충",
"틀딱",
"느개비",
"장애인놈",
"병신새끼",
"흑형",
"짱깨",
"쪽바리",
"왜놈",
"튀기",
"똥남아",
"야동",
"포르노",
"섹스",
"야사",
"음란",
"성인사이트",
"조건만남",
"원나잇",
"섹파",
"오피",
"키스방",
"매춘",
"성매매",
"몸파는"
],
"default_board_permissions": {
"admin.posts.read": [
"admin"
],
"admin.posts.write": [
"admin"
],
"admin.posts.read-secret": [
"admin"
],
"admin.comments.read": [
"admin"
],
"admin.comments.write": [
"admin"
],
"admin.attachments.upload": [
"admin"
],
"admin.attachments.download": [
"admin"
],
"admin.manage": [
"admin"
],
"posts.read": [
"admin",
"user",
"guest"
],
"posts.write": [
"admin",
"user"
],
"posts.read-secret": [
"admin"
],
"comments.read": [
"admin",
"user",
"guest"
],
"comments.write": [
"admin",
"user"
],
"attachments.upload": [
"admin",
"user"
],
"attachments.download": [
"admin",
"user",
"guest"
],
"manager": [
"admin"
]
}
},
"report_policy": {
"auto_hide_threshold": 5,
"auto_hide_target": "both",
"daily_report_limit": 10,
"rejection_limit_count": 5,
"rejection_limit_days": 30,
"notify_admin_on_report": true,
"notify_admin_on_report_scope": "per_case",
"notify_admin_on_report_channels": [
"mail",
"database"
],
"notify_author_on_report_action": true,
"notify_author_on_report_action_channels": [
"mail",
"database"
]
},
"spam_security": {
"post_cooldown_seconds": 0,
"comment_cooldown_seconds": 0,
"report_cooldown_seconds": 60,
"view_count_cache_ttl": 86400
},
"display": {
"date_display_format": "standard"
},
"seo": {
"meta_boards_title": "{site_name}",
"meta_boards_description": "",
"meta_board_title": "{board_name}",
"meta_board_description": "{board_description}",
"meta_post_title": "{board_name} - {post_title}",
"meta_post_description": "",
"seo_boards": true,
"seo_board": true,
"seo_post_detail": true
},
"notifications": {
"channels": [
{
"id": "mail",
"is_active": true,
"sort_order": 0
},
{
"id": "database",
"is_active": true,
"sort_order": 1
}
]
},
"report_permissions": {
"view_roles": [],
"manage_roles": []
}
}
}
```
**에러 응답**
@@ -106,10 +694,37 @@ _단건 응답: `data` 객체의 필드._
| board_ids | body | array | 아니오 | — | board 식별자 배열 |
| override_values | body | array | 아니오 | — | 환경설정 기본값 대신 사용할 재정의 값 맵. 지정한 필드에 대해 기본값이 아닌 임의의 값으로 일괄 적용할 때 사용합니다. |
**요청 예시**
```http
POST /api/modules/sirsoft-board/admin/settings/bulk-apply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"fields": [
"예시값"
],
"apply_all": true,
"board_ids": [
"예시값"
],
"override_values": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -133,10 +748,23 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/modules/sirsoft-board/admin/settings/clear-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -161,10 +789,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| category | path | string | 예 | — | 분류 필터 (해당 분류의 항목만 조회) |
**요청 예시**
```http
GET /api/modules/sirsoft-board/admin/settings/{category} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -29,6 +29,15 @@
| --- | --- | --- | --- | --- | --- |
| user | path | string | 예 | — | 대상 user의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -49,6 +58,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| created_at_formatted | string | `4시간 전` | `created_at` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| content_plain | string | `API 레퍼런스 실측용 완전 샘플 게시글 본문입니다.` | 게시글 본문의 순수 텍스트입니다. HTML 모드 글은 태그를 제거한 평문으로 변환되며, 목록 미리보기용으로 사용합니다. |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "게시글 목록을 조회했습니다.",
"data": {
"current_page": 1,
"data": [
{
"id": 1,
"board_slug": "apidoc-sample-board",
"board_name": "API 문서 샘플 게시판",
"activity_type": "authored",
"activity_count": 0,
"title": "API 문서 샘플 게시글",
"is_secret": "{MASKED}",
"status": "published",
"view_count": 44,
"comment_count": 0,
"created_at": "2026-07-08 10:41:34",
"created_at_formatted": "4시간 전",
"content_plain": "API 레퍼런스 실측용 완전 샘플 게시글 본문입니다."
}
],
"first_page_url": "https://api.example.com/api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.example.com/api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts?page=1",
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://api.example.com/api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts",
"per_page": 25,
"prev_page_url": null,
"to": 1,
"total": 1
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -72,6 +144,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| user | path | string | 예 | — | 대상 user의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-board/users/a234c2b1-cde8-437f-b28b-23323be2b98d/posts/stats HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -81,6 +162,23 @@ _단건 응답: `data` 객체의 필드._
| posts_count | integer | `1` | posts 개수 (집계) |
| comments_count | integer | `1` | comments 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "성공적으로 처리되었습니다.",
"data": {
"posts_count": 1,
"comments_count": 1
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/addresses HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,59 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| addresses | object | `{"data":[{"id":263,"user_id":"a1e0a91a-fba6-491c-a53e-728…` | 회원 본인 소유 배송지 컬렉션 (`data[]` 배송지 항목 배열 + `abilities.can_create` — UserAddressCollection 파생) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송지 목록을 조회했습니다.",
"data": {
"addresses": {
"data": [
{
"id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 배송지",
"recipient_name": "원진수",
"recipient_phone": "010-4988-7051",
"country_code": "KR",
"country_name": {
"ko": "한국",
"en": "South Korea"
},
"zipcode": "27058",
"address": "충청남도 남양주시 테헤란로 6114",
"address_detail": "Quidem natus iste.",
"address_line_1": null,
"address_line_2": null,
"city": null,
"state": null,
"postal_code": null,
"is_default": false,
"is_domestic": true,
"is_international": false,
"full_address": "충청남도 남양주시 테헤란로 6114 Quidem natus iste.",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_update": true,
"can_delete": true,
"can_set_default": true
}
}
],
"abilities": {
"can_create": true
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -76,9 +138,89 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.user_address.store_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/addresses HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"recipient_name": "예시 이름",
"recipient_phone": "010-1234-5678",
"country_code": "KR",
"zipcode": "06234",
"province_code": "예시값",
"city": "예시값",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"address_type_code": "R",
"address_line_1": "서울특별시 강남구 테헤란로 1",
"address_line_2": "서울특별시 강남구 테헤란로 1",
"intl_city": "예시값",
"intl_state": "예시값",
"intl_postal_code": "06234",
"is_default": true,
"force_overwrite": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| address | object | `{"id":9,"user_id":"a234c2b1-cde8-437f-b28b-23323be2b98d",…` | 기본주소 (국내) |
**응답 예시**
```http
HTTP/1.1 201
```
```json
{
"success": true,
"message": "배송지가 추가되었습니다.",
"data": {
"address": {
"id": 9,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "실측 예시값",
"recipient_name": "실측 예시값",
"recipient_phone": "실측 예시값",
"country_code": "KR",
"country_name": {
"ko": "한국",
"en": "South Korea"
},
"zipcode": "실측 예시값",
"address": "실측 예시값",
"address_detail": "실측 예시값",
"address_line_1": null,
"address_line_2": null,
"city": "실측 예시값",
"state": null,
"postal_code": null,
"is_default": true,
"is_domestic": true,
"is_international": false,
"full_address": "실측 예시값 실측 예시값",
"created_at": "2026-07-08 15:00:36",
"updated_at": "2026-07-08 15:00:36",
"abilities": {
"can_update": true,
"can_delete": false,
"can_set_default": false
}
}
}
}
```
**에러 응답**
@@ -104,9 +246,34 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/user/addresses/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송지가 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -132,9 +299,68 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/addresses/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| address | object | `{"id":1,"user_id":"a234c2b1-cde8-437f-b28b-23323be2b98d",…` | 기본주소 (국내) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송지 정보를 조회했습니다.",
"data": {
"address": {
"id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 배송지",
"recipient_name": "원진수",
"recipient_phone": "010-4988-7051",
"country_code": "KR",
"country_name": {
"ko": "한국",
"en": "South Korea"
},
"zipcode": "27058",
"address": "충청남도 남양주시 테헤란로 6114",
"address_detail": "Quidem natus iste.",
"address_line_1": null,
"address_line_2": null,
"city": null,
"state": null,
"postal_code": null,
"is_default": false,
"is_domestic": true,
"is_international": false,
"full_address": "충청남도 남양주시 테헤란로 6114 Quidem natus iste.",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_update": true,
"can_delete": true,
"can_set_default": true
}
}
}
}
```
**에러 응답**
@@ -178,9 +404,88 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.user_address.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/user/addresses/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"recipient_name": "예시 이름",
"recipient_phone": "010-1234-5678",
"country_code": "KR",
"zipcode": "06234",
"province_code": "예시값",
"city": "예시값",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"address_type_code": "R",
"address_line_1": "서울특별시 강남구 테헤란로 1",
"address_line_2": "서울특별시 강남구 테헤란로 1",
"intl_city": "예시값",
"intl_state": "예시값",
"intl_postal_code": "06234",
"is_default": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| address | object | `{"id":1,"user_id":"a234c2b1-cde8-437f-b28b-23323be2b98d",…` | 기본주소 (국내) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송지가 수정되었습니다.",
"data": {
"address": {
"id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "실측 예시값",
"recipient_name": "실측 예시값",
"recipient_phone": "실측 예시값",
"country_code": "KR",
"country_name": {
"ko": "한국",
"en": "South Korea"
},
"zipcode": "실측 예시값",
"address": "실측 예시값",
"address_detail": "실측 예시값",
"address_line_1": null,
"address_line_2": null,
"city": "실측 예시값",
"state": null,
"postal_code": null,
"is_default": true,
"is_domestic": true,
"is_international": false,
"full_address": "실측 예시값 실측 예시값",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:36",
"abilities": {
"can_update": true,
"can_delete": false,
"can_set_default": false
}
}
}
}
```
**에러 응답**
@@ -207,9 +512,34 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/user/addresses/1/default HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "기본 배송지로 설정되었습니다.",
"data": null
}
```
**에러 응답**
@@ -34,6 +34,15 @@
| sort_order | query | string | 아니오 | `asc`, `desc` | 정렬 방향 (asc 오름차순 / desc 내림차순) |
| locale | query | string | 아니오 | `ko`, `en`, `fr`, `ja` | 로케일 코드 (표시 언어/지역) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/brands?is_active=1&search=%EC%98%88%EC%8B%9C%EA%B0%92&sort=name_asc&sort_by=name&sort_order=asc&locale=ko HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -57,6 +66,53 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| products_count | integer | `0` | products 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "브랜드 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"name": {
"ko": "API 문서 샘플 브랜드",
"en": "API Doc Sample Brand"
},
"localized_name": "API 문서 샘플 브랜드",
"slug": "apidoc-sample-brand",
"url": "apidoc-sample-brand",
"website": null,
"sort_order": 0,
"is_active": true,
"icon": "tag",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"creator": [],
"updater": [],
"products_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -88,10 +144,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.brand.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/brands HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"slug": "example-key",
"website": "예시값",
"sort_order": 1,
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -117,9 +197,40 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| brand | path | string | 예 | — | 대상 brand의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/brands/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| brand_id | integer | `1` | brand 식별자 (연관 리소스 참조) |
| products_count | integer | `0` | products 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "브랜드가 삭제되었습니다.",
"data": {
"brand_id": 1,
"products_count": 0
}
}
```
**에러 응답**
@@ -153,10 +264,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.brand.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/brands/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"slug": "example-key",
"website": "예시값",
"sort_order": 1,
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -183,9 +318,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/brands/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 브랜드","en":"API Doc Sample Brand"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 브랜드` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| slug | string | `apidoc-sample-brand` | URL 친화 식별자 (slug) |
| url | string | `apidoc-sample-brand` | SortableMenuItem 표시용 URL (slug 값을 그대로 노출) |
| website | null | `null` | 브랜드 웹사이트 URL |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| is_active | boolean | `true` | active 여부 |
| icon | string | `tag` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| products_count | integer | `0` | products 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "브랜드 정보를 조회했습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 브랜드",
"en": "API Doc Sample Brand"
},
"localized_name": "API 문서 샘플 브랜드",
"slug": "apidoc-sample-brand",
"url": "apidoc-sample-brand",
"website": null,
"sort_order": 0,
"is_active": true,
"icon": "tag",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"products_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -212,9 +407,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/brands/1/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 브랜드","en":"API Doc Sample Brand"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 브랜드` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| slug | string | `apidoc-sample-brand` | URL 친화 식별자 (slug) |
| url | string | `apidoc-sample-brand` | SortableMenuItem 표시용 URL (slug 값을 그대로 노출) |
| website | null | `null` | 브랜드 웹사이트 URL |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| is_active | boolean | `false` | active 여부 |
| icon | string | `tag` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:16` | 최종 수정 일시 |
| products_count | integer | `0` | products 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.brands.status_changed",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 브랜드",
"en": "API Doc Sample Brand"
},
"localized_name": "API 문서 샘플 브랜드",
"slug": "apidoc-sample-brand",
"url": "apidoc-sample-brand",
"website": null,
"sort_order": 0,
"is_active": false,
"icon": "tag",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:16",
"products_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -31,10 +31,23 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.delete_items_validation_rules`).
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/cart?ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -60,6 +73,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.get_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/cart?selected_ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -73,6 +95,81 @@ _단건 응답: `data` 객체의 필드._
| has_unshippable_items | boolean | `false` | unshippable items 여부 |
| selected_shipping_country | string | `KR` | 배송비 계산에 적용된 배송 국가 코드 (ResolveShippingCountry 해석 결과) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "장바구니 목록을 조회했습니다.",
"data": {
"items": [],
"item_ids": [],
"item_count": 0,
"calculation": {
"items": [],
"summary": {
"subtotal": 0,
"subtotal_formatted": "0원",
"product_coupon_discount": 0,
"product_coupon_discount_formatted": "0원",
"code_discount": 0,
"code_discount_formatted": "0원",
"order_coupon_discount": 0,
"order_coupon_discount_formatted": "0원",
"total_coupon_discount": 0,
"total_coupon_discount_formatted": "0원",
"total_discount": 0,
"discount_formatted": "0원",
"base_shipping_total": 0,
"extra_shipping_total": 0,
"total_shipping": 0,
"total_shipping_fee": 0,
"shipping_fee_formatted": "0원",
"shipping_discount": 0,
"shipping_discount_formatted": "0원",
"taxable_amount": 0,
"tax_free_amount": 0,
"points_earning": 0,
"total_mileage": 0,
"mileage_formatted": "0P",
"points_used": 0,
"points_used_formatted": "0원",
"payment_amount": 0,
"payment_amount_formatted": "0원",
"final_amount": 0,
"final_amount_formatted": "0원",
"coupon_discount": 0,
"coupon_discount_formatted": "0원",
"order_discount": 0,
"order_discount_formatted": "0원"
},
"promotions": {
"coupon_issue_ids": [],
"item_coupons": [],
"discount_code": null,
"product_promotions": {
"coupons": [],
"discount_codes": [],
"events": []
},
"order_promotions": {
"coupons": [],
"discount_codes": [],
"events": []
}
},
"validation_errors": []
},
"has_unshippable_items": false,
"selected_shipping_country": "KR"
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -99,10 +196,31 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.bulk_add_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/cart HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"product_id": 1,
"items": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -124,9 +242,38 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/cart/all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| deleted_count | integer | `0` | deleted 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "장바구니가 비워졌습니다. 0개 상품이 삭제되었습니다.",
"data": {
"deleted_count": 0
}
}
```
**에러 응답**
@@ -151,6 +298,15 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.get_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/cart/count?selected_ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -159,6 +315,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| count | integer | `1` | 장바구니에 담긴 아이템 개수 (`selected_ids` 지정 시 해당 항목만 집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "장바구니 수량을 조회했습니다.",
"data": {
"count": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -180,9 +352,38 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/cart/key HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| cart_key | string | `ck_QBQT80EINHHXLO8HYHntMpmUZrPujPka` | 비회원 장바구니 식별 키 (`ck_` + 32자 영숫자. 이후 요청의 `X-Cart-Key` 헤더에 실어 자신의 장바구니를 식별) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "장바구니 키가 발급되었습니다.",
"data": {
"cart_key": "ck_QBQT80EINHHXLO8HYHntMpmUZrPujPka"
}
}
```
**에러 응답**
@@ -203,10 +404,23 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/cart/merge HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -230,9 +444,109 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.get_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/cart/query HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"selected_ids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| items | array | `[]` | 장바구니 라인 아이템 목록 (상품·옵션·수량 등 — CartItemResource 파생) |
| item_ids | array | `[]` | item 식별자 배열 (연관 리소스 참조) |
| item_count | integer | `0` | item 개수 (집계) |
| calculation | object | `{"items":[],"summary":{"subtotal":0,"subtotal_formatted":…` | 선택 아이템 기준 금액 계산 결과 (상품 소계·할인·배송비 등 — OrderCalculationResult 파생) |
| has_unshippable_items | boolean | `false` | unshippable items 여부 |
| selected_shipping_country | string | `KR` | 배송비 계산에 적용된 배송 국가 코드 (ResolveShippingCountry 해석 결과) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "장바구니 목록을 조회했습니다.",
"data": {
"items": [],
"item_ids": [],
"item_count": 0,
"calculation": {
"items": [],
"summary": {
"subtotal": 0,
"subtotal_formatted": "0원",
"product_coupon_discount": 0,
"product_coupon_discount_formatted": "0원",
"code_discount": 0,
"code_discount_formatted": "0원",
"order_coupon_discount": 0,
"order_coupon_discount_formatted": "0원",
"total_coupon_discount": 0,
"total_coupon_discount_formatted": "0원",
"total_discount": 0,
"discount_formatted": "0원",
"base_shipping_total": 0,
"extra_shipping_total": 0,
"total_shipping": 0,
"total_shipping_fee": 0,
"shipping_fee_formatted": "0원",
"shipping_discount": 0,
"shipping_discount_formatted": "0원",
"taxable_amount": 0,
"tax_free_amount": 0,
"points_earning": 0,
"total_mileage": 0,
"mileage_formatted": "0P",
"points_used": 0,
"points_used_formatted": "0원",
"payment_amount": 0,
"payment_amount_formatted": "0원",
"final_amount": 0,
"final_amount_formatted": "0원",
"coupon_discount": 0,
"coupon_discount_formatted": "0원",
"order_discount": 0,
"order_discount_formatted": "0원"
},
"promotions": {
"coupon_issue_ids": [],
"item_coupons": [],
"discount_code": null,
"product_promotions": {
"coupons": [],
"discount_codes": [],
"events": []
},
"order_promotions": {
"coupons": [],
"discount_codes": [],
"events": []
}
},
"validation_errors": []
},
"has_unshippable_items": false,
"selected_shipping_country": "KR"
}
}
```
**에러 응답**
@@ -257,10 +571,23 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/cart/{id} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -289,10 +616,32 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.change_option_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/cart/{id}/option HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"product_option_id": 1,
"quantity": 1,
"additional_option_selections": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -320,10 +669,28 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.cart.update_quantity_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/cart/{id}/quantity HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"quantity": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -34,6 +34,15 @@
| flat | query | boolean | 아니오 | — | true 면 깊이 들여쓰기를 포함한 평면 리스트로 반환 (TagInput 등에 사용) |
| max_depth | query | integer | 아니오 | min 1, max 10 | 조회할 최대 계층 깊이 제한 (1~10) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/categories?parent_id=%EC%98%88%EC%8B%9C%EA%B0%92&is_active=1&search=%EC%98%88%EC%8B%9C%EA%B0%92&hierarchical=1&flat=1&max_depth=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -61,6 +70,57 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| children_count | integer | `0` | children 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "카테고리 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"description": null,
"localized_name": "API 문서 샘플 카테고리",
"parent_id": null,
"path": "0",
"depth": 0,
"sort_order": 0,
"is_active": true,
"slug": "apidoc-sample-category",
"url": "apidoc-sample-category",
"icon": "folder",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-08 01:44:49",
"updated_at": "2026-07-08 01:44:49",
"images": [],
"products_count": 0,
"children_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -95,10 +155,39 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/categories HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"description": [
"예시 내용입니다."
],
"parent_id": "예시값",
"slug": "example-key",
"is_active": true,
"meta_title": "예시 제목",
"meta_description": "예시 내용입니다.",
"temp_key": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -129,10 +218,43 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category-image.filter_upload_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/categories/images HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary
Content-Disposition: form-data; name="temp_key"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="collection"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="alt_text"
예시값
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -160,10 +282,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category-image.filter_reorder_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/categories/images/reorder HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"order": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -189,10 +331,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/categories/images/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -221,10 +376,33 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category.reorder_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/categories/order HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"parent_menus": [
"예시값"
],
"child_menus": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -248,6 +426,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/categories/tree HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -277,6 +464,59 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| children_count | integer | `2` | children 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "카테고리 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"description": null,
"localized_name": "API 문서 샘플 카테고리",
"parent_id": null,
"path": "0",
"depth": 0,
"sort_order": 0,
"is_active": true,
"slug": "apidoc-sample-category",
"url": "apidoc-sample-category",
"icon": "folder",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-08 01:44:49",
"updated_at": "2026-07-08 01:44:49",
"parent": null,
"children": [],
"images": [],
"products_count": 0,
"children_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -307,10 +547,43 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category-image.filter_upload_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/categories/{categoryId}/images HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary
Content-Disposition: form-data; name="temp_key"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="collection"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="alt_text"
예시값
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -337,9 +610,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| category | path | string | 예 | — | 분류 필터 (해당 분류의 항목만 조회) |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/categories/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| category_id | integer | `1` | category 식별자 (연관 리소스 참조) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "카테고리가 삭제되었습니다.",
"data": {
"category_id": 1
}
}
```
**에러 응답**
@@ -376,10 +678,39 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.category.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/categories/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"description": [
"예시 내용입니다."
],
"parent_id": "예시값",
"slug": "example-key",
"is_active": true,
"meta_title": "예시 제목",
"meta_description": "예시 내용입니다.",
"temp_key": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -406,9 +737,87 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/categories/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 카테고리","en":"API Doc Sample Category"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 카테고리` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| parent_id | null | `null` | parent 식별자 (연관 리소스 참조) |
| path | string | `0` | Materialized Path: 1/5/23 |
| depth | integer | `0` | 계층 트리에서의 깊이 (0 = 최상위, 하위로 갈수록 증가) |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| is_active | boolean | `true` | active 여부 |
| slug | string | `apidoc-sample-category` | URL 친화 식별자 (slug) |
| url | string | `apidoc-sample-category` | SortableMenuItem 표시용 URL (slug 값을 그대로 사용) |
| icon | string | `folder` | 아이콘 식별자 (아이콘 클래스/이름) |
| meta_title | null | `null` | SEO 제목 (다국어 JSON) |
| meta_description | null | `null` | SEO 설명 (다국어 JSON) |
| created_at | string | `2026-07-08 01:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 01:44:49` | 최종 수정 일시 |
| parent | null | `null` | 상위 항목 객체 (parent 관계 파생) |
| children | array | `[]` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
| images | array | `[]` | 카테고리 이미지 배열 (images 관계 로드 시 — id/hash/download_url/alt_text 등) |
| products_count | integer | `0` | products 개수 (집계) |
| children_count | integer | `0` | children 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "카테고리 정보를 조회했습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"description": null,
"localized_name": "API 문서 샘플 카테고리",
"parent_id": null,
"path": "0",
"depth": 0,
"sort_order": 0,
"is_active": true,
"slug": "apidoc-sample-category",
"url": "apidoc-sample-category",
"icon": "folder",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-08 01:44:49",
"updated_at": "2026-07-08 01:44:49",
"parent": null,
"children": [],
"images": [],
"products_count": 0,
"children_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -435,9 +844,83 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/categories/1/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 카테고리","en":"API Doc Sample Category"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 카테고리` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| parent_id | null | `null` | parent 식별자 (연관 리소스 참조) |
| path | string | `0` | Materialized Path: 1/5/23 |
| depth | integer | `0` | 계층 트리에서의 깊이 (0 = 최상위, 하위로 갈수록 증가) |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| is_active | boolean | `false` | active 여부 |
| slug | string | `apidoc-sample-category` | URL 친화 식별자 (slug) |
| url | string | `apidoc-sample-category` | SortableMenuItem 표시용 URL (slug 값을 그대로 사용) |
| icon | string | `folder` | 아이콘 식별자 (아이콘 클래스/이름) |
| meta_title | null | `null` | SEO 제목 (다국어 JSON) |
| meta_description | null | `null` | SEO 설명 (다국어 JSON) |
| created_at | string | `2026-07-08 01:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 06:00:17` | 최종 수정 일시 |
| images | array | `[]` | 카테고리 이미지 배열 (images 관계 로드 시 — id/hash/download_url/alt_text 등) |
| products_count | integer | `0` | products 개수 (집계) |
| children_count | integer | `0` | children 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.categories.status_changed",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"description": null,
"localized_name": "API 문서 샘플 카테고리",
"parent_id": null,
"path": "0",
"depth": 0,
"sort_order": 0,
"is_active": false,
"slug": "apidoc-sample-category",
"url": "apidoc-sample-category",
"icon": "folder",
"meta_title": null,
"meta_description": null,
"created_at": "2026-07-08 01:44:49",
"updated_at": "2026-07-08 06:00:17",
"images": [],
"products_count": 0,
"children_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -462,6 +945,14 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/categories HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -475,6 +966,34 @@ _요청 파라미터 없음._
| products_count | integer | `22` | products 개수 (집계) |
| children | array | `[{"id":88,"name":{"ko":"남성","en":"Men"},"name_localized":…` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "카테고리 목록을 조회했습니다.",
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"name_localized": "API 문서 샘플 카테고리",
"slug": "apidoc-sample-category",
"depth": 0,
"parent_id": null,
"products_count": 0,
"children": []
}
]
}
```
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -496,9 +1015,68 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
| --- | --- | --- | --- | --- | --- |
| slug | path | string | 예 | — | 대상 리소스의 slug (URL 친화 식별자) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/categories/apidoc-sample-category HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 카테고리","en":"API Doc Sample Category"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| name_localized | string | `API 문서 샘플 카테고리` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| description_localized | null | `null` | `description` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| slug | string | `apidoc-sample-category` | URL 친화 식별자 (slug) |
| depth | integer | `0` | 계층 트리에서의 깊이 (0 = 최상위, 하위로 갈수록 증가) |
| parent_id | null | `null` | parent 식별자 (연관 리소스 참조) |
| products_count | integer | `0` | products 개수 (집계) |
| breadcrumb | array | `[{"id":1,"name":"API 문서 샘플 카테고리","slug":"apidoc-sample-ca…` | 최상위부터 현재 카테고리까지의 상위 경로 배열 (각 항목에 id·현지화 name·slug — 스토어프론트 breadcrumb 표시용) |
| images | array | `[]` | 카테고리 이미지 배열 (images 관계 로드 시 — id/hash/download_url/alt_text 등) |
| children | array | `[]` | 하위 항목 배열 (계층 트리 — children 관계 파생) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.categories.fetch_success",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 카테고리",
"en": "API Doc Sample Category"
},
"name_localized": "API 문서 샘플 카테고리",
"description": null,
"description_localized": null,
"slug": "apidoc-sample-category",
"depth": 0,
"parent_id": null,
"products_count": 0,
"breadcrumb": [
{
"id": 1,
"name": "API 문서 샘플 카테고리",
"slug": "apidoc-sample-category"
}
],
"images": [],
"children": []
}
}
```
**에러 응답**
@@ -29,10 +29,22 @@
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/category-image/{hash} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,10 +27,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/checkout HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -56,10 +69,23 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
| city | query | string | 아니오 | max 100 | 도시명 (배송비 미리보기 산출용 배송 주소) |
| address | query | string | 아니오 | max 255 | 기본 주소 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/checkout?country_code=KR&zipcode=06234&region=%EC%98%88%EC%8B%9C%EA%B0%92&city=%EC%98%88%EC%8B%9C%EA%B0%92&address=%EC%84%9C%EC%9A%B8%ED%8A%B9%EB%B3%84%EC%8B%9C%20%EA%B0%95%EB%82%A8%EA%B5%AC%20%ED%85%8C%ED%97%A4%EB%9E%80%EB%A1%9C%201 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-404 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -88,10 +114,37 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.checkout.validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/checkout HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"item_ids": [
"예시값"
],
"direct_items": [
"예시값"
],
"coupon_issue_ids": [
"예시값"
],
"use_points": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -123,10 +176,36 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.checkout.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/checkout HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
Content-Type: application/json
{
"item_coupons": [
"예시값"
],
"order_coupon_issue_id": 1,
"shipping_coupon_issue_id": 1,
"use_points": 1,
"zipcode": "06234",
"country_code": "KR",
"payment_method": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -148,10 +227,23 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/checkout/extend HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/claim-reasons HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -50,6 +59,79 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| updater | array | `[]` | 최종 수정자 정보 객체 (id/name — updater 관계 로드 시) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 8,
"id": 1,
"type": "refund",
"code": "order_mistake",
"name": {
"ko": "주문 실수",
"en": "Order Mistake"
},
"localized_name": "주문 실수",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"number": 7,
"id": 8,
"type": "refund",
"code": "apidoc_sample",
"name": {
"ko": "API 문서 샘플 사유",
"en": "API Doc Sample Reason"
},
"localized_name": "API 문서 샘플 사유",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 8건 중 2건 표시)"
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -80,10 +162,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
| sort_order | body | integer | 아니오 | min 0 | 표시 정렬 순서 값 (작을수록 우선) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/claim-reasons HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"type": "예시값",
"code": "예시값",
"name": [
"예시 이름"
],
"fault_type": "예시값",
"is_user_selectable": true,
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -107,6 +215,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/claim-reasons/active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -130,6 +247,79 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| updater | array | `[]` | 최종 수정자 정보 객체 (id/name — updater 관계 로드 시) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 8,
"id": 1,
"type": "refund",
"code": "order_mistake",
"name": {
"ko": "주문 실수",
"en": "Order Mistake"
},
"localized_name": "주문 실수",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"number": 7,
"id": 8,
"type": "refund",
"code": "apidoc_sample",
"name": {
"ko": "API 문서 샘플 사유",
"en": "API Doc Sample Reason"
},
"localized_name": "API 문서 샘플 사유",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 8건 중 2건 표시)"
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -154,9 +344,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/claim-reasons/8 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| reason_id | integer | `8` | reason 식별자 (연관 리소스 참조) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유가 삭제되었습니다.",
"data": {
"reason_id": 8
}
}
```
**에러 응답**
@@ -183,9 +402,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/claim-reasons/8 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `8` | 기본 키 (내부 식별자) |
| type | string | `refund` | 사유 유형 (refund, exchange, return 등) |
| code | string | `apidoc_sample` | 고유 코드 (order_mistake 등) |
| name | object | `{"ko":"API 문서 샘플 사유","en":"API Doc Sample Reason"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 사유` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| fault_type | string | `customer` | 귀책 구분 (customer, seller, carrier) |
| fault_type_label | string | `고객 귀책` | `fault_type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| is_user_selectable | boolean | `true` | user selectable 여부 |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유 정보를 조회했습니다.",
"data": {
"id": 8,
"type": "refund",
"code": "apidoc_sample",
"name": {
"ko": "API 문서 샘플 사유",
"en": "API Doc Sample Reason"
},
"localized_name": "API 문서 샘플 사유",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -219,10 +498,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
| sort_order | body | integer | 아니오 | min 0 | 표시 정렬 순서 값 (작을수록 우선) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/claim-reasons/8 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"type": "예시값",
"code": "예시값",
"name": [
"예시 이름"
],
"fault_type": "예시값",
"is_user_selectable": true,
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -249,9 +554,69 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/claim-reasons/8/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `8` | 기본 키 (내부 식별자) |
| type | string | `refund` | 사유 유형 (refund, exchange, return 등) |
| code | string | `apidoc_sample` | 고유 코드 (order_mistake 등) |
| name | object | `{"ko":"API 문서 샘플 사유","en":"API Doc Sample Reason"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 사유` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| fault_type | string | `customer` | 귀책 구분 (customer, seller, carrier) |
| fault_type_label | string | `고객 귀책` | `fault_type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| is_user_selectable | boolean | `true` | user selectable 여부 |
| is_active | boolean | `false` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:18` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유 상태가 변경되었습니다.",
"data": {
"id": 8,
"type": "refund",
"code": "apidoc_sample",
"name": {
"ko": "API 문서 샘플 사유",
"en": "API Doc Sample Reason"
},
"localized_name": "API 문서 샘플 사유",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:18",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -276,6 +641,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/claim-reasons HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -299,6 +673,79 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| updater | array | `[]` | 최종 수정자 정보 객체 (id/name — updater 관계 로드 시) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "클래임 사유 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 7,
"id": 1,
"type": "refund",
"code": "order_mistake",
"name": {
"ko": "주문 실수",
"en": "Order Mistake"
},
"localized_name": "주문 실수",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"number": 6,
"id": 8,
"type": "refund",
"code": "apidoc_sample",
"name": {
"ko": "API 문서 샘플 사유",
"en": "API Doc Sample Reason"
},
"localized_name": "API 문서 샘플 사유",
"fault_type": "customer",
"fault_type_label": "고객 귀책",
"is_user_selectable": true,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 7건 중 2건 표시)"
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -32,6 +32,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_list_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons?status=available&per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -40,6 +49,33 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| coupons | object | `{"data":[{"id":8611,"coupon_id":156,"user_id":"a1e0a91a-f…` | 회원이 발급받은 쿠폰(발급 내역) 페이지네이션 객체 (`data[]` 발급 건 + `pagination` — CouponIssueCollection 직렬화, 쿠폰 정의가 아닌 회원별 발급 건) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰 목록을 조회했습니다.",
"data": {
"coupons": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -66,6 +102,15 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_available_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons/available?product_ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -74,6 +119,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| coupons | array | `[{"id":4702,"coupon_id":106,"user_id":1,"coupon_code":nul…` | 현재 장바구니 상품에 적용 가능한 보유 쿠폰(발급 건) 배열 (상품/카테고리 범위·최소 주문금액·유효기간을 만족해 주문에 곧바로 선택 가능한 후보만) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "사용 가능한 쿠폰 목록을 조회했습니다.",
"data": {
"coupons": []
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -100,6 +161,15 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.user_downloadable_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/coupons/downloadable?per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -146,6 +216,98 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| included_categories | array | `[]` | 적용 대상 포함 카테고리 목록 (target_scope=categories 시 이 카테고리에만 적용) |
| excluded_categories | array | `[]` | 적용 제외 카테고리 목록 (해당 카테고리는 쿠폰 적용에서 제외) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "다운로드 가능한 쿠폰 목록을 불러왔습니다.",
"data": {
"current_page": 1,
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 쿠폰",
"en": "API Doc Sample Coupon"
},
"description": null,
"target_type": "order_amount",
"discount_type": "fixed",
"discount_value": "1000.00",
"discount_max_amount": null,
"min_order_amount": "0.00",
"issue_method": "download",
"issue_condition": "manual",
"issue_status": "issuing",
"total_quantity": null,
"issued_count": 0,
"per_user_limit": 1,
"valid_type": "period",
"valid_days": null,
"valid_from": null,
"valid_to": null,
"issue_from": null,
"issue_to": null,
"is_combinable": false,
"target_scope": "all",
"created_by": null,
"created_at": "2026-07-08T01:44:49.000000Z",
"updated_at": "2026-07-08T01:44:49.000000Z",
"deleted_at": null,
"is_downloaded": false,
"user_issued_count": 0,
"coupon_id": 1,
"localized_name": "API 문서 샘플 쿠폰",
"target_type_short_label": "주문",
"valid_period_formatted": "-",
"min_order_amount_formatted": "0원",
"remaining_quantity": null,
"benefit_formatted": "1,000원 할인",
"included_products": [],
"excluded_products": [],
"included_categories": [],
"excluded_categories": []
}
],
"first_page_url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"next_page_url": null,
"path": "https://api.example.com/api/modules/sirsoft-ecommerce/user/coupons/downloadable",
"per_page": 25,
"prev_page_url": null,
"to": 1,
"total": 1
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -171,10 +333,28 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| couponId | path | string | 예 | — | 대상 coupon의 식별자 |
| coupon_id | body | integer | 예 | — | coupon 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/coupons/{couponId}/download HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"coupon_id": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/currency HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| preferred_currency | null | `null` | 회원이 저장한 선호 결제 통화 코드 (미설정 시 `null`) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "결제 통화를 조회했습니다.",
"data": {
"preferred_currency": null
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -58,10 +83,28 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| currency | body | string | 예 | — | 저장할 선호 결제 통화 코드 (등록 통화: is_default 또는 exchange_rate>0 인 통화만 허용) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/user/currency HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"currency": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/dashboard/overview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -41,6 +50,28 @@ _단건 응답: `data` 객체의 필드._
| cancellations | integer | `0` | 오늘 취소 상태 주문상품 수량 (전체취소 기준, 부분취소 포함) |
| returns | integer | `0` | 오늘 반품 상태 주문상품 수량 (환불 도메인 미반영으로 현재 항상 0) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "대시보드 데이터를 조회했습니다.",
"data": {
"pending_payment": 0,
"payment_complete": 0,
"preparing": 0,
"shipping_ready": 0,
"shipping": 0,
"cancellations": 0,
"returns": 0
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -65,6 +96,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| limit | query | integer | 아니오 | min 1, max 50 | 반환할 최대 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/dashboard/pending-inquiries?limit=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -75,6 +115,33 @@ _단건 응답: `data` 객체의 필드._
| total | integer | `1` | 전체 개수 (집계) |
| board_slug | null | `null` | 문의가 저장된 연동 게시판의 slug (관리자 문의 상세 링크용, 미연동 시 null) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "대시보드 데이터를 조회했습니다.",
"data": {
"items": [
{
"id": 1,
"product_id": 1,
"inquirable_id": 59404,
"product_name": "iste et inventore",
"author_name": "API 문서 샘플 사용자",
"created_at": "2026-07-08 10:44:49"
}
],
"total": 1,
"board_slug": null
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -100,6 +167,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| limit | query | integer | 아니오 | min 1, max 50 | 반환할 최대 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/dashboard/recent-reviews?limit=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -111,6 +187,29 @@ _단건 응답: `data` 객체의 필드._
| author_name | string | `API 문서 샘플 사용자` | 리뷰 작성자 이름 (user 관계 파생) |
| created_at | string | `2026-07-07 14:47:31` | 생성 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "대시보드 데이터를 조회했습니다.",
"data": [
{
"id": 1,
"product_id": 1,
"product_name": "API 문서 샘플 상품",
"rating": 5,
"author_name": "API 문서 샘플 사용자",
"created_at": "2026-07-08 10:44:49"
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -134,6 +233,15 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/dashboard/sales-graph HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -148,6 +256,64 @@ _단건 응답: `data` 객체의 필드._
| updated_at | null | `null` | 최종 수정 일시 |
| updated_at_display | string | `` | 집계 마지막 갱신 시각의 사용자 타임존 HH:mm 캡션 (갱신 이력 없으면 빈 문자열) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "대시보드 데이터를 조회했습니다.",
"data": {
"days": [
{
"date": "2026-07-02",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-03",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-04",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-05",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-06",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-07",
"sales_quantity": 0,
"sales_amount": 0
},
{
"date": "2026-07-08",
"sales_quantity": 0,
"sales_amount": 0
}
],
"total_quantity": 0,
"total_sales": 0,
"quantity_change": null,
"sales_change": null,
"updated_at": null,
"updated_at_display": ""
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -35,6 +35,15 @@
| per_page | query | integer | 아니오 | min 10, max 100 | 페이지당 항목 수 |
| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/extra-fee-templates?search=%EC%98%88%EC%8B%9C%EA%B0%92&region=%EC%98%88%EC%8B%9C%EA%B0%92&is_active=%2C%20&sort_by=id&sort_order=asc&per_page=1&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -55,6 +64,62 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| updated_at | string | `2026-07-07 14:47:31` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "추가배송비 템플릿 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"zipcode": "00000",
"fee": 3000,
"fee_formatted": "3,000원",
"region": null,
"description": null,
"is_active": true,
"created_by": null,
"updated_by": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
},
"statistics": {
"total": 1,
"active": 1,
"inactive": 0,
"by_region": []
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -84,10 +149,32 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| description | body | string | 아니오 | max 1000 | 설명 |
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/extra-fee-templates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"zipcode": "06234",
"fee": 1,
"region": "예시값",
"description": "예시 내용입니다.",
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -111,6 +198,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/active-settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -119,6 +215,26 @@ _요청 파라미터 없음._
| fee | integer | `3000` | 해당 우편번호의 추가 배송비 (float 변환값) |
| region | string | `` | 지역명 (없으면 빈 문자열) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "활성 추가배송비 템플릿 설정을 조회했습니다.",
"data": [
{
"zipcode": "00000",
"fee": 3000,
"region": ""
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -143,10 +259,23 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| ids | query | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/bulk?ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -172,10 +301,30 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| items | body | array | 예 | min 1, max 1000 | 처리 대상 항목 배열 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/bulk HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"items": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -202,10 +351,31 @@ _요청 파라미터 없음._
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
| is_active | body | boolean | 예 | — | 활성 여부 (true 활성 / false 비활성) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/bulk-toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -231,9 +401,34 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "추가배송비 템플릿이 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -260,9 +455,64 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| zipcode | string | `00000` | 우편번호 (단일 또는 범위) |
| fee | integer | `3000` | 추가 배송비 |
| fee_formatted | string | `3,000원` | `fee` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| region | null | `null` | 지역명 (예: 제주도, 울릉도) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| is_active | boolean | `true` | active 여부 |
| created_by | null | `null` | 생성자 ID |
| updated_by | null | `null` | 최종 수정한 사용자 정보 (uuid/name — updated_by 관계 파생, 없으면 null) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "추가배송비 템플릿 정보를 조회했습니다.",
"data": {
"id": 1,
"zipcode": "00000",
"fee": 3000,
"fee_formatted": "3,000원",
"region": null,
"description": null,
"is_active": true,
"created_by": null,
"updated_by": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -294,10 +544,32 @@ _요청 파라미터 없음._
| description | body | string | 아니오 | max 1000 | 설명 |
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"zipcode": "06234",
"fee": 1,
"region": "예시값",
"description": "예시 내용입니다.",
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -324,9 +596,64 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/extra-fee-templates/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| zipcode | string | `00000` | 우편번호 (단일 또는 범위) |
| fee | integer | `3000` | 추가 배송비 |
| fee_formatted | string | `3,000원` | `fee` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| region | null | `null` | 지역명 (예: 제주도, 울릉도) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| is_active | boolean | `false` | active 여부 |
| created_by | null | `null` | 생성자 ID |
| updated_by | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | 최종 수정한 사용자 정보 (uuid/name — updated_by 관계 파생, 없으면 null) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:20` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "추가배송비 템플릿 사용여부가 변경되었습니다.",
"data": {
"id": 1,
"zipcode": "00000",
"fee": 3000,
"fee_formatted": "3,000원",
"region": null,
"description": null,
"is_active": false,
"created_by": null,
"updated_by": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:20",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -31,10 +31,29 @@
| orderer_phone | body | string | 예 | max 20 | 주문자 전화번호 (본인 확인 키 ②) |
| guest_lookup_password | body | string | 예 | max 255 | 비회원 주문 조회 비밀번호 (본인 확인 키 ③) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/guest/orders/verify HTTP/1.1
Host: api.example.com
Accept: application/json
Content-Type: application/json
{
"order_number": "예시값",
"orderer_phone": "010-1234-5678",
"guest_lookup_password": "Password123!"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-404 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -62,10 +81,32 @@
| items | body | array | 아니오 | min 1 | 처리 대상 항목 배열 (전달 시 부분취소, 미전달 시 전체취소) |
| refund_priority | body | string | 아니오 | `pg_first`, `points_first` | 환불 배분 우선순위 (PG 우선 / 포인트 우선, 미전달 시 pg_first) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/guest/orders/{orderNumber}/cancel HTTP/1.1
Host: api.example.com
Accept: application/json
Content-Type: application/json
{
"reason": "예시값",
"reason_detail": "예시값",
"items": [
"예시값"
],
"refund_priority": "pg_first"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -92,10 +133,30 @@
| items | body | array | 예 | min 1 | 처리 대상 항목 배열 |
| refund_priority | body | string | 아니오 | `pg_first`, `points_first` | 환불 배분 우선순위 (PG 우선 / 포인트 우선, 미전달 시 pg_first) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/guest/orders/{orderNumber}/estimate-refund HTTP/1.1
Host: api.example.com
Accept: application/json
Content-Type: application/json
{
"items": [
"예시값"
],
"refund_priority": "pg_first"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -121,10 +182,22 @@
| orderNumber | path | string | 예 | — | 대상 order number의 식별자 |
| optionId | path | string | 예 | — | 대상 option의 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/guest/orders/{orderNumber}/options/{optionId}/confirm HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -161,10 +234,39 @@
| intl_postal_code | body | string | 아니오 | max 20 | 우편번호 (국제 주소) |
| delivery_memo | body | string | 아니오 | max 255 | 배송 메모 (배송 시 요청사항) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/guest/orders/{orderNumber}/shipping-address HTTP/1.1
Host: api.example.com
Accept: application/json
Content-Type: application/json
{
"recipient_name": "예시 이름",
"recipient_phone": "010-1234-5678",
"recipient_tel": "예시값",
"country_code": "KR",
"zipcode": "06234",
"address": "서울특별시 강남구 테헤란로 1",
"address_detail": "서울특별시 강남구 테헤란로 1",
"address_line_1": "서울특별시 강남구 테헤란로 1",
"address_line_2": "서울특별시 강남구 테헤란로 1",
"intl_city": "예시값",
"intl_state": "예시값",
"intl_postal_code": "06234",
"delivery_memo": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -29,10 +29,23 @@
| --- | --- | --- | --- | --- | --- |
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/inquiries/{inquiryId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -58,10 +71,23 @@
| --- | --- | --- | --- | --- | --- |
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -88,10 +114,28 @@
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
| content | body | string | 예 | min 1, max 5000 | 본문 내용 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"content": "예시 내용입니다."
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -119,10 +163,28 @@
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
| content | body | string | 예 | min 1, max 5000 | 본문 내용 |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"content": "예시 내용입니다."
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -147,6 +209,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/inquiries HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -156,6 +227,69 @@ _단건 응답: `data` 객체의 필드._
| items | array | `[]` | 내 문의 항목 배열 (각 항목: id, product 요약, product_name, is_answered, 게시판 연동 시 title/category/content/is_secret/reply/attachments) |
| meta | object | `{"current_page":1,"per_page":25,"total":0,"last_page":1,"…` | 페이지네이션 메타 (current_page/per_page/total/last_page/from/to, 문의 게시판 연동 여부 inquiry_available, abilities 답변·삭제 권한, board_settings) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "문의 목록을 조회했습니다.",
"data": {
"items": [
{
"id": 1,
"product_id": 1,
"product": {
"id": 1,
"name": "API 문서 샘플 상품",
"thumbnail_url": null,
"url": "/shop/products/1"
},
"product_name": "iste et inventore",
"is_answered": false,
"answered_at": null,
"created_at": "2026-07-08T01:44:49+00:00",
"updated_at": "2026-07-08T01:44:49+00:00",
"title": null,
"category": null,
"content": null,
"is_secret": "{MASKED}",
"reply": null,
"attachments": []
}
],
"meta": {
"current_page": 1,
"per_page": 25,
"total": 1,
"last_page": 1,
"from": 1,
"to": 1,
"inquiry_available": false,
"abilities": {
"can_update": true,
"can_delete": true
},
"board_settings": {
"secret_mode": "{MASKED}",
"categories": [],
"use_file_upload": false,
"max_file_count": 5,
"max_file_size": 10485760,
"allowed_extensions": [],
"min_title_length": 2,
"max_title_length": 200,
"min_content_length": 10,
"max_content_length": 10000
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -179,10 +313,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/user/inquiries/{inquiryId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -213,10 +360,31 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.inquiry.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/user/inquiries/{inquiryId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"title": "예시 제목",
"category": "예시값",
"content": "예시 내용입니다.",
"is_secret": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -242,10 +410,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/user/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -271,10 +452,28 @@ _단건 응답: `data` 객체의 필드._
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
| content | body | string | 예 | min 1, max 5000 | 본문 내용 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"content": "예시 내용입니다."
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -301,10 +500,28 @@ _단건 응답: `data` 객체의 필드._
| inquiryId | path | string | 예 | — | 대상 inquiry의 식별자 |
| content | body | string | 예 | min 1, max 5000 | 본문 내용 |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/user/inquiries/{inquiryId}/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"content": "예시 내용입니다."
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -37,6 +37,15 @@
| start_date | query | date | 아니오 | — | 조회 기간 시작일 (이 날짜 이후 데이터) |
| end_date | query | date | 아니오 | — | 조회 기간 종료일 (이 날짜 이전 데이터) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/mileage-transactions?page=1&per_page=1&sort=created_at_desc&search_field=member&search_keyword=%EC%98%88%EC%8B%9C%EA%B0%92&type=earn&currency=%EC%98%88%EC%8B%9C%EA%B0%92&start_date=2026-01-01&end_date=2026-01-01 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -83,6 +92,82 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| expiry_state | string | `active` | 적립건 소멸 상태 (active=미소멸, partial_expired=일부 소멸, fully_expired=전액 소멸 — 적립계만 의미) |
| abilities | object | `{"can_manage":true,"can_edit":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "마일리지 내역을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"user_id": 6,
"currency": "KRW",
"type": "admin_earn",
"type_label": "관리자 지급",
"admin_badge_group": "amber",
"user_display_category": "adjust",
"amount": 1000,
"amount_formatted": "1,000원",
"remaining_amount": 0,
"remaining_amount_formatted": "0원",
"balance_after": 1000,
"order_id": null,
"order_option_id": null,
"order_cancel_id": null,
"source_transaction_id": null,
"granted_by": null,
"granted_by_name": null,
"granted_by_uuid": null,
"user_name": "API 문서 샘플 사용자",
"user_uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"order_number": null,
"description": null,
"memo": null,
"expires_at": null,
"expires_at_formatted": null,
"expires_at_date": null,
"expired_at": null,
"expired_at_formatted": null,
"created_at": "2026-07-08T01:44:49+00:00",
"created_at_formatted": "2026-07-08 10:44:49",
"created_at_date": "2026-07-08",
"is_earning": true,
"can_edit_expiry": false,
"expired_amount": 0,
"expired_amount_formatted": "0원",
"expiry_state": "active",
"abilities": {
"can_manage": true,
"can_edit": true
}
}
],
"abilities": {
"can_manage": true
},
"currencies": [
"KRW"
],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -115,10 +200,35 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| expires_at | body | date | 아니오 | — | 만료일 직접 지정 (지급 시 `use_default_expiry`=false 인 경우 적용) |
| use_default_expiry | body | boolean | 아니오 | — | 지급 시 정책 기본 만료일 적용 여부 (기본 true, false 면 `expires_at` 사용) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/mileage-transactions HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"user_id": "9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d",
"action": "earn",
"amount": 1,
"currency": "예시값",
"memo": "예시값",
"description": "예시 내용입니다.",
"expires_at": "2026-01-01",
"use_default_expiry": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -146,10 +256,32 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| lot_ids | body | array | 예 | min 1 | lot 식별자 배열 |
| days | body | integer | 예 | min 1, max 3650 | 각 lot 만료일을 연장할 일수 (최대 3650일) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/mileage-transactions/extend-expiry HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"user_id": "9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d",
"lot_ids": [
"예시값"
],
"days": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -177,10 +309,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| memo | body | string | 아니오 | max 1000 | 관리자 메모 보정값 (요청에 포함된 경우만 갱신, 빈 값으로 비우기 허용) |
| expires_at | body | date | 아니오 | — | expires 일시 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/mileage-transactions/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"memo": "예시값",
"expires_at": "2026-01-01"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -207,9 +358,40 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/mileage-transactions/1/linked HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_목록 응답: `data.data[]` 배열 항목의 필드._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "연결 거래를 조회했습니다.",
"data": {
"data": [],
"abilities": {
"can_manage": true
},
"currencies": []
}
}
```
**에러 응답**
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/mileage HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,31 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| mileage | object | `{"enabled":false,"available":12910,"pending":14000,"expir…` | 마일리지 잔액 요약 객체 (enabled 기능 활성화 여부, available 사용 가능, pending 적립 대기, expiring_soon/expiring_date 소멸 예정, total_earned/total_used 누적 적립·사용, by_currency 통화별 잔액) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "마일리지 잔액을 조회했습니다.",
"data": {
"mileage": {
"enabled": false,
"available": 0,
"pending": 0,
"expiring_soon": 0,
"expiring_date": null,
"total_earned": 0,
"total_used": 0,
"by_currency": []
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -60,6 +94,15 @@ _단건 응답: `data` 객체의 필드._
| currency | query | string | 아니오 | max 10 | 통화 코드 필터 (해당 통화의 마일리지 거래만 조회) |
| per_page | query | integer | 아니오 | min 1, max 100 | 페이지당 항목 수 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/mileage/history?category=earn&currency=%EC%98%88%EC%8B%9C%EA%B0%92&per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -68,6 +111,82 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| transactions | object | `{"data":[{"number":6,"id":527,"user_id":1,"currency":"KRW…` | 마일리지 거래 내역 페이지네이션 객체 (`data` 거래 항목 배열 + 페이지 메타, `MileageTransactionCollection` 으로 직렬화) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "마일리지 내역을 조회했습니다.",
"data": {
"transactions": {
"data": [
{
"number": 1,
"id": 1,
"user_id": 6,
"currency": "KRW",
"type": "admin_earn",
"type_label": "관리자 지급",
"admin_badge_group": "amber",
"user_display_category": "adjust",
"amount": 1000,
"amount_formatted": "1,000원",
"remaining_amount": 0,
"remaining_amount_formatted": "0원",
"balance_after": 1000,
"order_id": null,
"order_option_id": null,
"order_cancel_id": null,
"source_transaction_id": null,
"granted_by": null,
"granted_by_name": [],
"granted_by_uuid": [],
"user_name": [],
"user_uuid": [],
"order_number": [],
"description": null,
"memo": null,
"expires_at": null,
"expires_at_formatted": null,
"expires_at_date": null,
"expired_at": null,
"expired_at_formatted": null,
"created_at": "2026-07-08T01:44:49+00:00",
"created_at_formatted": "2026-07-08 10:44:49",
"created_at_date": "2026-07-08",
"is_earning": true,
"can_edit_expiry": false,
"expired_amount": 0,
"expired_amount_formatted": "0원",
"expiry_state": "active",
"abilities": {
"can_manage": true,
"can_edit": true
}
}
],
"abilities": {
"can_manage": true
},
"currencies": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -94,10 +213,23 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.mileage.max_usable_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/mileage/max-usable?order_amount=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: http-422 — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -35,10 +35,36 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product_option.bulk_price_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/options/bulk-price HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"product_ids": [
"예시값"
],
"option_ids": [
"예시값"
],
"method": "increase",
"value": 1,
"unit": "won"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -69,10 +95,35 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product_option.bulk_stock_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/options/bulk-stock HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"product_ids": [
"예시값"
],
"option_ids": [
"예시값"
],
"method": "increase",
"value": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -102,10 +153,36 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.option.bulk_update_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/options/bulk-update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"bulk_changes": [
"예시값"
],
"items": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
@@ -29,10 +29,22 @@
| --- | --- | --- | --- | --- | --- |
| provider | path | string | 예 | — | 대상 provider의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/payments/client-config/{provider} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -31,12 +31,35 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.search_preset.list_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/presets?target_screen=products HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.presets.fetch_success",
"data": []
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -65,10 +88,32 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.preset.store_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/presets HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"target_screen": "예시값",
"name": "예시 이름",
"conditions": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -93,10 +138,23 @@
| --- | --- | --- | --- | --- | --- |
| preset | path | string | 예 | — | 대상 preset의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/presets/{preset} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -126,10 +184,32 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.preset.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/presets/{preset} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": "예시 이름",
"conditions": [
"예시값"
],
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-common-infos HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -49,6 +58,61 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| language_count | integer | `2` | language 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "공통정보 목록을 불러왔습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 공통정보",
"en": "API Doc Sample Common Info"
},
"localized_name": "API 문서 샘플 공통정보",
"content": [],
"localized_content": "",
"content_mode": "text",
"is_default": false,
"is_active": true,
"sort_order": 0,
"icon": "info-circle",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"products_count": 0,
"language_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -80,10 +144,37 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product-common-info.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/product-common-infos HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"content": [
"예시 내용입니다."
],
"content_mode": "text",
"is_default": true,
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -109,9 +200,40 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/product-common-infos/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| common_info_id | integer | `1` | common info 식별자 (연관 리소스 참조) |
| products_count | integer | `0` | products 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "공통정보가 삭제되었습니다.",
"data": {
"common_info_id": 1,
"products_count": 0
}
}
```
**에러 응답**
@@ -138,9 +260,73 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-common-infos/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 공통정보","en":"API Doc Sample Common Info"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 공통정보` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| content | array | `[]` | 본문 내용 |
| localized_content | string | `` | `content` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| content_mode | string | `text` | 내용 모드 (text: 텍스트, html: HTML) |
| is_default | boolean | `false` | default 여부 |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| icon | string | `info-circle` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| products_count | integer | `0` | products 개수 (집계) |
| language_count | integer | `0` | language 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "공통정보 목록을 불러왔습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 공통정보",
"en": "API Doc Sample Common Info"
},
"localized_name": "API 문서 샘플 공통정보",
"content": [],
"localized_content": "",
"content_mode": "text",
"is_default": false,
"is_active": true,
"sort_order": 0,
"icon": "info-circle",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"products_count": 0,
"language_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -175,10 +361,37 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product-common-info.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/product-common-infos/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"content": [
"예시 내용입니다."
],
"content_mode": "text",
"is_default": true,
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -205,9 +418,73 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/product-common-infos/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 공통정보","en":"API Doc Sample Common Info"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 공통정보` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| content | array | `[]` | 본문 내용 |
| localized_content | string | `` | `content` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| content_mode | string | `text` | 내용 모드 (text: 텍스트, html: HTML) |
| is_default | boolean | `false` | default 여부 |
| is_active | boolean | `false` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| icon | string | `info-circle` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:26` | 최종 수정 일시 |
| products_count | integer | `0` | products 개수 (집계) |
| language_count | integer | `0` | language 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "공통정보가 비활성화되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 공통정보",
"en": "API Doc Sample Common Info"
},
"localized_name": "API 문서 샘플 공통정보",
"content": [],
"localized_content": "",
"content_mode": "text",
"is_default": false,
"is_active": false,
"sort_order": 0,
"icon": "info-circle",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:26",
"products_count": 0,
"language_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -29,10 +29,22 @@
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/product-image/{hash} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -33,6 +33,15 @@
| sort | query | string | 아니오 | `name_asc`, `name_desc`, `created_asc`, `created_desc` | 정렬 기준 (필드명, `-` 접두 시 내림차순) |
| locale | query | string | 아니오 | `ko`, `en`, `fr`, `ja` | 로케일 코드 (표시 언어/지역) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-labels?is_active=1&active_only=1&search=%EC%98%88%EC%8B%9C%EA%B0%92&sort=name_asc&locale=ko HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -49,6 +58,46 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| assignments_count | integer | `0` | assignments 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "라벨 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 라벨",
"en": "API Doc Sample Label"
},
"color": null,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"assignments_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -77,10 +126,33 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
| sort_order | body | integer | 아니오 | min 0 | 표시 정렬 순서 값 (작을수록 우선) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/product-labels HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"color": "#4F46E5",
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -106,9 +178,40 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/product-labels/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| label_id | integer | `1` | label 식별자 (연관 리소스 참조) |
| products_count | integer | `0` | products 개수 (집계) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "라벨이 삭제되었습니다.",
"data": {
"label_id": 1,
"products_count": 0
}
}
```
**에러 응답**
@@ -135,9 +238,61 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-labels/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 라벨","en":"API Doc Sample Label"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| color | null | `null` | 색상 코드 (예: #FF5733) |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| assignments_count | integer | `0` | assignments 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "라벨 정보를 조회했습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 라벨",
"en": "API Doc Sample Label"
},
"color": null,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"assignments_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -168,10 +323,33 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_active | body | boolean | 아니오 | — | 활성 여부 (true 활성 / false 비활성) |
| sort_order | body | integer | 아니오 | min 0 | 표시 정렬 순서 값 (작을수록 우선) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/product-labels/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"color": "#4F46E5",
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -198,9 +376,61 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/product-labels/1/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 라벨","en":"API Doc Sample Label"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| color | null | `null` | 색상 코드 (예: #FF5733) |
| is_active | boolean | `false` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:27` | 최종 수정 일시 |
| assignments_count | integer | `0` | assignments 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "라벨 상태가 변경되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 라벨",
"en": "API Doc Sample Label"
},
"color": null,
"is_active": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:27",
"assignments_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -32,6 +32,15 @@
| per_page | query | string | 아니오 | — | 페이지당 항목 수 |
| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-notice-templates?search=%EC%98%88%EC%8B%9C%EA%B0%92&active_only=1&per_page=%EC%98%88%EC%8B%9C%EA%B0%92&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -51,6 +60,63 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| updated_at | string | `2026-07-07 14:47:31` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "상품정보제공고시 목록을 불러왔습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 고시템플릿",
"en": "API Doc Sample Notice Template"
},
"localized_name": "API 문서 샘플 고시템플릿",
"category": null,
"fields": [
{
"label": "품명",
"value": "샘플"
}
],
"fields_count": 1,
"is_active": true,
"sort_order": 0,
"icon": "file-alt",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -82,10 +148,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product-notice-template.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/product-notice-templates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"category": "예시값",
"fields": [
"예시값"
],
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -111,9 +203,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/product-notice-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| template_id | integer | `1` | template 식별자 (연관 리소스 참조) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "상품정보제공고시가 삭제되었습니다.",
"data": {
"template_id": 1
}
}
```
**에러 응답**
@@ -140,9 +261,72 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/product-notice-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 고시템플릿","en":"API Doc Sample Notice Templ…` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 고시템플릿` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| category | null | `null` | 품목 카테고리 |
| fields | array | `[{"label":"품명","value":"샘플"}]` | 필드 정의 JSON |
| fields_count | integer | `1` | fields 개수 (집계) |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| icon | string | `file-alt` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "상품정보제공고시 목록을 불러왔습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 고시템플릿",
"en": "API Doc Sample Notice Template"
},
"localized_name": "API 문서 샘플 고시템플릿",
"category": null,
"fields": [
{
"label": "품명",
"value": "샘플"
}
],
"fields_count": 1,
"is_active": true,
"sort_order": 0,
"icon": "file-alt",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -176,10 +360,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.product-notice-template.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/product-notice-templates/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"category": "예시값",
"fields": [
"예시값"
],
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -206,9 +416,72 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/product-notice-templates/1/copy HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `2` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 고시템플릿 (복사)","en":"API Doc Sample Notice …` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 고시템플릿 (복사)` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| category | null | `null` | 품목 카테고리 |
| fields | array | `[{"label":"품명","value":"샘플"}]` | 필드 정의 JSON |
| fields_count | integer | `1` | fields 개수 (집계) |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `1` | 표시 정렬 순서 값 (작을수록 우선) |
| icon | string | `file-alt` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 15:00:27` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:27` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 201
```
```json
{
"success": true,
"message": "상품정보제공고시가 복사되었습니다.",
"data": {
"id": 2,
"name": {
"ko": "API 문서 샘플 고시템플릿 (복사)",
"en": "API Doc Sample Notice Template (Copy)"
},
"localized_name": "API 문서 샘플 고시템플릿 (복사)",
"category": null,
"fields": [
{
"label": "품명",
"value": "샘플"
}
],
"fields_count": 1,
"is_active": true,
"sort_order": 1,
"icon": "file-alt",
"created_at": "2026-07-08 15:00:27",
"updated_at": "2026-07-08 15:00:27",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -235,9 +508,72 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/product-notice-templates/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 고시템플릿","en":"API Doc Sample Notice Templ…` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 고시템플릿` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| category | null | `null` | 품목 카테고리 |
| fields | array | `[{"label":"품명","value":"샘플"}]` | 필드 정의 JSON |
| fields_count | integer | `1` | fields 개수 (집계) |
| is_active | boolean | `false` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| icon | string | `file-alt` | 아이콘 식별자 (아이콘 클래스/이름) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:27` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "상품정보제공고시가 비활성화되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 고시템플릿",
"en": "API Doc Sample Notice Template"
},
"localized_name": "API 문서 샘플 고시템플릿",
"category": null,
"fields": [
{
"label": "품명",
"value": "샘플"
}
],
"fields_count": 1,
"is_active": false,
"sort_order": 0,
"icon": "file-alt",
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:27",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
File diff suppressed because it is too large Load Diff
@@ -49,6 +49,15 @@
| issue_end_date | query | date | 아니오 | — | 발급기간 범위 종료 (시작일 이후) |
| created_by | query | uuid | 아니오 | — | 등록자(생성한 관리자) UUID 필터 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/promotion-coupons?page=1&per_page=1&sort_by=created_at&sort_order=asc&search_field=all&search_keyword=%EC%98%88%EC%8B%9C%EA%B0%92&target_type=all&discount_type=all&issue_status=all&issue_method=all&issue_condition=all&min_benefit_amount=1&max_benefit_amount=1&min_order_amount=1&created_start_date=2026-01-01&created_end_date=2026-01-01&valid_start_date=2026-01-01&valid_end_date=2026-01-01&issue_start_date=2026-01-01&issue_end_date=2026-01-01&created_by=9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -106,6 +115,131 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| issues_count | integer | `0` | issues 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"name": {
"ko": "API 문서 샘플 쿠폰",
"en": "API Doc Sample Coupon"
},
"localized_name": "API 문서 샘플 쿠폰",
"description": null,
"localized_description": null,
"target_type": "order_amount",
"target_type_label": "주문금액",
"target_type_badge_color": "blue",
"discount_type": "fixed",
"discount_type_label": "정액할인",
"discount_value": 1000,
"discount_max_amount": null,
"min_order_amount": 0,
"benefit_formatted": "1,000원 할인",
"multi_currency_discount_value": {
"KRW": {
"price": 1000,
"formatted": "1,000원",
"is_default": true,
"editable": true
},
"USD": {
"price": 0.85,
"formatted": "$0.85",
"is_default": false,
"editable": false,
"exchange_rate": 0.85
},
"JPY": {
"price": 115,
"formatted": "¥115",
"is_default": false,
"editable": false,
"exchange_rate": 115
},
"CNY": {
"price": 5.8,
"formatted": "元5.80",
"is_default": false,
"editable": false,
"exchange_rate": 5.8
},
"EUR": {
"price": 0.78,
"formatted": "€0.78",
"is_default": false,
"editable": false,
"exchange_rate": 0.78
}
},
"multi_currency_min_order_amount": null,
"multi_currency_discount_max_amount": null,
"issue_method": "download",
"issue_method_label": "다운로드",
"issue_method_badge_color": "teal",
"issue_condition": "manual",
"issue_condition_label": "수동발급",
"issue_condition_badge_color": "orange",
"issue_status": "issuing",
"issue_status_label": "발급중",
"issue_status_badge_color": "blue",
"total_quantity": null,
"issued_count": 0,
"per_user_limit": 1,
"issue_count_formatted": "0/무제한",
"valid_type": "period",
"valid_days": null,
"valid_from": null,
"valid_to": null,
"valid_period_formatted": "-",
"issue_from": null,
"issue_to": null,
"issue_period_formatted": "상시발급",
"is_combinable": false,
"target_scope": "all",
"target_scope_label": "전체상품",
"is_issuable": true,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"created_by": null,
"created_by_name": "-",
"created_by_email": null,
"creator": null,
"issues_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -154,10 +288,57 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/promotion-coupons HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"description": [
"예시 내용입니다."
],
"target_type": "product_amount",
"discount_type": "fixed",
"discount_value": 1,
"discount_max_amount": 1,
"min_order_amount": 1,
"issue_method": "direct",
"issue_condition": "manual",
"issue_status": "issuing",
"total_quantity": 1,
"per_user_limit": 1,
"valid_type": "period",
"valid_days": 1,
"valid_from": "2026-01-01",
"valid_to": "2026-01-01",
"issue_from": "2026-01-01",
"issue_to": "2026-01-01",
"is_combinable": true,
"target_scope": "all",
"products": [
"예시값"
],
"categories": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -184,10 +365,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
| issue_status | body | string | 예 | `issuing`, `stopped` | 일괄 적용할 발급상태: issuing(발급중), stopped(발급중단) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/promotion-coupons/bulk-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"issue_status": "issuing"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -213,9 +415,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| coupon_id | integer | `1` | coupon 식별자 (연관 리소스 참조) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰이 삭제되었습니다.",
"data": {
"coupon_id": 1
}
}
```
**에러 응답**
@@ -242,9 +473,186 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 쿠폰","en":"API Doc Sample Coupon"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 쿠폰` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| description | null | `null` | 설명 (다국어 필드는 로케일별 값 객체) |
| localized_description | null | `null` | `description` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| target_type | string | `order_amount` | 적용대상: product_amount(상품금액), order_amount(주문금액), shipping_fee(배송비) |
| target_type_label | string | `주문금액` | `target_type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| target_type_badge_color | string | `blue` | `target_type` 배지 색상 (상품금액=teal, 주문금액=blue, 배송비=orange) |
| discount_type | string | `fixed` | 혜택유형: fixed(정액), rate(정률) |
| discount_type_label | string | `정액할인` | `discount_type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| discount_value | integer | `1000` | 혜택값 (정액: 금액, 정률: %) |
| discount_max_amount | null | `null` | 최대 할인액 (정률 시) |
| min_order_amount | integer | `0` | 최소 주문금액 |
| benefit_formatted | string | `1,000원 할인` | `benefit` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| multi_currency_discount_value | object | `{"KRW":{"price":1000,"formatted":"1,000원","is_default":tr…` | 정액 할인 금액의 통화별 환산 맵 (정률은 통화 무관이라 null) |
| multi_currency_min_order_amount | null | `null` | 최소 주문금액의 통화별 환산 맵 (0이면 null) |
| multi_currency_discount_max_amount | null | `null` | 최대 할인액의 통화별 환산 맵 (미설정 시 null) |
| issue_method | string | `download` | 발급방법: direct(직접발급), download(다운로드), auto(자동발급) |
| issue_method_label | string | `다운로드` | `issue_method` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| issue_method_badge_color | string | `teal` | `issue_method` 배지 색상 (직접발급=gray, 다운로드=teal, 자동발급=blue) |
| issue_condition | string | `manual` | 발급조건: manual(수동), signup(회원가입), first_purchase(첫구매), birthday(생일) |
| issue_condition_label | string | `수동발급` | `issue_condition` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| issue_condition_badge_color | string | `orange` | `issue_condition` 배지 색상 (수동=orange, 회원가입=blue, 첫구매=teal, 생일=pink) |
| issue_status | string | `issuing` | 발급상태: issuing(발급중), stopped(발급중단) |
| issue_status_label | string | `발급중` | `issue_status` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| issue_status_badge_color | string | `blue` | `issue_status` 배지 색상 (발급중=blue, 발급중단=orange) |
| total_quantity | null | `null` | 총 발급 수량 (NULL=무제한) |
| issued_count | integer | `0` | issued 개수 (집계) |
| per_user_limit | integer | `1` | 회원당 발급 제한 |
| issue_count_formatted | string | `0/무제한` | `issue_count` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| valid_type | string | `period` | 유효기간 유형: period(기간지정), days_from_issue(발급일로부터) |
| valid_days | null | `null` | 발급일로부터 N일 (valid_type=days_from_issue) |
| valid_from | null | `null` | 유효기간 시작 |
| valid_to | null | `null` | 유효기간 종료 |
| valid_period_formatted | string | `-` | `valid_period` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| issue_from | null | `null` | 발급기간 시작 |
| issue_to | null | `null` | 발급기간 종료 |
| issue_period_formatted | string | `상시발급` | `issue_period` 값의 표시용 포맷 문자열 (통화/용량/일시 등 로케일·단위 포맷) |
| is_combinable | boolean | `false` | combinable 여부 |
| target_scope | string | `all` | 적용 범위: all(전체), products(특정상품), categories(특정카테고리) |
| target_scope_label | string | `전체상품` | `target_scope` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| is_issuable | boolean | `true` | issuable 여부 |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| created_by | null | `null` | 쿠폰 등록자(관리자) 식별자 (users 참조, 삭제 시 null) |
| created_by_name | string | `-` | 등록자 이름 (creator 미로드/미설정 시 `-`) |
| created_by_email | null | `null` | 등록자 이메일 (creator 관계 파생) |
| creator | null | `null` | 생성자 정보 객체 (uuid/name/email — creator 관계 파생) |
| included_products | array | `[]` | 적용 대상 포함 상품 목록 (target_scope=products 시 이 상품에만 적용) |
| excluded_products | array | `[]` | 적용 제외 상품 목록 (해당 상품은 쿠폰 적용에서 제외) |
| included_categories | array | `[]` | 적용 대상 포함 카테고리 목록 (target_scope=categories 시 이 카테고리에만 적용) |
| excluded_categories | array | `[]` | 적용 제외 카테고리 목록 (해당 카테고리는 쿠폰 적용에서 제외) |
| issues_count | integer | `0` | issues 개수 (집계) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰 정보를 조회했습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 쿠폰",
"en": "API Doc Sample Coupon"
},
"localized_name": "API 문서 샘플 쿠폰",
"description": null,
"localized_description": null,
"target_type": "order_amount",
"target_type_label": "주문금액",
"target_type_badge_color": "blue",
"discount_type": "fixed",
"discount_type_label": "정액할인",
"discount_value": 1000,
"discount_max_amount": null,
"min_order_amount": 0,
"benefit_formatted": "1,000원 할인",
"multi_currency_discount_value": {
"KRW": {
"price": 1000,
"formatted": "1,000원",
"is_default": true,
"editable": true
},
"USD": {
"price": 0.85,
"formatted": "$0.85",
"is_default": false,
"editable": false,
"exchange_rate": 0.85
},
"JPY": {
"price": 115,
"formatted": "¥115",
"is_default": false,
"editable": false,
"exchange_rate": 115
},
"CNY": {
"price": 5.8,
"formatted": "元5.80",
"is_default": false,
"editable": false,
"exchange_rate": 5.8
},
"EUR": {
"price": 0.78,
"formatted": "€0.78",
"is_default": false,
"editable": false,
"exchange_rate": 0.78
}
},
"multi_currency_min_order_amount": null,
"multi_currency_discount_max_amount": null,
"issue_method": "download",
"issue_method_label": "다운로드",
"issue_method_badge_color": "teal",
"issue_condition": "manual",
"issue_condition_label": "수동발급",
"issue_condition_badge_color": "orange",
"issue_status": "issuing",
"issue_status_label": "발급중",
"issue_status_badge_color": "blue",
"total_quantity": null,
"issued_count": 0,
"per_user_limit": 1,
"issue_count_formatted": "0/무제한",
"valid_type": "period",
"valid_days": null,
"valid_from": null,
"valid_to": null,
"valid_period_formatted": "-",
"issue_from": null,
"issue_to": null,
"issue_period_formatted": "상시발급",
"is_combinable": false,
"target_scope": "all",
"target_scope_label": "전체상품",
"is_issuable": true,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"created_by": null,
"created_by_name": "-",
"created_by_email": null,
"creator": null,
"included_products": [],
"excluded_products": [],
"included_categories": [],
"excluded_categories": [],
"issues_count": 0,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -295,10 +703,57 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"description": [
"예시 내용입니다."
],
"target_type": "product_amount",
"discount_type": "fixed",
"discount_value": 1,
"discount_max_amount": 1,
"min_order_amount": 1,
"issue_method": "direct",
"issue_condition": "manual",
"issue_status": "issuing",
"total_quantity": 1,
"per_user_limit": 1,
"valid_type": "period",
"valid_days": 1,
"valid_from": "2026-01-01",
"valid_to": "2026-01-01",
"issue_from": "2026-01-01",
"issue_to": "2026-01-01",
"is_combinable": true,
"target_scope": "all",
"products": [
"예시값"
],
"categories": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -326,10 +781,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| id | path | string | 예 | — | 대상 리소스의 식별자 |
| user_uuids | body | array | 예 | min 1 | 쿠폰을 직접 발급할 대상 회원 UUID 배열 (내부 회원 ID 로 해석 후 발급) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1/issue-direct HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"user_uuids": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -361,9 +836,45 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.coupon.issues_list_validation_rules`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1/issues?user_id=9f8b2c1a-4d3e-4a2b-8c1d-0e1f2a3b4c5d&status=available&per_page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "쿠폰 발급 내역을 조회했습니다.",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
```
**에러 응답**
@@ -392,10 +903,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| id | path | string | 예 | — | 대상 리소스의 식별자 |
| issueId | path | string | 예 | — | 대상 issue의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/promotion-coupons/1/issues/{issueId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -29,10 +29,22 @@
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/review-image/{hash} HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -44,6 +44,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.review.list_validation_rules`, `sirsoft-ecommerce.review.list_validation_messages`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/reviews?search_field=all&search_keyword=%EC%98%88%EC%8B%9C%EA%B0%92&rating=1&reply_status=all&photo=photo&has_photo=1&status=%EC%98%88%EC%8B%9C%EA%B0%92&start_date=2026-01-01&end_date=2026-01-01&sort=created_at_desc&sort_by=created_at&sort_order=asc&per_page=1&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -80,6 +89,106 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| updated_at | string | `2026-07-07 14:47:31` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "리뷰 목록을 조회했습니다.",
"data": {
"data": [
{
"id": 1,
"product_id": 1,
"order_option_id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"product": {
"id": 1,
"name": "API 문서 샘플 상품",
"thumbnail_url": null
},
"option_snapshot": null,
"option_snapshot_label": "",
"rating": 5,
"content": "Alias quas iusto dolorem eum eveniet ad omnis. Id neque consequatur fuga ut. Enim cum mollitia nisi. Adipisci sunt tenetur et tempora tempora eius rerum.",
"content_mode": "text",
"status": "visible",
"status_label": "전시중",
"status_badge_color": "blue",
"images": [],
"image_count": 0,
"orderOption": {
"id": 1,
"order_id": 5,
"order_number": "ORD-20260708-000002",
"quantity": 2,
"created_at": "2026-07-08 10:44:49"
},
"has_reply": false,
"has_reply_label": "미답변",
"has_reply_badge_color": "gray",
"reply_content": null,
"reply_content_mode": "text",
"reply_admin_uuid": null,
"reply_admin": null,
"replied_at": null,
"reply_updated_at": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_update": true,
"can_delete": true
}
}
],
"links": {
"first": "https://api.example.com/api/modules/sirsoft-ecommerce/admin/reviews?page=1",
"last": "https://api.example.com/api/modules/sirsoft-ecommerce/admin/reviews?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "pagination.previous",
"page": null,
"active": false
},
{
"url": "https://api.example.com/api/modules/sirsoft-ecommerce/admin/reviews?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "pagination.next",
"page": null,
"active": false
}
],
"path": "https://api.example.com/api/modules/sirsoft-ecommerce/admin/reviews",
"per_page": 25,
"to": 1,
"total": 1
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -109,10 +218,32 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.review.bulk_validation_rules`, `sirsoft-ecommerce.review.bulk_validation_messages`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/reviews/bulk HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"action": "delete",
"status": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -138,9 +269,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| review | path | string | 예 | — | 대상 review의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/reviews/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| deleted | boolean | `true` | 삭제 처리 성공 여부 (true 이면 리뷰와 첨부 이미지가 제거됨) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "리뷰가 삭제되었습니다.",
"data": {
"deleted": true
}
}
```
**에러 응답**
@@ -167,6 +327,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| review | path | string | 예 | — | 대상 review의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/reviews/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -203,6 +372,67 @@ _단건 응답: `data` 객체의 필드._
| updated_at | string | `2026-07-07 14:47:31` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "리뷰 목록을 조회했습니다.",
"data": {
"id": 1,
"product_id": 1,
"order_option_id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"product": {
"id": 1,
"name": "API 문서 샘플 상품",
"thumbnail_url": null
},
"option_snapshot": null,
"option_snapshot_label": "",
"rating": 5,
"content": "Alias quas iusto dolorem eum eveniet ad omnis. Id neque consequatur fuga ut. Enim cum mollitia nisi. Adipisci sunt tenetur et tempora tempora eius rerum.",
"content_mode": "text",
"status": "visible",
"status_label": "전시중",
"status_badge_color": "blue",
"images": [],
"image_count": 0,
"orderOption": {
"id": 1,
"order_id": 5,
"order_number": "ORD-20260708-000002",
"quantity": 2,
"created_at": "2026-07-08 10:44:49"
},
"has_reply": false,
"has_reply_label": "미답변",
"has_reply_badge_color": "gray",
"reply_content": null,
"reply_content_mode": "text",
"reply_admin_uuid": null,
"reply_admin": null,
"replied_at": null,
"reply_updated_at": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -228,9 +458,89 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| review | path | string | 예 | — | 대상 review의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/reviews/1/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| product_id | integer | `1` | product 식별자 (연관 리소스 참조) |
| order_option_id | integer | `1` | order option 식별자 (연관 리소스 참조) |
| user_id | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | user 식별자 (연관 리소스 참조) |
| user | object | `{"uuid":"a234c2b1-cde8-437f-b28b-23323be2b98d","name":"AP…` | 대상 사용자 정보 객체 (uuid/name/email 등 — user 관계 파생) |
| option_snapshot | null | `null` | 주문 시점 옵션 스냅샷 (옵션명 보존용) |
| option_snapshot_label | string | `` | `option_snapshot` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| rating | integer | `5` | 별점 (1~5) |
| content | string | `Alias quas iusto dolorem eum eveniet …` | 본문 내용 |
| content_mode | string | `text` | 콘텐츠 모드: text / html |
| status | string | `visible` | 상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조) |
| status_label | string | `전시중` | 상태의 사람이 읽는 라벨 (상태 Enum label() 산물) |
| status_badge_color | string | `blue` | 상태 뱃지 색상 (visible=blue / hidden=gray) |
| has_reply | boolean | `false` | reply 여부 |
| has_reply_label | string | `미답변` | `has_reply` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| has_reply_badge_color | string | `gray` | 답변 여부 뱃지 색상 (답변완료=green / 미답변=gray) |
| reply_content | null | `null` | 판매자 답변 내용 |
| reply_content_mode | string | `text` | 답변 콘텐츠 모드: text / html |
| replied_at | null | `null` | replied 일시 |
| reply_updated_at | null | `null` | reply updated 일시 |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "답변이 삭제되었습니다.",
"data": {
"id": 1,
"product_id": 1,
"order_option_id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"option_snapshot": null,
"option_snapshot_label": "",
"rating": 5,
"content": "Alias quas iusto dolorem eum eveniet ad omnis. Id neque consequatur fuga ut. Enim cum mollitia nisi. Adipisci sunt tenetur et tempora tempora eius rerum.",
"content_mode": "text",
"status": "visible",
"status_label": "전시중",
"status_badge_color": "blue",
"has_reply": false,
"has_reply_label": "미답변",
"has_reply_badge_color": "gray",
"reply_content": null,
"reply_content_mode": "text",
"replied_at": null,
"reply_updated_at": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -261,9 +571,95 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.review.store_reply_validation_rules`, `sirsoft-ecommerce.review.store_reply_validation_messages`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/reviews/1/reply HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"reply_content": "예시 내용입니다.",
"reply_content_mode": "text"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| product_id | integer | `1` | product 식별자 (연관 리소스 참조) |
| order_option_id | integer | `1` | order option 식별자 (연관 리소스 참조) |
| user_id | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | user 식별자 (연관 리소스 참조) |
| user | object | `{"uuid":"a234c2b1-cde8-437f-b28b-23323be2b98d","name":"AP…` | 대상 사용자 정보 객체 (uuid/name/email 등 — user 관계 파생) |
| option_snapshot | null | `null` | 주문 시점 옵션 스냅샷 (옵션명 보존용) |
| option_snapshot_label | string | `` | `option_snapshot` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| rating | integer | `5` | 별점 (1~5) |
| content | string | `Alias quas iusto dolorem eum eveniet …` | 본문 내용 |
| content_mode | string | `text` | 콘텐츠 모드: text / html |
| status | string | `visible` | 상태 값 (도메인별 상태 집합 — 사람이 읽는 라벨은 status_label, UI 변형은 status_variant 참조) |
| status_label | string | `전시중` | 상태의 사람이 읽는 라벨 (상태 Enum label() 산물) |
| status_badge_color | string | `blue` | 상태 뱃지 색상 (visible=blue / hidden=gray) |
| has_reply | boolean | `true` | reply 여부 |
| has_reply_label | string | `답변완료` | `has_reply` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| has_reply_badge_color | string | `green` | 답변 여부 뱃지 색상 (답변완료=green / 미답변=gray) |
| reply_content | string | `실측 예시값` | 판매자 답변 내용 |
| reply_content_mode | string | `text` | 답변 콘텐츠 모드: text / html |
| replied_at | string | `2026-07-08 15:00:32` | replied 일시 |
| reply_updated_at | null | `null` | reply updated 일시 |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:32` | 최종 수정 일시 |
| abilities | object | `{"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "답변이 저장되었습니다.",
"data": {
"id": 1,
"product_id": 1,
"order_option_id": 1,
"user_id": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"user": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자",
"email": "apidoc-sample-user@example.com"
},
"option_snapshot": null,
"option_snapshot_label": "",
"rating": 5,
"content": "Alias quas iusto dolorem eum eveniet ad omnis. Id neque consequatur fuga ut. Enim cum mollitia nisi. Adipisci sunt tenetur et tempora tempora eius rerum.",
"content_mode": "text",
"status": "visible",
"status_label": "전시중",
"status_badge_color": "blue",
"has_reply": true,
"has_reply_label": "답변완료",
"has_reply_badge_color": "green",
"reply_content": "실측 예시값",
"reply_content_mode": "text",
"replied_at": "2026-07-08 15:00:32",
"reply_updated_at": null,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:32",
"abilities": {
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -294,10 +690,28 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.review.update_status_validation_rules`, `sirsoft-ecommerce.review.update_status_validation_messages`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/reviews/1/status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"status": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -330,10 +744,32 @@ _단건 응답: `data` 객체의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.review.store_validation_rules`, `sirsoft-ecommerce.review.store_validation_messages`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/reviews HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"product_id": 1,
"order_option_id": 1,
"rating": 1,
"content": "예시 내용입니다.",
"content_mode": "text"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -359,9 +795,40 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| orderOptionId | path | string | 예 | — | 대상 order option의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/reviews/can-write/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| can_write | boolean | `false` | write 수행 가능 여부 (권한 기반) |
| reason | string | `not_own_order` | 리뷰 작성 불가 사유 코드 (`can_write` 가 false 일 때만 값이 있고 가능하면 null. order_option_not_found 주문옵션 없음, not_own_order 본인 주문 아님, not_confirmed 구매확정 전, deadline_passed 작성 기한 초과, already_written 이미 작성함) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "리뷰 작성 가능 여부를 확인했습니다.",
"data": {
"can_write": false,
"reason": "not_own_order"
}
}
```
**에러 응답**
@@ -387,9 +854,38 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| review | path | string | 예 | — | 대상 review의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/user/reviews/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| deleted | boolean | `true` | 삭제 처리 성공 여부 (true 이면 리뷰와 첨부 이미지가 제거됨) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "리뷰가 삭제되었습니다.",
"data": {
"deleted": true
}
}
```
**에러 응답**
@@ -417,10 +913,31 @@ _단건 응답: `data` 객체의 필드._
| review | path | string | 예 | — | 대상 review의 식별자 |
| image | body | file | 예 | max 10240 | 첨부할 이미지 파일 (최대 용량은 리뷰 설정 `review_settings.max_image_size_mb` 기반, 폴백 10MB) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/user/reviews/1/images HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="image"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -448,10 +965,23 @@ _단건 응답: `data` 객체의 필드._
| review | path | string | 예 | — | 대상 review의 식별자 |
| image | path | string | 예 | — | 대상 image의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/user/reviews/1/images/{image} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-carriers HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -49,6 +58,77 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| updater | array | `[]` | 최종 수정자 정보 객체 (id/name — updater 관계 로드 시) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송사 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 13,
"id": 13,
"code": "apidoc",
"name": {
"ko": "API 문서 샘플 배송사",
"en": "API Doc Sample Carrier"
},
"localized_name": "API 문서 샘플 배송사",
"type": "domestic",
"type_label": "국내",
"tracking_url": null,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"number": 12,
"id": 1,
"code": "cj",
"name": {
"ko": "CJ대한통운",
"en": "CJ Logistics"
},
"localized_name": "CJ대한통운",
"type": "domestic",
"type_label": "국내",
"tracking_url": "https://trace.cjlogistics.com/next/tracking.html?wblNo={tracking_number}",
"is_active": true,
"sort_order": 1,
"created_at": "2026-07-08 10:43:32",
"updated_at": "2026-07-08 10:43:32",
"creator": [],
"updater": [],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 13건 중 2건 표시)"
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -80,10 +160,35 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_carrier.create_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/shipping-carriers HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"code": "예시값",
"name": [
"예시 이름"
],
"type": "예시값",
"tracking_url": "https://example.com",
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -107,6 +212,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-carriers/active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -117,6 +231,112 @@ _요청 파라미터 없음._
| type | string | `domestic` | 배송사 유형 (`domestic` 국내 / `international` 해외) |
| tracking_url | string | `https://trace.cjlogistics.com/next/tr…` | tracking URL |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송사 목록을 조회했습니다.",
"data": [
{
"value": 13,
"label": "API 문서 샘플 배송사",
"code": "apidoc",
"type": "domestic",
"tracking_url": null
},
{
"value": 1,
"label": "CJ대한통운",
"code": "cj",
"type": "domestic",
"tracking_url": "https://trace.cjlogistics.com/next/tracking.html?wblNo={tracking_number}"
},
{
"value": 2,
"label": "한진택배",
"code": "hanjin",
"type": "domestic",
"tracking_url": "https://www.hanjin.com/kor/CMS/DeliveryMgr/WaybillResult.do?wblnb={tracking_number}"
},
{
"value": 3,
"label": "롯데택배",
"code": "lotte",
"type": "domestic",
"tracking_url": "https://www.lotteglogis.com/home/reservation/tracking/link498?InvNo={tracking_number}"
},
{
"value": 4,
"label": "로젠택배",
"code": "logen",
"type": "domestic",
"tracking_url": "https://www.ilogen.com/web/personal/trace/{tracking_number}"
},
{
"value": 5,
"label": "UPS",
"code": "ups",
"type": "international",
"tracking_url": "https://www.ups.com/track?tracknum={tracking_number}"
},
{
"value": 6,
"label": "EMS",
"code": "ems",
"type": "international",
"tracking_url": "https://service.epost.go.kr/trace.RetrieveEmsRi498.postal?POST_CODE={tracking_number}"
},
{
"value": 7,
"label": "DHL",
"code": "dhl",
"type": "international",
"tracking_url": "https://www.dhl.com/kr-ko/home/tracking/tracking-express.html?submit=1&tracking-id={tracking_number}"
},
{
"value": 8,
"label": "FedEx",
"code": "fedex",
"type": "international",
"tracking_url": "https://www.fedex.com/fedextrack/?tracknumbers={tracking_number}"
},
{
"value": 9,
"label": "SF Express",
"code": "sf",
"type": "international",
"tracking_url": null
},
{
"value": 10,
"label": "야마토운수",
"code": "yamato",
"type": "international",
"tracking_url": null
},
{
"value": 11,
"label": "사가와익스프레스",
"code": "sagawa",
"type": "international",
"tracking_url": null
},
{
"value": 12,
"label": "기타",
"code": "other",
"type": "domestic",
"tracking_url": null
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -141,9 +361,38 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/shipping-carriers/13 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| carrier_id | integer | `13` | carrier 식별자 (연관 리소스 참조) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송사가 삭제되었습니다.",
"data": {
"carrier_id": 13
}
}
```
**에러 응답**
@@ -170,9 +419,67 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-carriers/13 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `13` | 기본 키 (내부 식별자) |
| code | string | `apidoc` | 고유 코드 (cj, fedex 등) |
| name | object | `{"ko":"API 문서 샘플 배송사","en":"API Doc Sample Carrier"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 배송사` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| type | string | `domestic` | 배송사 유형: domestic(국내), international(해외) |
| type_label | string | `국내` | `type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| tracking_url | null | `null` | tracking URL |
| is_active | boolean | `true` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송사 정보를 조회했습니다.",
"data": {
"id": 13,
"code": "apidoc",
"name": {
"ko": "API 문서 샘플 배송사",
"en": "API Doc Sample Carrier"
},
"localized_name": "API 문서 샘플 배송사",
"type": "domestic",
"type_label": "국내",
"tracking_url": null,
"is_active": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -207,10 +514,35 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_carrier.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/shipping-carriers/13 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"code": "예시값",
"name": [
"예시 이름"
],
"type": "예시값",
"tracking_url": "https://example.com",
"is_active": true,
"sort_order": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -237,9 +569,67 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/shipping-carriers/13/toggle-status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `13` | 기본 키 (내부 식별자) |
| code | string | `apidoc` | 고유 코드 (cj, fedex 등) |
| name | object | `{"ko":"API 문서 샘플 배송사","en":"API Doc Sample Carrier"}` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| localized_name | string | `API 문서 샘플 배송사` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| type | string | `domestic` | 배송사 유형: domestic(국내), international(해외) |
| type_label | string | `국내` | `type` 값의 사람이 읽는 라벨 (현지화/Enum 파생) |
| tracking_url | null | `null` | tracking URL |
| is_active | boolean | `false` | active 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:34` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.shipping_carriers.status_changed",
"data": {
"id": 13,
"code": "apidoc",
"name": {
"ko": "API 문서 샘플 배송사",
"en": "API Doc Sample Carrier"
},
"localized_name": "API 문서 샘플 배송사",
"type": "domestic",
"type_label": "국내",
"tracking_url": null,
"is_active": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:34",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/user/shipping-country HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| preferred_shipping_country | null | `null` | 회원이 저장한 선호 배송국가 코드 (2자리 대문자, 미설정 시 `null`) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송국가를 조회했습니다.",
"data": {
"preferred_shipping_country": null
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -58,9 +83,43 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| shipping_country | body | string | 예 | — | 저장할 선호 배송국가 2자리 코드 (활성 배송가능 국가만 허용, 대문자로 정규화되어 저장) |
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/user/shipping-country HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"shipping_country": "KR"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| preferred_shipping_country | string | `KR` | 변경 후 저장된 회원 선호 배송국가 코드 (2자리 대문자 ISO 국가코드) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송국가가 변경되었습니다.",
"data": {
"preferred_shipping_country": "KR"
}
}
```
**에러 응답**
@@ -39,6 +39,15 @@
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_policy.list_validation_rules`, `sirsoft-ecommerce.shipping_policy.list_validation_messages`).
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-policies?search=%EC%98%88%EC%8B%9C%EA%B0%92&shipping_methods=%EC%98%88%EC%8B%9C%EA%B0%92&charge_policies=%EC%98%88%EC%8B%9C%EA%B0%92&countries=KR&is_active=%2C%20&sort_by=id&sort_order=asc&per_page=1&page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -59,6 +68,66 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| updated_at | string | `2026-07-07 14:47:31` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송정책 목록을 조회했습니다.",
"data": {
"data": [
{
"number": 1,
"id": 1,
"name": {
"ko": "API 문서 샘플 배송정책",
"en": "API Doc Sample Shipping Policy"
},
"name_localized": "API 문서 샘플 배송정책",
"country_settings": [],
"fee_summary": "",
"countries_display": "",
"is_active": true,
"is_default": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
],
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
},
"statistics": {
"total": 1,
"active": 1,
"inactive": 0,
"shipping_method": [],
"charge_policy": []
},
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -90,10 +159,36 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_policy.store_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/shipping-policies HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"is_active": true,
"is_default": true,
"sort_order": 1,
"country_settings": [
"KR"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -117,6 +212,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-policies/active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -127,6 +231,28 @@ _요청 파라미터 없음._
| fee_summary | string | `KR: 외부 API 연동 (실시간 계산)` | 국가별 배송비 요약 텍스트 (`country_code: fee` 형태를 ` \| ` 로 결합) |
| is_default | boolean | `false` | default 여부 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "sirsoft-ecommerce::messages.shipping_policy.active_list_retrieved",
"data": [
{
"value": 1,
"label": "API 문서 샘플 배송정책",
"countries_display": "",
"fee_summary": "",
"is_default": false
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -153,10 +279,23 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_policy.bulk_delete_validation_rules`).
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/shipping-policies/bulk?ids=%EC%98%88%EC%8B%9C%EA%B0%92 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -185,10 +324,31 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_policy.bulk_toggle_active_validation_rules`).
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/shipping-policies/bulk-toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"is_active": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -217,10 +377,37 @@ _요청 파라미터 없음._
| config | body | array | 아니오 | — | API 호출 고급 설정 (HTTP 메서드·인증방식·필드 매핑·응답 형식/경로 등) |
| sample | body | array | 아니오 | — | 테스트 계산에 사용할 샘플 주문 데이터 (무게/금액/수량 등) |
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/admin/shipping-policies/test-api-call HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"endpoint": "예시값",
"request_fields": [
"예시값"
],
"config": [
"예시값"
],
"sample": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -246,9 +433,34 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/admin/shipping-policies/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송정책이 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -275,9 +487,67 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/admin/shipping-policies/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 배송정책","en":"API Doc Sample Shipping Poli…` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| name_localized | string | `API 문서 샘플 배송정책` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| country_settings | array | `[]` | 국가별 배송 설정 목록 (countrySettings 관계 로드 시 각 국가의 배송방식·부과정책·배송비 상세) |
| fee_summary | string | `` | 활성 국가별 설정을 종합한 배송비 요약 텍스트 (예: `KR: 3000원 \| US: $20`, 활성 설정 없으면 빈 문자열) |
| countries_display | string | `` | 활성 배송 국가를 국기 이모지로 표시한 문자열 (최대 3개 노출, 초과분은 `+N` 축약) |
| is_active | boolean | `true` | active 여부 |
| is_default | boolean | `false` | default 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:49` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송정책 정보를 조회했습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 배송정책",
"en": "API Doc Sample Shipping Policy"
},
"name_localized": "API 문서 샘플 배송정책",
"country_settings": [],
"fee_summary": "",
"countries_display": "",
"is_active": true,
"is_default": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 10:44:49",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -311,10 +581,36 @@ _요청 파라미터 없음._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.shipping_policy.update_validation_rules`).
**요청 예시**
```http
PUT /api/modules/sirsoft-ecommerce/admin/shipping-policies/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"name": [
"예시 이름"
],
"is_active": true,
"is_default": true,
"sort_order": 1,
"country_settings": [
"KR"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -341,9 +637,65 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/shipping-policies/1/set-default HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 배송정책","en":"API Doc Sample Shipping Poli…` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| name_localized | string | `API 문서 샘플 배송정책` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| fee_summary | string | `` | 활성 국가별 설정을 종합한 배송비 요약 텍스트 (예: `KR: 3000원 \| US: $20`, 활성 설정 없으면 빈 문자열) |
| countries_display | string | `` | 활성 배송 국가를 국기 이모지로 표시한 문자열 (최대 3개 노출, 초과분은 `+N` 축약) |
| is_active | boolean | `true` | active 여부 |
| is_default | boolean | `true` | default 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:35` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "기본 배송정책이 설정되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 배송정책",
"en": "API Doc Sample Shipping Policy"
},
"name_localized": "API 문서 샘플 배송정책",
"fee_summary": "",
"countries_display": "",
"is_active": true,
"is_default": true,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:35",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -370,9 +722,65 @@ _요청 파라미터 없음._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/shipping-policies/1/toggle-active HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `1` | 기본 키 (내부 식별자) |
| name | object | `{"ko":"API 문서 샘플 배송정책","en":"API Doc Sample Shipping Poli…` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| name_localized | string | `API 문서 샘플 배송정책` | `name` 의 현재 로케일 해석 값 (다국어 필드를 표시용 문자열로 해석) |
| fee_summary | string | `` | 활성 국가별 설정을 종합한 배송비 요약 텍스트 (예: `KR: 3000원 \| US: $20`, 활성 설정 없으면 빈 문자열) |
| countries_display | string | `` | 활성 배송 국가를 국기 이모지로 표시한 문자열 (최대 3개 노출, 초과분은 `+N` 축약) |
| is_active | boolean | `false` | active 여부 |
| is_default | boolean | `false` | default 여부 |
| sort_order | integer | `0` | 표시 정렬 순서 값 (작을수록 우선) |
| created_at | string | `2026-07-08 10:44:49` | 생성 일시 |
| updated_at | string | `2026-07-08 15:00:35` | 최종 수정 일시 |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송정책 사용여부가 변경되었습니다.",
"data": {
"id": 1,
"name": {
"ko": "API 문서 샘플 배송정책",
"en": "API Doc Sample Shipping Policy"
},
"name_localized": "API 문서 샘플 배송정책",
"fee_summary": "",
"countries_display": "",
"is_active": false,
"is_default": false,
"sort_order": 0,
"created_at": "2026-07-08 10:44:49",
"updated_at": "2026-07-08 15:00:35",
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -30,10 +30,28 @@
| user | path | string | 예 | — | 대상 user의 식별자 |
| currency | body | string | 예 | — | 회원에게 지정할 결제 통화 코드 (등록 통화: is_default 또는 exchange_rate>0 인 통화만 허용) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/users/a234c2b1-cde8-437f-b28b-23323be2b98d/currency HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"currency": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -61,9 +79,43 @@
| user | path | string | 예 | — | 대상 user의 식별자 |
| shipping_country | body | string | 예 | — | 회원에게 지정할 배송국가 2자리 코드 (활성 배송가능 국가만 허용, 대문자로 정규화) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-ecommerce/admin/users/a234c2b1-cde8-437f-b28b-23323be2b98d/shipping-country HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"shipping_country": "KR"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| preferred_shipping_country | string | `KR` | 변경 후 저장된 회원 선호 배송국가 코드 (2자리 대문자 ISO 국가코드) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "배송국가가 변경되었습니다.",
"data": {
"preferred_shipping_country": "KR"
}
}
```
**에러 응답**
@@ -27,12 +27,46 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/modules/sirsoft-ecommerce/wishlist HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "찜 목록을 불러왔습니다.",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null,
"has_more_pages": false
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -58,9 +92,43 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
> 이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (`sirsoft-ecommerce.wishlist.toggle_validation_rules`).
**요청 예시**
```http
POST /api/modules/sirsoft-ecommerce/wishlist/toggle HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"product_id": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| added | boolean | `true` | 토글 후 찜 상태 (true 이면 찜 목록에 추가됨 / false 이면 제거됨) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "상품이 찜 목록에 추가되었습니다.",
"data": {
"added": true
}
}
```
**에러 응답**
@@ -86,10 +154,23 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-ecommerce/wishlist/{id} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -32,10 +32,43 @@
| collection | body | string | 아니오 | max 100 | 첨부 컬렉션 그룹명 (첨부를 용도별로 묶는 키, 미지정 시 default) |
| temp_key | body | string | 아니오 | max 64 | 저장 전 임시 귀속 키. 아직 저장되지 않은 페이지의 첨부를 임시로 묶어 두고, 이후 페이지 저장 시 이 키로 확정 귀속합니다 (`page_id` 미지정 시 사용) |
**요청 예시**
```http
POST /api/modules/sirsoft-page/admin/attachments HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary
Content-Disposition: form-data; name="page_id"
1
------G7ExampleBoundary
Content-Disposition: form-data; name="collection"
예시값
------G7ExampleBoundary
Content-Disposition: form-data; name="temp_key"
예시값
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -61,10 +94,30 @@
| --- | --- | --- | --- | --- | --- |
| order | body | array | 예 | min 1 | 새 정렬 순서 배열. 각 원소는 `{"id": 첨부ID(정수), "order": 순서값(0 이상 정수)}` 형태이며, 이 순서대로 첨부 표시 순위가 갱신됩니다 |
**요청 예시**
```http
PATCH /api/modules/sirsoft-page/admin/attachments/reorder HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"order": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -90,10 +143,23 @@
| --- | --- | --- | --- | --- | --- |
| id | path | string | 예 | — | 대상 리소스의 식별자 |
**요청 예시**
```http
DELETE /api/modules/sirsoft-page/admin/attachments/{id} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
+514 -3
View File
@@ -36,6 +36,15 @@
| sort_by | query | string | 아니오 | `created_at`, `published_at` | 정렬 기준 필드명 |
| sort_order | query | string | 아니오 | `asc`, `desc` | 정렬 방향 (asc 오름차순 / desc 내림차순) |
**요청 예시**
```http
GET /api/modules/sirsoft-page/admin/pages?published=1&search=%EC%98%88%EC%8B%9C%EA%B0%92&search_field=all&filters=%EC%98%88%EC%8B%9C%EA%B0%92&per_page=1&page=1&sort_by=created_at&sort_order=asc HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -54,6 +63,72 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지 정보를 조회했습니다.",
"data": {
"data": [
{
"id": 10,
"slug": "apidoc-sample-page",
"title": "API 문서 샘플 페이지",
"published": true,
"published_at": "2026-07-08 10:51:59",
"current_version": 2,
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 10:51:59",
"updated_at": "2026-07-08 10:51:59",
"is_owner": true,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
{
"id": 4,
"slug": "terms",
"title": "이용약관",
"published": true,
"published_at": "2026-07-08 10:44:43",
"current_version": 1,
"creator": null,
"created_at": "2026-07-08 10:44:43",
"updated_at": "2026-07-08 10:44:43",
"is_owner": false,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
},
"... (총 7건 중 2건 표시)"
],
"meta": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 7
},
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -85,10 +160,38 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| seo_meta | body | array | 아니오 | — | SEO 메타 정보 맵. 하위 키 `title`(max 255)·`description`(max 500)·`keywords`(max 500)를 담습니다 |
| temp_key | body | string | 아니오 | max 64 | 저장 전 첨부 업로드 시 발급받은 임시 키. 생성된 페이지에 임시 첨부를 귀속시키는 데 사용합니다 |
**요청 예시**
```http
POST /api/modules/sirsoft-page/admin/pages HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"slug": "example-key",
"title": [
"예시 제목"
],
"content": "예시 내용입니다.",
"content_mode": "html",
"published": true,
"seo_meta": [
"예시값"
],
"temp_key": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -115,10 +218,31 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
| published | body | boolean | 예 | — | 발행 여부 (발행된 항목만 필터) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-page/admin/pages/bulk-publish HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"ids": [
"예시값"
],
"published": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -145,10 +269,29 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| slug | body | string | 예 | max 255 | URL 친화 식별자 (slug) |
| exclude_id | body | integer | 아니오 | min 1 | exclude 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-page/admin/pages/check-slug HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"slug": "example-key",
"exclude_id": 1
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -174,9 +317,34 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
DELETE /api/modules/sirsoft-page/admin/pages/10 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
<!-- 실측 응답에 필드 없음(빈 목록 등) — 데이터가 있는 상태로 재실측하거나 사람이 작성. -->
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지가 삭제되었습니다.",
"data": null
}
```
**에러 응답**
@@ -203,6 +371,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/modules/sirsoft-page/admin/pages/10 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -226,6 +403,71 @@ _단건 응답: `data` 객체의 필드._
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지 정보를 조회했습니다.",
"data": {
"id": 10,
"slug": "apidoc-sample-page",
"title": {
"ko": "API 문서 샘플 페이지",
"en": "API Doc Sample Page"
},
"content": {
"ko": "<p>API 레퍼런스 실측용 완전 샘플 페이지 본문입니다.</p>",
"en": "<p>Complete sample page body for API reference probing.</p>"
},
"content_mode": "html",
"published": true,
"published_at": "2026-07-08 10:51:59",
"seo_meta": {
"title": "Dolorum et aut officia ipsam doloribus inventore.",
"description": "Sint ipsa impedit enim natus tempore quasi dignissimos praesentium numquam rerum tempore nam.",
"keywords": "illo,pariatur,quis,necessitatibus,aperiam"
},
"current_version": 2,
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"updater": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"attachments": [
{
"id": 4,
"hash": "ninrdtsmhhta",
"original_filename": "nesciunt.jpg",
"mime_type": "image/jpeg",
"size": 7124557,
"collection": "attachments",
"order": 0,
"is_image": true,
"download_url": "/api/modules/sirsoft-page/pages/attachment/ninrdtsmhhta",
"preview_url": "/api/modules/sirsoft-page/pages/attachment/ninrdtsmhhta/preview",
"created_at": "2026-07-08 10:51:59"
}
],
"created_at": "2026-07-08 10:51:59",
"updated_at": "2026-07-08 10:51:59",
"is_owner": true,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -258,10 +500,38 @@ _단건 응답: `data` 객체의 필드._
| seo_meta | body | array | 아니오 | — | SEO 메타 정보 맵. 하위 키 `title`(max 255)·`description`(max 500)·`keywords`(max 500)를 담습니다 |
| temp_key | body | string | 아니오 | max 64 | 저장 전 첨부 업로드 시 발급받은 임시 키. 새로 업로드한 첨부를 이 페이지에 귀속시키는 데 사용합니다 |
**요청 예시**
```http
PUT /api/modules/sirsoft-page/admin/pages/10 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"slug": "example-key",
"title": [
"예시 제목"
],
"content": "예시 내용입니다.",
"content_mode": "html",
"published": true,
"seo_meta": [
"예시값"
],
"temp_key": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -289,9 +559,81 @@ _단건 응답: `data` 객체의 필드._
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
| published | body | boolean | 예 | — | 발행 여부 (발행된 항목만 필터) |
**요청 예시**
```http
PATCH /api/modules/sirsoft-page/admin/pages/10/publish HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"published": true
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `10` | 기본 키 (내부 식별자) |
| slug | string | `apidoc-sample-page` | URL 친화 식별자 (slug) |
| title | object | `{"ko":"API 문서 샘플 페이지","en":"API Doc Sample Page"}` | 제목 |
| content | object | `{"ko":"<p>API 레퍼런스 실측용 완전 샘플 페이지 본문입니다.<\/p>","en":"<p>Co…` | 본문 내용 |
| content_mode | string | `html` | 본문 형식 (html, text) |
| published | boolean | `true` | 발행 여부 (true: 발행, false: 미발행) |
| published_at | string | `2026-07-08 15:03:15` | published 일시 |
| seo_meta | object | `{"title":"Dolorum et aut officia ipsam doloribus inventor…` | SEO 메타 정보 (title, description, keywords) |
| current_version | integer | `2` | 현재 버전 번호 |
| created_at | string | `2026-07-08 10:51:59` | 생성 일시 |
| updated_at | string | `2026-07-08 15:03:15` | 최종 수정 일시 |
| is_owner | boolean | `true` | 현재 인증 사용자가 이 리소스의 소유자인지 여부 (BaseApiResource 표준 메타) |
| abilities | object | `{"can_create":true,"can_update":true,"can_delete":true}` | 현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지 발행 상태가 변경되었습니다.",
"data": {
"id": 10,
"slug": "apidoc-sample-page",
"title": {
"ko": "API 문서 샘플 페이지",
"en": "API Doc Sample Page"
},
"content": {
"ko": "<p>API 레퍼런스 실측용 완전 샘플 페이지 본문입니다.</p>",
"en": "<p>Complete sample page body for API reference probing.</p>"
},
"content_mode": "html",
"published": true,
"published_at": "2026-07-08 15:03:15",
"seo_meta": {
"title": "Dolorum et aut officia ipsam doloribus inventore.",
"description": "Sint ipsa impedit enim natus tempore quasi dignissimos praesentium numquam rerum tempore nam.",
"keywords": "illo,pariatur,quis,necessitatibus,aperiam"
},
"current_version": 2,
"created_at": "2026-07-08 10:51:59",
"updated_at": "2026-07-08 15:03:15",
"is_owner": true,
"abilities": {
"can_create": true,
"can_update": true,
"can_delete": true
}
}
}
```
**에러 응답**
@@ -319,6 +661,15 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
**요청 예시**
```http
GET /api/modules/sirsoft-page/admin/pages/10/versions HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
@@ -334,6 +685,63 @@ _단건 응답: `data` 객체의 필드._
| creator | object | `{"uuid":"a231747f-e82e-4cf2-9ae1-a261849dce40","name":"AP…` | 생성자 정보 객체 (uuid/name/email — creator 관계 파생) |
| created_at | string | `2026-07-06 23:57:18` | 생성 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지 정보를 조회했습니다.",
"data": [
{
"id": 14,
"page_id": 10,
"version": 2,
"title": {
"ko": "API 문서 샘플 페이지 (v2)",
"en": "API Doc Sample Page (v2)"
},
"content": {
"ko": "<p>수정 버전 본문.</p>",
"en": "<p>Revised version body.</p>"
},
"content_mode": "html",
"seo_meta": null,
"changes_summary": "본문 보강",
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 10:51:59"
},
{
"id": 13,
"page_id": 10,
"version": 1,
"title": {
"ko": "API 문서 샘플 페이지 (v1)",
"en": "API Doc Sample Page (v1)"
},
"content": {
"ko": "<p>초기 버전 본문.</p>",
"en": "<p>Initial version body.</p>"
},
"content_mode": "html",
"seo_meta": null,
"changes_summary": "최초 작성",
"creator": {
"uuid": "a234c2b1-cde8-437f-b28b-23323be2b98d",
"name": "API 문서 샘플 사용자"
},
"created_at": "2026-07-08 10:51:59"
}
]
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -360,10 +768,23 @@ _단건 응답: `data` 객체의 필드._
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
| versionId | path | string | 예 | — | 대상 version의 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-page/admin/pages/10/versions/{versionId} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -390,10 +811,23 @@ _단건 응답: `data` 객체의 필드._
| page | path | string | 예 | — | 조회할 페이지 번호 (1부터 시작) |
| versionId | path | string | 예 | — | 대상 version의 식별자 |
**요청 예시**
```http
POST /api/modules/sirsoft-page/admin/pages/10/versions/{versionId}/restore HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: side-effectful-write — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -419,10 +853,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-page/pages/attachment/{hash} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -446,10 +893,23 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 대상 리소스의 해시 식별자 |
**요청 예시**
```http
GET /api/modules/sirsoft-page/pages/attachment/{hash}/preview HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: unresolved-path-param — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -473,9 +933,60 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- | --- | --- |
| slug | path | string | 예 | — | 대상 리소스의 slug (URL 친화 식별자) |
**요청 예시**
```http
GET /api/modules/sirsoft-page/pages/terms HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| id | integer | `4` | 기본 키 (내부 식별자) |
| slug | string | `terms` | URL 친화 식별자 (slug) |
| title | string | `이용약관` | 제목 |
| content | string | `<h2 style="font-size: 1.25rem; font-w…` | 본문 내용 |
| content_mode | string | `html` | 본문 형식 (html, text) |
| is_preview | boolean | `false` | preview 여부 |
| published_at | string | `2026-07-08 10:44:43` | published 일시 |
| seo_meta | null | `null` | SEO 메타 정보 (title, description, keywords) |
| current_version | integer | `1` | 현재 버전 번호 |
| attachments | array | `[]` | 페이지에 귀속된 첨부파일 목록 (PageAttachmentResource 배열 — id/hash/원본파일명/URL 등, 로드된 경우에만 포함) |
| created_at | string | `2026-07-08 10:44:43` | 생성 일시 |
| updated_at | string | `2026-07-08 10:44:43` | 최종 수정 일시 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "페이지 정보를 조회했습니다.",
"data": {
"id": 4,
"slug": "terms",
"title": "이용약관",
"content": "<h2 style=\"font-size: 1.25rem; font-weight: 700; margin-top: 0; margin-bottom: 1rem;\">제1조 (목적)</h2>\n<p style=\"font-size: 1rem; line-height: 1.75; margin-bottom: 1rem;\">[이용약관의 목적을 입력하세요.]</p>\n\n<h2 style=\"font-size: 1.25rem; font-weight: 700; margin-top: 2rem; margin-bottom: 1rem;\">제2조 (정의)</h2>\n<p style=\"font-size: 1rem; line-height: 1.75; margin-bottom: 1rem;\">[주요 용어의 정의를 입력하세요.]</p>\n\n<h2 style=\"font-size: 1.25rem; font-weight: 700; margin-top: 2rem; margin-bottom: 1rem;\">제3조 (약관의 효력 및 변경)</h2>\n<p style=\"font-size: 1rem; line-height: 1.75; margin-bottom: 1rem;\">[약관의 효력 및 변경에 관한 내용을 입력하세요.]</p>\n\n<h2 style=\"font-size: 1.25rem; font-weight: 700; margin-top: 2rem; margin-bottom: 1rem;\">제4조 (서비스의 제공 및 변경)</h2>\n<p style=\"font-size: 1rem; line-height: 1.75; margin-bottom: 1rem;\">[서비스 제공 및 변경에 관한 내용을 입력하세요.]</p>\n\n<h2 style=\"font-size: 1.25rem; font-weight: 700; margin-top: 2rem; margin-bottom: 1rem;\">제5조 (회원의 의무)</h2>\n<p style=\"font-size: 1rem; line-height: 1.75; margin-bottom: 1rem;\">[회원의 의무에 관한 내용을 입력하세요.]</p>",
"content_mode": "html",
"is_preview": false,
"published_at": "2026-07-08 10:44:43",
"seo_meta": null,
"current_version": 1,
"attachments": [],
"created_at": "2026-07-08 10:44:43",
"updated_at": "2026-07-08 10:44:43"
}
}
```
**에러 응답**
@@ -29,6 +29,14 @@
| --- | --- | --- | --- | --- | --- |
| hash | path | string | 예 | — | 업로드 시 발급된 12자리 소문자 16진수 이미지 해시(라우트 제약 `[a-f0-9]{12}`). 저장된 `<img src>` 의 마지막 경로 세그먼트로, 이 값으로 서빙할 이미지를 조회한다. |
**요청 예시**
```http
GET /api/plugins/sirsoft-ckeditor5/images/a1b2c3d4e5f6 HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
@@ -29,6 +29,23 @@
| --- | --- | --- | --- | --- | --- |
| upload | body | file | 예 | max 2048 | 에디터에 드롭/붙여넣은 이미지 파일 1개(multipart). 허용 MIME 은 `jpeg,jpg,png,gif,webp`, 최대 크기는 플러그인 설정 `imageMaxSizeMb`(기본 2MB) 로 결정된다. |
**요청 예시**
```http
POST /api/plugins/sirsoft-ckeditor5/upload HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: multipart/form-data; boundary=----G7ExampleBoundary
------G7ExampleBoundary
Content-Disposition: form-data; name="upload"; filename="example.pdf"
Content-Type: application/octet-stream
(바이너리 파일 내용)
------G7ExampleBoundary--
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
@@ -0,0 +1,80 @@
<?php
namespace Plugins\Sirsoft\Ckeditor5\Support\ApiDoc;
use App\Contracts\ApiDoc\ApiDocSampleSeeder;
use App\Models\User;
use Plugins\Sirsoft\Ckeditor5\Models\Ckeditor5ImageUpload;
/**
* sirsoft-ckeditor5 API 문서 실측용 완전 샘플 시더
*
* 이미지 서빙 GET(`images/{hash}`)은 route 제약 `[a-f0-9]{12}` 의 12자리 소문자 16진수
* `hash` 로 이미지 레코드를 조회한다. 실측/요청 예시 시 `{hash}` 를 실제 해시로 치환하지
* 못하면 상세 GET 의 요청 예시에 `{hash}` placeholder 가 그대로 남으므로(결함 4), 이 시더는
* 고정 해시의 이미지 업로드 레코드 1건을 멱등 생성하고 `images` 도메인의 `path_params` 맵으로
* `{hash}` 를 실제 값으로 정확 일치 치환한다.
*
* 성공 응답은 이미지 바이너리 스트림(envelope 아님)이며, 실제 스토리지 파일이 없으면
* `serve()` 가 null 을 반환해 404 JSON 이 되므로 응답 예시는 실측 제외로 남는다(정상 —
* 바이너리 서빙 계약). 이 시더의 목적은 요청 예시의 path 치환이다.
*
* `api:docgen --scope=plugin:sirsoft-ckeditor5 --seed` 실행 시 커맨드가 규약 위치
* (`Plugins\Sirsoft\Ckeditor5\Support\ApiDoc\ApiDocSampleService`)로 자동 발견한다.
*/
class ApiDocSampleService implements ApiDocSampleSeeder
{
/**
* @var string 샘플 이미지 해시 (12자리 소문자 16진수 — 라우트 제약 `[a-f0-9]{12}` 충족)
*/
private const SAMPLE_HASH = 'a1b2c3d4e5f6';
/**
* 이미지 업로드 완전 샘플을 멱등 생성하고 `images` 도메인의 path_params 맵을 반환합니다.
*
* @return array<string, array{model: class-string, key: string, value: string, path_params?: array<string, string>}> 도메인 => 대표 레코드 정보
*/
public function seed(): array
{
if (! class_exists(Ckeditor5ImageUpload::class)) {
return [];
}
$image = $this->seedImage();
$entry = [
'model' => Ckeditor5ImageUpload::class,
'key' => 'hash',
'value' => (string) $image->hash,
'path_params' => [
'hash' => (string) $image->hash,
],
];
return [
'images' => $entry,
];
}
/**
* 고정 해시의 이미지 업로드 레코드 1건을 멱등 생성합니다.
*
* @return Ckeditor5ImageUpload 샘플 이미지 업로드
*/
private function seedImage(): Ckeditor5ImageUpload
{
$actor = User::query()->orderBy('id')->first();
return Ckeditor5ImageUpload::query()->firstOrCreate(
['hash' => self::SAMPLE_HASH],
[
'original_name' => 'apidoc-sample.png',
'file_path' => 'ckeditor5/apidoc-sample.png',
'storage_disk' => 'public',
'file_size' => 1024,
'mime_type' => 'image/png',
'uploaded_by' => $actor?->getKey(),
]
);
}
}
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Plugins\Sirsoft\Ckeditor5\Tests\Unit\Support;
use App\Contracts\ApiDoc\ApiDocSampleSeeder;
use App\Models\User;
use Plugins\Sirsoft\Ckeditor5\Models\Ckeditor5ImageUpload;
use Plugins\Sirsoft\Ckeditor5\Support\ApiDoc\ApiDocSampleService;
use Plugins\Sirsoft\Ckeditor5\Tests\PluginTestCase;
/**
* sirsoft-ckeditor5 API 문서 실측용 완전 샘플 시더 테스트.
*
* 이미지 서빙 GET(`images/{hash}`)의 요청 예시 path 치환을 위한 고정 해시 이미지
* 대표 샘플이 생성되고, `images` 도메인의 path_params 맵이 반환되며, 재실행 시
* 멱등한지 검증한다.
*/
class ApiDocSampleServiceTest extends PluginTestCase
{
/**
* @var string 샘플 이미지 해시
*/
private const SAMPLE_HASH = 'a1b2c3d4e5f6';
protected function setUp(): void
{
parent::setUp();
User::factory()->create([
'email' => 'apidoc-sample-user@example.com',
'name' => 'API 문서 샘플 사용자',
]);
}
/**
* 시더가 계약을 구현한다.
*/
public function test_seeder_implements_contract(): void
{
$this->assertInstanceOf(ApiDocSampleSeeder::class, new ApiDocSampleService);
}
/**
* 시더가 고정 해시(라우트 제약 `[a-f0-9]{12}` 충족)의 이미지 대표 샘플을 생성한다.
*/
public function test_seed_creates_image_with_valid_hash(): void
{
(new ApiDocSampleService)->seed();
$image = Ckeditor5ImageUpload::query()->where('hash', self::SAMPLE_HASH)->first();
$this->assertNotNull($image);
$this->assertMatchesRegularExpression('/^[a-f0-9]{12}$/', $image->hash);
}
/**
* 시더가 images 도메인의 path_params 맵을 반환한다.
*/
public function test_seed_returns_images_path_params_map(): void
{
$map = (new ApiDocSampleService)->seed();
$this->assertArrayHasKey('images', $map);
$entry = $map['images'];
$this->assertSame(Ckeditor5ImageUpload::class, $entry['model']);
$this->assertSame('hash', $entry['key']);
$this->assertArrayHasKey('hash', $entry['path_params']);
$this->assertSame(self::SAMPLE_HASH, $entry['path_params']['hash']);
}
/**
* 재실행 시 이미지가 중복 생성되지 않는다 (멱등).
*/
public function test_seed_is_idempotent(): void
{
(new ApiDocSampleService)->seed();
(new ApiDocSampleService)->seed();
$this->assertSame(1, Ckeditor5ImageUpload::query()->where('hash', self::SAMPLE_HASH)->count());
}
}
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/admin/consent-log HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -47,6 +56,30 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| created_at | string | `2026-07-03 00:24:27` | 생성 일시 |
| user | object | `{"id":130,"uuid":"a21d03b4-df0b-4aa6-ab7a-f51143f6375a","…` | 동의 주체 회원 정보(id/uuid/name/email). 관계가 로드된 경우에만 포함되며 게스트 이력이거나 삭제로 익명화된 경우 null입니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"data": [],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 0,
"from": null,
"to": null
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,10 +27,23 @@
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/plugins/sirsoft-gdpr/consent/cookie HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -50,6 +63,15 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/consent/cookie/status HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN} (optional.sanctum: 비회원은 헤더 생략 가능)
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -62,6 +84,26 @@ _단건 응답: `data` 객체의 필드._
| current_policy_version | string | `10` | 현재 발행된 최신 정책 버전 문자열(정책 버전 서비스 기준). 방문자 동의 버전과 비교해 배너 재노출 여부를 판단합니다 |
| is_member | boolean | `true` | member 여부 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"has_consented": false,
"consents": [],
"needs_renewal": false,
"current_policy_version": "1",
"is_member": true
}
}
```
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_
@@ -81,10 +123,23 @@ _대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/plugins/sirsoft-gdpr/consent/grant HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -106,6 +161,15 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/consent/history HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -114,6 +178,22 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| histories | array | `[]` | 회원 본인의 동의 변경 이력 배열. 각 항목은 `consent_key`, `action`(granted/revoked), `source`, `policy_version`, `categories`, `created_at`로 구성되며 부여/철회 기록을 시간순으로 제공합니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"histories": []
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -135,6 +215,15 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/consent/me HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -146,6 +235,94 @@ _단건 응답: `data` 객체의 필드._
| current_policy_version | string | `10` | 현재 발행된 최신 정책 버전 문자열. 각 동의 항목의 `policy_version`과 비교해 항목별 갱신 필요 여부를 계산하는 기준입니다 |
| consents | array | `[{"id":null,"consent_key":"cookie_necessary","consent_lab…` | 카탈로그의 모든 쿠키 카테고리와 회원 status를 합친 동의 매트릭스. 항목마다 다국어 라벨(`consent_label`), 필수 여부(`is_required`), 현재 동의 상태(`is_consented`), 철회/재동의 가능 여부(`can_revoke`/`can_grant`), 항목별 갱신 필요(`needs_renewal_this_item`)를 담아 한 화면에서 철회·재동의·신규 동의를 처리하게 합니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"user_id": 6,
"needs_renewal": false,
"current_policy_version": "1",
"consents": [
{
"id": null,
"consent_key": "cookie_necessary",
"consent_label": "필수 쿠키",
"consent_description": "세션·CSRF·로그인 토큰, 장바구니 식별자, 사용자가 가입 시 선택한 언어 설정, 쿠키 동의 기록 등 사이트 운영에 반드시 필요한 항목입니다. 비활성화할 수 없습니다.",
"consent_category": "necessary",
"is_required": true,
"is_consented": false,
"can_revoke": false,
"can_grant": false,
"needs_renewal_this_item": false,
"consented_at": null,
"revoked_at": null,
"consent_count": 0,
"policy_version": null,
"last_source": null
},
{
"id": null,
"consent_key": "cookie_functional",
"consent_label": "기능 쿠키",
"consent_description": "사용자 선호도(다크모드, 표시 통화 등)를 기억하는 쿠키입니다. 거부 시 매 방문마다 기본값으로 표시됩니다.",
"consent_category": "functional",
"is_required": false,
"is_consented": false,
"can_revoke": false,
"can_grant": true,
"needs_renewal_this_item": false,
"consented_at": null,
"revoked_at": null,
"consent_count": 0,
"policy_version": null,
"last_source": null
},
{
"id": null,
"consent_key": "cookie_analytics",
"consent_label": "분석 쿠키",
"consent_description": "방문자가 사이트를 어떻게 이용하는지 익명으로 측정해 더 나은 서비스를 만드는 데 사용됩니다. (예: Google Analytics, Hotjar)",
"consent_category": "analytics",
"is_required": false,
"is_consented": false,
"can_revoke": false,
"can_grant": true,
"needs_renewal_this_item": false,
"consented_at": null,
"revoked_at": null,
"consent_count": 0,
"policy_version": null,
"last_source": null
},
{
"id": null,
"consent_key": "cookie_marketing",
"consent_label": "마케팅 쿠키",
"consent_description": "관심사에 맞는 광고를 보여주거나, 광고가 얼마나 효과적이었는지 측정하는 데 사용됩니다. SNS 영상 임베드 등도 포함됩니다. (예: Facebook 픽셀, Google 광고, YouTube 영상)",
"consent_category": "marketing",
"is_required": false,
"is_consented": false,
"can_revoke": false,
"can_grant": true,
"needs_renewal_this_item": false,
"consented_at": null,
"revoked_at": null,
"consent_count": 0,
"policy_version": null,
"last_source": null
}
]
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -167,9 +344,38 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/plugins/sirsoft-gdpr/consent/renew-all HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_단건 응답: `data` 객체의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| renewed | integer | `0` | 이번 호출로 현재 정책 버전으로 재동의 처리된 활성 선택형 동의 항목 수. 필수 쿠키와 이미 철회한 항목·이미 현재 버전인 항목은 제외되며, 프론트 toast 메시지의 `{renewed}` 보간값으로 사용됩니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "활성 항목 {renewed} 개를 새 정책 버전으로 갱신했습니다.",
"data": {
"renewed": 0
}
}
```
**에러 응답**
@@ -192,10 +398,23 @@ _요청 파라미터 없음._
_요청 파라미터 없음._
**요청 예시**
```http
POST /api/plugins/sirsoft-gdpr/consent/revoke HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/admin/policy-versions HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
@@ -40,6 +49,39 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| created_at | string | `2026-06-18 09:27:39` | 생성 일시 |
| publisher | object | `{"uuid":"a1e0a91a-fba6-491c-a53e-7285a5686857","name":"관리…` | 이 버전을 발행한 운영자 정보(uuid/name/email). raw FK(created_by)는 노출하지 않고 관계가 로드된 경우에만 포함되며, 발행자가 없으면 null입니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"data": [
{
"id": 1,
"version": 1,
"change_type": "initial",
"memo": null,
"created_at": "2026-07-07 16:38:26",
"publisher": null
}
],
"pagination": {
"current_page": 1,
"last_page": 1,
"per_page": 25,
"total": 1,
"from": 1,
"to": 1
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -64,9 +106,49 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
| --- | --- | --- | --- | --- | --- |
| memo | body | string | 예 | min 1, max 500 | 발행 사유 메모(1~500자, 필수). 자동 감지 밖의 변경을 인지하고 명시적으로 새 버전을 발행할 때 감사 추적용으로 남기는 설명입니다 |
**요청 예시**
```http
POST /api/plugins/sirsoft-gdpr/admin/policy-versions HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"memo": "예시값"
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
_목록 응답: `data.data[]` 배열 항목의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| uuid | string | `a234c2b1-cde8-437f-b28b-23323be2b98d` | 외부 노출용 UUID (URL/API 식별자, 내부 id 비노출) |
| name | string | `API 문서 샘플 사용자` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| email | string | `apidoc-sample-user@example.com` | 이 버전을 발행한 운영자의 이메일. publisher 관계에서 노출되며 감사 화면에서 발행자를 식별하는 용도입니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "새 정책 버전이 발행되었습니다. 모든 회원이 다음 방문 시 재동의를 진행합니다.",
"data": {
"data": {
"id": 6,
"version": 2,
"0": "... (총 6건 중 2건 표시)"
}
}
}
```
**에러 응답**
@@ -91,6 +173,15 @@ _목록 응답: `data.data[]` 배열 항목의 필드 + `data.pagination`._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/admin/policy-versions/current HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_목록 응답: `data.data[]` 배열 항목의 필드._
@@ -101,6 +192,26 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| name | string | `관리자` | 대상의 이름/명칭 (다국어 필드는 로케일별 값 객체) |
| email | string | `heuristing@gmail.com` | 이 버전을 발행한 운영자의 이메일. publisher 관계에서 노출되며 감사 화면에서 발행자를 식별하는 용도입니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"data": {
"id": 1,
"version": 1,
"0": "... (총 6건 중 2건 표시)"
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -125,9 +236,48 @@ _목록 응답: `data.data[]` 배열 항목의 필드._
| --- | --- | --- | --- | --- | --- |
| version | path | string | 예 | — | 대상 버전 (버전 문자열) |
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/admin/policy-versions/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: unresolved-path-param — 응답 필드는 사람이 작성하세요. -->
_목록 응답: `data.data[]` 배열 항목의 필드._
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
| --- | --- | --- | --- |
| banner_enabled | boolean | `true` | 발행 시점 스냅샷의 쿠키 배너·자동 차단 통합 활성 여부. `true`일 때만 게스트에게 배너가 노출되고 도메인 차단 엔진이 동작합니다 |
| banner_position | string | `bottom_bar` | 발행 시점의 쿠키 배너 노출 위치(bottom_bar / bottom_left_popup / bottom_right_popup / centered_modal) |
| blocked_domains | object | `{"analytics":["google-analytics.com","*.google-analytics.…` | 발행 시점에 설정된 카테고리별 차단 도메인 패턴(키→도메인 배열). 동의 전 로드를 차단하는 대상이며, 분쟁 시 당시 어떤 도메인이 차단 대상이었는지 확인하는 근거입니다 |
| cookie_categories | array | `[{"key":"necessary","label":{"en":"Strictly Necessary","k…` | 발행 시점의 쿠키 카테고리 카탈로그 배열. 각 항목은 `key`, 필수 여부, 다국어 `label`/`description`을 담으며, 회원 분쟁 시 당시 동의 대상 카테고리 본문을 확인하는 근거가 됩니다 |
| legal_entity_name | string | `` | 발행 시점에 표기된 개인정보 처리 법인/사업자명. 미설정 시 빈 문자열입니다 |
| privacy_policy_slug | string | `privacy` | 발행 시점의 개인정보 처리방침 페이지 slug. 배너/마이페이지의 방침 링크 대상이며, 미설정 시 빈 값입니다 |
| data_storage_location | string | `` | 발행 시점에 표기된 데이터 저장 위치 문자열(국가 단위 안내). 미설정 시 빈 문자열입니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"data": {
"id": 1,
"version": 1,
"0": "... (총 8건 중 2건 표시)"
}
}
}
```
**에러 응답**
@@ -27,6 +27,15 @@
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -35,6 +44,90 @@ _단건 응답: `data` 객체의 필드._
| --- | --- | --- | --- |
| settings | object | `{"privacy_policy_slug":"privacy","legal_entity_name":"","…` | GDPR 플러그인의 관리자 설정 전체 객체. 정책 메타데이터(slug/법인명/저장 위치), 배너 설정, 쿠키 카테고리 카탈로그, 차단 도메인을 담으며 `cookie_categories` 등 JSON 필드는 디코드되어 객체/배열로 노출됩니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"settings": {
"privacy_policy_slug": "privacy",
"legal_entity_name": "",
"data_storage_location": "",
"banner_enabled": true,
"banner_position": "bottom_bar",
"blocked_domains": {
"functional": [
"*.crisp.chat",
"widget.intercom.io"
],
"analytics": [
"google-analytics.com"
],
"marketing": [
"facebook.com"
]
},
"cookie_categories": [
{
"key": "necessary",
"required": true,
"label": {
"ko": "필수 쿠키",
"en": "Strictly Necessary"
},
"description": {
"ko": "세션·CSRF·로그인 토큰, 장바구니 식별자, 사용자가 가입 시 선택한 언어 설정, 쿠키 동의 기록 등 사이트 운영에 반드시 필요한 항목입니다. 비활성화할 수 없습니다.",
"en": "Strictly necessary for site operation: session/CSRF/auth tokens, shopping basket identifier, user-selected language preference at registration, cookie consent record. Cannot be disabled."
}
},
{
"key": "functional",
"required": false,
"label": {
"ko": "기능 쿠키",
"en": "Functional"
},
"description": {
"ko": "사용자 선호도(다크모드, 표시 통화 등)를 기억하는 쿠키입니다. 거부 시 매 방문마다 기본값으로 표시됩니다.",
"en": "Cookies that remember user preferences such as dark mode and display currency. If declined, defaults are used on every visit."
}
},
{
"key": "analytics",
"required": false,
"label": {
"ko": "분석 쿠키",
"en": "Analytics"
},
"description": {
"ko": "방문자가 사이트를 어떻게 이용하는지 익명으로 측정해 더 나은 서비스를 만드는 데 사용됩니다. (예: Google Analytics, Hotjar)",
"en": "Used to anonymously measure how visitors use the site so we can improve it. (e.g. Google Analytics, Hotjar)"
}
},
{
"key": "marketing",
"required": false,
"label": {
"ko": "마케팅 쿠키",
"en": "Marketing"
},
"description": {
"ko": "관심사에 맞는 광고를 보여주거나, 광고가 얼마나 효과적이었는지 측정하는 데 사용됩니다. SNS 영상 임베드 등도 포함됩니다. (예: Facebook 픽셀, Google 광고, YouTube 영상)",
"en": "Used to show ads relevant to your interests, measure ad performance, and embed social media content. (e.g. Facebook Pixel, Google Ads, YouTube embeds)"
}
}
]
}
}
}
```
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -65,10 +158,36 @@ _단건 응답: `data` 객체의 필드._
| cookie_categories | body | string | 아니오 | — | 쿠키 카테고리 카탈로그(JSON 문자열 또는 배열). 각 항목의 `key`는 necessary/functional/analytics/marketing 중 하나이며 `label.ko`/`label.en`이 필수입니다 |
| blocked_domains | body | array | 아니오 | — | 카테고리별 차단 도메인 패턴 배열(necessary 제외). FQDN 및 `*.` 와일드카드 prefix만 허용되며 textarea 줄바꿈 입력도 배열로 정규화됩니다 |
**요청 예시**
```http
PUT /api/plugins/sirsoft-gdpr/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
Content-Type: application/json
{
"privacy_policy_slug": "example-key",
"legal_entity_name": "예시 이름",
"data_storage_location": "예시값",
"banner_enabled": true,
"banner_position": "bottom_bar",
"cookie_categories": "예시값",
"blocked_domains": [
"예시값"
]
}
```
**응답 필드** (`data` 내부)
<!-- 실측 제외: write-method — 응답 필드는 사람이 작성하세요. -->
**응답 예시**
<!-- 실측 제외: http-422 — 응답 예시는 사람이 작성하세요. -->
**에러 응답**
| 상태코드 | 의미 | 발생 조건 |
@@ -92,6 +211,14 @@ _단건 응답: `data` 객체의 필드._
_요청 파라미터 없음._
**요청 예시**
```http
GET /api/plugins/sirsoft-gdpr/settings HTTP/1.1
Host: api.example.com
Accept: application/json
```
**응답 필드** (`data` 내부)
_단건 응답: `data` 객체의 필드._
@@ -109,6 +236,144 @@ _단건 응답: `data` 객체의 필드._
| blocked_domains | object | `{"functional":["*.crisp.chat","widget.intercom.io"],"anal…` | 현재 설정된 카테고리별 차단 도메인 패턴(키→도메인 배열). 게스트도 차단이 동작해야 하므로 공개 응답에 노출되며 본 컨트롤러가 노출 SSoT입니다 |
| default_blocked_domains_preview | object | `{"functional":["*.crisp.chat","client.crisp.chat","*.inte…` | 플러그인 기본 차단 도메인 카탈로그 미리보기(변경 불가 기본값). 관리자가 커스텀 차단 목록을 편집할 때 참고용 기본 패턴을 보여줍니다 |
**응답 예시**
```http
HTTP/1.1 200
```
```json
{
"success": true,
"message": "messages.success",
"data": {
"cookie_policy_version": "1",
"privacy_policy_slug": "privacy",
"privacy_policy_available": true,
"legal_entity_name": "",
"data_storage_location": "",
"banner_enabled": true,
"banner_position": "bottom_bar",
"cookie_categories": [
{
"key": "necessary",
"required": true,
"label": {
"ko": "필수 쿠키",
"en": "Strictly Necessary"
},
"description": {
"ko": "세션·CSRF·로그인 토큰, 장바구니 식별자, 사용자가 가입 시 선택한 언어 설정, 쿠키 동의 기록 등 사이트 운영에 반드시 필요한 항목입니다. 비활성화할 수 없습니다.",
"en": "Strictly necessary for site operation: session/CSRF/auth tokens, shopping basket identifier, user-selected language preference at registration, cookie consent record. Cannot be disabled."
}
},
{
"key": "functional",
"required": false,
"label": {
"ko": "기능 쿠키",
"en": "Functional"
},
"description": {
"ko": "사용자 선호도(다크모드, 표시 통화 등)를 기억하는 쿠키입니다. 거부 시 매 방문마다 기본값으로 표시됩니다.",
"en": "Cookies that remember user preferences such as dark mode and display currency. If declined, defaults are used on every visit."
}
},
{
"key": "analytics",
"required": false,
"label": {
"ko": "분석 쿠키",
"en": "Analytics"
},
"description": {
"ko": "방문자가 사이트를 어떻게 이용하는지 익명으로 측정해 더 나은 서비스를 만드는 데 사용됩니다. (예: Google Analytics, Hotjar)",
"en": "Used to anonymously measure how visitors use the site so we can improve it. (e.g. Google Analytics, Hotjar)"
}
},
{
"key": "marketing",
"required": false,
"label": {
"ko": "마케팅 쿠키",
"en": "Marketing"
},
"description": {
"ko": "관심사에 맞는 광고를 보여주거나, 광고가 얼마나 효과적이었는지 측정하는 데 사용됩니다. SNS 영상 임베드 등도 포함됩니다. (예: Facebook 픽셀, Google 광고, YouTube 영상)",
"en": "Used to show ads relevant to your interests, measure ad performance, and embed social media content. (e.g. Facebook Pixel, Google Ads, YouTube embeds)"
}
}
],
"blocked_domains": {
"functional": [
"*.crisp.chat",
"widget.intercom.io"
],
"analytics": [
"google-analytics.com"
],
"marketing": [
"facebook.com"
]
},
"default_blocked_domains_preview": {
"functional": [
"*.crisp.chat",
"client.crisp.chat",
"*.intercom.io",
"widget.intercom.io",
"*.tawk.to",
"embed.tawk.to",
"cdn.weglot.com",
"*.weglot.com",
"*.usercentrics.eu"
],
"analytics": [
"google-analytics.com",
"*.google-analytics.com",
"googletagmanager.com",
"*.googletagmanager.com",
"ssl.google-analytics.com",
"*.hotjar.com",
"static.hotjar.com",
"*.mixpanel.com",
"cdn.mxpnl.com",
"*.amplitude.com",
"cdn.amplitude.com",
"*.segment.io",
"*.segment.com",
"wcs.naver.net",
"wcs.naver.com",
"*.beusable.net"
],
"marketing": [
"facebook.net",
"connect.facebook.net",
"facebook.com",
"*.facebook.com",
"doubleclick.net",
"*.doubleclick.net",
"googleadservices.com",
"googlesyndication.com",
"ads.google.com",
"*.criteo.com",
"static.criteo.net",
"*.adnxs.com",
"*.taboola.com",
"cdn.taboola.com",
"*.outbrain.com",
"*.kakao.com",
"analytics.ad.daum.net",
"platform.twitter.com",
"*.twitter.com",
"platform.linkedin.com",
"*.linkedin.com"
]
}
}
}
```
**에러 응답**
_대표 에러 없음 (공개 조회). <!-- TODO: 도메인 특이 에러가 있으면 보강 -->_

Some files were not shown because too many files have changed in this diff Show More