문제와 원인
제보 제목 121자와 본문 4,001자를 입력하면 각각 끝의 1자가 잘린 채 저장 경로로 넘어갔다. cleanText()가 최대 길이를 검사하기 전에 slice(0, limit)를 적용해 초과 여부를 판정할 정보가 사라졌다.
수정 전 코드
cleanText
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
function cleanText(value: unknown, limit: number) {
return String(value ?? "")
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "")
.trim()
.slice(0, limit);
}
POST
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
const title = cleanText(payload.title, 120);
const description = cleanText(payload.description, 4000);
수정 후 코드
user-report-validation.ts
cleanReportText()는 제어 문자와 양끝 공백을 제거하고, validateReportText()가 정리된 값의 상·하한을 검사한다. 화면과 서버가 같은 함수를 사용하며 제목은 4–120, 본문은 10–4,000의 범위를 허용한다. 길이 단위는 JavaScript .length의 UTF-16 코드 단위다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
export function cleanReportText(value: unknown): string {
return typeof value === "string"
? value
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "")
.trim()
: "";
}
export function validateReportText(
title: unknown,
description: unknown,
): string | null {
const t = cleanReportText(title);
const d = cleanReportText(description);
if (t.length > 120 || d.length > 4000) {
return "제목은 120자, 상세 내용은 4,000자 이내로 입력해 주세요.";
}
if (t.length < 4 || d.length < 10) {
return "제목은 4자, 상세 내용은 10자 이상 입력해 주세요.";
}
return null;
}
POST
서버 POST는 INSERT 전에 길이를 검사하고 오류 메시지와 HTTP 400을 반환한다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
const title = cleanReportText(payload.title);
const description = cleanReportText(payload.description);
const textError = validateReportText(title, description);
if (textError) return json({ error: textError }, 400);
검증 결과
Swagger UI에서 원본 정리·길이 판정 함수를 다시 실행했다. 같은 합성 문자열 4조건을 사용했으며, 길이는 UTF-16 코드 단위로 비교했다.
좌우로 이동해 전체 내용을 확인하세요. 키보드: ←·→, Home·End
| 입력 조건 | 수정 전 | 수정 후 |
|---|---|---|
| 제목 120자·본문 4,000자 | 절단 없이 허용 | 절단 없이 허용 |
| 제목 121자·유효한 본문 | 제목 1자 절단 후 허용 | 원문 유지·거부 |
| 유효한 제목·본문 4,001자 | 본문 1자 절단 후 허용 | 원문 유지·거부 |
| 제목 3자·유효한 본문 | 거부 | 거부 |
초과 입력 거부는 0/2개(0%)→2/2개(100%), 길이 제한에 따른 절단량 합계는 2자→0자로 바뀌었다. 허용 경계와 최소 길이 거부의 기존 동작도 유지됐다.