KISA 신고포상제 접수 3건(KVE-2026-1677/1678/1679)의 공통 원인은 하나다 — "위험한 것을 골라 막았고, 목록 밖으로 우회됐다". 세 지점 모두 판정을 뒤집는다. 예약 작업 Artisan (KVE-2026-1679) 차단 9개 / 허용 229개였던 구조를 허용목록으로 바꾸고 명령마다 허용 옵션을 선언한다. 검증 과정에서 보고서보다 짧은 경로를 추가로 발견했다 — 검증기는 preg_split 의 첫 토큰을 명령명으로 봤지만 실행부는 문자열째 넘겼고, parameters 가 비면 Laravel 이 StringInput 으로 재파싱한다. 따옴표 한 쌍이면 차단목록의 tinker 가 그대로 실행됐다 (마커 파일로 확인). 파서를 엄격 형태 하나로 고정하고 실행은 (명령명, 인자배열)로 넘겨 재해석 지점 자체를 없앴다. 언어팩 (KVE-2026-1678) 위험 함수 11개를 정규식으로 나열하던 검사를 토큰 상태기계로 교체해 "번역 배열만" 통과시킨다. 목록에 없는 함수도, 함수 이름을 아예 쓰지 않는 형태도 원리적으로 막힌다. 파일 형식·PHP 위치·심볼릭 링크도 화이트리스트로 뒤집었다. 활성 승격이 require 배선의 필수 조건이므로 설치와 활성화의 권한 경계를 네 설치 경로 전부에 같은 강도로 건다 — 경로별 면제는 곧 우회로가 되기 때문이다. 화면은 운영자의 활성화 권한을 보고 auto_activate 를 실을지 결정하므로 재설치 흐름은 그대로 동작한다. 인스톨러 (KVE-2026-1677) 실행 바이너리 자리에 인자가 들어가는 것을 형태 규칙으로 차단한다. escapeshellarg 는 "하나의 인자" 임만 보장할 뿐 그 인자가 실행 파일인지 스크립트인지는 보장하지 않는다. 판정을 의존성 0 공용 정책으로 모아 진단 API·설치 워커·저장 시점이 같은 규칙을 쓴다. stat 은 호출하지 않는다 — 접근이 제한된 서버에서 정상 경로가 거부되던 회귀를 재발시키지 않기 위함이다. 검증: 백엔드 392건 + 프론트 7건 green(skip 1, symlink 미지원 환경). PoC 마커로 red 를 먼저 확보하고, 수정 후 stash 역회귀로 다시 red 가 되는 것까지 확인했다.
43 KiB
Language Packs API 레퍼런스
소유: 코어 · 생성:
php artisan api:docgen(실측 기반). @generated 블록은 재생성 시 갱신되며, 사람이 작성한 설명은 보존됩니다.
TL;DR (5초 요약)
1. 이 문서는 실제 API 호출로 실측한 Language Packs 엔드포인트 레퍼런스입니다
2. 각 엔드포인트: 메서드/URI/권한 + 요청 파라미터 표 + 실측 응답 필드 표
3. 응답 필드의 예시값은 실제 호출 응답에서 관측된 값입니다
4. 갱신: 코드 변경 후 php artisan api:docgen 재실행
5. 설명(TODO) 칸은 사람이 채웁니다
GET /api/admin/language-packs
- 라우트명:
api.admin.language-packs.index - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@index - 인증/권한:
auth:sanctum+permission:core.language_packs.read
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| scope | query | string | 아니오 | — | 조회 범위 한정 키 |
| target_identifier | query | string | 아니오 | max 150 | 대상 확장 식별자 |
| locale | query | string | 아니오 | max 20 | 로케일 코드 (표시 언어/지역) |
| status | query | string | 아니오 | — | 상태 필터 (해당 상태의 항목만 조회) |
| vendor | query | string | 아니오 | max 100 | 벤더명 (확장 제작자 식별자) |
| search | query | string | 아니오 | max 150 | 검색어 (지정한 검색 대상 필드에서 부분 일치) |
| exclude_protected | query | boolean | 아니오 | — | 보호 항목 제외 여부 |
| per_page | query | integer | 아니오 | min 1, max 100 | 페이지당 항목 수 |
| page | query | integer | 아니오 | min 1 | 조회할 페이지 번호 (1부터 시작) |
요청 예시
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[] 배열 항목의 필드.
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
|---|---|---|---|
| id | integer | 16 |
기본 키 (내부 식별자) |
| identifier | string | g7-core-en |
언어팩 고유 식별자 ({vendor}-{scope}-{target?}-{locale}) |
| vendor | string | g7 |
언어팩 제작자 식별자 |
| scope | string | core |
적용 대상 분류 |
| target_identifier | string | gnuboard7-hello_module |
대상 확장 식별자 (scope=core일 때 null) |
| locale | string | en |
IETF BCP-47 locale 태그 |
| locale_name | string | EN |
영문 언어명 |
| locale_native_name | string | English |
원어 언어명 |
| text_direction | string | ltr |
텍스트 방향 |
| version | string | 7.0.1 |
언어팩 버전 |
| 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 |
언어팩 상태 |
| is_protected | boolean | true |
protected 여부 |
| source_type | string | built_in |
설치 소스 유형 (zip/github/url/bundled/bundled_with_extension) |
| origin | string | built_in |
출처 (설치/등록 원천 구분 값) |
| source_url | string | lang/en |
설치 소스 URL 또는 경로 |
| github_url | null | null |
GitHub 저장소 URL (manifest 파생) |
| github_changelog_url | null | null |
GitHub 변경 이력(CHANGELOG) URL (manifest 파생) |
| bundled_identifier | string | g7-module-gnuboard7-hello_module-ja |
대응하는 번들 확장 식별자 (번들 원본 매칭용) |
| install_blocked_reason | string | target_not_installed |
설치가 차단된 사유 (차단 없으면 null) |
| files_missing | boolean | false |
드리프트 여부 — active 로 기록됐으나 설치본 파일이 부재해 런타임에 조용히 ko 로 폴백하는 상태이면 true |
| bundled_source_available | boolean | false |
번들 소스(lang-packs/_bundled/{identifier})가 실재해 재설치로 복구 가능하면 true. 설치 경로(source_type)와 무관하게 소스 실재 여부로 판정 |
| target_name | string | 게시판 |
대상 확장의 표시 이름 (scope+target_identifier 로 해석) |
| installed_at | string | 2026-07-03 19:20:23 |
installed 일시 |
| activated_at | string | 2026-07-03 19:20:23 |
activated 일시 |
| created_at | string | 2026-07-06 19:20:23 |
생성 일시 |
| updated_at | string | 2026-07-06 19:20:23 |
최종 수정 일시 |
| has_update | boolean | false |
update 여부 |
| abilities | object | {"can_activate":true,"can_deactivate":true,"can_uninstall… |
현재 사용자가 이 리소스에 수행 가능한 작업 불리언 맵 (can_update, can_delete 등 — 권한 맵 기반) |
응답 예시
HTTP/1.1 200
{
"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,
"files_missing": false,
"bundled_source_available": false,
"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,
"files_missing": false,
"bundled_source_available": false,
"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
}
}
}
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.read)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 설치된 언어팩과 번들 소스로부터 노출되는 언어팩 목록을 페이지네이션으로 조회합니다. core.language_packs.read 권한이 필요합니다. scope/locale/status/vendor/search 등으로 필터링하고 exclude_protected 로 보호(protected) 팩을 제외할 수 있습니다. 관리자 언어팩 관리 화면의 목록/필터 표시에 사용합니다.
POST /api/admin/language-packs/bulk-activate
- 라우트명:
api.admin.language-packs.bulk-activate - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@bulkActivate - 인증/권한:
auth:sanctum+permission:core.language_packs.manage
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| ids | body | array | 예 | min 1 | 대상 리소스 식별자 배열 (대량 작업 대상) |
이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (
core.language_packs.bulk_activate_validation_rules).
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.manage)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 전달된 ids 배열의 언어팩을 일괄 활성화합니다. core.language_packs.manage 권한이 필요합니다. 성공/실패를 분리한 결과를 반환하므로 일부만 실패해도 전체가 롤백되지 않습니다. 비활성 팩을 한 번에 재활성화하는 reactivate 모달의 "활성화" 동작에 사용합니다.
POST /api/admin/language-packs/check-updates
- 라우트명:
api.admin.language-packs.check-updates - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@checkUpdates - 인증/권한:
auth:sanctum+permission:core.language_packs.update
요청 파라미터
요청 파라미터 없음.
요청 예시
POST /api/admin/language-packs/check-updates HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.update)이 없는 경우 |
설명 GitHub 소스로 설치된 언어팩들의 원격 최신 버전을 조회해 업데이트 가능 여부를 확인합니다. core.language_packs.update 권한이 필요합니다. 실제 업데이트를 수행하지 않고 검사 결과(checked, updates, details)만 반환하며, 외부 GitHub 호출을 동반합니다. 언어팩 목록의 업데이트 배지 표시에 사용합니다.
POST /api/admin/language-packs/install-from-bundled
- 라우트명:
api.admin.language-packs.install-from-bundled - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@installFromBundled - 인증/권한:
auth:sanctum+permission:core.language_packs.install
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| identifier | body | string | 예 | max 200 | 대상 확장/리소스의 식별자 |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부. 이 값을 참으로 보내려면 core.language_packs.manage 권한이 함께 필요하며, 없으면 이 항목만 422 로 거부됩니다 — 네 설치 경로(파일·URL·GitHub·번들) 모두 동일한 경계를 적용합니다. 관리자 화면의 번들 재설치는 활성화 권한이 없는 운영자에게 이 값을 보내지 않으므로 재설치 자체는 그대로 진행되며, 다만 대상 언어팩이 installed 로 내려가 다시 켜려면 관리 권한이 필요합니다 |
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.install)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 lang-packs/_bundled/{identifier} 디렉토리의 번들 소스에서 언어팩을 설치(또는 재설치)합니다. core.language_packs.install 권한이 필요합니다. 파일 형식·PHP 내용 제약은 ZIP 업로드와 동일합니다. auto_activate 가 true면 설치 후 곧바로 활성화하며, 이때는 core.language_packs.manage 권한이 함께 필요합니다. 활성 언어팩을 재설치하면서 이 값을 보내지 않으면 대상이 installed 로 내려갑니다 — 설치 트랜잭션이 항상 installed 로만 기록하고 활성화는 별도 단계로 분리되어 있기 때문입니다. 코어/확장에 선탑재된 번들 언어팩을 DB에 등록할 때 사용합니다.
POST /api/admin/language-packs/install-from-file
- 라우트명:
api.admin.language-packs.install-from-file - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@installFromFile - 인증/권한:
auth:sanctum+permission:core.language_packs.install
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| file | body | file | 예 | max 10240 | 업로드 파일 |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부. 외부 소스(파일·URL·GitHub)에서 설치할 때 이 값을 참으로 보내려면 core.language_packs.manage 권한이 함께 필요하며, 없으면 이 항목만 422 로 거부됩니다 — 항목을 빼면 설치는 그대로 진행되고 상태는 installed 로 남습니다. 설치(install)와 활성화(manage)를 별도 권한으로 두는 정책을 따르기 위함입니다 |
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.install)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 업로드된 ZIP 파일에서 언어팩을 설치합니다. core.language_packs.install 권한이 필요합니다. manifest 검증에 실패하면 422로 응답합니다. 패키지에 담을 수 있는 파일은 .json/.php/.md 뿐이며, PHP 파일은 backend/{언어코드}/{그룹}.php 위치에 번역 배열만 담고 있어야 합니다 — 그 밖의 코드(함수 호출·변수·닫는 태그 뒤 코드 등)가 있으면 위반 줄 번호와 함께 거부됩니다. 심볼릭 링크도 거부됩니다. auto_activate 가 true면 설치 후 즉시 활성화하며, 이때는 core.language_packs.manage 권한이 함께 필요합니다. 관리자가 로컬 ZIP 파일을 직접 업로드해 언어팩을 추가하는 화면에 사용합니다.
POST /api/admin/language-packs/install-from-github
- 라우트명:
api.admin.language-packs.install-from-github - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@installFromGithub - 인증/권한:
auth:sanctum+permission:core.language_packs.install
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| github_url | body | string | 예 | — | GitHub 저장소 URL |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부. 외부 소스(파일·URL·GitHub)에서 설치할 때 이 값을 참으로 보내려면 core.language_packs.manage 권한이 함께 필요하며, 없으면 이 항목만 422 로 거부됩니다 — 항목을 빼면 설치는 그대로 진행되고 상태는 installed 로 남습니다. 설치(install)와 활성화(manage)를 별도 권한으로 두는 정책을 따르기 위함입니다 |
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.install)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 GitHub 저장소 URL에서 언어팩을 다운로드해 설치합니다. core.language_packs.install 권한이 필요합니다. 외부 GitHub 호출을 동반하며 manifest 검증 실패 시 422로 응답합니다. 파일 형식·PHP 내용 제약은 ZIP 업로드와 동일합니다. auto_activate 가 true면 설치 후 즉시 활성화하며, 이때는 core.language_packs.manage 권한이 함께 필요합니다. GitHub로 배포되는 언어팩을 URL 만으로 설치할 때 사용하며, 이후 check-updates/update 로 갱신을 추적할 수 있습니다.
POST /api/admin/language-packs/install-from-url
- 라우트명:
api.admin.language-packs.install-from-url - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@installFromUrl - 인증/권한:
auth:sanctum+permission:core.language_packs.install
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| url | body | string | 예 | max 500 | 언어팩 ZIP 다운로드 URL. https 만 허용하며, 내부 네트워크 주소(사설 IP·루프백·localhost·*.internal 등)와 userinfo(https://a@b/) 위장 주소는 422 로 거부됩니다 — 서버가 대신 내려받는 요청이므로 내부망 접근을 막기 위함(SSRF). 원격 코드를 가져오는 경로라 security.allow_internal_outbound_urls 설정을 켜도 내부 주소는 계속 차단됩니다 |
| checksum | body | string | 아니오 | — | 무결성 검증 체크섬 (SHA-256) |
| auto_activate | body | boolean | 아니오 | — | 설치 후 자동 활성화 여부. 외부 소스(파일·URL·GitHub)에서 설치할 때 이 값을 참으로 보내려면 core.language_packs.manage 권한이 함께 필요하며, 없으면 이 항목만 422 로 거부됩니다 — 항목을 빼면 설치는 그대로 진행되고 상태는 installed 로 남습니다. 설치(install)와 활성화(manage)를 별도 권한으로 두는 정책을 따르기 위함입니다 |
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.install)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 임의의 URL에서 언어팩 ZIP을 내려받아 설치합니다. core.language_packs.install 권한이 필요합니다. checksum 을 함께 전달하면 다운로드 무결성을 검증하며, manifest 검증 실패 시 422로 응답합니다. auto_activate 가 true면 설치 후 즉시 활성화합니다. GitHub 외 임의 호스팅에 배포된 언어팩을 설치할 때 사용합니다.
POST /api/admin/language-packs/manifest-preview
- 라우트명:
api.admin.language-packs.manifest-preview - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@manifestPreview - 인증/권한:
auth:sanctum+permission:core.language_packs.install
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| file | body | file | 예 | max 5120 | 업로드 파일 |
요청 예시
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 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.install)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
설명 업로드된 ZIP을 실제로 설치하지 않고 manifest 와 검증 결과만 미리 조회합니다. core.language_packs.install 권한이 필요합니다. 부수 효과 없이 읽기만 수행하며 검증 실패 시 422로 응답합니다. 설치 확인 모달에서 대상 언어팩의 메타데이터와 유효성을 미리 보여줄 때 사용합니다.
POST /api/admin/language-packs/refresh-cache
- 라우트명:
api.admin.language-packs.refresh-cache - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@refreshCache - 인증/권한:
auth:sanctum+permission:core.language_packs.manage
요청 파라미터
요청 파라미터 없음.
요청 예시
POST /api/admin/language-packs/refresh-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.manage)이 없는 경우 |
설명 번역/레지스트리/템플릿 언어 캐시를 무효화합니다. core.language_packs.manage 권한이 필요합니다. 언어팩 파일을 직접 수정했거나 활성 상태가 프론트에 반영되지 않을 때 캐시를 강제로 갱신하는 용도로 사용합니다.
DELETE /api/admin/language-packs/{id}
- 라우트명:
api.admin.language-packs.uninstall - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@uninstall - 인증/권한:
auth:sanctum+permission:core.language_packs.manage
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
| cascade | query | boolean | 아니오 | — | 연쇄 처리 여부 (의존 항목 함께 처리) |
요청 예시
DELETE /api/admin/language-packs/1?cascade=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.manage)이 없는 경우 |
| 422 | Unprocessable Entity | 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지) |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 언어팩을 제거하고 설치된 파일을 삭제합니다. core.language_packs.manage 권한이 필요합니다. cascade 가 true면 연관 자원까지 함께 제거합니다. 대상이 존재하지 않으면 404로 응답합니다. 관리자 언어팩 관리 화면의 삭제(제거) 동작에 사용합니다.
GET /api/admin/language-packs/{id}
- 라우트명:
api.admin.language-packs.show - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@show - 인증/권한:
auth:sanctum+permission:core.language_packs.read
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
요청 예시
GET /api/admin/language-packs/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
단건 응답: 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/1.1 200
{
"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": []
}
}
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.read)이 없는 경우 |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 단일 언어팩의 상세 정보를 조회합니다. core.language_packs.read 권한이 필요합니다. {id} 가 정수면 DB 레코드를, 문자열(번들 식별자)이면 lang-packs/_bundled/{id} manifest 로 합성된 가상 행을 반환합니다. 미설치 번들 언어팩까지 상세 모달로 열람할 수 있도록 하며, 없으면 404로 응답합니다.
POST /api/admin/language-packs/{id}/activate
- 라우트명:
api.admin.language-packs.activate - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@activate - 인증/권한:
auth:sanctum+permission:core.language_packs.manage
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
요청 예시
POST /api/admin/language-packs/1/activate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.manage)이 없는 경우 |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 언어팩을 활성화합니다(슬롯 스위칭). core.language_packs.manage 권한이 필요합니다. 동일 슬롯(scope·target·locale)에 이미 다른 활성 팩이 있으면 409(slot_conflict)로 현재/대상 팩을 함께 반환하며, 프론트는 확인 모달을 띄운 뒤 force=true 로 재호출해 교체합니다. 대상이 없으면 404로 응답합니다.
GET /api/admin/language-packs/{id}/changelog
- 라우트명:
api.admin.language-packs.changelog - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@changelog - 인증/권한:
auth:sanctum+permission:core.language_packs.read
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
요청 예시
GET /api/admin/language-packs/1/changelog HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
단건 응답: data 객체의 필드.
| 필드 | 타입 | 실측 예시값 | 용도/설명 |
|---|---|---|---|
| identifier | string | apidoc-sample-lang |
언어팩 고유 식별자 ({vendor}-{scope}-{target?}-{locale}) |
| entries | array | [] |
항목 목록 (각 원소는 대상 도메인 레코드) |
| changelog | string | `` | 변경 이력 텍스트 (원격/파일 CHANGELOG 본문) |
| has_changelog | boolean | false |
changelog 여부 |
응답 예시
HTTP/1.1 200
{
"success": true,
"message": "언어팩 목록을 조회했습니다.",
"data": {
"identifier": "apidoc-sample-lang",
"entries": [],
"changelog": "",
"has_changelog": false
}
}
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.read)이 없는 경우 |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 언어팩의 CHANGELOG.md 내용을 반환합니다. core.language_packs.read 권한이 필요합니다. {id} 가 정수면 DB 레코드를, 문자열이면 번들 가상 행을 사용합니다. 파싱된 항목(entries), 원문(changelog), 존재 여부(has_changelog)를 함께 반환하며, CHANGELOG 파일이 없어도 빈 값으로 정상 응답합니다. 상세 모달의 변경 이력 탭에 사용합니다.
POST /api/admin/language-packs/{id}/deactivate
- 라우트명:
api.admin.language-packs.deactivate - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@deactivate - 인증/권한:
auth:sanctum+permission:core.language_packs.manage
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
요청 예시
POST /api/admin/language-packs/1/deactivate HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.manage)이 없는 경우 |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 활성 언어팩을 비활성화합니다. core.language_packs.manage 권한이 필요합니다. 해당 슬롯의 번역 적용이 해제되며, 대상이 없으면 404로 응답합니다. 관리자 언어팩 관리 화면에서 활성 팩을 끄는 동작에 사용합니다.
POST /api/admin/language-packs/{id}/update
- 라우트명:
api.admin.language-packs.update - 컨트롤러:
App\Http\Controllers\Api\Admin\LanguagePackController@performUpdate - 인증/권한:
auth:sanctum+permission:core.language_packs.update
요청 파라미터
| 이름 | 위치 | 타입 | 필수 | 허용값 | 용도 |
|---|---|---|---|---|---|
| id | path | string | 예 | — | 대상 리소스의 식별자 |
요청 예시
POST /api/admin/language-packs/1/update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}
응답 필드 (data 내부)
응답 예시
에러 응답
| 상태코드 | 의미 | 발생 조건 |
|---|---|---|
| 401 | Unauthenticated | 유효한 Bearer 토큰이 없거나 만료된 경우 |
| 403 | Forbidden | 요구 권한(core.language_packs.update)이 없는 경우 |
| 404 | Not Found | path 파라미터에 해당하는 리소스가 없는 경우 |
설명 GitHub 소스 언어팩을 최신 버전으로 다시 내려받아 적용합니다. core.language_packs.update 권한이 필요합니다. 외부 GitHub 재다운로드와 파일 교체를 동반하며, 갱신된 언어팩 정보를 반환합니다. 대상이 없으면 404로 응답합니다. check-updates 로 업데이트가 감지된 팩을 실제로 갱신할 때 사용합니다.