제보 입력 잘림 방지: 길이 검증과 HTTP 400

이 글의 목차 8개 항목

문제와 원인

제보 제목 121자와 본문 4,001자를 입력하면 각각 끝의 1자가 잘린 채 저장 경로로 넘어갔다. cleanText()가 최대 길이를 검사하기 전에 slice(0, limit)를 적용해 초과 여부를 판정할 정보가 사라졌다.

수정 전 코드

cleanText

function cleanText(value: unknown, limit: number) {
  return String(value ?? "")
    .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "")
    .trim()
    .slice(0, limit);
}

POST

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 코드 단위다.

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을 반환한다.

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 코드 단위로 비교했다.

입력 조건수정 전수정 후
제목 120자·본문 4,000자절단 없이 허용절단 없이 허용
제목 121자·유효한 본문제목 1자 절단 후 허용원문 유지·거부
유효한 제목·본문 4,001자본문 1자 절단 후 허용원문 유지·거부
제목 3자·유효한 본문거부거부
길이 판정 함수 재실행: 초과 거부 0/2→2/2, 절단량 2자→0자원본 보기 (새 창)

길이 판정 함수 재실행: 초과 거부 0/2→2/2, 절단량 2자→0자

100%원본 보기 (새 창)

확대한 이미지는 상하좌우로 이동할 수 있습니다. 키보드는 이미지 영역에 초점을 두고 방향키를 사용하세요. Esc로 닫습니다.

초과 입력 거부는 0/2개(0%)→2/2개(100%), 길이 제한에 따른 절단량 합계는 2자→0자로 바뀌었다. 허용 경계와 최소 길이 거부의 기존 동작도 유지됐다.