호스트 측 템플릿 치환
난독화 파이프라인을 깨뜨리지 않고 서버에서 렌더링한 값(예: Go 템플릿 스타일의 `{{ .Field }}` 플레이스홀더)을 VM 난독화된 JavaScript에 주입합니다.
문제
백엔드(Go, Rails, Django, PHP 등)가 요청마다 내용 일부를 렌더링해야 하는 JavaScript 파일을 제공한다고 해 봅시다.
API 엔드포인트, 기능 플래그 목록, 초기 상태 데이터, 빌드 ID, nonce 같은 값입니다. JS는 vmObfuscation: true로
난독화하지만, 템플릿 엔진이 난독화가 끝난 뒤에 난독화된 출력의 플레이스홀더를 치환해야 합니다. reservedNames와
reservedStrings는 플레이스홀더를 출력에 그대로 남겨 치환할 수 있게 합니다. 이 옵션이 없으면 문자열 플레이스홀더는
VM 바이트코드에 흡수되고 식별자 플레이스홀더는 다시 작성된 여러 위치에 출력되므로, 템플릿 엔진은 치환할 대상을 찾지 못하거나
출력을 깨뜨립니다.
값을 데이터로 불러오는 방법을 먼저 고려하세요
가능하다면 보호된 출력을 아예 수정하지 마세요. 보호된 번들이 실행되기 전에 런타임 구성을 데이터로 불러오면 됩니다.
구성은 별도 스크립트에 두거나 인증된 엔드포인트에서 가져오세요. 이렇게 하면 보호된 출력이 그대로 유지되므로
vmSelfDefending을 계속 켜 둘 수 있습니다. 어느 방식이든 브라우저에 전달된 데이터는 그 브라우저에서 볼 수 있습니다.
값을 정말로 난독화된 파일 안에 치환해야 할 때는 아래 패턴을 사용하세요.
Go 예시에 대한 참고: 이 예시들은 난독화된 출력에 단순 문자열 치환(strings.ReplaceAll / strings.NewReplacer)을
수행하며, 패턴마다 보여 주듯 이스케이프를 직접 처리합니다. Go의 html/template 패키지를 거치지 않습니다.
그 패키지를 거치면 자체적인 문맥별 JavaScript 이스케이프가 추가로 적용되어 페이로드가 이중으로 이스케이프됩니다. html/template으로
렌더링한다면 수동 이스케이프를 빼고 엔진이 한 번만 이스케이프하게 하세요.
어떤 옵션을 선택해야 하나요?
| 서버 값의 형태 | 사용할 방법 |
|---|---|
| JS 값 원문(배열, 객체, 숫자, 불리언 - 모든 JSON 값) | 패턴 1 - reservedNames + 식별자 플레이스홀더 |
| 문자열(일반적인 경우 - 렌더링된 JSON, 빌드 ID, nonce) | 패턴 2 - reservedStrings + 템플릿 리터럴 플레이스홀더 |
| 문자열이며 코드가 ES5를 유지해야 하거나 주변 API가 따옴표 문자열 리터럴을 요구함 | 패턴 3 - reservedStrings + 따옴표 문자열 플레이스홀더 |
난독화 후 치환은 vmSelfDefending: true와 호환되지 않습니다. 이 레시피 끝의
호환성 참고 사항을 참고하세요.
패턴 1 - 식별자 플레이스홀더와 reservedNames
적합한 경우: 따옴표 이스케이프를 전혀 신경 쓰지 않고 JS 표현식 원문(배열, 객체, 숫자 등)을 주입할 때.
실제 코드와 절대 겹치지 않을 독특한 식별자를 정해 직접 참조하고, 그 식별자에 일치하는 정규식을 reservedNames에
추가하세요. VM 난독화에서 이 식별자는 예약 표현식 배열을 거쳐 출력에 그대로 나타납니다. 예를 들면 다음과 같습니다.
식별자를 JSON으로 직렬화한 완전한 표현식으로 바꾸세요. 값을 따옴표나 백틱 안에 넣지 마세요. 식별자는 문자열 리터럴
안에 있지 않으므로 주입된 값은 JS 엔진이 일반 표현식으로 파싱합니다. 신뢰할 수 있는 직렬화 도구를 사용하고, 스크립트가
HTML에 삽입될 때는 <를 이스케이프하며, 따옴표, 백슬래시, 줄바꿈, 백틱, ${...}가 포함된 값으로 테스트하세요.
패턴 2 - 템플릿 리터럴 플레이스홀더와 reservedStrings
적합한 경우: {{ .Field }} 같은 구분자를 요구하는 Go / Jinja 방식의 템플릿 엔진을 사용할 때. 이런 구분자는 백틱으로
감싸면 마침 유효한 JS 텍스트가 됩니다.
플레이스홀더는 보간이 없는 단일 quasi 템플릿 리터럴 안에 둡니다. VM 난독화에서 이는 예약 표현식 배열을 거치므로 백틱 형태 그대로 출력에 유지됩니다.
소스
난독화 옵션
UI
위 소스의 {{.Config.FeatureFlags}} 플레이스홀더를 예약하려면 Reserved Strings 필드에 다음 정규식을 백슬래시를
하나씩만 사용해 추가하세요. UI는 값을 그대로 저장하므로 JS 코드에서와 달리 백슬래시를 두 번 쓰지 않습니다.

API
난독화 후
플레이스홀더는 감싸는 백틱까지 포함해 바이트 단위로 그대로 유지됩니다.
호스트 측 치환
{{.Config.FeatureFlags}}를 템플릿 리터럴용으로 이스케이프한 JSON 문자열로 바꾸세요. 백틱 안에서는 "를 이스케이프할
필요가 없지만, 페이로드 안의 백슬래시, 백틱, ${는 여전히 리터럴을 바꾸거나 끝내 버리므로 이 세 가지는 이스케이프하세요.
런타임에서는 JSON.parse(`{"newCheckout":true,"darkMode":false}`)로 정상 동작합니다.
문자열 값에 작은따옴표/큰따옴표보다 템플릿 리터럴이 나은 이유는 무엇일까요? 백틱을 쓰면 JSON 페이로드 안의 "를
이스케이프할 필요가 없습니다. 서버에서 렌더링하는 값은 대부분 JSON이고 JSON에는 큰따옴표가 가득하므로 이 점이 중요합니다.
큰따옴표 플레이스홀더를 쓰면 내부의 "를 모두 이스케이프해야 하지만(패턴 3 참고), 백틱을 쓰면 드물게 나오는 백슬래시,
백틱, ${만 이스케이프하면 됩니다.
패턴 3 - 따옴표 문자열 플레이스홀더와 reservedStrings
적합한 경우: 소스 코드를 ES5로 유지해야 하거나(템플릿 리터럴 사용 불가), 주변 API가 일반 문자열 리터럴을 기대하는 경우.
플레이스홀더는 작은따옴표나 큰따옴표로 감싼 문자열 리터럴입니다. VM 난독화에서 이는 예약 문자열 배열을 거치며, 이
배열은 JSON.stringify로 직렬화된 JS 배열로 출력됩니다. 따라서 입력에서 어떤 따옴표를 썼든 항상 큰따옴표가 됩니다.
소스
난독화 옵션
UI
위 소스의 {{.Page.Tags}} 플레이스홀더를 예약하려면 Reserved Strings 필드에 다음 정규식을 백슬래시를 하나씩만
사용해 추가하세요. UI는 값을 그대로 저장하므로 JS 코드에서와 달리 백슬래시를 두 번 쓰지 않습니다.

API
난독화 후
호스트 측 치환
플레이스홀더가 큰따옴표로 감싼 JS 문자열 안에 있으므로, 주입하는 JSON 페이로드의 백슬래시와 내부 " 문자를
이스케이프해야 합니다.
결과 출력:
런타임에서는 JSON.parse("[\"news\",\"tech\",\"release\"]") → ["news","tech","release"]가 됩니다.
이스케이프를 잊으면 브라우저가 짝이 맞지 않는 따옴표를 만나 SyntaxError를 던집니다. 패턴 2는 백틱을 사용해
큰따옴표 문제를 아예 피합니다.
한 프로그램에 여러 플레이스홀더 사용하기
세 가지 패턴은 함께 사용할 수 있습니다. 선택(alternation)을 포함한 reservedStrings 정규식 하나로 템플릿 엔진이 출력하는
모든 형태의 플레이스홀더에 일치시킬 수 있습니다. 여기서는 {{ .Field }}와 %{ .Field } 두 스타일 모두입니다.
같은 소스 안에서 식별자 플레이스홀더(JS 값 원문용)와 문자열 플레이스홀더(렌더링된 JSON용)를 섞어 쓸 수 있습니다. 서버가 실제로 주입할 값에 따라 플레이스홀더마다 방식을 고르세요.
호환성 참고 사항
vmSelfDefending은 난독화 후 치환을 깨뜨립니다
VM Self Defending은 빌드된 뒤 난독화된 출력에 생긴 모든 변경(정상적인 템플릿 치환 포함)을 감지하며, 그러면 보호된 코드는 실행을 거부합니다.
호스트 측 템플릿 치환을 사용한다면 vmSelfDefending: false로 설정하세요. selfDefending도 꺼 두세요. VM 난독화에서는
아무 효과가 없지만, VM 없이 사용하면 이 옵션 역시 출력의 어떤 변경도 허용하지 않습니다.
플레이스홀더 팁
- 식별자와 문자열에 같은 플레이스홀더를 재사용하지 마세요.
__TOKEN__같은 예약 이름과__TOKEN__에 일치하는 예약 문자열은 서로 다른 두 코드 경로(예약 표현식 배열과 예약 문자열 배열)를 나타냅니다. 각각 다른 텍스트 형태를 사용하세요. 예를 들어 식별자 플레이스홀더에는UPPER_SNAKE규칙을, 문자열 플레이스홀더에는 구분자로 감싼 형태({{ ... }},%{...},<<<...>>>)를 쓰세요. 그러면 한쪽 정규식의 버그가 다른 쪽에 조용히 일치하는 일이 없습니다. reservedStrings정규식은 문자열 값 자체에 대해 실행됩니다. 정규식은 소스 텍스트가 아니라 문자열의 런타임 값과 비교됩니다.\{\{[^}]+\}\}는{{.something}}(또는 다른{{...}}형태)을 포함하는 문자열에 일치합니다. 플레이스홀더가 추가 내용과 함께 감싸여 있을 수 있다면("prefix-{{.Field}}-suffix") 정규식은 여전히 일치하지만 문자열 전체가 유지되므로, 이에 맞게 치환을 계획하세요.- 어떤 템플릿 엔진과도 동작합니다. 예제는 Go 문법을 사용하지만 javascript-obfuscator 연동에는 Go 전용인 부분이 전혀 없습니다. 난독화 도구의 출력에 문자열 수준의 치환을 할 수 있다면 무엇이든 동작합니다. Rails ERB, Django, PHP short tag, CI 파이프라인의 sed 등이 그 예입니다. 엔진이 자연스럽게 출력하고 실제 JS 문법과 겹치지 않는 구분자를 고르세요.
