feat: CSP 다음/카카오 도메인 허용 + 입고 성적서 파일 백엔드 연동 + 팝업 이미지 중앙정렬
- middleware CSP: *.kakao.com, *.kakaocdn.net 추가 (다음 주소찾기 차단 해결) - frame-src에 'self' 추가 - 공지 팝업 이미지 중앙정렬 ([&_img]:mx-auto) - HR 사원관리, 결재, 품목, 생산 등 다수 개선 - API 에러 핸들링 및 JSON 파싱 안정화
This commit is contained in:
46
scripts/patch-json-parse.cjs
Normal file
46
scripts/patch-json-parse.cjs
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* JSON.parse 글로벌 패치 - macOS 26 파일시스템 손상 대응
|
||||
*
|
||||
* macOS 26에서 atomic write(tmp + rename)가 실패하면
|
||||
* .next/prerender-manifest.json 등의 파일에 데이터가 중복 기록됨.
|
||||
* 이로 인해 "Unexpected non-whitespace character after JSON at position N" 발생.
|
||||
*
|
||||
* 이 패치는 JSON.parse 실패 시 유효한 JSON 부분만 추출하여 자동 복구.
|
||||
* NODE_OPTIONS='--require ./scripts/patch-json-parse.cjs' 로 로드.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const originalParse = JSON.parse;
|
||||
|
||||
JSON.parse = function patchedJsonParse(text, reviver) {
|
||||
try {
|
||||
return originalParse.call(this, text, reviver);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError && typeof text === 'string') {
|
||||
// "Unexpected non-whitespace character after JSON at position N"
|
||||
// → position N까지가 유효한 JSON
|
||||
const match = e.message.match(/after JSON at position\s+(\d+)/);
|
||||
if (match) {
|
||||
const pos = parseInt(match[1], 10);
|
||||
if (pos > 0) {
|
||||
try {
|
||||
const result = originalParse.call(this, text.substring(0, pos), reviver);
|
||||
// 한 번만 경고 (같은 position이면 반복 출력 방지)
|
||||
if (!patchedJsonParse._warned) patchedJsonParse._warned = new Set();
|
||||
const key = pos + ':' + text.length;
|
||||
if (!patchedJsonParse._warned.has(key)) {
|
||||
patchedJsonParse._warned.add(key);
|
||||
console.warn(
|
||||
`[patch-json-parse] macOS 파일 손상 자동 복구 (position ${pos}, total ${text.length} bytes)`
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch {
|
||||
// truncation으로도 실패하면 원래 에러 throw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
49
scripts/validate-next-cache.mjs
Normal file
49
scripts/validate-next-cache.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* .next 빌드 캐시 무결성 검증
|
||||
*
|
||||
* macOS 26 파일시스템 이슈로 .next/ 내 JSON 파일이 손상될 수 있음.
|
||||
* (atomic write 실패 → 데이터 중복 기록)
|
||||
* dev 서버 시작 전 자동 검증하여 손상 시 .next 삭제.
|
||||
*/
|
||||
import { readFileSync, rmSync, existsSync, readdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
const NEXT_DIR = '.next';
|
||||
|
||||
if (!existsSync(NEXT_DIR)) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const jsonFiles = [];
|
||||
try {
|
||||
// .next/ 루트의 JSON 파일들
|
||||
for (const f of readdirSync(NEXT_DIR)) {
|
||||
if (f.endsWith('.json')) jsonFiles.push(join(NEXT_DIR, f));
|
||||
}
|
||||
// .next/server/ 의 JSON 파일들
|
||||
const serverDir = join(NEXT_DIR, 'server');
|
||||
if (existsSync(serverDir)) {
|
||||
for (const f of readdirSync(serverDir)) {
|
||||
if (f.endsWith('.json')) jsonFiles.push(join(serverDir, f));
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// 디렉토리 읽기 실패 시 무시
|
||||
}
|
||||
|
||||
let corrupted = false;
|
||||
for (const file of jsonFiles) {
|
||||
try {
|
||||
const content = readFileSync(file, 'utf8');
|
||||
JSON.parse(content);
|
||||
} catch (e) {
|
||||
console.warn(`⚠️ 손상된 캐시 발견: ${file}`);
|
||||
console.warn(` ${e.message}`);
|
||||
corrupted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (corrupted) {
|
||||
console.warn('🗑️ .next 캐시를 삭제하고 재빌드합니다...');
|
||||
rmSync(NEXT_DIR, { recursive: true, force: true });
|
||||
}
|
||||
Reference in New Issue
Block a user