PHP로 웹을 만들다 보면 거의 매 페이지마다 반복해서 하게 되는 작업이 있습니다.
바로 HTML 출력 시 이스케이프 처리입니다.
htmlspecialchars($value, ENT_QUOTES, 'UTF-8');문제는 이 코드가 길고 매번 옵션을 쓰기 귀찮고 실수하기 쉽다는 점입니다.
그래서 저는 아래처럼 출력 전용 헬퍼 함수를 하나 만들어 사용합니다.
전역 함수파일에 가장 우선적으로 만들어 두는 헬퍼 함수입니다.
function h(mixed $s): string {
return htmlspecialchars(
(string)$s,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
}
각 요소가 왜 필요한지를 하나씩 설명하면...
h() 함수의 역할
h()는 한마디로 말해 “HTML에 출력해도 안전한 문자열로 변환하는 함수”입니다.
즉, 다음과 같은 위험한 입력이 있더라도 출력 시에는 브라우저가 실행(XSS, Cross-Site Scripting 방지)하지 못하도록 이렇게 문자 그대로 보이게 만드는 것이 목적입니다.
- "><script>alert(1)</script>
- "><script>alert(1)</script>
왜 htmlspecialchars()를 직접 안 쓰고 h()로 감싸는가?
① 타이핑과 가독성 짧고, 읽기 쉽고, “여기는 안전한 출력이다”라는 의도가 바로 보이기 때문입니다.
② 옵션 실수 방지
사람이 직접 쓰면 꼭 이런 코드가 섞입니다.
htmlspecialchars($s); // 옵션 빠짐
htmlspecialchars($s, ENT_COMPAT); // 작은따옴표 미처리
헬퍼 함수로 감싸면 항상 같은 규칙을 강제할 수 있습니다.
③ mixed $s를 사용하는 이유
function h(mixed $s): string
왜 string $s가 아닌가?
HTML에 출력되는 값은 문자열만 있는 게 아닙니다.
$row['id']; // int
$row['view_count']; // int
$row['price']; // float
이런 값들도 결국 HTML에서는 문자열로 출력됩니다.
PHP 8에서 mixed를 사용하면 아래의 출력 가능한 모든 타입을 안전하게 받을 수 있습니다.
- string
- int
- float
- null
- bool
④ (string)$s 캐스팅의 의미
(string)$s
이 부분은 의도적인 설계입니다.
“출력 시점에서는 모든 값을 문자열로 취급한다”라는 정책을 코드로 명확히 표현한 것입니다.
또한 PHP 8에서 타입 경고 없이 안정적으로 동작합니다.
⑤ 왜 ENT_QUOTES만 쓰는가
이 옵션은 " (큰따옴표), ' (작은따옴표) 둘 다 HTML 엔티티로 변환합니다.
HTML 속성은 이렇게 쓰일 수 있습니다.
<input value='<?= h($value) ?>'>
<input value="<?= h($value) ?>">
ENT_COMPAT처럼 작은따옴표를 변환하지 않으면 속성 탈출 → XSS로 이어질 수 있습니다.
실무에서는 ENT_QUOTES로 하는 것이 가장 좋은 선택입니다.
⑥ ENT_SUBSTITUTE를 추가한 이유
ENT_QUOTES | ENT_SUBSTITUTE
ENT_SUBSTITUTE는 깨진 UTF-8 문자열이 들어왔을 때 경고를 내거나 출력이 깨지는 대신 문제 되는 문자를*�로 대체합니다.
운영 환경에서는 에러 없이, 화면이 깨지지 않는 것이 훨씬 중요합니다.
⑦. 'UTF-8'을 명시하는 이유
- 기본 인코딩에 의존하지 않기 위해
- 한글/멀티바이트 환경에서 일관성 유지
- 보안 이슈 방지
이 함수를 제작하고 사용하는 이유는
- “HTML에 출력되는 모든 값은 의심하고, 문자열로 만들고, 안전하게 이스케이프한다.”
- 그래서 h()는 귀찮음을 줄여주고 실수를 막아주고 코드의 보안 의도를 명확하게 드러냅니다.
- 작은 함수 하나지만, 이게 프로젝트 전체의 보안 수준을 결정합니다.