daemon-idc init
This commit is contained in:
parent
86261f4a0d
commit
08578efd40
@ -4,6 +4,7 @@ namespace App\Controllers;
|
||||
|
||||
use App\Entities\CommonEntity;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
@ -53,7 +54,7 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
);
|
||||
}
|
||||
|
||||
final public function create(): string|RedirectResponse
|
||||
public function create(): string|RedirectResponse|ResponseInterface
|
||||
{
|
||||
try {
|
||||
$action = __FUNCTION__;
|
||||
@ -81,7 +82,7 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
|
||||
final public function modify_form($uid): string|RedirectResponse
|
||||
public function modify_form($uid): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
@ -111,7 +112,7 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
$redirect_url ?? '/' . implode('/', [...$this->getActionPaths(), 'view']) . '/' . $entity->getPK()
|
||||
);
|
||||
}
|
||||
final public function modify($uid): string|RedirectResponse
|
||||
public function modify($uid): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
@ -136,7 +137,7 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
{
|
||||
return $this->action_redirect_process('info', "{$this->getTitle()}에서 {$entity->getTitle()} 삭제가 완료되었습니다.", $redirect_url);
|
||||
}
|
||||
final public function delete($uid): RedirectResponse
|
||||
public function delete($uid): RedirectResponse
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
@ -160,7 +161,7 @@ abstract class AbstractCRUDController extends AbstractWebController
|
||||
{
|
||||
return $this->action_render_process($action, $this->getViewDatas(), $this->request->getVar('ActionTemplate'));
|
||||
}
|
||||
final public function view($uid): string|RedirectResponse
|
||||
public function view($uid): string|RedirectResponse
|
||||
{
|
||||
try {
|
||||
if (!$uid) {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace App\Controllers\Ajax;
|
||||
|
||||
use App\Controllers\AbstractCRUDController;
|
||||
use App\Exceptions\FormValidationException;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
@ -32,4 +33,46 @@ abstract class AjaxController extends AbstractCRUDController
|
||||
$this->addViewDatas('index_batchjobFields', $this->service->getActionForm()->getBatchjobFilters());
|
||||
$this->addViewDatas('index_batchjobButtons', $this->service->getActionForm()->getBatchjobButtons());
|
||||
}
|
||||
|
||||
protected function ok(array $data = [], int $status = 200): ResponseInterface
|
||||
{
|
||||
return $this->response->setStatusCode($status)->setJSON([
|
||||
'ok' => true,
|
||||
...$data,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function fail(string $message, int $status = 400, array $extra = []): ResponseInterface
|
||||
{
|
||||
return $this->response->setStatusCode($status)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => $message,
|
||||
...$extra,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function requireAjax()
|
||||
{
|
||||
// fetch + X-Requested-With 로 들어오는 경우
|
||||
if (!$this->request->isAJAX()) {
|
||||
return $this->fail('Invalid request', 400);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function handleException(\Throwable $e): ResponseInterface
|
||||
{
|
||||
if ($e instanceof FormValidationException) {
|
||||
// ✅ 필드별 + 전역 메시지
|
||||
return $this->fail(
|
||||
$e->getMessage() ?: '입력값을 확인해 주세요.',
|
||||
422,
|
||||
['errors' => $e->getErrors()]
|
||||
);
|
||||
}
|
||||
|
||||
log_message('error', '[AJAX] ' . $e->getMessage());
|
||||
|
||||
return $this->fail('처리 중 오류가 발생했습니다.', 500);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@
|
||||
|
||||
namespace App\Controllers\Ajax;
|
||||
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use App\Exceptions\FormValidationException;
|
||||
|
||||
class InquiryController extends AjaxController
|
||||
{
|
||||
public function initController(RequestInterface $request, ResponseInterface $response, LoggerInterface $logger)
|
||||
@ -21,11 +22,57 @@ class InquiryController extends AjaxController
|
||||
//기본 함수 작업
|
||||
//Custom 추가 함수
|
||||
|
||||
protected function create_result_process($entity, ?string $redirect_url = null): string|RedirectResponse
|
||||
public function create(): ResponseInterface
|
||||
{
|
||||
return $this->action_redirect_process(
|
||||
'info',
|
||||
"{$this->getTitle()}에서 {$entity->getTitle()} 문의 등록이 완료되었습니다.",
|
||||
);
|
||||
log_message('error', 'HIT InquiryController::create');
|
||||
|
||||
if (!$this->request->isAJAX()) {
|
||||
return $this->response->setStatusCode(400)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => 'Invalid request'
|
||||
]);
|
||||
}
|
||||
|
||||
try {
|
||||
$formDatas = $this->request->getPost();
|
||||
log_message('error', 'POST=' . json_encode($formDatas, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$entity = $this->service->create($formDatas);
|
||||
|
||||
return $this->response->setStatusCode(201)->setJSON([
|
||||
'ok' => true,
|
||||
'message' => '문의가 접수되었습니다.',
|
||||
'data' => ['pk' => $entity->getPK()],
|
||||
]);
|
||||
|
||||
} catch (FormValidationException $e) {
|
||||
log_message('error', 'CAUGHT FormValidationException: ' . print_r($e->getErrors(), true));
|
||||
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => '입력값을 확인해 주세요.',
|
||||
'errors' => $e->getErrors(),
|
||||
]);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
// ✅ 혹시 서비스에서 예외를 감싸버린 경우에도 에러를 최대한 복구
|
||||
$errors = service('validation')->getErrors();
|
||||
if (!empty($errors)) {
|
||||
log_message('error', 'FALLBACK validation errors: ' . print_r($errors, true));
|
||||
|
||||
return $this->response->setStatusCode(422)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => '입력값을 확인해 주세요.',
|
||||
'errors' => $errors,
|
||||
]);
|
||||
}
|
||||
|
||||
log_message('error', '[AJAX create] ' . $e->getMessage());
|
||||
|
||||
return $this->response->setStatusCode(500)->setJSON([
|
||||
'ok' => false,
|
||||
'message' => '처리 중 오류가 발생했습니다.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,11 +6,16 @@ use RuntimeException;
|
||||
|
||||
class FormValidationException extends RuntimeException
|
||||
{
|
||||
public array $errors;
|
||||
private array $errors = [];
|
||||
|
||||
public function __construct(array $errors, string $message = 'Validation failed', int $code = 0, ?\Throwable $previous = null)
|
||||
{
|
||||
$this->errors = $errors;
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->errors = $errors;
|
||||
}
|
||||
}
|
||||
|
||||
public function getErrors(): array
|
||||
{
|
||||
return $this->errors;
|
||||
}
|
||||
}
|
||||
@ -245,6 +245,7 @@ abstract class CommonForm
|
||||
// 5) run
|
||||
$this->validation->setRules($dynamicRules);
|
||||
if (!$this->validation->run($formDatas)) {
|
||||
log_message('error', 'CI4 getErrors=' . print_r($this->validation->getErrors(), true));
|
||||
throw new FormValidationException($this->validation->getErrors());
|
||||
}
|
||||
|
||||
|
||||
@ -34,11 +34,18 @@ abstract class CommonService
|
||||
final protected function dbTransaction(callable $callback, string $functionName = ''): mixed
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
|
||||
try {
|
||||
$db->transException(true)->transStart();
|
||||
$result = $callback($db);
|
||||
$db->transComplete();
|
||||
return $result;
|
||||
|
||||
} catch (FormValidationException $e) {
|
||||
// ✅ 검증 에러는 감싸지 말고 그대로 던져야 Ajax가 422 + errors 받음
|
||||
$db->transRollback();
|
||||
throw $e;
|
||||
|
||||
} catch (DatabaseException $e) {
|
||||
$errorMessage = sprintf(
|
||||
"\n----[%s]에서 트랜잭션 실패: DB 오류----\n%s\n%s\n------------------------------\n",
|
||||
@ -48,8 +55,10 @@ abstract class CommonService
|
||||
);
|
||||
log_message('error', $errorMessage);
|
||||
throw new RuntimeException($errorMessage, $e->getCode(), $e);
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$db->transRollback();
|
||||
// ✅ 여기서 FormValidationException까지 RuntimeException으로 바뀌던게 문제였음
|
||||
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,12 @@
|
||||
const form = document.getElementById('inquiryForm');
|
||||
if (!form) return;
|
||||
|
||||
const setGlobalError = (msg) => {
|
||||
const globalBox = document.querySelector(`[data-error-for="_global"]`);
|
||||
if (globalBox) globalBox.textContent = msg;
|
||||
else alert(msg);
|
||||
};
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
@ -37,18 +43,40 @@
|
||||
|
||||
const formData = new FormData(form);
|
||||
|
||||
const res = await fetch('/ajax/inquiry/create', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
let res;
|
||||
try {
|
||||
res = await fetch('/ajax/inquiry/create', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
||||
});
|
||||
} catch (err) {
|
||||
setGlobalError('네트워크 오류가 발생했습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 응답이 JSON이 아닐 수도 있으니 안전하게
|
||||
let data = {};
|
||||
try { data = await res.json(); } catch (e) { }
|
||||
// ✅ JSON 응답인지 먼저 체크 (리다이렉트/HTML이면 여기서 차단)
|
||||
const ct = (res.headers.get('content-type') || '').toLowerCase();
|
||||
if (!ct.includes('application/json')) {
|
||||
// 보통 세션만료/권한필터/리다이렉트/에러페이지가 이 케이스
|
||||
setGlobalError('세션이 만료되었거나 서버가 JSON이 아닌 응답을 반환했습니다. 다시 시도/로그인 해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// JSON 파싱 (여기서 실패하면 서버가 JSON을 깨뜨린 것)
|
||||
let data;
|
||||
try {
|
||||
data = await res.json();
|
||||
} catch (err) {
|
||||
setGlobalError('서버 JSON 파싱 실패(응답 형식 오류).');
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 422: 필드별 에러 + 전역 메시지
|
||||
if (res.status === 422 && data && data.errors) {
|
||||
const globalMsg = data.message || '입력값을 확인해 주세요.';
|
||||
setGlobalError(globalMsg);
|
||||
|
||||
// ✅ 422: 필드별 에러
|
||||
if (res.status === 422 && data.errors) {
|
||||
Object.entries(data.errors).forEach(([field, msg]) => {
|
||||
const box = document.querySelector(`[data-error-for="${field}"]`);
|
||||
if (box) box.textContent = msg;
|
||||
@ -56,16 +84,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 그 외 에러: message를 전역 에러로 표시
|
||||
if (!res.ok || data.ok === false) {
|
||||
const globalBox = document.querySelector(`[data-error-for="_global"]`);
|
||||
if (globalBox) globalBox.textContent = data.message || '처리 중 오류가 발생했습니다.';
|
||||
else alert(data.message || '처리 중 오류가 발생했습니다.');
|
||||
// ✅ 성공은 "반드시 data.ok === true" 로만 인정
|
||||
if (!res.ok || !data || data.ok !== true) {
|
||||
setGlobalError((data && data.message) ? data.message : '처리 중 오류가 발생했습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 성공
|
||||
alert('문의가 접수되었습니다.');
|
||||
alert(data.message || '문의가 접수되었습니다.');
|
||||
form.reset();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user