Files
Gnuboard7/docs/backend/api/settings.md
T
HeuJung 1a6fca0d6b feat(seo): 사이트맵 기여자 지연 스트리밍 + hreflang 계산 선형화
AbstractSitemapContributor 브리지 base 로 인터페이스를 바꾸지 않고
board/ecommerce/page 기여자를 getUrlsLazy 지연 스트리밍으로 전환해,
게시글이 수백만 건인 사이트에서도 URL 배열을 통째로 적재하지 않는다.
SitemapGenerator 는 capability 를 감지해 지연 경로를 쓰고, base 를
상속하지 않은 제3자 구현체는 기존 getUrls 경로로 그대로 동작한다.

hreflang alternate 집합을 base URL 당 한 번만 계산해 로케일별 <url>
에 재사용하도록 바꿔 계산 비용을 로케일 수의 제곱에서 선형으로 줄였다
(정상 구성 출력은 불변). 다국어 대체 링크 사용 여부 토글과 로케일 수
상한을 추가하고 설정/검증/관리자 UI/다국어(ko·en·ja) 표면을 동반했다.
2026-07-18 15:05:56 +09:00

51 KiB

Settings API 레퍼런스

소유: 코어 · 생성: php artisan api:docgen (실측 기반). @generated 블록은 재생성 시 갱신되며, 사람이 작성한 설명은 보존됩니다.


TL;DR (5초 요약)

1. 이 문서는 실제 API 호출로 실측한 Settings 엔드포인트 레퍼런스입니다
2. 각 엔드포인트: 메서드/URI/권한 + 요청 파라미터 표 + 실측 응답 필드 표
3. 응답 필드의 예시값은 실제 호출 응답에서 관측된 값입니다
4. 갱신: 코드 변경 후 php artisan api:docgen 재실행
5. 설명(TODO) 칸은 사람이 채웁니다

GET /api/admin/settings

  • 라우트명: api.admin.settings.index
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@index
  • 인증/권한: auth:sanctum + permission:core.settings.read

요청 파라미터

요청 파라미터 없음.

요청 예시

GET /api/admin/settings HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

단건 응답: data 객체의 필드.

필드 타입 실측 예시값 용도/설명
general object {"site_name":"Test Site","site_url":"https:\/\/test.examp… 일반 탭 설정 그룹 (사이트명·사이트 URL·설명·관리자 이메일·타임존·기본 언어·통화·점검 모드·사이트 로고 첨부). site_logo 는 SettingsService 가 별도 주입한 첨부 정보
security object {"force_https":true,"login_attempt_enabled":true,"auth_to… 보안 탭 설정 그룹 (HTTPS 강제·로그인 시도 제한 사용·인증 토큰 유지시간(분, 0=무한)·최대 로그인 시도 횟수·잠금 시간·내부 네트워크 주소 호출 허용). allow_internal_outbound_urls(boolean, 기본 false): 서버가 대신 호출하는 outbound 요청(예약 작업 URL 호출, 외부 API 연동)에서 사설 IP·localhost 등 내부 주소를 허용할지 여부 — 켜면 사내 서버 호출이 가능해지지만 서버가 내부망으로 요청을 보낼 수 있게 되므로 기본은 차단입니다. 언어팩 URL 설치는 원격 코드를 내려받으므로 이 설정과 무관하게 항상 내부 주소를 거부합니다
mail object {"mailer":"smtp","host":"","port":587,"username":"","pass… 메일 탭 설정 그룹 (메일러 종류(smtp/mailgun/ses)·SMTP 호스트/포트/인증 정보·암호화 방식·발신자 주소/이름·Mailgun/SES 자격 정보)
upload object {"max_file_size":10,"allowed_extensions":["jpg","jpeg","p… 업로드 탭 설정 그룹 (최대 파일 크기(MB)·허용 확장자 목록·이미지 최대 가로/세로·이미지 품질)
seo object {"meta_title_suffix":"","meta_description":"","meta_keywo… SEO 탭 설정 그룹 (메타 타이틀 접미사·메타 설명/키워드·검색엔진 인증 코드·봇 감지·OG/Twitter 기본값·SEO 캐시·사이트맵·생성기 설정)
advanced object {"cache_enabled":true,"cache_default_ttl":86400,"layout_c… 고급 탭 설정 그룹 (캐시·디버그·코어 업데이트·GeoIP 설정을 한 탭으로 합친 병합 뷰). cache/debug 카테고리 값이 함께 노출됨
cache object {"cache_enabled":true,"cache_default_ttl":86400,"layout_c… 캐시 원본 카테고리 (전역 캐시 사용·기본 TTL·레이아웃/통계/SEO 캐시 사용 및 TTL). advanced 탭에 병합되면서 개별 접근용으로 별도 노출된 파생 뷰
debug object {"debug_mode":true,"sql_query_log":false,"log_level":"err… 디버그 원본 카테고리 (디버그 모드·SQL 쿼리 로그·로그 레벨). advanced 탭에 병합되면서 개별 접근용으로 별도 노출된 파생 뷰
drivers object {"storage_driver":"local","s3_bucket":null,"s3_region":"a… 드라이버 탭 설정 그룹 (스토리지/캐시/세션/큐/로그 드라이버 선택 + S3·Redis·Memcached·WebSocket·검색엔진 접속 파라미터)
core_update object {"core_update_github_url":"https:\/\/github.com\/custom\/… 코어 업데이트 원본 카테고리 (코어 업데이트를 받아올 GitHub 저장소 URL·비공개 저장소 접근용 토큰). advanced 탭에 병합된 파생 뷰
geoip object {"geoip_enabled":false,"geoip_license_key":null,"geoip_au… GeoIP 원본 카테고리 (GeoIP 사용 여부·MaxMind 라이선스 키·DB 자동 갱신 사용). advanced 탭에 병합된 파생 뷰
notifications object {"channels":[{"id":"mail","is_active":true,"sort_order":1… 알림 탭 설정 그룹. channels 는 알림 채널 목록으로 각 원소가 id(채널 식별자)·is_active(활성 여부)·sort_order(표시 순서)를 가짐
identity object {"default_provider":"g7:core.mail","purpose_providers":{"… 본인인증(IDV) 탭 설정 그룹 (기본 provider·목적별 provider 매핑(purpose_providers)·챌린지 유효시간(분)·최대 시도 횟수)
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/1.1 200
{
    "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_urls_per_file": 50000,
            "sitemap_gzip": false,
            "sitemap_serve_stale_on_miss": true,
            "sitemap_max_urls_per_contributor": 0,
            "sitemap_hreflang_enabled": true,
            "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
        }
    }
}

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.read)이 없는 경우

설명

관리자 통합 환경설정 화면(admin_settings.json)이 사용하는 전체 설정 조회 엔드포인트입니다. 각 탭에 해당하는 설정 그룹(general/security/mail/upload/seo/advanced/drivers/geoip/notifications/identity 등)과 드라이버 선택지 카탈로그(available_drivers)를 한 번에 반환합니다. 응답은 Eloquent 모델이 아니라 SettingsService 가 여러 설정 소스를 병합해 만든 집계 배열이며, 일부 그룹(cache/debug 등)은 원본 카테고리 값을 별도 키로 함께 노출한 파생 뷰입니다.

POST /api/admin/settings

  • 라우트명: api.admin.settings.store
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@store
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
_tab body string 아니오 — 활성 탭 식별자 (general/mail/upload/seo/security/drivers/advanced/notifications/identity). 지정 시 해당 탭 필드만 필수 검증되고 나머지 탭은 nullable 처리되어 탭 단위 부분 저장을 가능케 함
general body array 아니오 — 일반 탭 설정 묶음 (사이트명·URL·설명·관리자 이메일·타임존·기본 언어·통화·점검 모드·사이트 로고)
mail body array 아니오 — 메일 탭 설정 묶음 (메일러 종류·SMTP 호스트/포트/인증·암호화·발신자 정보·Mailgun/SES 자격 정보)
upload body array 아니오 — 업로드 탭 설정 묶음 (최대 파일 크기·허용 확장자·이미지 최대 크기 및 품질)
seo body array 아니오 — SEO 탭 설정 묶음 (메타 태그·검색엔진 인증·봇 감지·OG/Twitter 기본값·SEO 캐시·사이트맵·생성기)
security body array 아니오 — 보안 탭 설정 묶음 (HTTPS 강제·로그인 시도 제한·인증 토큰 유지시간·최대 시도 횟수·잠금 시간)
drivers body array 아니오 — 드라이버 탭 설정 묶음 (스토리지/캐시/세션/큐/로그 드라이버 및 S3·Redis·Memcached·WebSocket·검색엔진 접속 정보)
advanced body array 아니오 — 고급 탭 설정 묶음 (캐시·디버그·코어 업데이트·GeoIP 설정)
notifications body array 아니오 — 알림 탭 설정 묶음. channels 배열로 각 알림 채널의 id·is_active(활성 여부)·sort_order(표시 순서)를 저장
identity body array 아니오 — 본인인증(IDV) 탭 설정 묶음 (기본 provider·목적별 provider 매핑·챌린지 유효시간·최대 시도 횟수)

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.save_validation_rules, core.search.engine_drivers).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)

설명

통합 환경설정 화면에서 한 탭의 설정을 일괄 저장합니다. _tab 으로 활성 탭을 지정하면 해당 탭의 필드만 필수 검증되고 다른 탭 필드는 nullable 로 처리되므로, 탭 단위로 부분 저장할 수 있습니다. 저장 성공 시 응답 data.settings 에 갱신된 전체 설정과 available_drivers 를 함께 반환하여, 프론트엔드가 새로고침 없이 전역 상태를 갱신할 수 있습니다. 검증 실패 시 422, 그 외 오류 시 500 을 반환합니다.

GET /api/admin/settings/app-key

  • 라우트명: api.admin.settings.app-key
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@getAppKey
  • 인증/권한: auth:sanctum + permission:core.settings.read

요청 파라미터

요청 파라미터 없음.

요청 예시

GET /api/admin/settings/app-key HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

단건 응답: data 객체의 필드.

필드 타입 실측 예시값 용도/설명
app_key string base64:97gZH*************************… 현재 애플리케이션 키(APP_KEY)를 마스킹한 문자열. 앞부분 일부만 노출하고 나머지는 별표로 가려 전체 원문은 반환하지 않음

응답 예시

HTTP/1.1 200
{
    "success": true,
    "message": "성공적으로 처리되었습니다.",
    "data": {
        "app_key": "{MASKED}"
    }
}

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.read)이 없는 경우

설명

현재 애플리케이션 키(APP_KEY)를 마스킹된 형태로 조회합니다. 관리자 화면에서 앱 키 존재/일부만 표시하는 용도이며, 전체 키 원문은 반환하지 않습니다.

POST /api/admin/settings/backup

  • 라우트명: api.admin.settings.backup
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@backup
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

요청 파라미터 없음.

요청 예시

POST /api/admin/settings/backup HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우

설명

현재 설정을 백업 파일로 저장합니다. 응답 data.backup_path 에 생성된 백업 경로를 반환하며, 이 경로는 이후 POST /restore 의 backup_path 로 사용할 수 있습니다. 설정 변경 전 스냅샷을 남길 때 사용합니다.

POST /api/admin/settings/backup-database

  • 라우트명: api.admin.settings.backup-database
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@backupDatabase
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

요청 파라미터 없음.

요청 예시

POST /api/admin/settings/backup-database HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우

설명

데이터베이스를 백업합니다. SettingsService 에 위임하며, 성공/실패를 메시지로 반환합니다. 설정 백업(POST /backup)이 설정 파일만 다루는 것과 달리, 이 엔드포인트는 DB 데이터를 백업 대상으로 합니다.

POST /api/admin/settings/clear-cache

  • 라우트명: api.admin.settings.clear-cache
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@clearCache
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

요청 파라미터 없음.

요청 예시

POST /api/admin/settings/clear-cache HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우

설명

시스템 캐시를 정리합니다. 시스템 정보 캐시를 지원 로케일별로 비운 뒤 cache:clear, route:clear, view:clear 를 실행하고, config 캐시는 비운 직후 즉시 재생성합니다(비워 두면 이후 모든 요청이 config 를 재파싱하므로). 설정/코드 변경 후 오래된 캐시를 초기화할 때 사용합니다.

POST /api/admin/settings/geoip/update

  • 라우트명: api.admin.settings.geoip.update
  • 컨트롤러: App\Http\Controllers\Api\Admin\GeoIpController@update
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

요청 파라미터 없음.

요청 예시

POST /api/admin/settings/geoip/update HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우

설명

MaxMind GeoLite2-City DB 를 즉시 재다운로드합니다. GeoIpDatabaseService 에 위임하며 동기(즉시) 실행되므로 웹서버/PHP-FPM 타임아웃(90초 이상)이 필요합니다. 라이선스 키 미설정 시 400, 키가 잘못된 경우 401, 연결 실패/기타 오류 시 500 을 반환합니다. 정기 갱신은 스케줄(geoip:update)이 담당하고, 이 엔드포인트는 수동 갱신 트리거입니다.

POST /api/admin/settings/optimize-system

  • 라우트명: api.admin.settings.optimize-system
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@optimizeSystem
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

요청 파라미터 없음.

요청 예시

POST /api/admin/settings/optimize-system HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

HTTP/1.1 200
{
    "success": true,
    "message": "시스템이 성공적으로 최적화되었습니다.",
    "data": null
}

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우

설명

시스템을 최적화합니다. config:cache, route:cache, view:cache 를 실행해 설정·라우트·뷰 캐시를 생성함으로써 이후 요청의 부팅 비용을 줄입니다. 캐시를 비우는 clear-cache 와 반대로, 캐시를 사전 생성하는 프로덕션 성능용 작업입니다.

POST /api/admin/settings/regenerate-app-key

  • 라우트명: api.admin.settings.regenerate-app-key
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@regenerateAppKey
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
password body string 예 — 비밀번호

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.regenerate_app_key_validation_rules).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)

설명

애플리케이션 키(APP_KEY)를 재생성합니다. FormRequest 단계에서 super_admin 역할만 허용하고, Service 단계에서 요청자 본인의 비밀번호가 일치하는지 다시 확인합니다(불일치 시 401). 성공 시 새 키를 .env 의 APP_KEY 에 기록하고 config 캐시를 재생성하며, 응답 data.app_key 에 새 키를 반환합니다. 앱 키 변경은 기존 암호화 값/서명 무효화를 동반하므로 주의가 필요합니다.

POST /api/admin/settings/restore

  • 라우트명: api.admin.settings.restore
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@restore
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
backup_path body string 예 — 복원할 백업 파일 경로. POST /api/admin/settings/backup 응답의 backup_path 로 받은 값을 그대로 지정

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.restore_validation_rules).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)

설명

이전에 만든 설정 백업에서 설정을 복원합니다. backup_path 로 POST /backup 이 반환한 백업 경로를 지정합니다. 복원 성공 시 시스템 설정 캐시를 무효화합니다. 잘못된 설정을 되돌릴 때 사용합니다.

GET /api/admin/settings/system-info

  • 라우트명: api.admin.settings.system-info
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@systemInfo
  • 인증/권한: auth:sanctum + permission:core.settings.read

요청 파라미터

요청 파라미터 없음.

요청 예시

GET /api/admin/settings/system-info HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

단건 응답: data 객체의 필드.

필드 타입 실측 예시값 용도/설명
os_info string Windows NT 10.0 운영체제 종류와 버전 (php_uname 산물). probe 차단 시 "알 수 없음" 폴백
web_server string Apache/2.4.62 (Win64) OpenSSL/3.0.16 … 웹서버 소프트웨어 식별 문자열 ($_SERVER['SERVER_SOFTWARE'])
php_version string 8.3.26 실행 중인 PHP 버전 (PHP_VERSION)
mysql_version string Mysql 8.4.3 연결된 데이터베이스 서버 종류와 버전 (DB 조회 산물). probe 실패 시 "알 수 없음" 폴백
g7_version string 7.0.1 G7 코어 버전 (config('app.version'))
g7_release_year string 2026 G7 릴리즈 연도 (config('app.release_year'), 저작권 표기 등에 사용)
laravel_version string 12.54.1 프레임워크 Laravel 버전 (app()->version())
environment string local 현재 실행 환경 (app()->environment() — local/production/testing 등)
cpu_info string Intel(R) Core(TM) Ultra 5 225H CPU 모델명 (OS별 시스템 probe 산물). 수집 실패 시 "알 수 없음" 폴백
memory_usage object {"total":"31.49 GB","used":"29.63 GB","free":"1.86 GB","p… 물리 메모리 사용량. total/used/free 는 사람이 읽기 쉬운 단위 문자열, percentage 는 사용률(%)
disk_usage object {"total":"474.72 GB","used":"360.2 GB","free":"114.51 GB"… 설치 볼륨 디스크 사용량. total/used/free 단위 문자열 + percentage 사용률(%)
php_memory_limit string 512M PHP memory_limit ini 값
max_execution_time string 36000초 PHP max_execution_time ini 값 (초 단위 접미사 부착)
upload_max_filesize string 2G PHP upload_max_filesize ini 값
install_path string C:\Users\HeuJung\htdocs\g7_2 애플리케이션 설치 루트 경로 (base_path())
config_path string C:\Users\HeuJung\htdocs\g7_2\storage\… 설정 파일 저장 경로 (storage/app/settings)
log_path string C:\Users\HeuJung\htdocs\g7_2\storage\… 로그 파일 저장 경로 (storage/logs)
upload_path string C:\Users\HeuJung\htdocs\g7_2\storage\… 공개 업로드 파일 저장 경로 (storage/app/public)
php_extensions object {"required":{"openssl":true,"pdo":true,"mbstring":true,"t… PHP 확장 로드 상태. required(필수)·optional(선택) 두 그룹으로 나뉘며 각 확장명→로드 여부(bool) 매핑
database_config object {"has_read_write_split":false,"write":{"host":"localhost"… DB 연결 구성 요약. has_read_write_split(읽기/쓰기 분리 여부)·write(쓰기 연결 정보)·read(읽기 replica 목록, write 와 동일하면 제외)
timezone string UTC 애플리케이션 기본 타임존 (config('app.timezone'))
server_time string 2026-07-07 05:08:58 서버 현재 시각 (Y-m-d H:i:s)

응답 예시

HTTP/1.1 200
{
    "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"
    }
}

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.read)이 없는 경우

설명

서버 실행 환경 정보를 한 번에 조회합니다. OS/웹서버/PHP/DB/Laravel/코어 버전, CPU·메모리·디스크 사용량, PHP 주요 설정값(memory_limit·max_execution_time·upload_max_filesize), 주요 경로, PHP 확장 로드 상태, DB 연결 구성 요약 등을 포함합니다. 관리자 시스템 정보 화면과 요구사항 점검용 진단 데이터로 사용됩니다.

POST /api/admin/settings/test-driver

  • 라우트명: api.admin.settings.test-driver
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@testDriverConnection
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
storage_driver body string 아니오 — 스토리지 드라이버 (local/s3)
cache_driver body string 아니오 — 캐시 드라이버 (file/redis/memcached)
session_driver body string 아니오 — 세션 드라이버 (file/database/redis)
queue_driver body string 아니오 — 큐 드라이버 (sync/database/redis)
websocket_enabled body boolean 아니오 — WebSocket 사용 여부
s3_bucket body string 아니오 max 255 S3 버킷명
s3_region body string 아니오 — S3 리전
s3_access_key body string 아니오 max 255 S3 액세스 키
s3_secret_key body string 아니오 max 255 S3 시크릿 키
s3_url body string 아니오 max 500 S3 엔드포인트 URL
redis_host body string 아니오 max 255 Redis 호스트 주소
redis_port body integer 아니오 min 1, max 65535 Redis 포트 번호
redis_password body string 아니오 max 255 Redis 비밀번호
redis_database body integer 아니오 min 0, max 15 Redis 데이터베이스 번호
memcached_host body string 아니오 max 255 Memcached 호스트 주소
memcached_port body integer 아니오 min 1, max 65535 Memcached 포트 번호
websocket_app_key body string 아니오 max 255 WebSocket 앱 키
websocket_host body string 아니오 max 255 WebSocket 호스트 주소
websocket_port body integer 아니오 min 1, max 65535 WebSocket 포트 번호
websocket_scheme body string 아니오 — WebSocket 스킴 (http/https)

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.test_driver_connection_validation_rules).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)

설명

폼에 입력한 드라이버 접속 정보(S3·Redis·Memcached·Websocket 등)로 실제 연결을 시도해 결과를 반환합니다. 설정을 저장하기 전에 접속 정보가 유효한지 확인하는 용도입니다. 모든 테스트 통과 시 성공 메시지, 일부 실패 시에도 HTTP 성공 응답으로 항목별 결과(all_passed=false 포함)를 함께 반환합니다.

POST /api/admin/settings/test-mail

  • 라우트명: api.admin.settings.test-mail
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@testMail
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
to_email body email 예 max 255 테스트 수신 주소
mailer body string 아니오 smtp, mailgun, ses 메일 발송 드라이버 (smtp/mailgun/ses)
from_address body email 예 max 255 발신자 주소
from_name body string 예 max 255 발신자 이름
host body string 예 max 255 호스트 주소
port body integer 예 min 1, max 65535 포트 번호
username body string 아니오 max 255 사용자명 (로그인/인증 아이디)
password body string 아니오 max 255 비밀번호
encryption body string 아니오 tls, ssl, null 전송 암호화 방식 (tls/ssl)
mailgun_domain body string 아니오 max 255 Mailgun 도메인
mailgun_secret body string 아니오 max 255 Mailgun 시크릿 키
mailgun_endpoint body string 아니오 max 255 Mailgun 엔드포인트
ses_key body string 아니오 max 255 SES 액세스 키
ses_secret body string 아니오 max 255 SES 시크릿 키
ses_region body string 아니오 max 255 SES 리전

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.test_mail_validation_rules).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)

설명

폼에 입력한 메일 설정으로 지정한 주소에 테스트 메일을 발송합니다. 요청에서 전달한 값(호스트·포트·인증 정보 등)을 저장된 메일 설정 위에 임시로 덮어써 그 값으로만 발송을 시도하므로, 설정을 저장하기 전에 실제 발송 가능 여부를 검증할 수 있습니다. 성공 시 발송한 제목/본문을 응답에 포함하고, 실패 시 오류 사유와 함께 500 을 반환합니다.

GET /api/admin/settings/{key}

  • 라우트명: api.admin.settings.show
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@show
  • 인증/권한: auth:sanctum + permission:core.settings.read

요청 파라미터

이름 위치 타입 필수 허용값 용도
key path string 예 — 대상 설정/항목의 키

요청 예시

GET /api/admin/settings/{key} HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer {YOUR_TOKEN}

응답 필드 (data 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.read)이 없는 경우
404 Not Found path 파라미터에 해당하는 리소스가 없는 경우

설명

단일 설정 키의 값을 조회합니다. 응답의 data.key 는 요청한 키, data.value 는 해당 설정 값입니다. 통합 조회(GET /api/admin/settings)와 달리 특정 키 하나만 필요할 때 사용합니다.

PUT /api/admin/settings/{key}

  • 라우트명: api.admin.settings.update
  • 컨트롤러: App\Http\Controllers\Api\Admin\SettingsController@update
  • 인증/권한: auth:sanctum + permission:core.settings.update

요청 파라미터

이름 위치 타입 필수 허용값 용도
key path string 예 — 대상 설정/항목의 키
value body string 예 max 1000 값

이 엔드포인트는 확장이 파라미터를 추가할 수 있습니다 (core.settings.update_validation_rules).

요청 예시

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 내부)

응답 예시

에러 응답

상태코드 의미 발생 조건
401 Unauthenticated 유효한 Bearer 토큰이 없거나 만료된 경우
403 Forbidden 요구 권한(core.settings.update)이 없는 경우
422 Unprocessable Entity 요청 파라미터가 검증 규칙을 위반한 경우 (error.errors 에 필드별 메시지)
404 Not Found path 파라미터에 해당하는 리소스가 없는 경우

설명

단일 설정 키의 값을 업데이트합니다. 경로의 key 로 대상 설정을, 본문의 value 로 새 값을 지정합니다. 탭 단위 일괄 저장(POST /api/admin/settings)과 달리 개별 키 하나만 변경할 때 사용합니다. 검증 실패 시 422, 그 외 오류 시 500 을 반환합니다.