최근 Next.js 환경에서 PHP 클라이언트로부터 데이터를 받아 사이트맵을 자동 생성하는 시스템을 구현하면서 겪었던 문제와 그 해결 과정을 공유하고자 합니다. 이 글이 비슷한 문제를 겪고 있는 개발자들에게 도움이 되길 바랍니다.
도전 과제
- PHP 클라이언트에서 URL 데이터를 Next.js API로 전송합니다.
- 구글 권장사항에 따라 이 데이터를 200개씩 묶어 XML 사이트맵 파일로 생성합니다.
- 생성된 모든 사이트맵 파일을 가리키는 인덱스 파일을 생성합니다.
대규모 사이트에서는 URL이 수천 개에 달할 수 있어, 하나의 사이트맵 파일에 모든 URL을 포함시키는 것보다 200개씩 분할하여 여러 개의 사이트맵 파일을 생성하는 방식이 권장됩니다. 하지만 구현 과정에서 예상치 못한 여러 문제가 발생했습니다.
문제 상황
문제 #1: 대용량 데이터 처리
증상
초기 구현에서는 Next.js API 라우트의 기본 bodyParser를 사용했는데, PHP 클라이언트에서 대량의 URL을 포함하는 요청이 들어올 때마다 body exceeded 1mb limit 에러가 발생했습니다.
원인
Next.js API 라우트는 기본적으로 요청 본문 크기를 1MB로 제한합니다. 대규모 웹사이트의 URL 목록은 쉽게 이 제한을 초과할 수 있습니다.
해결책
bodyParser: false 설정을 통해 Next.js의 기본 본문 파서를 비활성화하고, 요청 스트림을 직접 처리하는 방식으로 변경했습니다:
export const config = {
api: {
bodyParser: false, // 기본 bodyParser 비활성화
},
};
export default async function handler(req, res) {
// 요청 본문 직접 읽기
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const buffer = Buffer.concat(chunks);
// 이후 처리 로직...
}
문제 #2: PHP에서 전송된 파일 데이터 접근
증상
PHP 클라이언트에서 파일을 전송했지만, Next.js API에서는 파일 내용을 읽을 수 없었습니다. 로그를 확인해보니 파일 경로만 전송되고 실제 내용은 포함되어 있지 않았습니다.
파일 정보: { name: 'C:/home/caret_api//api/tmp/sitemap_store.txt', mime: 'text/plain', postname: 'sitemap_store.txt' }
파일이 존재하지 않음: C:/home/caret_api//api/tmp/sitemap_store.txt
원인
PHP의 $_FILES 구조는 파일을 임시 디렉토리에 저장하고 파일 경로만 전송합니다. Next.js 서버는 PHP 클라이언트의 파일 시스템에 접근할 수 없기 때문에 파일을 읽지 못했습니다.
해결책
여러 방법을 시도한 끝에, PHP 클라이언트에서 파일 내용을 직접 읽어 요청 본문에 포함시키는 방식으로 해결했습니다:
// PHP 클라이언트 코드
$fileContent = file_get_contents('sitemap_store.txt');
$data = [
'licenses' => 'SECRET_KEY',
'fileContent' => $fileContent // 파일 내용 직접 전송
];
$ch = curl_init('https://your-nextjs-app.com/api/generate-sitemap');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
$response = curl_exec($ch);
curl_close($ch);
Next.js API에서는 여러 가능한 형태의 파일 내용 전송을 처리할 수 있도록 다음과 같이 구현했습니다:
// 파일 내용 추출 시도 (여러 방법)
let fileContent = "";
// 방법 1: fileContent 필드 확인
if (formData.fileContent) {
fileContent = formData.fileContent;
}
// 방법 2: 이진 데이터 검색
else if (buffer.includes("Content-Type: application/octet-stream")) {
// 이진 데이터 추출 로직...
}
// 방법 3: 요청 본문에서 텍스트 추출
else if (buffer.length > 1000) {
// 텍스트 추출 로직...
}
문제 #3: 오래된 사이트맵 파일 관리
증상
사이트 콘텐츠가 줄어들었을 때 이전에 생성된 사이트맵 파일이 그대로 남아있어, 검색 엔진에 잘못된 정보를 제공하는 문제가 발생했습니다.
원인
새로운 사이트맵을 생성할 때 기존 파일을 정리하는 로직이 없었습니다.
해결책
요청이 들어올 때마다 기존 사이트맵 파일을 모두 삭제한 후 새로 생성하는 방식으로 수정했습니다:
// 디렉토리 내의 모든 파일 삭제 함수
function cleanDirectory(directory) {
if (!fs.existsSync(directory)) {
return;
}
const files = fs.readdirSync(directory);
for (const file of files) {
const filePath = path.join(directory, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
cleanDirectory(filePath);
fs.rmdirSync(filePath);
} else {
fs.unlinkSync(filePath);
}
}
}
// 사이트맵 생성 전 기존 파일 정리
if (fs.existsSync(sitemapDir)) {
cleanDirectory(sitemapDir);
}
문제 #4: 다양한 데이터 형식 처리
증상
텍스트 파일, JSON 파일 등 다양한 형식의 데이터를 처리해야 했지만, 초기 구현은 특정 형식만 처리할 수 있었습니다.
원인
파일 내용을 특정 형식으로만 가정하고 처리했기 때문입니다.
해결책
파일 내용을 먼저 JSON으로 파싱 시도하고, 실패할 경우 텍스트 파일로 처리하는 방식을 구현했습니다:
try {
// JSON 파일로 파싱 시도
const jsonData = JSON.parse(fileContent);
// JSON 처리 로직...
} catch (parseError) {
// 텍스트 파일로 처리
const lines = fileContent.trim().split(/\r?\n/);
// 텍스트 파일 처리 로직...
}
결론
이번 트러블슈팅을 통해 Next.js API 라우트에서 대용량 데이터를 처리할 때는 기본 bodyParser에 의존하기보다 스트림 기반의 직접 처리 방식이 효과적임을 알게 되었습니다.
- 대용량 데이터는 스트림 방식으로 청크 단위 처리가 효율적
- bodyParser: false 설정으로 원시 요청 데이터에 직접 접근 가능
- 여러 형식의 입력을 처리할 수 있는 유연한 파싱 로직이 중요
- 자동 정리 프로세스로 이전 작업과의 충돌 방지
PHP 클라이언트에서 Next.js API로 데이터를 전송하고 처리하는 과정에서 발생할 수 있는 다양한 문제를 해결하는 과정은 쉽지 않았지만, 결과적으로 더욱 견고하고 유연한 사이트맵 생성기를 구현할 수 있었습니다.
하지만 대용량 파일을 string으로 처리하면서 다음과 같은 아쉬운 점이 있었습니다..
- 메모리 효율성 문제: 전체 파일 내용을 메모리에 한 번에 로드하여 대용량 데이터 처리 시 메모리 사용량이 급증하고, 서버 리소스에 부담을 주며 동시 요청 처리 시 성능 저하가 발생합니다.
- 처리 속도 제한: 대용량 문자열 데이터 파싱 과정에서 JavaScript 단일 스레드 특성상 블로킹이 발생하여 전체 API 응답 시간이 길어집니다.
- 확장성 한계: URL 데이터가 계속 증가할 경우 결국 메모리 한계에 도달할 수 있어 확장성에 제약이 생깁니다.
- 에러 처리 복잡성: 대용량 문자열 처리 중 오류 발생 시 전체 프로세스가 실패하며, 부분적 성공/실패 처리가 어렵습니다.
- 인코딩 이슈: 다양한 언어와 특수문자가 포함된 URL을 문자열로 처리할 때 인코딩 문제가 발생할 가능성이 있습니다.
다음에는 파일 기반 처리 방식을 도입하여 스트림 처리, 청크 단위 처리 등을 통해 이러한 문제들을 효과적으로 해결해 보겠습니다 :>
'트러블슈팅' 카테고리의 다른 글
React Query와 Next.js에서 헤더 데이터 캐싱 문제 트러블슈팅 (0) | 2025.04.04 |
---|---|
React Coupon Selector에서 발생한 isToggle과 maxPriceCoupon 동기화 문제 해결하기 (2) | 2025.02.02 |
[react-beautiful-dnd] Could not find a declaration file for module 'react-beautiful-dnd'. (0) | 2024.11.30 |
[Vercel] 배포 중 만난 에러와 해결 과정 (0) | 2024.11.04 |
SSE(Server-Sent Events)를 활용한 실시간 알림 시스템 개선 (6) | 2024.11.03 |