Files
Gnuboard7/docs/backend
HeuJung 85e266d361 fix(core,extensions): 확장 쓰기 경로 공통화 · HTMLPurifier 정의 캐시 storage 이전
https://github.com/gnuboard/g7/issues/125 — 상품 상세설명을 HTML 로 저장할 때
HTMLPurifier 가 모듈 vendor 폴더 안에 정의 캐시를 만들려다 실패해 저장이 매번 500 으로
끝나던 문제를 고친다. vendor 를 읽기 전용으로 두는 표준 배포에서 그 쓰기는 예외가 아니라
PHP 경고로 나오고 Laravel 이 이를 ErrorException 으로 승격시킨다. 캐시는 설정 해시당 1회만
기록되므로 캐시가 영영 생기지 않아 재시도해도 같은 결과였다.

캐시 경로를 storage 아래로 옮기고, 그 경로마저 확보하지 못하면 캐시만 끄고 정화는 그대로
수행한다 — 캐시는 성능 장치이고 정화는 보안 장치라, 전자의 실패가 후자를 건너뛰게 만들면
안 된다. 저장은 성공하므로 운영자에게 도달하는 흔적이 로그 하나뿐이라 error 수준으로 남긴다
(출하 기본 로그 수준이 error 라 warning 은 기본 설치 상태에서 파일에 남지 않는다).

그 과정에서 갈라져 있던 두 축을 코어 한 곳으로 모은다.

- 쓰기 디렉토리 확보: 억제 생성·chmod·setgid·소유권 상속·쓰기 판정 절차가 정적 게시와
 정의 캐시 두 곳에 서로 다른 하드닝으로 복제돼 있었다(억제 mkdir·setgid·clearstatcache 가
 사본마다 한쪽씩 빠져 있었다). FilePermissionHelper 의 ensureWritableDirectory 와
 hardenDirectory 로 통합하고, 실패 사유는 out 파라미터로 올려 정책(조용한 성능 저하 대
 시끄러운 실패)은 호출부가 정하게 둔다.

- 확장 저장 경로: storage_path('app/modules/…') 손조립이 30곳에 흩어져 있어 테스트 격리
 분기를 넣으려면 사본마다 복제해야 했고, 한 곳만 빠뜨려도 그 확장의 테스트가 운영 설정
 파일을 덮어쓴다. 디스크 root 를 단일 출처로 읽는 ExtensionStoragePath 로 전환하고 테스트
 분기는 config/filesystems.php 한 줄에서 끝낸다.

함께 고친 것

- 테스트가 운영 라우트 캐시로 부팅해 확장 allowlist 가 라우트 축에서 통째로 무력화되던
 문제. 삭제가 아니라 경로를 돌린다 — 라우트 캐시는 확장 작업 전까지 재생성되지 않아,
 삭제하면 운영 사이트가 그때까지 라우트 파일 스캔 경로로 떨어진다.
- PHPUnit 프로세스가 확장 vendor 의 제3자 composer 패키지를 오토로드하지 않아 그 패키지를
 쓰는 코드 경로가 통째로 테스트 불가였던 문제. 확장 자신의 오토로더를 그대로 쓰면 활성
 디렉토리가 _bundled 를 이기고 base path 유추까지 깨지므로, 생성된 맵에서 제3자 항목만
 골라 별도 로더로 등록한다.
- 게시 폴더가 setgid 를 갖지 않아, 명령줄과 웹이 번갈아 만든 하위 폴더를 다른 쪽이 쓰지
 못하던 문제.
- 관리자 템플릿이 HTML 정화 라이브러리를 직접 지정하지 않아 전이 의존으로 딸려온 구버전이
 쓰이던 문제.

동반 산출물

- 규정 표(·AGENTS.md) 6행 + storage-driver/service-repository/testing-guide 문서
- audit 룰 2종 + coverage 6항목. 저장소가 이미 전량 전환돼 전수 실행이 공허 통과하므로
 판정식은 픽스처 36건이 잠근다
- INSTALL.md 에 설치 후 파일 권한 절 추가 (vendor 쓰기 권한 불요를 명시)
2026-08-28 15:22:22 +09:00
..
2026-04-01 10:30:52 +09:00
2026-04-01 10:30:52 +09:00
2026-05-11 11:29:41 +09:00
2026-05-11 11:29:41 +09:00
2026-04-01 10:30:52 +09:00
2026-04-20 20:37:49 +09:00
2026-05-11 11:29:41 +09:00

백엔드 개발 가이드

그누보드7 백엔드 개발을 위한 종합 가이드입니다.


핵심 원칙

필수: FormRequest + Custom Rule 사용 (Service에 검증 로직 금지)
필수: FormRequest + Custom Rule 패턴 사용
✅ 필수: __() 함수를 사용한 다국어 처리
✅ 필수: 상태/타입/분류는 Enum으로 정의

API 레퍼런스

엔드포인트별 요청 파라미터·응답 필드·요청/응답 예시는 api/README.md 에 있습니다. 공통 규약(Bearer 토큰 인증, 응답 봉투, 페이지네이션, 401/403/422/428 에러)도 그 문서 상단에 정리되어 있습니다. 확장(모듈·플러그인)이 소유한 API 문서 목록도 같은 문서에서 찾을 수 있습니다.

문서 작성·갱신 규정은 api-documentation.md 를 참고하세요.


문서 목록

문서 제목 핵심 내용
activity-log-hooks.md 활동 로그 훅 레퍼런스 (Activity Log Hooks Reference) 코어 66훅 + 이커머스 92훅 + 게시판 32훅 + 페이지 8훅 = 총 198훅
activity-log.md 활동 로그 시스템 (Activity Log System) Monolog 기반: Service 훅 → Listener → Log::channel...
admin-settings-access.md Admin 환경설정 값 접근 (g7_core_settings vs config()) 동기화 SSoT: storage/app/settings/*.json → Setting...
api-documentation.md API 레퍼런스 문서 규정 (API Documentation) 모든 API 엔드포인트는 레퍼런스 문서 필수 — 메서드/URI/파라미터/응답 필드 +...
api-resources.md API 리소스 Resource: BaseApiResource 상속 필수 / Collection: B...
authentication.md 인증 및 세션 처리 Laravel Sanctum 토큰 전용 인증 (Bearer 토큰만 사용)
benchmark.md 성능 계측 시스템 (Benchmark) g7:bench 가 4축(list/screen/write/batch)을 잰다 — ...
broadcasting.md Broadcasting (실시간 이벤트) Laravel Reverb 사용 (WebSocket)
console-confirm.md 콘솔 yes/no 프롬프트 (ConsoleConfirm) 콘솔 커맨드의 yes/no 프롬프트는 $this->unifiedConfirm() 사용...
controllers.md 컨트롤러 계층 구조 AdminBaseController / AuthBaseController / Publ...
core-config.md 코어 설정 (config/core.php) config/core.php = 코어 권한/역할/메뉴/메일템플릿의 SSoT (Sing...
core-update-system.md 코어 업데이트 시스템 (Core Update System) 코어 업그레이드 스텝: upgrades/ 디렉토리 (프로젝트 루트), 네임스페이스 A...
data-sync-helpers.md 데이터 동기화 Helper (Data Sync Helpers) 모든 데이터 동기화는 Service/Seeder 가 Helper 를 호출해 수행 (직...
dto.md DTO (Data Transfer Object) 사용 규칙 DTO 두 패턴 — Value Object(불변 1회 전달) vs Data Carri...
enum.md Enum 사용 규칙 상태/타입/분류 = Enum 필수 (PHP 8.1+ Backed Enum)
exceptions.md Custom Exception 다국어 처리 예외 메시지 하드코딩 금지 → __() 함수 필수
geoip.md GeoIP 시스템 (MaxMind GeoLite2) MaxMind GeoLite2-City DB 기반 IP → 타임존 감지 (SetTim...
identity-messages.md 본인인증 메시지 템플릿 시스템 (Identity Messages) 알림 시스템(notification_*)과 완전 분리된 IDV 전용 템플릿 인프라
identity-policies.md 본인인증 정책 시스템 (Identity Policies) -
identity-providers.md IDV Provider 작성 가이드 (Identity Verification Providers) VerificationProviderInterface 구현 + IdentityProv...
language-pack-service.md LanguagePackService (백엔드 Service 레이어) LanguagePackService 가 install/activate/deactiva...
middleware.md 미들웨어 등록 규칙 인증 필요 미들웨어 → 전역 등록 금지!
notification-system.md 알림 시스템 (Notification System) GenericNotification 범용 클래스 1개로 모든 알림 처리 (개별 클래스...
pagination.md 대용량 목록 페이지네이션 (Pagination) 총 건수만 상한을 받는다 — 상한 이하면 정확, 초과면 "이상"(total_relat...
response-helper.md API 응답 규칙 (ResponseHelper) 모든 API 응답은 ResponseHelper 사용
routing.md 라우트 네이밍 및 경로 모든 라우트는 name() 필수: ->name('api.users.index')
search-system.md Scout 검색 엔진 시스템 (Search System) Laravel Scout + DatabaseFulltextEngine: MySQL F...
seo-system.md SEO 페이지 생성기 시스템 (SEO Page Generator) SeoMiddleware: 봇 요청 감지 → ?locale= 파라미터 해석 → Seo...
service-provider.md 서비스 프로바이더 안전성 DB 접근 전 .env 파일 존재 확인 필수
service-repository.md Service-Repository 패턴 RepositoryInterface 주입 필수 (구체 클래스 직접 주입 금지)
settings-multilingual-enrichment.md Settings 카탈로그 다국어 자동 보강 settings JSON 의 다국어 카탈로그 라벨(_cached_name 등)은 카탈...
static-asset-publishing.md 부트스트랩 리소스 정적 게시 (Static Asset Publishing) 게시물: public/build/ext/{cache_version}/ — 수명주기 이...
translatable-seeders.md 다국어 시더 인터페이스 (Translatable Seeders) 다국어 JSON 컬럼(name 등)을 시드하는 확장 entity 시더는 Transla...
user-overrides.md 사용자 수정 보존 (HasUserOverrides Trait) 모델에 use HasUserOverrides; + `protected array ...
validation.md 검증 (Validation) 필수: FormRequest에서 검증 (Service에 검증 로직 배치 금지)

아키텍처 개요

계층 분리

Controller → Request → Service → Repository → Model

컨트롤러 계층 구조

BaseApiController (최상위)
├── AdminBaseController (관리자 전용)
├── AuthBaseController (인증된 사용자)
└── PublicBaseController (공개 API)

Service-Repository 패턴

// Service에서 훅 실행
HookManager::doAction('module.entity.before_create', $data);
$data = HookManager::applyFilters('module.entity.filter_data', $data);
$result = $this->repository->create($data);
HookManager::doAction('module.entity.after_create', $result);

파사드 사용 규칙

// ✅ DO: 파사드 앞 역슬래시 제거
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;

Log::info('메시지');
Auth::user();

// ❌ DON'T: 역슬래시 사용
\Log::info('메시지');
auth()->user();

핵심 규칙 요약

1. 검증 로직 위치

  • ❌ Service 클래스에서 검증 금지
  • ✅ FormRequest에서 기본 검증
  • ✅ Custom Rule에서 복잡한 검증

2. 다국어 처리

  • ❌ 하드코딩된 메시지 금지
  • ✅ __() 함수 사용 필수
  • ✅ lang/ko/, lang/en/ 파일 관리

3. Enum 사용

  • ❌ 문자열/숫자 상수 직접 사용 금지
  • ✅ Backed Enum 정의
  • ✅ 타입 힌트 활용

4. 미들웨어 등록

  • ❌ 인증이 필요한 미들웨어를 글로벌 등록 금지
  • ✅ 그룹별 적절한 위치에 등록
  • ✅ 실행 순서 고려

5. 서비스 프로바이더 안전성

  • ❌ .env 없이 실패하는 코드 금지
  • ✅ 환경 검증 후 로직 실행
  • ✅ 인스톨러 안정성 확보

6. 외부 HTTP 호출

  • ❌ file_get_contents($url) / fopen($url, ...) / stream_context_create([...]) 로 원격 URL 직접 호출 금지
  • ✅ Illuminate\Support\Facades\Http (Laravel Http 파사드) 사용
  • ✅ GitHub 연동은 App\Extension\Helpers\GithubHelper 재사용
  • 이유: 공유 호스팅은 allow_url_fopen=Off 설정인 경우가 많아 URL 스트림 래퍼 기반 호출이 전부 실패합니다. Http 파사드는 cURL 기반이라 해당 설정의 영향을 받지 않습니다.

빠른 참조

자주 사용하는 클래스

클래스 위치 용도
ResponseHelper app/Helpers/ResponseHelper.php API 응답 표준화
BaseApiResource app/Http/Resources/BaseApiResource.php API 리소스 기본 클래스
AdminBaseController app/Http/Controllers/Api/Admin/AdminBaseController.php 관리자 컨트롤러

훅 네이밍 규칙

[vendor-module].[entity].[action]_[timing]

예시:
sirsoft-ecommerce.product.before_create
sirsoft-ecommerce.product.after_update
sirsoft-ecommerce.product.filter_create_data

관련 문서