코드에만 존재하던 REST API 계약(약 663 엔드포인트)을 코어/확장 책임별로
분리된 마크다운 레퍼런스로 전면 문서화한다. 674개 규모에서 수기 문서는
반드시 drift 하므로 "코드 추출 → 스캐폴딩 → 사람이 서술 채움 → 하네스가
커버리지 강제" 하이브리드로 구성했다.
추출 파이프라인 (app/Support/ApiDoc):
- ApiRouteInventory / FormRequestIntrospector / ApiEndpointProbe(실측 HTTP) /
ResponseSchemaInferrer / ApiDocScaffolder / ColumnCommentResolver /
ResourceFieldDescriber / ParameterDescriber
- api:docgen 커맨드(--scope/--seed/--check/--dry-run/--base-url/--user) +
응답/파라미터 in-place 백필 커맨드 2종(재생성 없이 TODO 셀만 치환, 멱등)
- ApiDocSampleSeeder 계약 + 코어/확장 시더로 실측용 완전 샘플 멱등 생성
문서화 (실측 기반, GET read-only 실호출):
- 코어 291엔드포인트 35파일(docs/backend/api) + 규정 docs/backend/api-documentation.md
- 확장: ecommerce(231)·board(80)·page(17)·hello_module(2)·pay_kginicis(22)·
gdpr(15)·ckeditor5(2)·marketing(2)·verification_kginicis(1)
- 표준 4구성(헤더·요청 파라미터·응답 필드·에러 표) + 엔드포인트 용도 서술
- 파라미터 용도·응답 필드 설명 셀 전수 채움(도메인 지식 수기)
하네스:
- audit 룰 api-doc-coverage — API 표면(라우트/컨트롤러/FormRequest/Resource)
변경 시 대응 문서 미동반이면 차단. 전 대상 문서 완비로 error 승격
- file-rules 리마인더(컨트롤러/라우트 편집 시), coverage.json, dev-dashboard 카드
- docs/backend/routing.md 확장 공개 API URL 스킴 정정(/api/modules|plugins/{id})
- /AGENTS.md/docs-index 동기
87 lines
3.1 KiB
PHP
87 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace Database\Factories;
|
|
|
|
use App\Enums\UserStatus;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* @extends Factory<User>
|
|
*/
|
|
class UserFactory extends Factory
|
|
{
|
|
/**
|
|
* The current password being used by the factory.
|
|
*/
|
|
protected static ?string $password;
|
|
|
|
/**
|
|
* Define the model's default state.
|
|
*
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function definition(): array
|
|
{
|
|
// status: DB default 는 'active' 이지만 factory create 시 in-memory 인스턴스에는
|
|
// default 가 반영되지 않아 `$user->status === null` 상태가 된다. 이 인스턴스를
|
|
// actingAs() 로 세팅하면 CheckUserStatus 미들웨어가 null !== 'active' 로 판정해
|
|
// 403 을 반환하므로, factory 단에서 명시 지정하여 production-like 완전 상태로 생성한다.
|
|
return [
|
|
'uuid' => Str::orderedUuid()->toString(),
|
|
'name' => fake()->name(),
|
|
'email' => fake()->unique()->safeEmail(),
|
|
'email_verified_at' => now(),
|
|
'password' => static::$password ??= Hash::make('password'),
|
|
'remember_token' => Str::random(10),
|
|
'is_super' => false,
|
|
'status' => UserStatus::Active->value,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Indicate that the model's email address should be unverified.
|
|
*/
|
|
public function unverified(): static
|
|
{
|
|
return $this->state(fn (array $attributes) => [
|
|
'email_verified_at' => null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 모든 프로필 필드가 채워진 완전한 상태를 만듭니다.
|
|
*
|
|
* API 문서 실측 시 응답 필드의 예시값이 null 이 되지 않도록, 모델 로직상
|
|
* 유효한 값으로 nullable 프로필 컬럼을 전수 채웁니다.
|
|
* (language=지원 로케일, country=ISO alpha-2, status=UserStatus enum 등)
|
|
*/
|
|
public function complete(): static
|
|
{
|
|
return $this->state(fn (array $attributes) => [
|
|
'nickname' => fake()->userName(),
|
|
'language' => 'ko',
|
|
'timezone' => 'Asia/Seoul',
|
|
'country' => 'KR',
|
|
'homepage' => 'https://example.com',
|
|
'mobile' => '010-'.fake()->numerify('####-####'),
|
|
'phone' => '02-'.fake()->numerify('###-####'),
|
|
'zipcode' => fake()->numerify('#####'),
|
|
'address' => fake()->address(),
|
|
'address_detail' => fake()->numerify('##동 ###호'),
|
|
'signature' => fake()->sentence(),
|
|
'bio' => fake()->paragraph(),
|
|
// avatar 는 users 테이블 컬럼이 아니라 avatarAttachment 관계에서 파생되는
|
|
// accessor(getAvatarUrl) 이므로 factory 에서 직접 세팅하지 않는다.
|
|
'admin_memo' => fake()->sentence(),
|
|
'ip_address' => fake()->ipv4(),
|
|
'last_login_at' => now()->subDays(1),
|
|
'identity_verified_at' => now()->subDays(5),
|
|
'mobile_verified_at' => now()->subDays(5),
|
|
'failed_login_attempts' => 0,
|
|
]);
|
|
}
|
|
}
|