diff --git a/app/Config/Routes.php b/app/Config/Routes.php index b0b376f..baf8ed7 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -17,15 +17,18 @@ $routes->addPlaceholder('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4} $routes->group('cli', ['namespace' => 'App\Controllers\CLI'], function ($routes) { }); -$routes->group('', ['namespace' => 'App\Controllers'], function ($routes) { - $routes->get('/', 'Welcome::index'); - $routes->group('auth', ['namespace' => 'App\Controllers\Auth'], function ($routes) { - $routes->get('login', 'LocalController::login_form'); - $routes->post('login', 'LocalController::login'); - $routes->get('google_login', 'GoogleController::login'); - $routes->get('logout', 'LocalController::logout'); +$routes->group('', ['namespace' => 'App\Controllers\Front'], function ($routes) { + $routes->get('/', 'WelcomeController::index'); + $routes->group('inquiry', function ($routes) { + $routes->post('create', 'InquiryController::create'); }); }); +$routes->group('auth', ['namespace' => 'App\Controllers\Auth'], function ($routes) { + $routes->get('login', 'LocalController::login_form'); + $routes->post('login', 'LocalController::login'); + $routes->get('google_login', 'GoogleController::login'); + $routes->get('logout', 'LocalController::logout'); +}); //Admin ๊ด€๋ จ $routes->group('admin', ['namespace' => 'App\Controllers\Admin', 'filter' => 'authFilter:manager'], function ($routes) { $routes->get('/', 'Welcome::index'); @@ -59,5 +62,18 @@ $routes->group('admin', ['namespace' => 'App\Controllers\Admin', 'filter' => 'au $routes->get('download/(:alpha)', 'BoardController::download/$1'); $routes->get('latest/(:alpha)', 'BoardController::latest/$1'); }); + $routes->group('inquiry', function ($routes) { + $routes->get('/', 'InquiryController::index'); + $routes->get('create', 'InquiryController::create_form'); + $routes->post('create', 'InquiryController::create'); + $routes->get('modify/(:num)', 'InquiryController::modify_form/$1'); + $routes->post('modify/(:num)', 'InquiryController::modify/$1'); + $routes->get('view/(:num)', 'InquiryController::view/$1'); + $routes->get('delete/(:num)', 'InquiryController::delete/$1'); + $routes->get('toggle/(:num)/(:any)', 'InquiryController::toggle/$1/$2'); + $routes->post('batchjob', 'InquiryController::batchjob'); + $routes->post('batchjob_delete', 'InquiryController::batchjob_delete'); + $routes->get('download/(:alpha)', 'InquiryController::download/$1'); + }); }); //choi.jh \ No newline at end of file diff --git a/app/Config/Services.php b/app/Config/Services.php index be04340..76d1190 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -7,8 +7,9 @@ use CodeIgniter\Config\BaseService; //choi.jh use App\Services\Auth\GoogleService; use App\Services\Auth\LocalService; -use App\Services\BoardService; use App\Services\UserService; +use App\Services\BoardService; +use App\Services\InquiryService; //choi.jh /** @@ -88,5 +89,14 @@ class Services extends BaseService new \App\Models\BoardModel(), ); } + public static function inquiryservice($getShared = true): InquiryService + { + if ($getShared) { + return static::getSharedInstance(__FUNCTION__); + } + return new InquiryService( + new \App\Models\InquiryModel(), + ); + } //choi.jh } diff --git a/app/Controllers/AbstractCRUDController.php b/app/Controllers/AbstractCRUDController.php index 0e74611..59da195 100644 --- a/app/Controllers/AbstractCRUDController.php +++ b/app/Controllers/AbstractCRUDController.php @@ -2,9 +2,11 @@ namespace App\Controllers; +use RuntimeException; use App\Entities\CommonEntity; use CodeIgniter\HTTP\RedirectResponse; -use RuntimeException; +use CodeIgniter\HTTP\ResponseInterface; +use App\Exceptions\FormValidationException; /** * AbstractCRUDController @@ -12,8 +14,6 @@ use RuntimeException; */ abstract class AbstractCRUDController extends AbstractWebController { - // ๐Ÿ’ก ํ•ต์‹ฌ 1: ๊ฐ ์ž์‹ ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉํ•  Entity ํด๋ž˜์Šค ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ฐ•์ œ - // ์ด ๋ฉ”์„œ๋“œ๋Š” ์ž์‹ ํด๋ž˜์Šค์—์„œ ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. // --- ์ƒ์„ฑ (Create) --- protected function create_form_process(array $formDatas = []): array { @@ -39,20 +39,24 @@ abstract class AbstractCRUDController extends AbstractWebController return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ƒ์„ฑํผ ์˜ค๋ฅ˜:" . $e->getMessage()); } } + protected function create_process(array $formDatas): CommonEntity { // POST ๋ฐ์ดํ„ฐ๋ฅผ DTO ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ $dto = $this->service->createDTO($formDatas); - // dd($dto->toArray()); + //DTO ํƒ€์ž… ์ฒดํฌ ๋กœ์ง์„ ์ผ๋ฐ˜ํ™” $dtoClass = $this->service->getDTOClass(); if (!$dto instanceof $dtoClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ: " . get_class($dto) . "๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ({$dtoClass} ํ•„์š”)"); } + + // ๐Ÿ’ก ์—ฌ๊ธฐ์„œ service->create() ๋‚ด๋ถ€์—์„œ CommonForm::validate()๊ฐ€ ์‹คํ–‰๋˜๊ณ  + // ์‹คํŒจ ์‹œ FormValidationException์ด throw ๋œ๋‹ค๊ณ  ๊ฐ€์ •(๊ถŒ์žฅ) return $this->service->create($dto->toArray()); } - protected function create_result_process($entity, ?string $redirect_url = null): string|RedirectResponse + protected function create_result_process(CommonEntity $entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process( 'info', @@ -61,19 +65,61 @@ abstract class AbstractCRUDController extends AbstractWebController ); } - final public function create(): string|RedirectResponse + final public function create(): string|RedirectResponse|ResponseInterface { try { $action = __FUNCTION__; $this->action_init_process($action); + $entity = $this->create_process($this->request->getPost()); + // ๐Ÿ’ก ๋™์ ์œผ๋กœ ๊ฐ€์ ธ์˜จ Entity ํด๋ž˜์Šค ์ด๋ฆ„์œผ๋กœ instanceof ๊ฒ€์‚ฌ $entityClass = $this->service->getEntityClass(); if (!$entity instanceof $entityClass) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ:Return Type์€ {$entityClass}๋งŒ ๊ฐ€๋Šฅ"); } + + // โœ… AJAX ์š”์ฒญ์ด๋ฉด JSON ์„ฑ๊ณต ์‘๋‹ต + if ($this->request->isAJAX()) { + return $this->response->setJSON([ + 'ok' => true, + 'message' => "{$this->getTitle()}์—์„œ {$entity->getTitle()} ์ƒ์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + 'id' => $entity->getPK(), + ]); + } + return $this->create_result_process($entity); + + } catch (FormValidationException $e) { + + // โœ… AJAX ์š”์ฒญ์ด๋ฉด 422 + ํ•„๋“œ๋ณ„ ์˜ค๋ฅ˜ + if ($this->request->isAJAX()) { + return $this->response + ->setStatusCode(422) + ->setJSON([ + 'ok' => false, + 'errors' => $e->errors, + ]); + } + + // ๊ธฐ์กด redirect ๋ฐฉ์‹ ์œ ์ง€ + return $this->action_redirect_process( + 'error', + static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ƒ์„ฑ ์˜ค๋ฅ˜:\n" . implode("\n", $e->errors) + ); + } catch (\Throwable $e) { + log_message('error', 'EXCEPTION_CLASS=' . get_class($e)); + // โœ… AJAX๋ฉด 500 JSON + if ($this->request->isAJAX()) { + return $this->response + ->setStatusCode(500) + ->setJSON([ + 'ok' => false, + 'message' => static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜:" . $e->getMessage(), + ]); + } + return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ƒ์„ฑ ์˜ค๋ฅ˜:" . $e->getMessage()); } } @@ -98,7 +144,6 @@ abstract class AbstractCRUDController extends AbstractWebController $entity = $this->modify_form_process($uid); $this->addViewDatas('entity', $entity); $action = __FUNCTION__; - //FormService์—์„œ ํ•„์š”ํ•œ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ $entity์—์„œ ์ถ”์ถœํ•ด์„œ ๋„˜๊น€ $this->action_init_process($action, $entity->toArray()); return $this->modify_form_result_process($action); } catch (\Throwable $e) { @@ -108,7 +153,6 @@ abstract class AbstractCRUDController extends AbstractWebController protected function modify_process($uid, array $formDatas): CommonEntity { - // POST ๋ฐ์ดํ„ฐ๋ฅผ DTO ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ $formDatas[$this->service->getPKField()] = $uid; $dto = $this->service->createDTO($formDatas); //DTO ํƒ€์ž… ์ฒดํฌ ๋กœ์ง์„ ์ผ๋ฐ˜ํ™” @@ -119,7 +163,76 @@ abstract class AbstractCRUDController extends AbstractWebController return $this->service->modify($uid, $dto->toArray()); } - protected function modify_result_process($entity, ?string $redirect_url = null): string|RedirectResponse + final public function modify($uid): string|RedirectResponse|ResponseInterface + { + try { + if (!$uid) { + throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()}์— ๋ฒˆํ˜ธ๊ฐ€ ์ •์˜ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); + } + + $action = __FUNCTION__; + $this->action_init_process($action); + + $entity = $this->modify_process($uid, $this->request->getPost()); + + // ๐Ÿ’ก ๋™์ ์œผ๋กœ ๊ฐ€์ ธ์˜จ Entity ํด๋ž˜์Šค ์ด๋ฆ„์œผ๋กœ instanceof ๊ฒ€์‚ฌ + $entityClass = $this->service->getEntityClass(); + if (!$entity instanceof $entityClass) { + throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ:Return Type์€ {$entityClass}๋งŒ ๊ฐ€๋Šฅ"); + } + + $this->addViewDatas('entity', $entity); + + // โœ… AJAX ์š”์ฒญ์ด๋ฉด JSON ์„ฑ๊ณต ์‘๋‹ต + if ($this->request->isAJAX()) { + return $this->response->setJSON([ + 'ok' => true, + 'message' => "{$this->getTitle()}์—์„œ {$entity->getTitle()} ์ˆ˜์ •์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + 'id' => $entity->getPK(), + ]); + } + + return $this->modify_result_process($entity); + + } catch (FormValidationException $e) { + + // โœ… AJAX ์š”์ฒญ์ด๋ฉด 422 + ํ•„๋“œ๋ณ„ ์˜ค๋ฅ˜ + if ($this->request->isAJAX()) { + return $this->response + ->setStatusCode(422) + ->setJSON([ + 'ok' => false, + 'errors' => $e->errors, + ]); + } + + // ๊ธฐ์กด redirect ๋ฐฉ์‹ ์œ ์ง€ + return $this->action_redirect_process( + 'error', + static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ˆ˜์ • ์˜ค๋ฅ˜:\n" . implode("\n", $e->errors) + ); + + } catch (\Throwable $e) { + log_message('error', 'EXCEPTION_CLASS=' . get_class($e)); + // โœ… AJAX๋ฉด 500 JSON + if ($this->request->isAJAX()) { + return $this->response + ->setStatusCode(500) + ->setJSON([ + 'ok' => false, + 'message' => static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜:" . $e->getMessage(), + ]); + } + + return $this->action_redirect_process( + 'error', + static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ˆ˜์ • ์˜ค๋ฅ˜:" . $e->getMessage() + ); + } + } + + + protected function modify_result_process(CommonEntity $entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process( 'info', @@ -127,31 +240,18 @@ abstract class AbstractCRUDController extends AbstractWebController $redirect_url ?? '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK() ); } - final public function modify($uid): string|RedirectResponse - { - try { - if (!$uid) { - throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()}์— ๋ฒˆํ˜ธ๊ฐ€ ์ •์˜ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - } - $action = __FUNCTION__; - $this->action_init_process($action); - $entity = $this->modify_process($uid, $this->request->getPost()); - $this->addViewDatas('entity', $entity); - return $this->modify_result_process($entity); - } catch (\Throwable $e) { - return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()} ์ˆ˜์ • ์˜ค๋ฅ˜:" . $e->getMessage()); - } - } // --- ์‚ญ์ œ (Delete) --- protected function delete_process($uid): CommonEntity { return $this->service->delete($uid); } + protected function delete_result_process($entity, ?string $redirect_url = null): string|RedirectResponse { return $this->action_redirect_process('info', "{$this->getTitle()}์—์„œ {$entity->getTitle()} ์‚ญ์ œ๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", $redirect_url); } + final public function delete($uid): RedirectResponse { try { @@ -159,7 +259,6 @@ abstract class AbstractCRUDController extends AbstractWebController throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()}์— ๋ฒˆํ˜ธ๊ฐ€ ์ •์˜ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); } $entity = $this->service->getEntity($uid); - //Delete์ฒ˜๋ฆฌ $entity = $this->delete_process($uid); return $this->delete_result_process($entity); } catch (\Throwable $e) { @@ -172,20 +271,20 @@ abstract class AbstractCRUDController extends AbstractWebController { return $this->service->getEntity($uid); } + protected function view_result_process(string $action): string { return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate')); } + final public function view($uid): string|RedirectResponse { try { if (!$uid) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ {$this->getTitle()}์— ๋ฒˆํ˜ธ๊ฐ€ ์ •์˜ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); } - //View์ฒ˜๋ฆฌ $entity = $this->view_process($uid); $action = __FUNCTION__; - //FormService์—์„œ ํ•„์š”ํ•œ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ $entity์—์„œ ์ถ”์ถœํ•ด์„œ ๋„˜๊น€ $this->action_init_process($action, $entity->toArray()); $this->addViewDatas('entity', $entity); return $this->view_result_process($action); diff --git a/app/Controllers/Admin/InquiryController.php b/app/Controllers/Admin/InquiryController.php new file mode 100644 index 0000000..924d5da --- /dev/null +++ b/app/Controllers/Admin/InquiryController.php @@ -0,0 +1,22 @@ +service === null) { + $this->service = service('inquiryservice'); + } + $this->addActionPaths('Inquiry'); + } + //Action์ž‘์—…๊ด€๋ จ + //๊ธฐ๋ณธ ํ•จ์ˆ˜ ์ž‘์—… + //Custom ์ถ”๊ฐ€ ํ•จ์ˆ˜ +} diff --git a/app/Controllers/FrontController.php b/app/Controllers/Front/FrontController.php similarity index 96% rename from app/Controllers/FrontController.php rename to app/Controllers/Front/FrontController.php index aeaa223..721d18d 100644 --- a/app/Controllers/FrontController.php +++ b/app/Controllers/Front/FrontController.php @@ -1,6 +1,6 @@ addActionPaths($this->_layout); $this->layouts = LAYOUTS[$this->_layout]; + helper('util'); } protected function action_init_process(string $action, array $formDatas = []): void { diff --git a/app/Controllers/Front/InquiryController.php b/app/Controllers/Front/InquiryController.php new file mode 100644 index 0000000..6039fed --- /dev/null +++ b/app/Controllers/Front/InquiryController.php @@ -0,0 +1,31 @@ +service === null) { + $this->service = service('inquiryservice'); + } + $this->addActionPaths('Inquiry'); + } + //Action์ž‘์—…๊ด€๋ จ + //๊ธฐ๋ณธ ํ•จ์ˆ˜ ์ž‘์—… + //Custom ์ถ”๊ฐ€ ํ•จ์ˆ˜ + + protected function create_result_process($entity, ?string $redirect_url = null): string|RedirectResponse + { + return $this->action_redirect_process( + 'info', + "{$this->getTitle()}์—์„œ {$entity->getTitle()} ๋ฌธ์˜ ๋“ฑ๋ก์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", + ); + } +} diff --git a/app/Controllers/Welcome.php b/app/Controllers/Front/WelcomeController.php similarity index 94% rename from app/Controllers/Welcome.php rename to app/Controllers/Front/WelcomeController.php index f2214a7..04f096e 100644 --- a/app/Controllers/Welcome.php +++ b/app/Controllers/Front/WelcomeController.php @@ -1,12 +1,12 @@ '', + 'email' => '', + 'status' => '', + 'content' => '' + ]; + public function __construct(array|null $data = null) + { + parent::__construct($data); + } +} diff --git a/app/Exceptions/FormValidationException.php b/app/Exceptions/FormValidationException.php new file mode 100644 index 0000000..6f7c205 --- /dev/null +++ b/app/Exceptions/FormValidationException.php @@ -0,0 +1,16 @@ +errors = $errors; + parent::__construct($message, $code, $previous); + } +} diff --git a/app/Forms/CommonForm.php b/app/Forms/CommonForm.php index aff5df4..1f09d15 100644 --- a/app/Forms/CommonForm.php +++ b/app/Forms/CommonForm.php @@ -2,6 +2,7 @@ namespace App\Forms; +use App\Exceptions\FormValidationException; use RuntimeException; /** @@ -13,11 +14,14 @@ use RuntimeException; * 3) validate()์—์„œ dynamicRules ๋ˆ„์  ๋ฒ„๊ทธ ์ˆ˜์ • (๋งˆ์ง€๋ง‰ ๊ทœ์น™๋งŒ ๋‚จ๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ) * 4) "ํ•„๋“œ ์กด์žฌ ๋ณด์žฅ"์œผ๋กœ ์ž„์˜ '' ์‚ฝ์ž… ์ œ๊ฑฐ (๋ฏธ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ FK/์ˆซ์ž ๊ทœ์น™์„ ๊นจ๋Š” ๋ฌธ์ œ ๋ฐฉ์ง€) * 5) role.* ๊ฐ™์€ ๋ฐฐ์—ด ์›์†Œ ๊ทœ์น™์„ ์œ„ํ•ด ๋ถ€๋ชจ ๋ฐฐ์—ด ๋ณด์ • ๋กœ์ง ์œ ์ง€/๊ฐ•ํ™” + * + * โœ… ์ถ”๊ฐ€: + * - validate() ์‹คํŒจ ์‹œ RuntimeException(implode) ๋Œ€์‹  + * FormValidationException(errors ๋ฐฐ์—ด)์„ throwํ•˜์—ฌ + * Controller์—์„œ AJAX(422 JSON errors) ์‘๋‹ต์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ */ abstract class CommonForm { - private $_validation = null; - private array $_attributes = []; private array $_formFields = []; private array $_formRules = []; @@ -27,10 +31,11 @@ abstract class CommonForm private array $_formOptions = []; private array $_actionButtons = ['view' => ICONS['SEARCH'], 'delete' => ICONS['DELETE']]; private array $_batchjobButtons = ['batchjob' => '์ผ๊ด„์ฒ˜๋ฆฌ', 'batchjob_delete' => '์ผ๊ด„์‚ญ์ œ']; + protected $validation = null; protected function __construct() { - $this->_validation = service('validation'); + $this->validation = service('validation'); } public function action_init_process(string $action, array &$formDatas = []): void @@ -160,7 +165,6 @@ abstract class CommonForm /** * 1) ๊นŠ์€ ๋ฐฐ์—ด ๊ตฌ์กฐ ์ •๋ฆฌ(๋ฐฐ์—ด์€ ์œ ์ง€) * - ์—ฌ๊ธฐ์„œ๋Š” null -> '' ๊ฐ™์€ ๋ณ€ํ™˜์„ ์ ˆ๋Œ€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - * - ์ด์œ : FK/์ˆซ์ž/๋‚ ์งœ ํ•„๋“œ๊ฐ€ ''๋กœ ๋ณ€ํ•˜๋ฉด validation/DB์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•จ. */ protected function sanitizeFormDatas($data, string $path = '') { @@ -179,11 +183,8 @@ abstract class CommonForm /** * 2) ์ˆซ์ž/FK ํ•„๋“œ ์ •๊ทœํ™” - * - ํผ์—์„œ ๋ฏธ์„ ํƒ์€ ๋ณดํ†ต ''๋กœ ๋“ค์–ด์˜ด -> NULL๋กœ ๋ณ€ํ™˜ - * - ์ˆซ์ž ๋ฌธ์ž์—ด์€ int ์บ์ŠคํŒ… (์„ ํƒ) - * - * ์ฃผ์˜: - * - "๋นˆ๊ฐ’์„ 0์œผ๋กœ ์ทจ๊ธ‰" ๊ฐ™์€ ์ •์ฑ…์ด ์žˆ๋‹ค๋ฉด ์—ฌ๊ธฐ์—์„œ ์กฐ์ •ํ•ด์•ผ ํ•จ. + * - '' -> null + * - ์ˆซ์ž ๋ฌธ์ž์—ด -> int */ protected function normalizeNumericEmptyToNull(array $data, array $numericFields): array { @@ -245,7 +246,7 @@ abstract class CommonForm $formDatas[$parent] = []; } - // โœ… 4) ํ•ต์‹ฌ: ๋ฐฐ์—ด ์›์†Œ์˜ null/'' ์ œ๊ฑฐ + ๋ฌธ์ž์—ดํ™”(Trim์ด null ๋ฐ›์ง€ ์•Š๋„๋ก) + // 4) ๋ฐฐ์—ด ์›์†Œ ์ •๋ฆฌ $clean = array_map( fn($v) => is_scalar($v) ? trim((string) $v) : '', $formDatas[$parent] @@ -256,14 +257,8 @@ abstract class CommonForm } } - /** - * 4) ๊ฒ€์ฆ rule์— ๋”ฐ๋ผ "numeric(ํŠนํžˆ FK)"๋กœ ์ทจ๊ธ‰ํ•  ํ•„๋“œ๋ฅผ ์ˆ˜์ง‘ - * - getFormRule()์—์„œ permit_empty|numeric ๋กœ ์ •์˜๋˜๋Š” ํ•„๋“œ๋ฅผ ๊ณตํ†ต ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ - * - * ๊ตฌํ˜„ ์ „๋žต: - * - formRules์—์„œ rule ๋ฌธ์ž์—ด์— 'numeric'๊ฐ€ ํฌํ•จ๋œ ํ•„๋“œ๋ฅผ ๋ชจ์Œ - * - wildcard(role.*) ์ œ์™ธ + * 4) ๊ฒ€์ฆ rule์— ๋”ฐ๋ผ numeric(FK ํฌํ•จ) ํ•„๋“œ ์ˆ˜์ง‘ */ protected function collectNumericFieldsFromRules(array $formRules): array { @@ -276,7 +271,7 @@ abstract class CommonForm continue; } - // getValidationRule hook ์ ์šฉ (ํ•„๋“œ๋ช…/๋ฃฐ์ด ๋ฐ”๋€” ์ˆ˜ ์žˆ์œผ๋‹ˆ) + // hook ์ ์šฉ [$fieldName, $ruleStr] = $this->getValidationRule($fieldName, (string) $rule); if (is_string($ruleStr) && str_contains($ruleStr, 'numeric')) { @@ -284,7 +279,6 @@ abstract class CommonForm } } - // ์ค‘๋ณต ์ œ๊ฑฐ return array_values(array_unique($numericFields)); } @@ -294,20 +288,15 @@ abstract class CommonForm /** * ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  ์œ ํšจํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค. - * 2025 CI4 ํ‘œ์ค€: ๊ทœ์น™ ๋ฐฐ์—ด ๋‚ด์— label์„ ํฌํ•จํ•˜์—ฌ ํ•œ๊ธ€ ๋ฉ”์‹œ์ง€ ์ถœ๋ ฅ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. + * โœ… ๋ณ€๊ฒฝ์ : + * - ์‹คํŒจ ์‹œ FormValidationException(errors ๋ฐฐ์—ด)์„ throw + * (AJAX์—์„œ 422๋กœ ๋‚ด๋ ค๋ณด๋‚ด๊ธฐ ์œ„ํ•จ) */ final public function validate(array &$formDatas): void { log_message('debug', '>>> CommonForm::validate CALLED: ' . static::class); - if ($this->_validation === null) { - throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ: Validation ์„œ๋น„์Šค๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."); - } - try { - // 0) ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ •๋ฆฌ (null ๋ณ€ํ™˜ X) $formDatas = $this->sanitizeFormDatas($formDatas); - - // 1) ํ•„๋“œ ๋ผ๋ฒจ/๊ทœ์น™ $formFields = $this->getFormFields(); $formRules = $this->getFormRules(); @@ -315,61 +304,42 @@ abstract class CommonForm throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ: ์ง€์ •๋œ Form RULE์ด ์—†์Šต๋‹ˆ๋‹ค."); } - // 2) wildcard(role.*) ๋ถ€๋ชจ ๋ฐฐ์—ด ๋ณด์ • $this->ensureParentArrayForWildcardRules($formDatas, $formRules); - // 3) numeric(FK ํฌํ•จ) ํ•„๋“œ: '' -> null, ์ˆซ์ž ๋ฌธ์ž์—ด -> int - // (๊ทœ์น™ ๊ธฐ๋ฐ˜ ์ž๋™ ์ˆ˜์ง‘) $numericFields = $this->collectNumericFieldsFromRules($formRules); $formDatas = $this->normalizeNumericEmptyToNull($formDatas, $numericFields); - // 4) dynamicRules ๋ˆ„์  ๊ตฌ์„ฑ (๋ฒ„๊ทธ ์ˆ˜์ •: ๋ฃจํ”„๋งˆ๋‹ค ์ดˆ๊ธฐํ™” ๊ธˆ์ง€) $dynamicRules = []; foreach ($formRules as $field => $rule) { - try { - // ํ•„๋“œ๋ช…/๊ทœ์น™ ์ถ”์ถœ(ํ™•์žฅ ํฌ์ธํŠธ) - [$fieldName, $ruleStr] = $this->getValidationRule((string) $field, (string) $rule); + [$fieldName, $ruleStr] = $this->getValidationRule((string) $field, (string) $rule); - // label ๊ฒฐ์ • - if (isset($formFields[$fieldName])) { - $label = $formFields[$fieldName]; - } elseif (str_contains($fieldName, '.*')) { - $parentField = str_replace('.*', '', $fieldName); - $label = ($formFields[$parentField] ?? $fieldName) . " ํ•ญ๋ชฉ"; - } else { - $label = $fieldName; - } - - $dynamicRules[$fieldName] = [ - 'label' => $label, - 'rules' => $ruleStr, - ]; - - // โŒ ์กด์žฌ ๋ณด์žฅ์œผ๋กœ '' ์‚ฝ์ž…ํ•˜์ง€ ์•Š์Œ - // - required๋Š” CI4๊ฐ€ "ํ‚ค ์—†์Œ"๋„ ์‹คํŒจ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ(์ผ๋ฐ˜์ ์œผ๋กœ) - // - permit_empty๋Š” ํ‚ค ์—†์–ด๋„ ํ†ต๊ณผ (๊ฐ•์ œ๋กœ '' ๋งŒ๋“ค๋ฉด FK/์ˆซ์ž ๋ฌธ์ œ ๋ฐœ์ƒ) - - } catch (\Throwable $e) { - throw new RuntimeException("์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๊ทœ์น™ ์ค€๋น„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ (ํ•„๋“œ: {$field}): " . $e->getMessage()); + if (isset($formFields[$fieldName])) { + $label = $formFields[$fieldName]; + } elseif (str_contains($fieldName, '.*')) { + $parentField = str_replace('.*', '', $fieldName); + $label = ($formFields[$parentField] ?? $fieldName) . ' ํ•ญ๋ชฉ'; + } else { + $label = $fieldName; } + + $dynamicRules[$fieldName] = [ + 'label' => $label, + 'rules' => $ruleStr, + ]; } - $this->_validation->setRules($dynamicRules); + $this->validation->setRules($dynamicRules); - try { - if (!$this->_validation->run($formDatas)) { - $errors = $this->_validation->getErrors(); - throw new RuntimeException(implode("\n", $errors)); - } - } catch (\TypeError $e) { - throw new RuntimeException("๊ฒ€์ฆ ๋„์ค‘ ํƒ€์ž… ์˜ค๋ฅ˜ ๋ฐœ์ƒ: " . $e->getMessage()); + if (!$this->validation->run($formDatas)) { + throw new FormValidationException($this->validation->getErrors()); } + } catch (FormValidationException $e) { + throw $e; // โœ… ํ•„๋“œ๋ณ„ errors ์œ ์ง€ + } catch (\TypeError $e) { + throw new RuntimeException('๊ฒ€์ฆ ๋„์ค‘ ํƒ€์ž… ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ' . $e->getMessage()); } catch (\Throwable $e) { - if ($e instanceof RuntimeException) { - throw $e; - } - throw new RuntimeException("์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ค‘ ์‹œ์Šคํ…œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: " . $e->getMessage()); + throw new RuntimeException('์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ค‘ ์‹œ์Šคํ…œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ' . $e->getMessage()); } } @@ -377,7 +347,6 @@ abstract class CommonForm * Overridable hooks * --------------------------------------------------------------------- */ - // ์‚ฌ์šฉ์ž ์ •์˜ hook: ํ•„๋“œ/๋ฃฐ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆ protected function getValidationRule(string $field, string $rule): array { return [$field, $rule]; @@ -393,11 +362,6 @@ abstract class CommonForm return $label; } - /** - * Form rule ์ •์˜ - * - permit_empty|numeric ์ธ FK๋“ค์ด ์—ฌ๊ธฐ์„œ ์ •์˜๋˜๋ฉด, - * validate()์—์„œ ์ž๋™์œผ๋กœ ''->null ์ •๊ทœํ™” ๋Œ€์ƒ์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. - */ public function getFormRule(string $action, string $field, array $formRules): array { switch ($field) { @@ -411,32 +375,39 @@ abstract class CommonForm $formRules[$field] = "required|numeric"; } break; + case $this->getAttribute('title_field'): $formRules[$field] = sprintf( "required|trim|string%s", in_array($action, ["create", "create_form"]) ? "|is_unique[{$this->getAttribute('table')}.{$field}]" : "" ); break; + case "code": $formRules[$field] = sprintf( "required|regex_match[/^[a-zA-Z0-9๊ฐ€-ํžฃ\-\_]+$/]|min_length[4]%s", in_array($action, ["create"]) ? "|is_unique[{$this->getAttribute('table')}.{$field}]" : "" ); break; + case "user_uid": $formRules[$field] = "required|numeric"; break; + case "status": $formRules[$field] = "required|trim|string"; break; + case 'picture': $formRules[$field] = "is_image[{$field}]|mime_in[{$field},image/jpg,image/jpeg,image/gif,image/png,image/webp]|max_size[{$field},300]|max_dims[{$field},2048,768]"; break; + case "updated_at": case "created_at": case "deleted_at": $formRules[$field] = "permit_empty|trim|valid_date"; break; + default: $formRules[$field] = "permit_empty|trim|string"; break; diff --git a/app/Forms/InquiryForm.php b/app/Forms/InquiryForm.php new file mode 100644 index 0000000..b91537a --- /dev/null +++ b/app/Forms/InquiryForm.php @@ -0,0 +1,82 @@ +setFormFields($fields); + $this->setFormRules($action, $fields); + $this->setFormFilters($filters); + $this->setFormOptions($action, $filters, $formDatas); + $this->setIndexFilters($indexFilter); + $this->setBatchjobFilters($batchjobFilters); + } + + public function getFormRule(string $action, string $field, array $formRules): array + { + switch ($field) { + case "title": + case "content": + $formRules[$field] = "required|trim|string"; + break; + case "email": + $formRules[$field] = sprintf("required|trim|valid_email%s", in_array($action, ["create", "create_form"]) ? "|is_unique[{$this->getAttribute('table')}.{$field}]" : ""); + break; + case "status": + $formRules[$field] = "permit_empty|trim|string"; + break; + default: + $formRules = parent::getFormRule($action, $field, $formRules); + break; + } + return $formRules; + } +} diff --git a/app/Helpers/InquiryHelper.php b/app/Helpers/InquiryHelper.php new file mode 100644 index 0000000..86aa7fd --- /dev/null +++ b/app/Helpers/InquiryHelper.php @@ -0,0 +1,11 @@ +alertTrait($msg, $url); + } +} diff --git a/app/Language/ko/Inquiry.php b/app/Language/ko/Inquiry.php new file mode 100644 index 0000000..ee05668 --- /dev/null +++ b/app/Language/ko/Inquiry.php @@ -0,0 +1,19 @@ + "๋ฌธ์˜์ •๋ณด", + 'label' => [ + 'uid' => "๋ฒˆํ˜ธ", + 'title' => "์ œ๋ชฉ", + 'email' => "์ด๋ฉ”์ผ", + 'content' => "๋‚ด์šฉ", + 'status' => "์ƒํƒœ", + 'updated_at' => "์ˆ˜์ •์ผ", + 'created_at' => "์ž‘์„ฑ์ผ", + 'deleted_at' => "์‚ญ์ œ์ผ", + ], + "STATUS" => [ + STATUS['AVAILABLE'] => "๋ฌธ์˜", + STATUS['PAUSE'] => "์ผ์‹œ์ •์ง€", + STATUS['TERMINATED'] => "์™„๋ฃŒ", + ], +]; diff --git a/app/Models/CommonModel.php b/app/Models/CommonModel.php index 2c60d32..81e16c9 100644 --- a/app/Models/CommonModel.php +++ b/app/Models/CommonModel.php @@ -9,21 +9,15 @@ abstract class CommonModel extends Model protected $table = ''; protected $primaryKey = ''; protected $useAutoIncrement = true; - // protected $returnType = 'array'; - //true์ด๋ฉด ๋ชจ๋“  delete * ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ์€ ์‹ค์ œ๋กœ ํ–‰์„ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์„ค์ • + protected $useSoftDeletes = false; protected $protectFields = true; protected $allowedFields = []; - // $allowEmptyInserts = false (๊ธฐ๋ณธ๊ฐ’): ์‚ฝ์ž…ํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์ „ํ˜€ ์—†๋Š” ๊ฒฝ์šฐ, CI4๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉฐ ์ฟผ๋ฆฌ ์‹คํ–‰์„ ๋ง‰์Šต๋‹ˆ๋‹ค. (๋ณด์•ˆ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ๋ชฉ์ ) - // $allowEmptyInserts = true: ์‚ฝ์ž…ํ•  ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์–ด๋„ INSERT INTO table_name () VALUES () ๊ฐ™์€ ๋นˆ ์ฟผ๋ฆฌ ์‹คํ–‰์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค (๊ทนํžˆ ๋“œ๋ฌธ ๊ฒฝ์šฐ์— ์‚ฌ์šฉ). protected bool $allowEmptyInserts = false; protected bool $updateOnlyChanged = true; - // protected $useEmptyStringIfNull = true; (๊ธฐ๋ณธ๊ฐ’) - // ์ด ๊ธฐ๋ณธ ์„ค์ • ๋•Œ๋ฌธ์— PHP์˜ null ๊ฐ’์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์ „๋‹ฌ๋  ๋•Œ ์‹ค์ œ SQL์˜ NULL ํ‚ค์›Œ๋“œ๊ฐ€ ์•„๋‹Œ **๋นˆ ๋ฌธ์ž์—ด ('')**๋กœ ๋ณ€ํ™˜๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. - // ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(MySQL ๋“ฑ)์˜ ์„ค์ •์— ๋”ฐ๋ผ ๋นˆ ๋ฌธ์ž์—ด์ด ์—…๋ฐ์ดํŠธ ์ฟผ๋ฆฌ์—์„œ ๋ฌด์‹œ๋˜๊ฑฐ๋‚˜, ํ•ด๋‹น ์ปฌ๋Ÿผ์˜ ๊ธฐ์กด ๊ฐ’์ด ์œ ์ง€๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. - protected $useEmptyStringIfNull = false; //NULL๊ฐ’๋„ ๋„ฃ์„๋ ค๋ฉด false + protected $useEmptyStringIfNull = false; // NULL๋„ DB๋กœ ๋ณด๋‚ด๊ธฐ protected array $casts = []; protected array $castHandlers = []; @@ -43,64 +37,97 @@ abstract class CommonModel extends Model // Callbacks protected $allowCallbacks = true; - protected $beforeInsert = ['emptyStringToNull']; //Field ๊ฐ’์ด NULL์ผ ๊ฒฝ์šฐ DB Default๊ฐ’ ์ ์šฉ์šฉ + + /** + * โœ… ๋ณ€๊ฒฝ: + * - beforeInsert: emptyStringToNull + applyDbDefaultsOnInsert + * - beforeUpdate: emptyStringToNull๋งŒ ์œ ์ง€ (UPDATE์—์„œ๋Š” DB default๋ฅผ ์“ฐ๋ ค๊ณ  ์ปฌ๋Ÿผ์„ ๋นผ๋ฉด ์œ„ํ—˜/์˜๋„์™€ ๋‹ค๋ฆ„) + */ + protected $beforeInsert = ['emptyStringToNull', 'applyDbDefaultsOnInsert']; protected $afterInsert = []; - protected $beforeUpdate = ['emptyStringToNull']; //Field ๊ฐ’์ด NULL์ผ ๊ฒฝ์šฐ DB Default๊ฐ’ ์ ์šฉ์šฉ + protected $beforeUpdate = ['emptyStringToNull']; protected $afterUpdate = []; + protected $beforeFind = []; protected $afterFind = []; protected $beforeDelete = []; protected $afterDelete = []; - protected array $nullableFields = []; // ๋ชจ๋ธ๋ณ„๋กœ override + /** + * ๋นˆ ๋ฌธ์ž์—ด์„ NULL๋กœ ๋ฐ”๊พธ๊ณ  ์‹ถ์€ ํ•„๋“œ๋“ค (๋ชจ๋ธ๋ณ„ override) + * - ์˜ˆ: FK, ์ˆซ์žํ•„๋“œ ๋“ฑ + */ + protected array $nullableFields = []; + + /** + * โœ… ์ถ”๊ฐ€: DB DEFAULT๋ฅผ โ€œ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€โ€ ํ•„๋“œ๋“ค (๋ชจ๋ธ๋ณ„ override) + * - INSERT ์‹œ ๊ฐ’์ด null/''/๊ณต๋ฐฑ์ด๋ฉด payload์—์„œ ์ œ๊ฑฐ(unset)ํ•ด์„œ DB default๊ฐ€ ๋™์ž‘ํ•˜๊ฒŒ ํ•จ + * - UPDATE์—์„œ๋Š” ์ ์šฉํ•˜์ง€ ์•Š์Œ (๋น„์šฐ๊ธฐ ๊ฐ€๋Šฅ) + */ + protected array $allowedDbDefaultFields = []; + + /** + * ๊ณต๋ฐฑ๋ฌธ์ž์—ด๋„ ๋นˆ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰ํ• ์ง€ ์ •์ฑ… + */ + protected bool $dbDefaultTreatWhitespaceAsEmpty = true; + protected function __construct() { parent::__construct(); } + final public function getTable(): string { return constant("static::TABLE"); } + final public function getPKField(): string { return constant("static::PK"); } + final public function getTitleField(): string { return constant("static::TITLE"); } + final public function useAutoIncrement(): bool { return $this->useAutoIncrement; } + final public function getAllowedFields(): array { return $this->allowedFields; } + /** + * ๊ธฐ์กด ๋กœ์ง ์œ ์ง€: + * - nullableFields์— ์ง€์ •๋œ ํ•„๋“œ๋งŒ '' => null ๋กœ ๋ณ€ํ™˜ + */ protected function emptyStringToNull(array $data): array { if (!isset($data['data']) || !is_array($data['data'])) { return $data; } - // ๊ณตํ†ต ๋ชจ๋ธ์—์„œ๋Š” ์•„๋ฌด ํ•„๋“œ๋„ ๊ฐ•์ œํ•˜์ง€ ์•Š์Œ (์•ˆ์ „) if (empty($this->nullableFields)) { return $data; } foreach ($this->nullableFields as $field) { - if (array_key_exists($field, $data['data'])) { - $v = $data['data'][$field]; + if (!array_key_exists($field, $data['data'])) { + continue; + } - // ๋ฌธ์ž์—ด์ด๋ฉด trim ํ›„, ๋นˆ๋ฌธ์ž๋ฉด null - if (is_string($v)) { - $v = trim($v); - $data['data'][$field] = ($v === '') ? null : $v; - } else { - // ๋ฌธ์ž์—ด์ด ์•„๋‹Œ๋ฐ๋„ '' ๊ฐ™์€ ์ผ€์ด์Šค ๋ฐฉ์–ด (๊ฑฐ์˜ ์—†์Œ) - if ($v === '') - $data['data'][$field] = null; + $v = $data['data'][$field]; + + if (is_string($v)) { + $v = trim($v); + $data['data'][$field] = ($v === '') ? null : $v; + } else { + if ($v === '') { + $data['data'][$field] = null; } } } @@ -108,4 +135,50 @@ abstract class CommonModel extends Model return $data; } + /** + * โœ… ์ถ”๊ฐ€ ๋กœ์ง: + * INSERT ๋•Œ๋งŒ DB DEFAULT๋ฅผ ์“ฐ๊ณ  ์‹ถ์€ ํ•„๋“œ๋ฅผ payload์—์„œ ์ œ๊ฑฐ(unset) + * + * - allowedDbDefaultFields์— ์žˆ๋Š” ํ•„๋“œ๋งŒ ์ฒ˜๋ฆฌ + * - ๊ฐ’์ด null / '' / (์˜ต์…˜)๊ณต๋ฐฑ๋ฌธ์ž์—ด ์ด๋ฉด unset + * - ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด INSERT ์ฟผ๋ฆฌ์—์„œ ์ปฌ๋Ÿผ ์ž์ฒด๊ฐ€ ๋น ์ ธ์„œ DB default๊ฐ€ ์ ์šฉ๋จ + */ + protected function applyDbDefaultsOnInsert(array $data): array + { + if (!isset($data['data']) || !is_array($data['data'])) { + return $data; + } + + if (empty($this->allowedDbDefaultFields)) { + return $data; + } + + foreach ($this->allowedDbDefaultFields as $field) { + if (!array_key_exists($field, $data['data'])) { + continue; + } + + $v = $data['data'][$field]; + + // null์ด๋ฉด ์ œ๊ฑฐ + if ($v === null) { + unset($data['data'][$field]); + continue; + } + + // ๋ฌธ์ž์—ด์ด๋ฉด '' ๋˜๋Š” (์˜ต์…˜)trim ํ›„ '' ์ด๋ฉด ์ œ๊ฑฐ + if (is_string($v)) { + if ($v === '') { + unset($data['data'][$field]); + continue; + } + if ($this->dbDefaultTreatWhitespaceAsEmpty && trim($v) === '') { + unset($data['data'][$field]); + continue; + } + } + } + + return $data; + } } diff --git a/app/Models/InquiryModel.php b/app/Models/InquiryModel.php new file mode 100644 index 0000000..27e0ff5 --- /dev/null +++ b/app/Models/InquiryModel.php @@ -0,0 +1,27 @@ +transComplete(); return $result; + } catch (FormValidationException $e) { + $db->transRollback(); + throw $e; // โœ… ์ด๊ฑฐ ํ•„์ˆ˜ } catch (DatabaseException $e) { $errorMessage = sprintf( "\n----[%s]์—์„œ ํŠธ๋žœ์žญ์…˜ ์‹คํŒจ: DB ์˜ค๋ฅ˜----\n%s\n%s\n------------------------------\n", @@ -52,7 +56,7 @@ abstract class CommonService throw new RuntimeException($errorMessage, $e->getCode(), $e); } catch (\Throwable $e) { $db->transRollback(); - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + throw $e; // โœ… ์—ฌ๊ธฐ์„œ๋„ RuntimeException์œผ๋กœ ๊ฐ์‹ธ์ง€ ๋ง ๊ฒƒ (๊ถŒ์žฅ) } } @@ -259,18 +263,13 @@ abstract class CommonService protected function create_process(array $formDatas): CommonEntity { try { - log_message('debug', "*** ENTER" . __METHOD__ . " ***"); $actionForm = $this->getActionForm(); - log_message('debug', 'FORMCLASS=' . $this->formClass . ' / FORMINST=' . (is_object($actionForm) ? get_class($actionForm) : 'NULL')); - log_message('debug', 'IS_COMMONFORM=' . (is_object($actionForm) && $actionForm instanceof CommonForm ? 'YES' : 'NO')); if ($actionForm instanceof CommonForm) { $actionForm->action_init_process('create', $formDatas); foreach ($formDatas as $field => $value) { $formDatas = $this->action_process_fieldhook($field, $value, $formDatas); } - log_message('debug', '>>> BEFORE validate: ' . get_class($actionForm)); $actionForm->validate($formDatas); // โœ… ์—ฌ๊ธฐ์„œ ๊ฒ€์ฆ - log_message('debug', '>>> AFTER validate'); } $entityClass = $this->getEntityClass(); $entity = new $entityClass($formDatas); @@ -278,6 +277,8 @@ abstract class CommonService throw new RuntimeException("Return Type์€ {$entityClass}๋งŒ ๊ฐ€๋Šฅ"); } return $this->save_process($entity); + } catch (FormValidationException $e) { + throw $e; // โœ… ๊ฐ์‹ธ์ง€ ๋ง๊ณ  ๊ทธ๋Œ€๋กœ } catch (\Throwable $e) { throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ:" . $e->getMessage()); } @@ -295,18 +296,13 @@ abstract class CommonService protected function modify_process($entity, array $formDatas): CommonEntity { try { - log_message('debug', "*** ENTER" . __METHOD__ . " ***"); $actionForm = $this->getActionForm(); - log_message('debug', 'FORMCLASS=' . $this->formClass . ' / FORMINST=' . (is_object($actionForm) ? get_class($actionForm) : 'NULL')); - log_message('debug', 'IS_COMMONFORM=' . (is_object($actionForm) && $actionForm instanceof CommonForm ? 'YES' : 'NO')); if ($actionForm instanceof CommonForm) { $actionForm->action_init_process('modify', $formDatas); foreach ($formDatas as $field => $value) { $formDatas = $this->action_process_fieldhook($field, $value, $formDatas); } - log_message('debug', '>>> BEFORE validate: ' . get_class($actionForm)); $actionForm->validate($formDatas); // โœ… ์—ฌ๊ธฐ์„œ ๊ฒ€์ฆ - log_message('debug', '>>> AFTER validate'); } // ๊ฒ€์ฆ ํ†ต๊ณผ ํ›„ ์—”ํ‹ฐํ‹ฐ ๋ฐ˜์˜ $entity->fill($formDatas); @@ -314,9 +310,10 @@ abstract class CommonService return $entity; } return $this->save_process($entity); - + } catch (FormValidationException $e) { + throw $e; // โœ… ๊ฐ์‹ธ์ง€ ๋ง๊ณ  ๊ทธ๋Œ€๋กœ } catch (\Throwable $e) { - throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ:" . $e->getMessage() . "\n" . var_export($entity, true)); + throw new RuntimeException(static::class . '->' . __FUNCTION__ . "์—์„œ ์˜ค๋ฅ˜๋ฐœ์ƒ:" . $e->getMessage()); } } diff --git a/app/Services/InquiryService.php b/app/Services/InquiryService.php new file mode 100644 index 0000000..77d590f --- /dev/null +++ b/app/Services/InquiryService.php @@ -0,0 +1,51 @@ +addClassPaths('Inquiry'); + } + public function getDTOClass(): string + { + return InquiryDTO::class; + } + public function createDTO(array $formDatas): InquiryDTO + { + return new InquiryDTO($formDatas); + } + public function getEntityClass(): string + { + return InquiryEntity::class; + } + //๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋ถ€๋ถ„ + protected function getEntity_process(mixed $entity): InquiryEntity + { + return $entity; + } + //List ๊ฒ€์ƒ‰์šฉ + //FormFilter ์กฐ๊ฑด์ ˆ ์ฒ˜๋ฆฌ + //๊ฒ€์ƒ‰์–ด์กฐ๊ฑด์ ˆ์ฒ˜๋ฆฌ + + //์ถ”๊ฐ€๊ธฐ๋Šฅ๋ถ€๋ถ„ + protected function create_process(array $formDatas): CommonEntity + { + if (!isset($formDatas['status']) || $formDatas['status'] === '' || $formDatas['status'] === null) { + $formDatas['status'] = STATUS['AVAILABLE']; // 'available' + } + return parent::create_process($formDatas); + } +} diff --git a/app/Views/admin/create_form.php b/app/Views/admin/create_form.php index 55537cd..75afda8 100644 --- a/app/Views/admin/create_form.php +++ b/app/Views/admin/create_form.php @@ -1,6 +1,5 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?>
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
@@ -10,7 +9,8 @@ $label): ?> - getFieldLabel($field, $label, $viewDatas) ?> + + getFieldLabel($field, $label, $viewDatas) ?> getFieldForm($field, old($field) ?? ($viewDatas['formDatas'][$field] ?? null), $viewDatas) ?>
diff --git a/app/Views/admin/index.php b/app/Views/admin/index.php index 1093cde..af4963f 100644 --- a/app/Views/admin/index.php +++ b/app/Views/admin/index.php @@ -1,6 +1,6 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?> +
include("{$viewDatas['layout']['layout']}/top"); ?>
@@ -8,37 +8,41 @@
include("{$viewDatas['layout']['template']}/index_header"); ?>
- + +
include("{$viewDatas['layout']['template']}/index_content_filter"); ?> 'batchjob_form', 'method' => "post"]) ?> + - $label): ?> - + $label): ?> + + - - + + - $label): ?> + + $label): ?> - +
๋ฒˆํ˜ธgetListLabel($field, $label, $viewDatas) ?>getListLabel($field, $label, $viewDatas) ?> ์ž‘์—…
getListButton('modify', $num, $viewDatas) ?>getFieldView($field, $entity->$field, $viewDatas) ?>getFieldView($field, $entity->$field, $viewDatas) ?> - $label): ?> - getListButton($action, $label, $viewDatas) ?>  + $label): ?> + getListButton($action, $label, $viewDatas) ?> 
diff --git a/app/Views/admin/modify_form.php b/app/Views/admin/modify_form.php index 5d69c56..af6a4cc 100644 --- a/app/Views/admin/modify_form.php +++ b/app/Views/admin/modify_form.php @@ -1,7 +1,8 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?> -
+ +
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
@@ -9,15 +10,15 @@ $label): ?> - - - - + + + +
- getFieldLabel($field, $label, $viewDatas) ?> - - getFieldForm($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?> -
-
+ getFieldLabel($field, $label, $viewDatas) ?> + + getFieldForm($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?> +
+
"btn btn-outline btn-primary")); ?>
diff --git a/app/Views/admin/popup/create_form.php b/app/Views/admin/popup/create_form.php index 55537cd..dfbbb65 100644 --- a/app/Views/admin/popup/create_form.php +++ b/app/Views/admin/popup/create_form.php @@ -1,6 +1,5 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?>
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
diff --git a/app/Views/admin/popup/index.php b/app/Views/admin/popup/index.php index 53fda09..db1808a 100644 --- a/app/Views/admin/popup/index.php +++ b/app/Views/admin/popup/index.php @@ -1,6 +1,5 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?>
diff --git a/app/Views/admin/popup/modify_form.php b/app/Views/admin/popup/modify_form.php index 3b7ac34..76cf7c0 100644 --- a/app/Views/admin/popup/modify_form.php +++ b/app/Views/admin/popup/modify_form.php @@ -1,7 +1,8 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?> -
+ +
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
@@ -9,13 +10,13 @@ $label): ?> - - - - + + + +
getFieldLabel($field, $label, $viewDatas) ?> - getFieldForm($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?> -
-
getFieldLabel($field, $label, $viewDatas) ?> + getFieldForm($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?> +
+
"btn btn-outline btn-primary")); ?>
diff --git a/app/Views/admin/popup/view.php b/app/Views/admin/popup/view.php index e8fac79..c725f9c 100644 --- a/app/Views/admin/popup/view.php +++ b/app/Views/admin/popup/view.php @@ -1,6 +1,5 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?>
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
diff --git a/app/Views/admin/view.php b/app/Views/admin/view.php index e8fac79..31578b6 100644 --- a/app/Views/admin/view.php +++ b/app/Views/admin/view.php @@ -1,17 +1,18 @@ extend($viewDatas['layout']['layout']) ?> section('content') ?> -alertTrait(session('message')) : ""; ?> -
+ +
include("{$viewDatas['layout']['template']}/form_content_top"); ?>
$label): ?> - - - - + + + +
getFieldLabel($field, $label, $viewDatas) ?>getFieldView($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?>
getFieldLabel($field, $label, $viewDatas) ?>getFieldView($field, old($field) ?? ($viewDatas['entity']->$field ?? null), $viewDatas) ?>
diff --git a/app/Views/auth/login_form.php b/app/Views/auth/login_form.php index 1f26bdc..afe9259 100644 --- a/app/Views/auth/login_form.php +++ b/app/Views/auth/login_form.php @@ -2,7 +2,7 @@ \ No newline at end of file diff --git a/app/Views/front/welcome/datacenter.php b/app/Views/front/welcome/datacenter.php new file mode 100644 index 0000000..d9b08ea --- /dev/null +++ b/app/Views/front/welcome/datacenter.php @@ -0,0 +1,26 @@ +
+
+
+
+ IDC Center +
+
+

์น˜๋ฐ”/๋„์ฟ„ ํ”„๋ฆฌ๋ฏธ์—„ IDC

+

์•„์‹œ์•„ ๋น„์ฆˆ๋‹ˆ์Šค์˜ ์ „๋žต์  ํ—ˆ๋ธŒ์ธ ์น˜๋ฐ”/๋„์ฟ„์— ์œ„์น˜ํ•˜์—ฌ ํ•œ๊ตญ, ์ผ๋ณธ, ๊ธ€๋กœ๋ฒŒ ํŠธ๋ž˜ํ”ฝ์„ ๊ฐ€์žฅ ๋‚ฎ์€ ์ง€์—ฐ์‹œ๊ฐ„(Low Latency)์œผ๋กœ + ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. +

+
+
+
์ „๋ ฅ ๋ณด์žฅ
+

N+1 ์ด์ค‘ํ™” ์‹œ์Šคํ…œ ์„ค๋น„๋กœ ์ตœ๋Œ€ ๊ฐ€์šฉ์„ฑ ๋ณด์žฅ.

+
+
+
๊ณต์กฐ ์‹œ์„ค
+

๊ณ ๋ฐ€๋„ ์„œ๋ฒ„์˜ ์—ด๊ธฐ๋ฅผ ์™„๋ฒฝํžˆ ์ œ์–ดํ•˜๋Š” ๊ธฐ์—…์šฉ ๋ƒ‰๋ฐฉ ์‹œ์Šคํ…œ ์™„๋น„.

+
+
+
+
+
+
\ No newline at end of file diff --git a/app/Views/front/welcome/hero.php b/app/Views/front/welcome/hero.php new file mode 100644 index 0000000..15024b8 --- /dev/null +++ b/app/Views/front/welcome/hero.php @@ -0,0 +1,18 @@ +
+
+
+
+

Enterprise-grade Infrastructure +

+

+ High Performance โ€ข Secure โ€ข Low Latency
+ ๋„์ฟ„ ๊ฐ€์•ผ๋ฐ”์ดˆ์˜ ์ตœ์ฒจ๋‹จ IDC์—์„œ ์ œ๊ณตํ•˜๋Š” ์••๋„์  ์„ฑ๋Šฅ์˜ ์ „์šฉ ์„œ๋ฒ„ ์ธํ”„๋ผ. +

+ +
+
+
+
\ No newline at end of file diff --git a/app/Views/front/welcome/index.php b/app/Views/front/welcome/index.php index 0fe1439..ffbc2fe 100644 --- a/app/Views/front/welcome/index.php +++ b/app/Views/front/welcome/index.php @@ -4,302 +4,15 @@
include($viewDatas['layout']['layout'] . '/top'); ?>
-
-
-
-
-

Enterprise-grade Infrastructure -

-

- High Performance โ€ข Secure โ€ข Low Latency
- ๋„์ฟ„ ๊ฐ€์•ผ๋ฐ”์ดˆ์˜ ์ตœ์ฒจ๋‹จ IDC์—์„œ ์ œ๊ณตํ•˜๋Š” ์••๋„์  ์„ฑ๋Šฅ์˜ ์ „์šฉ ์„œ๋ฒ„ ์ธํ”„๋ผ. -

- -
-
-
-
- +include($viewDatas['layout']['path'] . '/welcome/hero'); ?> -
-
-
-
- -
NVMe Gen4
-

์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ „์šฉ SSD

-
-
- -
DDoS Protected
-

์‹ค์‹œ๊ฐ„ L3/L4/L7 ๋ฐฉ์–ด

-
-
- -
100G Backbone
-

๊ธ€๋กœ๋ฒŒ ๊ณ ์† ๋„คํŠธ์›Œํฌ

-
-
- -
24/7 Monitoring
-

์ƒ์ฃผ ์—”์ง€๋‹ˆ์–ด ์ง€์›

-
-
-
-
- +include($viewDatas['layout']['path'] . '/welcome/statsbar'); ?> -
-
-
-

์ „์šฉ ์„œ๋ฒ„ ํ˜ธ์ŠคํŒ…

-

๋„์ฟ„ IDC ๊ธฐ๋ฐ˜์˜ ์ตœ์‹  ํ•˜๋“œ์›จ์–ด ๋ผ์ธ์—…์„ ํ™•์ธํ•˜์„ธ์š”.

-
-
-
- - -
-
-
-
Proxmox VPS
-
๊ณ ์„ฑ๋Šฅ ๊ฐ€์ƒ ์„œ๋ฒ„
-
-
-
Hypervisor: Proxmox VE - (KVM)
-
vCPU: Intel Xeon Gold - (Dedicated)
-
RAM: 8GB ~ 64GB (No - Overselling)
-
Storage: Enterprise NVMe - SSD
-
Feature: Snapshot, Backup, ์›น - ์ฝ˜์†”
-
- -
-
- - -
-
-
-
Bํƒ€์ž…
-
๋ฒ”์šฉ ์„œ๋ฒ„
-
-
-
CPU: Xeon (4 Core)
-
RAM: 16GB ์ด์ƒ
-
HDD: SSD 256GB x2 + SSD 500GB - x2
-
RAID ๊ตฌ์„ฑ: RAID 1/1
-
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
-
- -
-
- - -
- -
- - -
-
-
-
Dํƒ€์ž…
-
ํ”„๋ฆฌ๋ฏธ์—„ ์„œ๋ฒ„
-
-
-
CPU: Xeon (12 Core)
-
RAM: 16GB ์ด์ƒ
-
HDD: SSD 256GB x2 + SSD 500GB - x2
-
RAID ๊ตฌ์„ฑ: RAID 1/1
-
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
-
- -
-
- - -
-
-
-
Eํƒ€์ž…
-
์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์„œ๋ฒ„
-
-
-
CPU: Xeon (20 Core)
-
RAM: 32GB ์ด์ƒ
-
HDD: SSD 256GB x2 + SSD 500GB - x2
-
RAID ๊ตฌ์„ฑ: RAID 1/1
-
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
-
- -
-
- - -
-
-
-
E2ํƒ€์ž…
-
์ตœ์ƒ์œ„ ์„œ๋ฒ„
-
-
-
CPU: Xeon (28 Core)
-
RAM: 32GB ์ด์ƒ
-
HDD: SSD 256GB x2 + SSD 500GB - x2
-
RAID ๊ตฌ์„ฑ: RAID 1/1
-
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
-
- -
-
-
-
-
- +include($viewDatas['layout']['path'] . '/welcome/server'); ?> -
-
-
-
-
Network & Security
-

์‹ค์‹œ๊ฐ„ ์ง€๋Šฅํ˜• DDoS ๋ฐฉ์–ด ์‹œ์Šคํ…œ

-

๋‹จ์ˆœํ•œ ํŠธ๋ž˜ํ”ฝ ์ฐจ๋‹จ์ด ์•„๋‹Œ, ์ •๊ตํ•œ ๋ฐ์ดํ„ฐ ์ •ํ™” ์—”์ง„(Scrubbing Center)์„ ํ†ตํ•ด ์ •์ƒ์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ํŠธ๋ž˜ํ”ฝ๋งŒ์„ ์„œ๋ฒ„๋กœ - ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

-
    -
  • L3/L4 - Mitigation: UDP/SYN Flood ์™„๋ฒฝ ๋Œ€์‘
  • -
  • BGP + - Anycast: ์ „ ์„ธ๊ณ„ ์ฃผ์š” ๊ฑฐ์ ์—์„œ ๊ณต๊ฒฉ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌ
  • -
  • Always-on - Protection: 1์ดˆ ๋ฏธ๋งŒ์˜ ํƒ์ง€ ๋ฐ ์ฆ‰๊ฐ ๋Œ€์‘
  • -
-
-
-
-
ํŠธ๋ž˜ํ”ฝ ํ๋ฆ„๋„(Traffic Flow) -
-
-
์ „์ฒด ํŠธ๋ž˜ํ”ฝ (All Internet Traffic)
- -
DAEMON Clean Center -
- -
์ •์ƒ ํŠธ๋ž˜ํ”ฝ(Cleaned Data) -> ๊ณ ๊ฐ - ์„œ๋ฒ„(Client Server) -
-
-
-
-
-
-
- +include($viewDatas['layout']['path'] . '/welcome/network'); ?> -
-
-
-
- IDC Center -
-
-

์น˜๋ฐ”/๋„์ฟ„ ํ”„๋ฆฌ๋ฏธ์—„ IDC

-

์•„์‹œ์•„ ๋น„์ฆˆ๋‹ˆ์Šค์˜ ์ „๋žต์  ํ—ˆ๋ธŒ์ธ ์น˜๋ฐ”/๋„์ฟ„์— ์œ„์น˜ํ•˜์—ฌ ํ•œ๊ตญ, ์ผ๋ณธ, ๊ธ€๋กœ๋ฒŒ ํŠธ๋ž˜ํ”ฝ์„ ๊ฐ€์žฅ ๋‚ฎ์€ ์ง€์—ฐ์‹œ๊ฐ„(Low Latency)์œผ๋กœ - ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. -

-
-
-
์ „๋ ฅ ๋ณด์žฅ
-

N+1 ์ด์ค‘ํ™” ์‹œ์Šคํ…œ ์„ค๋น„๋กœ ์ตœ๋Œ€ ๊ฐ€์šฉ์„ฑ ๋ณด์žฅ.

-
-
-
๊ณต์กฐ ์‹œ์„ค
-

๊ณ ๋ฐ€๋„ ์„œ๋ฒ„์˜ ์—ด๊ธฐ๋ฅผ ์™„๋ฒฝํžˆ ์ œ์–ดํ•˜๋Š” ๊ธฐ์—…์šฉ ๋ƒ‰๋ฐฉ ์‹œ์Šคํ…œ ์™„๋น„.

-
-
-
-
-
-
- +include($viewDatas['layout']['path'] . '/welcome/datacenter'); ?>
@@ -323,7 +36,7 @@
Discord ์ปค๋ฎค๋‹ˆํ‹ฐ
-
k6nQg84N
+
๋ฐ๋ชฌ์•„์ด๋””์”จ
-
-
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
-
+ include($viewDatas['layout']['path'] . '/welcome/inquiry'); ?>
diff --git a/app/Views/front/welcome/inquiry.php b/app/Views/front/welcome/inquiry.php new file mode 100644 index 0000000..d9812f2 --- /dev/null +++ b/app/Views/front/welcome/inquiry.php @@ -0,0 +1,72 @@ +
+
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/app/Views/front/welcome/network.php b/app/Views/front/welcome/network.php new file mode 100644 index 0000000..2e5e49f --- /dev/null +++ b/app/Views/front/welcome/network.php @@ -0,0 +1,39 @@ +
+
+
+
+
Network & Security
+

์‹ค์‹œ๊ฐ„ ์ง€๋Šฅํ˜• DDoS ๋ฐฉ์–ด ์‹œ์Šคํ…œ

+

๋‹จ์ˆœํ•œ ํŠธ๋ž˜ํ”ฝ ์ฐจ๋‹จ์ด ์•„๋‹Œ, ์ •๊ตํ•œ ๋ฐ์ดํ„ฐ ์ •ํ™” ์—”์ง„(Scrubbing Center)์„ ํ†ตํ•ด ์ •์ƒ์ ์ธ ๋น„์ฆˆ๋‹ˆ์Šค ํŠธ๋ž˜ํ”ฝ๋งŒ์„ ์„œ๋ฒ„๋กœ + ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

+
    +
  • L3/L4 + Mitigation: UDP/SYN Flood ์™„๋ฒฝ ๋Œ€์‘
  • +
  • BGP + + Anycast: ์ „ ์„ธ๊ณ„ ์ฃผ์š” ๊ฑฐ์ ์—์„œ ๊ณต๊ฒฉ ๋ถ„์‚ฐ ์ฒ˜๋ฆฌ
  • +
  • Always-on + Protection: 1์ดˆ ๋ฏธ๋งŒ์˜ ํƒ์ง€ ๋ฐ ์ฆ‰๊ฐ ๋Œ€์‘
  • +
+
+
+
+
ํŠธ๋ž˜ํ”ฝ ํ๋ฆ„๋„(Traffic Flow) +
+
+
์ „์ฒด ํŠธ๋ž˜ํ”ฝ (All Internet Traffic)
+ +
DAEMON Clean Center +
+ +
์ •์ƒ ํŠธ๋ž˜ํ”ฝ(Cleaned Data) -> ๊ณ ๊ฐ + ์„œ๋ฒ„(Client Server) +
+
+
+
+
+
+
\ No newline at end of file diff --git a/app/Views/front/welcome/server.php b/app/Views/front/welcome/server.php new file mode 100644 index 0000000..081d18d --- /dev/null +++ b/app/Views/front/welcome/server.php @@ -0,0 +1,191 @@ +
+
+
+

์ „์šฉ ์„œ๋ฒ„ ํ˜ธ์ŠคํŒ…

+

๋„์ฟ„ IDC ๊ธฐ๋ฐ˜์˜ ์ตœ์‹  ํ•˜๋“œ์›จ์–ด ๋ผ์ธ์—…์„ ํ™•์ธํ•˜์„ธ์š”.

+
+
+
+ + +
+
+
+
Proxmox VPS
+
๊ณ ์„ฑ๋Šฅ ๊ฐ€์ƒ ์„œ๋ฒ„
+
+
+
Hypervisor: Proxmox VE + (KVM)
+
vCPU: Intel Xeon Gold + (Dedicated)
+
RAM: 8GB ~ 64GB (No + Overselling)
+
Storage: Enterprise NVMe + SSD
+
Feature: Snapshot, Backup, ์›น + ์ฝ˜์†”
+
๊ฒฐ์ œ: ๊ณ„์ขŒ์ด์ฒด , ๋น„ํŠธ์ฝ”์ธ ๊ฐ€๋Šฅ
+
+ +
+
+ + +
+
+
+
Bํƒ€์ž…
+
๋ฒ”์šฉ ์„œ๋ฒ„
+
+
+
CPU: Xeon (4 Core)
+
RAM: 16GB ์ด์ƒ
+
HDD: SSD 256GB x2 + SSD 500GB + x2
+
RAID ๊ตฌ์„ฑ: RAID 1/1
+
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
+
๊ฒฐ์ œ: ๊ณ„์ขŒ์ด์ฒด , ๋น„ํŠธ์ฝ”์ธ ๊ฐ€๋Šฅ
+
+ +
+
+ + +
+ +
+ + +
+
+
+
Dํƒ€์ž…
+
ํ”„๋ฆฌ๋ฏธ์—„ ์„œ๋ฒ„
+
+
+
CPU: Xeon (12 Core)
+
RAM: 16GB ์ด์ƒ
+
HDD: SSD 256GB x2 + SSD 500GB + x2
+
RAID ๊ตฌ์„ฑ: RAID 1/1
+
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
+
๊ฒฐ์ œ: ๊ณ„์ขŒ์ด์ฒด , ๋น„ํŠธ์ฝ”์ธ ๊ฐ€๋Šฅ
+
+ +
+
+ + +
+
+
+
Eํƒ€์ž…
+
์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์„œ๋ฒ„
+
+
+
CPU: Xeon (20 Core)
+
RAM: 32GB ์ด์ƒ
+
HDD: SSD 256GB x2 + SSD 500GB + x2
+
RAID ๊ตฌ์„ฑ: RAID 1/1
+
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
+
๊ฒฐ์ œ: ๊ณ„์ขŒ์ด์ฒด , ๋น„ํŠธ์ฝ”์ธ ๊ฐ€๋Šฅ
+
+ +
+
+ + +
+
+
+
E2ํƒ€์ž…
+
์ตœ์ƒ์œ„ ์„œ๋ฒ„
+
+
+
CPU: Xeon (28 Core)
+
RAM: 32GB ์ด์ƒ
+
HDD: SSD 256GB x2 + SSD 500GB + x2
+
RAID ๊ตฌ์„ฑ: RAID 1/1
+
์ฃผ์š” ์šฉ๋„: ๋ชจ๋“  ์„œ๋น„์Šค ์ง€์›
+
๊ฒฐ์ œ: ๊ณ„์ขŒ์ด์ฒด , ๋น„ํŠธ์ฝ”์ธ ๊ฐ€๋Šฅ
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/app/Views/front/welcome/statsbar.php b/app/Views/front/welcome/statsbar.php new file mode 100644 index 0000000..d3b6dfa --- /dev/null +++ b/app/Views/front/welcome/statsbar.php @@ -0,0 +1,26 @@ +
+
+
+
+ +
NVMe Gen4
+

์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์ „์šฉ SSD

+
+
+ +
DDoS Protected
+

์‹ค์‹œ๊ฐ„ L3/L4/L7 ๋ฐฉ์–ด

+
+
+ +
100G Backbone
+

๊ธ€๋กœ๋ฒŒ ๊ณ ์† ๋„คํŠธ์›Œํฌ

+
+
+ +
24/7 Monitoring
+

์ƒ์ฃผ ์—”์ง€๋‹ˆ์–ด ์ง€์›

+
+
+
+
\ No newline at end of file diff --git a/app/Views/layouts/admin.php b/app/Views/layouts/admin.php index 9925553..b26ba72 100644 --- a/app/Views/layouts/admin.php +++ b/app/Views/layouts/admin.php @@ -3,9 +3,9 @@ <?= $viewDatas['layout']['title'] ?> - - - + + + @@ -20,6 +20,6 @@ renderSection('content') ?> - + \ No newline at end of file diff --git a/app/Views/layouts/admin/left_menu/base.php b/app/Views/layouts/admin/left_menu/base.php index aeed902..dc3fb1d 100644 --- a/app/Views/layouts/admin/left_menu/base.php +++ b/app/Views/layouts/admin/left_menu/base.php @@ -1,3 +1,6 @@
+ \ No newline at end of file diff --git a/app/Views/layouts/empty.php b/app/Views/layouts/empty.php index b042c07..c7e0bd1 100644 --- a/app/Views/layouts/empty.php +++ b/app/Views/layouts/empty.php @@ -3,9 +3,9 @@ <?= $viewDatas['layout']['title'] ?> - - - + + + @@ -17,9 +17,9 @@ - getHelper()->alertTrait($error) ?> -
renderSection('content') ?>
+ + renderSection('content') ?> - + \ No newline at end of file diff --git a/app/Views/layouts/front.php b/app/Views/layouts/front.php index c0b6623..3765124 100644 --- a/app/Views/layouts/front.php +++ b/app/Views/layouts/front.php @@ -3,9 +3,9 @@ <?= $viewDatas['layout']['title'] ?> - - - + + + @@ -17,9 +17,9 @@ - getHelper()->alertTrait($error) ?> - renderSection('content') ?> + + renderSection('content') ?> - + \ No newline at end of file diff --git a/app/Views/layouts/front/bottom.php b/app/Views/layouts/front/bottom.php index 2148f96..d2ed2ee 100644 --- a/app/Views/layouts/front/bottom.php +++ b/app/Views/layouts/front/bottom.php @@ -6,8 +6,7 @@
- DAEMON - IDC + DAEMON IDC

๋ฐ๋ชฌ IDC๋Š” ๊ณ ์„ฑ๋Šฅ ์ „์šฉ ์„œ๋ฒ„์™€ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ ๋ณด์•ˆ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•˜๋Š” ๊ธ€๋กœ๋ฒŒ ์ธํ”„๋ผ ์ „๋ฌธ ๋ธŒ๋žœ๋“œ์ž…๋‹ˆ๋‹ค. ๋„์ฟ„ ๊ฐ€์•ผ๋ฐ”์ดˆ์˜ ์ตœ์ฒจ๋‹จ ์‹œ์„ค์—์„œ ๊ท€์‚ฌ์˜ ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค.

@@ -15,30 +14,33 @@
-
Contact Info
+
์—ฐ๋ฝ์ฒ˜
    -
  • - sales@daemon-idc.com
  • -
  • @daemonidc - (Telegram)
  • -
  • k6nQg84N - (Discord)
  • +
  • 070-8672-0021 +
  • +
  • webmaster@daemon-idc.com
  • +
  • + @daemonidc(Telegram)
  • +
  • ๋ฐ๋ชฌ์•„์ด๋””์”จ(Discord) +
  • System Status: Normal
@@ -48,7 +50,7 @@

© 2024 Daemon IDC. All rights reserved.

- Tokyo, Japan Facility + Chiba/Tokyo, Japan Facility Engineering for Excellence
diff --git a/app/Views/layouts/front/top.php b/app/Views/layouts/front/top.php index e9baca4..0665f1b 100644 --- a/app/Views/layouts/front/top.php +++ b/app/Views/layouts/front/top.php @@ -26,7 +26,7 @@ Discord - ๋กœ๊ทธ์ธ +
diff --git a/app/Views/templates/admin/index_header.php b/app/Views/templates/admin/index_header.php index 37400dd..87fcf66 100644 --- a/app/Views/templates/admin/index_header.php +++ b/app/Views/templates/admin/index_header.php @@ -12,7 +12,12 @@ + + ๊ฒŒ์‹œํŒ์ •๋ณด + + + ๋ฌธ์˜์ •๋ณด \ No newline at end of file diff --git a/public/css/front.css b/public/css/front.css index bebba88..b7486bf 100644 --- a/public/css/front.css +++ b/public/css/front.css @@ -65,13 +65,20 @@ body { /* Hero Section */ .hero-section { - padding: 160px 0 100px; - background: linear-gradient(rgba(15, 23, 42, 0.85), rgba(15, 23, 42, 0.85)), - url('https://images.unsplash.com/photo-1558494949-ef010cbdcc48?auto=format&fit=crop&q=80&w=1920'); + position: relative; + min-height: 600px; + display: flex; + align-items: center; + justify-content: center; + /* ๊ณ ํ’ˆ์งˆ ๋ฐ์ดํ„ฐ์„ผํ„ฐ ์„œ๋ฒ„๋ฃธ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ */ + background: linear-gradient(rgba(15, 23, 42, 0.8), rgba(15, 23, 42, 0.85)), + url('/images/main_background.png'); background-size: cover; - background-position: center; + background-position: center center; + background-attachment: scroll; color: white; text-align: center; + padding: 100px 0; } /* Product Cards */ diff --git a/public/images/main_background.png b/public/images/main_background.png new file mode 100644 index 0000000..da0467e Binary files /dev/null and b/public/images/main_background.png differ