데이터불러오기기

This commit is contained in:
2025-10-15 10:31:03 +00:00
parent ddb70018b9
commit 0ab4b037eb
5 changed files with 202 additions and 4 deletions

View File

@@ -28,10 +28,16 @@ function toInt(v: any): number {
export async function GET(request: Request) {
try {
// 세션 사용자 확인 (핸들 매핑용)
const session = await auth();
if (!session?.user?.email) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
const urlObj = new URL(request.url)
const hostHeader = (request.headers.get('host') || '').toLowerCase()
const isLocal = hostHeader.includes('localhost') || hostHeader.includes('127.0.0.1') || urlObj.hostname === 'localhost' || urlObj.hostname === '127.0.0.1'
if (!isLocal) {
// 세션 사용자 확인 (핸들 매핑용)
const session = await auth();
if (!session?.user?.email) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
}
const { searchParams } = new URL(request.url)

134
datareq/datareq.js Normal file
View File

@@ -0,0 +1,134 @@
#!/usr/bin/env node
// 요청 예시: https://www.everfactory.kr/api/contents/update?date=YYYYMMDD
// 사용법
// 1) 오늘(가장 최신)부터 특정 일자까지 요청:
// node datareq/datareq.js 20251014
// 2) 특정 최신일자부터 특정 과거일자까지 요청:
// node datareq/datareq.js 20251015 20251001
// 환경변수로 호출 간 지연(ms) 조절: RATE_MS=500 node datareq/datareq.js 20251014
const BASE_URL = "http://localhost:9551/api/contents/update";
const DEFAULT_RATE_MS = Number(process.env.RATE_MS || 400);
function isValidYyyyMmDd(value) {
return typeof value === "string" && /^\d{8}$/.test(value);
}
function truncateText(text, maxLen = 256) {
if (typeof text !== "string") return "";
return text.length > maxLen ? text.slice(0, maxLen) : text;
}
function parseYyyyMmDdToDate(yyyyMmDd) {
const year = Number(yyyyMmDd.slice(0, 4));
const month = Number(yyyyMmDd.slice(4, 6)) - 1; // 0-based
const day = Number(yyyyMmDd.slice(6, 8));
// Date(YYYY, M, D)는 로컬 타임존 기준. 간단성을 위해 로컬 기준 사용.
return new Date(year, month, day);
}
function formatDateToYyyyMmDd(date) {
const y = String(date.getFullYear());
const m = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
return `${y}${m}${d}`;
}
function addDays(date, delta) {
const next = new Date(date);
next.setDate(next.getDate() + delta);
return next;
}
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function requestForDate(dateStr) {
const url = `${BASE_URL}?date=${dateStr}`;
try {
const res = await fetch(url, { method: "GET" });
const text = await res.text().catch(() => "");
if (!res.ok) {
const preview = truncateText(text, 256);
console.error(`[FAIL] ${dateStr} | ${url} -> ${res.status} ${res.statusText} ${preview ? `| ${preview}` : ""}`);
return { ok: false, status: res.status, body: text };
}
const preview = truncateText(text, 256);
console.log(`[OK] ${dateStr} | ${url} -> ${preview || res.status}`);
return { ok: true, status: res.status, body: text };
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
console.error(`[ERROR] ${dateStr} | ${url} -> ${truncateText(errMsg, 256)}`);
return { ok: false, error: err };
}
}
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.log("사용법: node datareq/datareq.js <끝일자YYYYMMDD> [시작(최신)일자YYYYMMDD]");
console.log("예시1: node datareq/datareq.js 20251014");
console.log("예시2: node datareq/datareq.js 20251015 20251001");
process.exit(1);
}
let newestStr; // 최신일자 (내림차순 시작점)
let oldestStr; // 종료일자 (내림차순 끝점)
if (args.length === 1) {
const today = new Date();
newestStr = formatDateToYyyyMmDd(today);
oldestStr = args[0];
} else {
// args.length >= 2 인 경우: [최신일자, 종료일자]
newestStr = args[0];
oldestStr = args[1];
}
if (!isValidYyyyMmDd(newestStr) || !isValidYyyyMmDd(oldestStr)) {
console.error("날짜 형식은 YYYYMMDD 여야 합니다.");
process.exit(1);
}
let newestDate = parseYyyyMmDdToDate(newestStr);
let oldestDate = parseYyyyMmDdToDate(oldestStr);
// 최신 >= 종료 가 아니면 자동 교환
if (newestDate < oldestDate) {
const tmp = newestDate;
newestDate = oldestDate;
oldestDate = tmp;
newestStr = formatDateToYyyyMmDd(newestDate);
oldestStr = formatDateToYyyyMmDd(oldestDate);
}
console.log(`요청 범위: 최신 ${newestStr} → 종료 ${oldestStr} (내림차순)`);
console.log(`요청 간 지연: ${DEFAULT_RATE_MS}ms`);
let current = newestDate;
while (current >= oldestDate) {
const dateStr = formatDateToYyyyMmDd(current);
await requestForDate(dateStr);
// 너무 빠른 연속 호출 방지
await sleep(DEFAULT_RATE_MS);
current = addDays(current, -1);
}
console.log("모든 요청이 완료되었습니다.");
}
// Node 18+ 환경 가정: fetch 전역 제공. 없으면 종료 안내.
if (typeof fetch !== "function") {
console.error("현재 Node 런타임에 fetch가 없습니다. Node 18+를 사용하거나 폴리필을 추가하세요.");
process.exit(1);
}
main().catch((err) => {
console.error("예상치 못한 오류:", err);
process.exit(1);
});

39
datareq/datareq_latest.js Normal file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env node
const BASE_URL = "http://localhost:9551/api/contents/update";
function truncateText(text, maxLen = 256) {
if (typeof text !== "string") return "";
return text.length > maxLen ? text.slice(0, maxLen) : text;
}
async function requestLatest() {
const url = `${BASE_URL}?date=latest`;
try {
const res = await fetch(url, { method: "GET" });
const text = await res.text().catch(() => "");
if (!res.ok) {
const preview = truncateText(text, 256);
console.error(`[FAIL] latest | ${url} -> ${res.status} ${res.statusText} ${preview ? `| ${preview}` : ""}`);
return { ok: false, status: res.status, body: text };
}
const preview = truncateText(text, 256);
console.log(`[OK] latest | ${url} -> ${preview || res.status}`);
return { ok: true, status: res.status, body: text };
} catch (err) {
const errMsg = err instanceof Error ? err.message : String(err);
console.error(`[ERROR] latest | ${url} -> ${truncateText(errMsg, 256)}`);
return { ok: false, error: err };
}
}
// Node 18+ 환경 가정: fetch 전역 제공. 없으면 종료 안내.
if (typeof fetch !== "function") {
console.error("현재 Node 런타임에 fetch가 없습니다. Node 18+를 사용하거나 폴리필을 추가하세요.");
process.exit(1);
}
requestLatest().catch((err) => {
console.error("예상치 못한 오류:", err);
process.exit(1);
});

View File

@@ -10,10 +10,12 @@ export default auth(async (req) => {
// 경로 추출
const { pathname } = req.nextUrl;
console.log("request host:", req.headers.get('host'));
// 로컬 접속 확인 (특정 경로만 우회)
const isLocal = req.headers.get('host')?.includes('localhost') ||
req.headers.get('host')?.includes('127.0.0.1');
if (isLocal && pathname.startsWith("/api/contents")) {
return NextResponse.next();
}

View File

@@ -148,6 +148,16 @@ function formatKoreanDateFromYMD(ymd) {
return `${yyyy}. ${mm}. ${dd}.`;
}
// '2025. 9. 1.' → '20250901' 로 변환
function dotKoreanDateToYMD(dot) {
const m = dot?.match(/^(\d{4})\.\s*(\d{1,2})\.\s*(\d{1,2})\.?$/);
if (!m) return null;
const yyyy = m[1];
const mm = String(parseInt(m[2], 10)).padStart(2, '0');
const dd = String(parseInt(m[3], 10)).padStart(2, '0');
return `${yyyy}${mm}${dd}`;
}
// 입력칸의 기존 값을 지우고 새 값 입력
async function clearAndType(inputLocator, page, value) {
await inputLocator.click();
@@ -179,6 +189,13 @@ async function configureDateRangeSingleDay(page, ymdTarget) {
await clearAndType(startInput, page, endVal);
startVal = endVal;
} else if (ymdTarget) {
// 최신 종료일(=현재 UI가 가진 가장 최근 일자)과 비교하여 이후면 에러
const latestEndVal = await endInput.inputValue();
const latestYmd = dotKoreanDateToYMD(latestEndVal);
if (!latestYmd) throw new Error('최신 종료일 파싱에 실패했습니다.');
if (!/^\d{8}$/.test(ymdTarget)) throw new Error('잘못된 날짜 형식입니다. (예: 20250901)');
if (ymdTarget > latestYmd) throw new Error(`요청한 날짜(${ymdTarget})가 최신 데이터(${latestYmd})보다 이후입니다.`);
const formatted = formatKoreanDateFromYMD(ymdTarget);
if (!formatted) throw new Error(`잘못된 날짜 형식입니다. (예: 20250901)`);
// 시작/종료 모두 동일 날짜로 설정