feat(board,ecommerce,pay,verification,templates,core): 문의 답변 사슬·답글 삭제 정책·검증 계약 + 실패 사유 표면화 전수

공개 (답변 중복 등록)/(문의 삭제 시 답변 잔존) 수정과 동형 결함 전수 해소.

- 문의 답변 단일성: 1차(피벗 is_answered) + 2차(게시판 리스너 duplicate 마커) 이중 방어,
 경합 경로에서도 사유(reply_already_exists 422)가 보존되도록 훅 반환 계약 확장
- 질문·답변·피벗 생명주기 대칭: 삭제/복원/영구삭제 훅 연동 + 고아 데이터 백필 업그레이드 스텝
- 게시판별 답글 삭제 정책(cascade/block) 신설, 정렬 화이트리스트·per_page 게이트
 (전량 조회 소비처 200/999 를 수용하는 상한 999 — 조였다가 관리자 화면 2곳 422 회귀 즉시 정정)
- 결제/본인인증 플러그인 검증 계약: FormRequest 15종 승격 + 컨트롤러 catch 정합.
 거래 조회 tid 상한은 저장 컬럼(varchar 100) 기준 — 해외 CBT TID(40자) 수용
- onError 토스트 고정 문구 전수 해소(67건/50파일/15확장) — 서버 실패 사유를 그대로 표면화,
 change-password 오변수(response→error) 교정
- 코어 플러그인 설정 검증 안내의 항목 표시명이 스키마 라벨(ko/en) 폴백에 덮여 요청 로케일
 (ja) 라벨을 잃던 회귀 정정 — 번역 등록이 있으면 그것을 우선 (7.0.7 미출시 기능 자체 결함)
- 사전 결함 정리(Icon 등가표 27·darkmode 2·validated 통일), API 문서·CHANGELOG·ja 팩 동기
- develop(60652fdc0) 리베이스: getTable 정비와 병합 — 양측 의도 보존 (충돌 18파일)
This commit is contained in:
HeuJung
2026-08-17 17:04:44 +09:00
parent 60652fdc07
commit 3ebbda5d86
236 changed files with 6324 additions and 437 deletions
@@ -4,7 +4,7 @@
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)를 따르며,
[Semantic Versioning](https://semver.org/lang/ko/)을 준수합니다.
## [1.0.5] - 2026-08-12
## [1.0.5] - 2026-08-17
### Added
@@ -25,6 +25,7 @@
### Fixed
- 언어팩 재활성화 안내 창의 아이콘이 의도한 크기와 다르게 보이던 문제를 수정했습니다.
- 표 하단 합계 영역의 값이 계산되지 않고 표현식 원문 그대로 표시되던 문제를 다시 수정했습니다. 1.0.4 에 수록 예정이던 수정이 빌드 산출물에 반영되지 않은 채 배포되어, 주문 상세의 합계 행에서 수량·적립금·배송비 자리에 값 대신 알 수 없는 문자열이 보였습니다. (#98 @hwaryeon1234 님께서 제보해주셨습니다.)
- 회원 정보 수정 화면에서 회원 정보가 화면에 채워지기 전에 저장을 누르면 "이름은 필수입니다" 같은 오류로 저장이 거절되던 문제를 수정했습니다. 이제 회원 정보를 불러오는 동안에는 저장 버튼이 잠시 비활성화됩니다.
- 빌드 산출물이 존재하지 않는 소스맵 파일을 참조해 브라우저 개발자 도구 사용 시 불필요한 404 요청이 발생하던 문제를 수정했습니다.
@@ -33,6 +34,7 @@
- 목록에서 항목을 선택한 뒤 검색하거나 페이지를 넘기면, 화면에서 사라진 항목이 선택된 채로 남아 일괄 처리 대상에 포함되던 문제를 수정했습니다. 확인 창은 건수만 알려 주므로 실행 전에 알아챌 방법이 없었습니다. 이제 일괄 처리 대상은 언제나 화면에 보이면서 체크된 항목뿐입니다. 회원 목록·활동 기록·알림 발송 기록·스케줄 목록에 적용됩니다.
- 환경설정에서 사이트 로고를 목록에서 빼도 화면에 그대로 남아, 로고 교체가 저장되지 않던 문제를 수정했습니다.
- 스케줄 관리의 등록/수정 창에서 저장 버튼을 눌러도 아무 반응이 없던 문제를 수정했습니다. 함께, 작업 유형·실행 주기 선택란이 빈 값으로 표시되고 선택·켜기/끄기 값이 저장 요청에 실리지 않아 저장이 거절되던 문제도 수정했습니다. 실행 주기를 직접 입력(사용자 정의)하지 않은 경우에는 선택한 주기에 맞는 실행 시각이 자동으로 저장됩니다.
- 관리자 화면 전반(사이트 환경설정·역할/사용자 저장·메뉴 순서 변경·레이아웃 미리보기·메일/알림 템플릿·본인인증 메시지/정책·언어팩 재활성화·드라이버 연결 테스트 등)에서 저장·실행이 실패했을 때 실패 사유 대신 일반 안내 문구만 표시되던 문제를 수정했습니다. 이제 서버가 알려준 사유가 그대로 안내됩니다.
## [1.0.4] - 2026-08-10
@@ -336,7 +336,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:common.validation_error"
"message": "{{error.message ?? '$t:common.validation_error'}}"
}
}
]
@@ -430,7 +430,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.roles.toggle_status_failed"
"message": "{{error.message ?? '$t:admin.roles.toggle_status_failed'}}"
}
}
]
@@ -769,7 +769,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.save_error"
"message": "{{error.message ?? '$t:admin.settings.save_error'}}"
}
}
]
@@ -215,7 +215,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.templates.layout_edit.preview_error"
"message": "{{error.message ?? '$t:admin.templates.layout_edit.preview_error'}}"
}
}
]
@@ -286,7 +286,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.templates.layout_edit.preview_error"
"message": "{{error.message ?? '$t:admin.templates.layout_edit.preview_error'}}"
}
}
]
@@ -1611,7 +1611,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.users.form.validation_error"
"message": "{{error.message ?? '$t:admin.users.form.validation_error'}}"
}
}
]
@@ -1922,7 +1922,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.users.form.validation_error"
"message": "{{error.message ?? '$t:admin.users.form.validation_error'}}"
}
}
]
@@ -133,7 +133,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.menus.messages.order_update_failed"
"message": "{{error.message ?? '$t:admin.menus.messages.order_update_failed'}}"
}
},
{
@@ -49,7 +49,7 @@
"name": "Icon",
"props": {
"name": "language",
"className": "w-4 h-4 text-gray-500 dark:text-gray-400"
"className": "text-base text-gray-500 dark:text-gray-400"
}
},
{
@@ -174,7 +174,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.modules.modals.reactivate_language_packs_failed",
"message": "{{error.message ?? '$t:admin.modules.modals.reactivate_language_packs_failed'}}",
"type": "error"
}
}
@@ -49,7 +49,7 @@
"name": "Icon",
"props": {
"name": "language",
"className": "w-4 h-4 text-gray-500 dark:text-gray-400"
"className": "text-base text-gray-500 dark:text-gray-400"
}
},
{
@@ -174,7 +174,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.plugins.modals.reactivate_language_packs_failed",
"message": "{{error.message ?? '$t:admin.plugins.modals.reactivate_language_packs_failed'}}",
"type": "error"
}
}
@@ -584,7 +584,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.identity.messages.add_modal.save_failed",
"message": "{{error.message ?? '$t:admin.settings.identity.messages.add_modal.save_failed'}}",
"type": "error"
}
}
@@ -317,7 +317,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.identity.messages.modal.preview_failed",
"message": "{{error.message ?? '$t:admin.settings.identity.messages.modal.preview_failed'}}",
"type": "error"
}
}
@@ -376,7 +376,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.identity.messages.modal.reset_failed",
"message": "{{error.message ?? '$t:admin.settings.identity.messages.modal.reset_failed'}}",
"type": "error"
}
}
@@ -490,7 +490,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.identity.messages.modal.save_failed",
"message": "{{error.message ?? '$t:admin.settings.identity.messages.modal.save_failed'}}",
"type": "error"
}
}
@@ -591,7 +591,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.mail_templates.save_error",
"message": "{{error.message ?? '$t:admin.settings.mail_templates.save_error'}}",
"type": "error"
}
}
@@ -983,7 +983,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.notification_definitions.save_error",
"message": "{{error.message ?? '$t:admin.settings.notification_definitions.save_error'}}",
"type": "error"
}
}
@@ -530,7 +530,7 @@
"target": "local",
"driverTestResults.s3": {
"success": false,
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
}
},
@@ -538,7 +538,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
},
{
@@ -1101,7 +1101,7 @@
"target": "local",
"driverTestResults.redis": {
"success": false,
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
}
},
@@ -1109,7 +1109,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
},
{
@@ -1351,7 +1351,7 @@
"target": "local",
"driverTestResults.memcached": {
"success": false,
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
}
},
@@ -1359,7 +1359,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
},
{
@@ -2956,7 +2956,7 @@
"target": "local",
"driverTestResults.websocket": {
"success": false,
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
}
},
@@ -2964,7 +2964,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.drivers.test_failed"
"message": "{{error.message ?? '$t:admin.settings.drivers.test_failed'}}"
}
},
{
@@ -422,7 +422,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.settings.identity.messages.toggle_failed",
"message": "{{error.message ?? '$t:admin.settings.identity.messages.toggle_failed'}}",
"variant": "error"
}
},
@@ -619,7 +619,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.identity.policies.toggle_failed"
"message": "{{error.message ?? '$t:admin.identity.policies.toggle_failed'}}"
}
},
{
@@ -956,7 +956,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.identity.policies.toggle_failed"
"message": "{{error.message ?? '$t:admin.identity.policies.toggle_failed'}}"
}
},
{
@@ -354,7 +354,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.settings.info.changelog_load_failed"
"message": "{{error.message ?? '$t:admin.settings.info.changelog_load_failed'}}"
}
}
]
@@ -101,7 +101,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:admin.templates.layout_edit.preview_error"
"message": "{{error.message ?? '$t:admin.templates.layout_edit.preview_error'}}"
}
}
]
@@ -49,7 +49,7 @@
"name": "Icon",
"props": {
"name": "language",
"className": "w-4 h-4 text-gray-500 dark:text-gray-400"
"className": "text-base text-gray-500 dark:text-gray-400"
}
},
{
@@ -174,7 +174,7 @@
{
"handler": "toast",
"params": {
"message": "$t:admin.templates.modals.reactivate_language_packs_failed",
"message": "{{error.message ?? '$t:admin.templates.modals.reactivate_language_packs_failed'}}",
"type": "error"
}
}
@@ -4,7 +4,7 @@
형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)를 따르며,
[Semantic Versioning](https://semver.org/lang/ko/)을 준수합니다.
## [1.1.1] - 2026-08-11
## [1.1.1] - 2026-08-17
### Added
@@ -18,6 +18,7 @@
- 마이페이지 게시판 알림 수신 설정(댓글·답글 등 4개 항목)의 체크 상태가 저장값과 어긋날 수 있던 가능성을 차단했습니다. 저장된 값이 어떤 형태든 화면의 체크 표시와 저장 결과가 항상 일치합니다.
- 장바구니 "전체 선택" 체크박스가 다크 모드에서 테두리 색을 따르지 않던 문제를 수정했습니다.
- 빌드 산출물이 존재하지 않는 소스맵 파일을 참조해 브라우저 개발자 도구 사용 시 불필요한 404 요청이 발생하던 문제를 수정했습니다.
- 쇼핑(주문서 할인·재주문·배송지 저장·상품 문의)과 마이페이지 화면에서 요청이 실패했을 때 실패 사유 대신 일반 안내 문구만 표시되던 문제를 수정했습니다. 비밀번호 변경 실패 안내가 항상 일반 문구로만 나오던 문제도 함께 바로잡았습니다. 이제 서버가 알려준 사유가 그대로 안내됩니다.
## [1.1.0] - 2026-08-10
@@ -196,7 +196,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "{{response.message ?? '$t:mypage.profile.change_password_failed'}}"
"message": "{{error.message ?? '$t:mypage.profile.change_password_failed'}}"
}
}
]
@@ -95,7 +95,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:shop.coupon_download.fetch_failed"
"message": "{{error.message ?? '$t:shop.coupon_download.fetch_failed'}}"
}
}
]
@@ -442,7 +442,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:shop.checkout.discount_code_invalid"
"message": "{{error.message ?? '$t:shop.checkout.discount_code_invalid'}}"
}
}
]
@@ -518,7 +518,7 @@
{
"comment": "verify 실패는 결제 자체를 막지 않음 — 주문은 이미 생성되었으므로 사용자가 나중에 lookup 폼으로 재인증 가능. 토스트로만 안내.",
"handler": "toast",
"params": { "type": "warning", "message": "$t:shop.checkout.guest_token_issue_failed" }
"params": { "type": "warning", "message": "{{error.message ?? '$t:shop.checkout.guest_token_issue_failed'}}" }
}
]
}
@@ -161,7 +161,7 @@
],
"onError": [
{ "handler": "setState", "params": { "target": "global", "qnaDeleting": false } },
{ "handler": "toast", "params": { "message": "$t:shop.qna.delete_failed", "type": "error" } }
{ "handler": "toast", "params": { "message": "{{error.message ?? '$t:shop.qna.delete_failed'}}", "type": "error" } }
]
}
]
@@ -691,7 +691,7 @@
],
"onError": [
{ "handler": "setState", "params": { "target": "local", "qnaReplySubmitting": false } },
{ "handler": "toast", "params": { "message": "$t:shop.qna.reply_update_failed", "type": "error" } }
{ "handler": "toast", "params": { "message": "{{error.message ?? '$t:shop.qna.reply_update_failed'}}", "type": "error" } }
]
}
]
@@ -837,7 +837,7 @@
],
"onError": [
{ "handler": "setState", "params": { "target": "local", "qnaNewReplySubmitting": false } },
{ "handler": "toast", "params": { "message": "$t:shop.qna.reply_submit_failed", "type": "error" } }
{ "handler": "toast", "params": { "message": "{{error.message ?? '$t:shop.qna.reply_submit_failed'}}", "type": "error" } }
]
}
]
@@ -604,7 +604,7 @@
{ "handler": "toast", "params": { "type": "success", "message": "$t:shop.order_complete.address_saved" } }
],
"onError": [
{ "handler": "toast", "params": { "type": "error", "message": "$t:shop.order_complete.address_save_failed" } }
{ "handler": "toast", "params": { "type": "error", "message": "{{error.message ?? '$t:shop.order_complete.address_save_failed'}}" } }
]
}
]
@@ -53,7 +53,7 @@
"handler": "toast",
"params": {
"type": "error",
"message": "$t:shop.reorder.failed"
"message": "{{error.message ?? '$t:shop.reorder.failed'}}"
}
}
]