27 KiB
코어 업데이트 시스템 (Core Update System)
코어 버전 업데이트의 감지, 다운로드, 적용, 롤백, 업그레이드 스텝 실행을 관리하는 시스템
TL;DR (5초 요약)
1. 코어 업그레이드 스텝: upgrades/ 디렉토리 (프로젝트 루트), 네임스페이스 App\Upgrades
2. 마이그레이션 = 스키마 변경만, 데이터 백필/변환 = 업그레이드 스텝 (역할 분리 필수)
3. 업데이트 실행 흐름: 11단계 (감지 → 다운로드 → 백업 → 적용 → 마이그레이션 → 동기화 → 업그레이드 → 마무리)
4. 롤백: CoreBackupHelper로 백업 생성, 실패 시 자동 복원
5. 부트스트랩 호환성 검증: .env의 APP_VERSION < 확장 g7_version 시 자동 비활성화 (1시간 캐시)
목차
- 업데이트 감지
- 업데이트 실행 흐름 (11단계)
- 백업 및 롤백
- 마이그레이션 vs 업그레이드 스텝
- 업그레이드 스텝 작성
- 자동 발견 및 실행 규칙
- 버전 관리
- CoreUpdateService 주요 메서드
- Artisan 커맨드
- API 엔드포인트
- 설정 (config/app.php)
- 에러 처리 및 로깅
- 유지보수 모드
- 코어 vs 확장 업데이트 비교
1. 업데이트 감지
파일:
app/Extension/CoreVersionChecker.php,app/Services/CoreUpdateService.php
감지 흐름
1. CoreVersionChecker::getCoreVersion() → config('app.version') 읽기
2. CoreUpdateService::checkForUpdates() → GitHub API로 최신 릴리스 조회
3. version_compare(current, latest) → 업데이트 가용 여부 판단
4. 원격 CHANGELOG 캐시 → storage/app/temp/core_remote_changelog.md
GitHub API 통합
- config('app.update.github_url') 에서 리포지토리 URL 읽기
- config('app.update.github_token') 으로 인증 (선택)
- GitHub Releases API 호출 → 최신 릴리스 태그에서 버전 추출
감지 결과
[
'update_available' => true,
'current_version' => '7.0.0-alpha.14',
'latest_version' => '7.0.0-alpha.15',
'check_failed' => false, // GitHub API 실패 시 true
]
2. 업데이트 실행 흐름 (11단계)
파일:
app/Console/Commands/Core/CoreUpdateCommand.php
┌─ Step 1: 업데이트 확인 (GitHub API 또는 --source/--local 모드)
├─ Step 2: _pending 경로 검증 (디렉토리 존재/쓰기 권한)
├─ Step 3: 유지보수 모드 활성화 (--no-maintenance 시 스킵)
├─ Step 4: 다운로드 (GitHub zipball 또는 --source/--local에서 복사)
├─ Step 5: 백업 생성 (--no-backup 시 스킵)
├─ Step 6: Composer install (_pending에서 실행, 변경 없으면 스킵)
├─ Step 7: 파일 적용 (_pending → base_path 선택적 덮어쓰기)
├─ Step 8: vendor 복사 (_pending/vendor → base_path/vendor, Step 6 스킵 시 함께 스킵)
├─ Step 9: 마이그레이션 + 동기화 (migrate, roles, permissions, menus, mail templates)
├─ Step 10: 업그레이드 스텝 실행 (upgrades/Upgrade_X_Y_Z.php)
└─ Step 11: 마무리 (.env 버전 갱신, 캐시 클리어, _pending 삭제, 유지보수 해제)
Step 1: 업데이트 확인
| 모드 | 동작 |
|---|---|
| 일반 | checkForUpdates() → GitHub API 호출 → 사용자 확인 프롬프트 |
--source={path} |
지정된 디렉토리를 소스로 사용 (GitHub 스킵) |
--local |
현재 코드베이스를 소스로 사용 (GitHub 스킵) |
--force |
버전 비교 스킵, 동일 버전이어도 강제 실행 |
Step 4: 다운로드 및 추출
추출 전략 (폴백 체인):
1. ZipArchive (PHP zip 확장) → class_exists(ZipArchive::class)
2. unzip CLI 명령어 → ZipArchive 미사용 시 폴백
v접두사 자동 감지 (resolveGithubArchiveUrl):
1. "v{version}" 태그로 HEAD 요청 시도 (예: v7.0.0-alpha.15)
2. 실패 시 "{version}" 태그로 HEAD 요청 시도 (예: 7.0.0-alpha.15)
3. HTTP 200/302 → 유효한 URL 반환
4. 모두 실패 → null (업데이트 불가)
Step 5: 백업
- CoreBackupHelper::createBackup() 사용
- 백업 대상: config('app.update.targets') + config('app.update.backup_only') + config('app.update.backup_extra')
- 제외 패턴: config('app.update.excludes')
- 백업 위치: storage 경로에 타임스탬프 디렉토리
Step 6: Composer 최적화
1. composer.json + composer.lock의 MD5 비교 (_pending vs base_path)
2. 동일 → "composer 의존성 변경 없음 — 스킵" (Step 6 + Step 8 모두 스킵)
3. 변경됨 → composer install --no-dev --optimize-autoloader --no-interaction --no-scripts
4. --no-scripts: post-autoload-dump 방지 (Step 11에서 package:discover로 처리)
Step 7: 파일 적용
- _pending 소스에서 config('app.update.targets')에 해당하는 파일/디렉토리만 선택적 덮어쓰기
- FilePermissionHelper::copyDirectory() 사용 → 원본 파일 권한 보존
- ExtensionPendingHelper::copyToActive() 미사용 (권한 유실 방지)
Step 9: 마이그레이션 + 동기화
1. php artisan migrate --force
2. syncCoreRolesAndPermissions() — config/core.php의 roles/permissions 동기화
3. syncCoreMenus() — config/core.php의 menus 동기화
4. syncCoreMailTemplates() — config/core.php의 mail_templates 동기화
역할/권한/메뉴 동기화는
user_overrides패턴을 사용하여 사용자 커스터마이징 보존. 상세: extension-update-system.md § 13
Step 10: 업그레이드 스텝
1. upgrades/ 디렉토리 스캔
2. Upgrade_X_Y_Z.php 패턴 매칭 → 버전 변환
3. fromVersion < stepVersion <= toVersion 범위 필터링
4. version_compare 자연 정렬 (오름차순)
5. 각 스텝 순차 실행 (UpgradeContext 전달)
Step 11: 마무리
1. .env의 APP_VERSION 갱신
2. 캐시 클리어: config, cache, route, view
3. bootstrap/cache 파일 삭제 (services.php, packages.php)
4. php artisan package:discover 재실행
5. php artisan extension:update-autoload (코어 업데이트로 _bundled 변경 가능)
6. _pending 디렉토리 삭제
7. 성공 시 백업 삭제
8. 유지보수 모드 해제
3. 백업 및 롤백
파일:
app/Extension/Helpers/CoreBackupHelper.php
백업 생성
- CoreBackupHelper::createBackup() 호출
- 대상: config('app.update.targets') + config('app.update.backup_only') + config('app.update.backup_extra')
- 제외: config('app.update.excludes')
- 저장: storage 경로에 타임스탬프 디렉토리 (Ymd_His)
롤백 흐름 (실패 시)
1. Exception 발생
2. 백업이 존재하면:
├─ CoreUpdateService::restoreFromBackup($backupPath)
├─ CoreBackupHelper::restoreFromBackup() → 파일 원복
└─ _pending 디렉토리 삭제
3. 실패 보고서 생성: storage/logs/core_update_failure_YYYYMMDD_HHMMSS.log
4. 업데이트 로그 저장: storage/logs/core_update_failed_YYYYMMDD_HHMMSS.log
5. 유지보수 모드 유지 (수동으로 php artisan up 필요)
롤백 실패 시
- 복원 실패 자체는 예외를 전파하지 않음 (에러 로그만 기록)
- 관리자에게 안내: "이전 버전으로 사이트를 운영하려면: php artisan up"
- 유지보수 모드 bypass secret 출력
4. 마이그레이션 vs 업그레이드 스텝
주의: 마이그레이션과 업그레이드 스텝의 역할을 혼동하지 않을 것
| 구분 | 마이그레이션 (database/migrations/) |
업그레이드 스텝 (upgrades/) |
|---|---|---|
| 역할 | DB 스키마 변경 (컬럼 추가/삭제/변경, 인덱스, 테이블) | 데이터 백필/변환, 설정 마이그레이션 |
| 실행 시점 | php artisan migrate (설치/업데이트 모두) |
php artisan core:update Step 10 (업데이트 시만) |
| 신규 설치 | 실행됨 | 실행되지 않음 (fromVersion == toVersion) |
| 버전 업그레이드 | 실행됨 | 실행됨 (범위 내 스텝만) |
| 예시 | $table->uuid('uuid')->nullable()->after('id') |
기존 레코드 UUID 백필 + NOT NULL 변환 |
역할 분리 원칙
✅ 마이그레이션: 테이블/컬럼/인덱스 생성·변경·삭제 (스키마)
✅ 업그레이드 스텝: 기존 데이터 변환, 백필, 설정 구조 변경 (데이터)
❌ 마이그레이션에 데이터 백필 로직 포함 금지
❌ 업그레이드 스텝에 스키마 변경 포함 금지 (Schema::table 등)
단, 백필 후 NOT NULL 제약 추가처럼 데이터 완결성에 필수인 경우는 예외
신규 설치 시 데이터 초기화
신규 설치 시 업그레이드 스텝은 실행되지 않으므로, 모델의 boot() 이벤트나 Seeder에서 초기 데이터를 생성해야 합니다.
// 예: User 모델 — 신규 레코드 생성 시 UUID 자동 할당
protected static function boot(): void
{
parent::boot();
static::creating(function (self $user) {
if (empty($user->uuid)) {
$user->uuid = app(UniqueIdServiceInterface::class)->generateUuid();
}
});
}
업그레이드 스텝이 필요한 경우
| 변경 유형 | 업그레이드 스텝 필요 | 예시 |
|---|---|---|
| 기존 데이터 백필/변환 | ✅ | UUID 백필, 형식 변환 |
| NOT NULL 제약 추가 (백필 후) | ✅ (예외) | 스키마이지만 데이터 완결성 의존 |
| 권한/역할/메뉴 추가·수정 | ❌ (자동 동기화) | config/core.php 수정만으로 충분 |
| 정적 권한/메뉴 제거 | ✅ (cleanup 명시 호출) | 기존 메뉴/권한 삭제 |
| PHP 코드만 변경 | ❌ | 버그 수정, 성능 개선 |
5. 업그레이드 스텝 작성
디렉토리 구조
upgrades/ # 프로젝트 루트
├── .gitkeep
├── Upgrade_7_0_0_beta_4.php # 7.0.0-alpha.4
└── Upgrade_7_0_0_beta_15.php # 7.0.0-alpha.15
작성 예시
<?php
namespace App\Upgrades;
use App\Contracts\Extension\UpgradeStepInterface;
use App\Extension\UpgradeContext;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
/**
* 코어 7.0.0-alpha.15 업그레이드 스텝
*
* 기존 사용자 레코드에 UUID v7을 백필합니다.
*/
class Upgrade_7_0_0_beta_15 implements UpgradeStepInterface
{
public function run(UpgradeContext $context): void
{
// 1. 사전 조건 확인
if (! Schema::hasColumn('users', 'uuid')) {
$context->logger->warning('uuid 컬럼 없음. 마이그레이션을 먼저 실행하세요.');
return;
}
// 2. 데이터 백필
$nullCount = DB::table('users')->whereNull('uuid')->count();
if ($nullCount > 0) {
$context->logger->info("UUID 백필 시작: {$nullCount}건");
DB::table('users')->whereNull('uuid')->orderBy('id')->chunk(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['uuid' => Str::orderedUuid()->toString()]);
}
});
$context->logger->info("UUID 백필 완료");
}
// 3. 백필 완료 후 NOT NULL 제약 (데이터 완결성 예외)
// raw SQL에서는 $context->table()로 프리픽스 적용 필수
$column = DB::selectOne("SHOW COLUMNS FROM {$context->table('users')} WHERE Field = 'uuid'");
if ($column && $column->Null === 'YES') {
Schema::table('users', function ($table) {
$table->uuid('uuid')->nullable(false)->unique()->change();
});
}
}
}
작성 체크리스트
□ UpgradeStepInterface 구현
□ 네임스페이스: App\Upgrades
□ 파일명: Upgrade_X_Y_Z.php (버전에 맞는 언더스코어 표기)
□ 사전 조건 확인 (테이블/컬럼 존재 여부)
□ 로거를 통한 진행 상황 기록
□ 멱등성 보장 (여러 번 실행해도 안전)
□ 대량 데이터는 chunk() 사용
□ raw SQL 사용 시 $context->table()로 테이블 프리픽스 적용
6. 자동 발견 및 실행 규칙
파일:
app/Services/CoreUpdateService.php—runUpgradeSteps()
발견 프로세스
1. base_path('upgrades') 디렉토리 스캔
2. .php 확장자 파일만 대상
3. 정규식 매칭: /^Upgrade_(\d+)_(\d+)_(\d+)(?:_([a-zA-Z]\w*(?:_\d+)*))?$/
4. 버전 변환: 1_0_0 → 1.0.0, 7_0_0_beta_4 → 7.0.0-alpha.4
5. require_once 로딩 → App\Upgrades\{filename} 클래스 인스턴스화
6. UpgradeStepInterface 구현 확인
실행 규칙
- 필터 조건:
fromVersion < stepVersion <= toVersion(version_compare사용) - 정렬:
uksort($steps, 'version_compare')— 자연 정렬 오름차순 - 실행: 각 스텝에
UpgradeContext::withCurrentStep($version)불변 복제 전달
버전 네이밍 규칙
파일명의 언더스코어가 버전 구분자로 변환됩니다:
| 파일명 | 변환 버전 | 설명 |
|---|---|---|
Upgrade_1_0_0.php |
1.0.0 |
숫자 3자리 |
Upgrade_7_0_0_beta_4.php |
7.0.0-alpha.4 |
pre-release 포함 |
Upgrade_7_0_0_beta_15.php |
7.0.0-alpha.15 |
pre-release 2자리 |
규칙: 숫자 3자리(X_Y_Z) 후 알파벳으로 시작하는 부분은 -로 연결, 이후 _는 .로 변환
UpgradeStepInterface
파일:
app/Contracts/Extension/UpgradeStepInterface.php
interface UpgradeStepInterface
{
public function run(UpgradeContext $context): void;
}
UpgradeContext
파일:
app/Extension/UpgradeContext.php
// readonly 속성
public readonly LoggerInterface $logger; // upgrade 로그 채널
public readonly string $fromVersion; // 업그레이드 시작 버전
public readonly string $toVersion; // 업그레이드 목표 버전
public readonly string $currentStep; // 현재 실행 중인 스텝 버전
// 메서드
public function table(string $table): string // DB 프리픽스 적용 (raw SQL용)
public function withCurrentStep(string $stepVersion): self // 불변 복제
raw SQL에서 테이블명 사용 시
$context->table()필수
Schema::hasColumn('users', ...),DB::table('users')→ 프리픽스 자동 적용 (불필요)DB::selectOne("SHOW COLUMNS FROM ...")등 raw SQL →$context->table('users')필수
7. 버전 관리
버전 소스
| 파일 | 역할 |
|---|---|
config/app.php — 'version' |
SSoT (env 기본값 정의) |
.env — APP_VERSION |
런타임 오버라이드 |
.env.example |
배포 템플릿 |
버전 읽기
CoreVersionChecker::getCoreVersion() // → config('app.version')
버전 갱신 (Step 11)
CoreUpdateService::updateVersionInEnv($version)
// .env에 APP_VERSION이 존재하면: 정규식으로 라인 교체
// .env에 APP_VERSION이 없으면: "APP_VERSION={version}\n" 추가
버전 변경 시 필수 동기화
필수: 코어 버전 변경 시 3곳 동기화
1. config/app.php — env('APP_VERSION', '새버전')
2. .env.example — APP_VERSION=새버전
3. .env.testing — APP_VERSION=새버전 (테스트 환경)
4. CHANGELOG.md — 변경사항 기록
부트스트랩 호환성 자동 검증
파일:
app/Providers/CoreServiceProvider.php—validateAndDeactivateIncompatibleExtensions(),validateAndDeactivateIncompatibleTemplates()
애플리케이션 부트(boot) 시마다 CoreServiceProvider가 모든 활성 확장의 g7_version 요구사항을 검증합니다.
config('app.version')이 확장의 요구 버전을 충족하지 않으면 해당 확장을 자동으로 비활성화합니다.
검증 흐름
1. CoreServiceProvider::boot() 실행
2. CoreVersionChecker::getCacheKey($type) 캐시 확인 (TTL: 1시간)
3. 캐시 없으면 → 모든 활성 확장 순회
4. 각 확장의 g7_version (예: ">=7.0.0-beta.1") 확인
5. Semver::satisfies(config('app.version'), $constraint) 실행
6. false 반환 시 → Manager::deactivateModule/Plugin/Template() 호출
7. 캐시 저장 (1시간 유효)
주의사항
| 항목 | 설명 |
|---|---|
| 검증 주기 | 캐시 만료(1시간)마다 재검증 |
| 비활성화 대상 | 호환되지 않는 모든 활성 확장 (모듈, 플러그인, 템플릿) |
| activity_log | 기록되지 않음 (Service 레이어를 거치지 않고 Manager 직접 호출) |
| 로그 | storage/logs/laravel.log에 warning 기록 |
버전 확인 방법
php artisan tinker
> \Composer\Semver\Semver::satisfies(config('app.version'), '>=7.0.0-beta.1');
버전 우선순위
config('app.version') = env('APP_VERSION', 'config/app.php 기본값')
.env에 APP_VERSION이 있으면 → .env 값 사용 (config/app.php 기본값 무시)
.env에 APP_VERSION이 없으면 → config/app.php의 기본값 사용
8. CoreUpdateService 주요 메서드
파일:
app/Services/CoreUpdateService.php
업데이트 감지
| 메서드 | 시그니처 | 설명 |
|---|---|---|
checkForUpdates() |
(): array |
GitHub API로 최신 릴리스 조회 |
getChangelog() |
(?string $from, ?string $to): array |
CHANGELOG.md 파싱, 버전 범위 필터 |
checkSystemRequirements() |
(): array |
추출 도구 검증 (ZipArchive/unzip) |
validatePendingPath() |
(): array |
_pending 디렉토리 존재/권한 확인 |
다운로드 및 소스 준비
| 메서드 | 시그니처 | 설명 |
|---|---|---|
downloadUpdate() |
(string $version, ?Closure $onProgress): string |
GitHub 다운로드 + 추출 (폴백 체인) |
copySourceToPending() |
(string $sourceDir, ?Closure $onProgress): string |
--source 모드: 외부 디렉토리 → _pending 복사 |
prepareLocalSource() |
(?Closure $onProgress): string |
--local 모드: 현재 코드 → _pending 복사 |
validatePendingUpdate() |
(string $pendingPath): void |
패키지 구조 검증 (composer.json, app/, config/app.php) |
createPendingDirectory() |
(): string |
_pending/core_Ymd_His/ 디렉토리 생성 |
cleanupPending() |
(string $pendingPath): void |
_pending 하위 디렉토리 삭제 |
백업 및 복원
| 메서드 | 시그니처 | 설명 |
|---|---|---|
createBackup() |
(?Closure $onProgress): string |
CoreBackupHelper로 백업 생성 |
restoreFromBackup() |
(string $backupPath, ?Closure $onProgress): void |
백업에서 파일 복원 |
적용 및 설치
| 메서드 | 시그니처 | 설명 |
|---|---|---|
applyUpdate() |
(string $sourcePath, ?Closure $onProgress): void |
_pending → base_path 선택적 덮어쓰기 |
runComposerInstallInPending() |
(string $pendingPath, ?Closure $onProgress): void |
_pending에서 composer install (--no-scripts) |
isComposerUnchangedForCore() |
(string $pendingPath): bool |
composer.json/lock MD5 비교 |
runComposerInstall() |
(?Closure $onProgress): void |
base_path에서 composer install |
copyVendorFromPending() |
(string $pendingPath, ?Closure $onProgress): void |
_pending/vendor → base_path/vendor |
마이그레이션 및 동기화
| 메서드 | 시그니처 | 설명 |
|---|---|---|
runMigrations() |
(): void |
php artisan migrate --force |
syncCoreRolesAndPermissions() |
(): void |
config/core.php roles/permissions 동기화 |
syncCoreMenus() |
(): void |
config/core.php menus 동기화 |
syncCoreMailTemplates() |
(): void |
config/core.php mail_templates 동기화 (user_overrides 보존) |
업그레이드 및 마무리
| 메서드 | 시그니처 | 설명 |
|---|---|---|
runUpgradeSteps() |
(string $from, string $to, ?Closure $onStep): void |
upgrades/ 자동 발견 + 실행 |
updateVersionInEnv() |
(string $version): void |
.env의 APP_VERSION 갱신 |
clearAllCaches() |
(): void |
config/cache/route/view 클리어 + package:discover |
유지보수 모드
| 메서드 | 시그니처 | 설명 |
|---|---|---|
enableMaintenanceMode() |
(): string |
php artisan down --secret → bypass secret 반환 |
disableMaintenanceMode() |
(): void |
php artisan up |
유틸리티
| 메서드 | 시그니처 | 설명 |
|---|---|---|
generateFailureReport() |
(Throwable $e, string $from, string $to): string |
에러 보고서 생성 → storage/logs/ |
9. Artisan 커맨드
core:update
php artisan core:update [--force] [--no-backup] [--no-maintenance] [--local] [--source={path}]
| 옵션 | 설명 |
|---|---|
--force |
버전 비교 스킵, 동일 버전이어도 강제 업데이트 |
--no-backup |
백업 생성 스킵 (Step 5) |
--no-maintenance |
유지보수 모드 스킵 (Step 3) |
--local |
현재 코드베이스를 소스로 사용 (GitHub 스킵) |
--source={path} |
지정 디렉토리를 소스로 사용 (GitHub 스킵) |
종료 코드:
0(SUCCESS): 업데이트 성공 또는 이미 최신 버전1(FAILURE): 요건 미충족 또는 업데이트 실패
core:check-updates
php artisan core:check-updates
- GitHub API 호출 → 현재/최신 버전 표시
- "새로운 업데이트가 있습니다!" 또는 "현재 최신 버전입니다."
10. API 엔드포인트
파일:
app/Http/Controllers/Api/Admin/CoreUpdateController.php
| 메서드 | 경로 | 설명 |
|---|---|---|
| GET | /api/admin/core-update/check-updates |
코어 업데이트 확인 (422 시 check_failed) |
| POST | /api/admin/core-update/changelog |
코어 변경 로그 조회 (from_version, to_version) |
ActivityLog:
checkForUpdates호출 시core_update.check액티비티 기록
11. 설정 (config/app.php)
버전
'version' => env('APP_VERSION', '7.0.0-alpha.14'),
업데이트 설정
'update' => [
'github_url' => '...', // GitHub 리포지토리 URL
'github_token' => env('GITHUB_TOKEN'), // GitHub Personal Access Token (선택)
'pending_path' => '...', // _pending 디렉토리 경로
'targets' => [...], // 업데이트 적용 + 백업 대상 파일/디렉토리
'backup_only' => ['vendor'], // 백업/복원 전용 (applyUpdate 제외)
'backup_extra' => [...], // 추가 백업 대상
'excludes' => [...], // 제외 패턴
],
12. 에러 처리 및 로깅
시스템 요건 검증
- ZipArchive 또는 unzip 명령어 중 하나 이상 필요
- 미충족 시 커맨드 실패 (EXIT 1) + 사용 가능 방법 목록 출력
_pending 검증
- 디렉토리 미존재: File::ensureDirectoryExists() 시도
- 쓰기 불가: 경로, 소유자, 그룹, 권한 정보 포함 에러 출력
패키지 검증
- 필수 파일: composer.json, app/ 디렉토리, config/app.php
- config/app.php에 'version' 키 필수
- 미충족 시 RuntimeException
Composer 실패
- exit code 비정상 → RuntimeException (마지막 5줄 출력 포함)
로그 위치
| 로그 | 경로 | 내용 |
|---|---|---|
| 성공 로그 | storage/logs/core_update_success_YYYYMMDD_HHMMSS.log |
전체 실행 타임라인 |
| 실패 로그 | storage/logs/core_update_failed_YYYYMMDD_HHMMSS.log |
실패까지의 실행 타임라인 |
| 실패 보고서 | storage/logs/core_update_failure_YYYYMMDD_HHMMSS.log |
예외 상세 + 시스템 정보 |
| 일반 로그 | storage/logs/laravel.log |
Log 파사드 통한 기록 |
13. 유지보수 모드
활성화 (Step 3)
Artisan::call('down', [
'--secret' => $secret, // UUID 토큰 (bypass 접근용)
'--retry' => 60, // Retry-After 헤더 (초)
'--refresh' => 15, // 브라우저 자동 새로고침 (초)
]);
Bypass 접근
URL: https://example.com/{secret}
→ 쿠키 발급 → 유지보수 모드에서도 접근 가능
해제
- 성공 시: Step 11에서 자동 해제 (php artisan up)
- 실패 시: 유지보수 모드 유지 → 관리자에게 수동 해제 안내
"이전 버전으로 사이트를 운영하려면: php artisan up"
14. 코어 vs 확장 업데이트 비교
| 항목 | 코어 | 모듈/플러그인 | 템플릿 |
|---|---|---|---|
| 소스 | GitHub releases (zipball) | GitHub 또는 _bundled | GitHub 또는 _bundled |
| 추출 | 폴백 체인 (ZipArchive → unzip) | ExtensionPendingHelper | ExtensionPendingHelper |
| 백업 | CoreBackupHelper (선택) | ExtensionBackupHelper (선택) | ExtensionBackupHelper (선택) |
| 유지보수 모드 | 전체 앱 down (secret 토큰) | 없음 | 없음 |
| Composer | MD5 비교 → 변경 시만 실행 | 확장별 독립 vendor/ | 해당 없음 |
| 파일 적용 | FilePermissionHelper (권한 보존) | ExtensionPendingHelper::copyToActive() | ExtensionPendingHelper::copyToActive() |
| vendor 처리 | 변경 시 전체 복사 | 확장별 composer install | 해당 없음 |
| 마이그레이션 | 필수 (Step 9) | 선택 (확장별) | 없음 |
| 동기화 | roles + permissions + menus + mail templates | roles + permissions + menus | 없음 |
| 업그레이드 스텝 | upgrades/Upgrade_X_Y_Z.php |
{ext}/upgrades/Upgrade_X_Y_Z.php |
없음 |
| 네임스페이스 | App\Upgrades |
Modules\Vendor\Module\Upgrades |
해당 없음 |
| 실행 주체 | CoreUpdateService |
AbstractModule |
TemplateManager |
| 커맨드 | core:update |
module:update {id} |
template:update {id} |
| 레이아웃 | 해당 없음 | 자동 갱신 | 충돌 전략 (apply_new/keep_current) |
| 롤백 | 전체 복원 + 유지보수 유지 | 확장별 복원 + 상태 복원 | 확장별 복원 |
| 로깅 | 타임스탬프 성공/실패 로그 + 실패 보고서 | laravel.log | laravel.log |
참고 파일 위치
| 파일 | 경로 |
|---|---|
| CoreUpdateCommand | app/Console/Commands/Core/CoreUpdateCommand.php |
| CoreCheckUpdatesCommand | app/Console/Commands/Core/CoreCheckUpdatesCommand.php |
| CoreUpdateService | app/Services/CoreUpdateService.php |
| CoreUpdateController | app/Http/Controllers/Api/Admin/CoreUpdateController.php |
| CoreVersionChecker | app/Extension/CoreVersionChecker.php |
| CoreBackupHelper | app/Extension/Helpers/CoreBackupHelper.php |
| UpgradeStepInterface | app/Contracts/Extension/UpgradeStepInterface.php |
| UpgradeContext | app/Extension/UpgradeContext.php |
| 코어 업그레이드 디렉토리 | upgrades/ |
| 코어 설정 | config/app.php (version, update 섹션) |
| 역할/권한/메뉴 정의 | config/core.php |