# XML 포맷터 — 펼치거나, 납작하게 누르거나, JSON으로

읽으라고 펼친 XML, 보내라고 납작하게 누른 XML, 그리고 양쪽으로 오가는 JSON 변환. 그 어느 것도 남의 서버에 붙여 넣지 않습니다.

> XML을 포맷하고 들여쓰고 압축하며, XML을 JSON으로 또는 JSON을 XML로 바꿉니다. 파서가 브라우저에서 돌고 아무것도 업로드되지 않아서, 피드도 청구서도 설정 파일도 기기를 떠나지 않습니다.

이 페이지는 브라우저 안에서만 동작하는 대화형 도구입니다(https://abox.tools/ko/xml-포맷터/). 도구에 넘긴 어떤 것도 업로드되지 않습니다. 아래는 이 페이지가 도구에 대해 글로 설명하는 내용 전부입니다. 도구를 쓰려면 이 주소를 여세요.

## XML과 JSON은 **절대 업로드되지 않습니다**. 서버가 아예 없으니까요.

포맷과 변환은 문자열에 대한 산술이고, 그 일이 여기, 이 페이지에서 벌어집니다. 파서는 손으로 쓴 것이고 `src/shared/parse-xml.js`에 있으며, 그 밖에는 아무것도 없습니다. 이 도구에는 네트워크 기능 자체가 없어서 가져올 것도 보낼 것도 없습니다. 그것이 여기서는 ‘XML’이라는 말이 풍기는 것보다 더 무겁게 다가옵니다. 이 형식으로 들어오는 것은 대개 청구서나 은행 거래 내역서, 진료 기록, 아니면 헤더에 누군가의 자격 증명이 들어 있는 SOAP 요청이기 때문입니다.

- ✗ 업로드 없음
- ✗ 가입 없음
- ✗ 용량 제한 없음
- ✓ 오프라인 동작
- ✓ 오픈 소스

## XML을 업로드하지 않고 포맷하는 방법

1. **할 일을 고르세요.** 탭 둘에 입력란 하나입니다. *포맷*은 XML을 펼치거나 납작하게 누르고, *변환*은 그것을 JSON으로, 또는 JSON을 다시 XML로 돌립니다. 방금 펼친 XML이 그대로 변환할 XML이라, 두 번 붙여 넣을 일이 없습니다.
2. **붙여 넣거나, 파일을 놓으세요.** 선택해서 복사할 수 있는 것이면 무엇이든 됩니다. `.xml`, `.svg`, `.rss`, `.xsd` 파일을 선택기에 놓으면 사용자의 브라우저가 읽어서 입력란에 넣습니다. 건너뛸 업로드 단계 자체가 없습니다.
3. **들여쓰기를 고르거나, 납작하게 누르세요.** 공백 두 칸, 네 칸, 또는 탭. 납작하게 누른 것은 읽기 좋으라고만 있던 공백을 모두 뺀 같은 문서이고, 결과가 그것으로 몇 바이트를 아꼈는지 알려 줍니다.
4. **오류가 있는 자리에서 오류를 읽으세요.** 여기서 실패한 파서는, 브라우저가 한 번에 읽어 낸 문서를 두고 하는 ‘1행 오류’ 대신 *어떤 태그가* 끝내 닫히지 않았는지를 몇 행 몇 열인지와 함께 말해 줍니다.
5. **결과를 가져가세요.** 복사하거나, 나온 형식의 이름이 붙은 파일로 내려받으세요.

## 상자 속 다른 도구

- [텍스트 비교](https://abox.tools/ko/텍스트-비교/): 두 텍스트를 넣으면 모든 차이가 줄 단위, 단어 단위로 표시됩니다. 남의 서버에 붙여 넣는 것은 없습니다.
- [Base64 인코더 · 디코더](https://abox.tools/ko/base64-인코딩/): Base64, 퍼센트 인코딩, HTML 엔티티, 16진수, 역슬래시 이스케이프를 양방향으로. 남의 서버에 붙여 넣는 것은 없습니다.
- [텍스트·파일 공유](https://abox.tools/ko/텍스트-공유/): 공유는 이 열린 탭 안에서만 삽니다. 읽는 사람은 암호화된 내용을 당신의 브라우저에서 직접 받아 가고, 탭을 닫으면 끝납니다 - 어떤 서버에도 아무것도 남지 않습니다.
- [QR과 바코드 생성](https://abox.tools/ko/qr코드-만들기/): 입력하면 코드가 됩니다. 만드는 데 아무것도 나가지 않습니다.

## 자주 묻는 질문

### 제 XML이 어딘가로 업로드되나요?

아닙니다. 이 페이지의 파서와 프린터는 사용자의 브라우저에서, 사용자의 하드웨어 위에서 도는 함수입니다. 이 도구에는 네트워크 기능 자체가 없어서 무언가를 가져오지도, 보내지도 않습니다. 페이지의 `Content-Security-Policy`에는 연결할 수 있는 모든 주소가 적혀 있고 그중 우리 것은 하나도 없습니다. 이것은 XML에서 이 형식의 평판이 풍기는 것보다 더 무거운 이야기입니다. 여기 들어오는 것은 대개 청구서나 은행 거래 내역서, 진료 기록, 아니면 헤더에 자격 증명이 들어 있는 SOAP 요청이기 때문입니다.

### 외부 엔티티를 해석하나요?

아닙니다. 끌 것도 없습니다. 외부 엔티티 해석은 XML 파서를 구슬려 그것을 실행하는 기기의 파일을 읽게 만드는 방법이고, 보통 XXE라고 쓰는 공격입니다. `src/shared/parse-xml.js`는 엔티티 해석이 전혀 없는, 손으로 쓴 리더입니다. 사용자의 텍스트가 브라우저 자신의 `DOMParser`로 넘어가는 일도 없습니다. `DOCTYPE`은 한 번도 실행되지 않은 채 그대로 지나갑니다.

### XML을 JSON으로 바꾸면 무엇을 잃나요?

혼합 내용의 순서, 주석, 그리고 속성과 자식 요소의 차이입니다. 마지막 것은 지워지기보다 누그러집니다. 속성은 이름이 `@`로 시작하는 멤버가 되기 때문입니다. 요소 자신의 텍스트는 다른 것과 나란히 있어야 할 때 `#text`가 되고, 반복되는 자식은 배열이 됩니다. 모든 값은 문자열로 남습니다. XML에는 타입이 없고, `8080`이 숫자였다고 정하는 것은 정보를 지어내는 일이 됩니다.

### JSON을 XML로 바꾸면 무엇을 잃나요?

빈 객체와 빈 배열과 빈 문자열의 차이입니다. 셋 다 빈 요소가 됩니다. 그리고 모든 값의 타입도 잃습니다. XML에는 타입이 없기 때문입니다. 배열은 반복되는 요소가 되는데, 그것이 다시 읽히는 유일한 모양입니다. 요소 이름이 감당하지 못하는 키는, 어떤 파서도 읽지 않을 문서를 내놓는 대신 껄끄러운 문자를 바꿔 넣습니다.

### SVG나 RSS 피드, POM 파일도 포맷할 수 있나요?

됩니다. 셋 다 XML이고, 이것은 특정 방언이 아니라 XML을 읽습니다. 이렇게 펼친 SVG는 손으로 고치기 쉬워집니다. RSS나 Atom 피드는 보통 납작하게 눌린 채로 배포되어 무언가가 펼치기 전까지는 읽을 수 없습니다. 어떻게 늘어놓든 문서의 뜻은 달라지지 않습니다.

### 다시 들여쓰면 XML의 뜻이 달라지나요?

요소가 요소를 담고 있는 문서라면 달라지지 않습니다. 문제가 될 수 있는 것은 텍스트입니다. 낱말을 담은 요소 안의 공백은 그 텍스트의 일부이므로, 텍스트만 담은 요소는 펼치지 않고 한 줄에 그대로 둡니다. `CDATA` 구간은 있던 그대로 옮겨집니다.

### 왜 브라우저 자신의 XML 파서를 쓰지 않나요?

문서가 깨졌을 때 하는 말 때문입니다. `DOMParser`는 오류 문서를 돌려주는데, 그 표현은 브라우저마다 다르고 흔히 ‘1행 오류’로 귀결됩니다. 손으로 쓴 리더는 어떤 태그가 끝내 닫히지 않았는지, 그것이 어디서 열렸는지를 말해 줄 수 있고, 그것이 바로 알고 싶었던 바입니다. 외부 엔티티를 해석하지 않는다는 것이 나머지 이유입니다.

### 얼마나 큰 파일까지 다룰 수 있나요?

여기에 걸어 둔 제한은 없습니다. 그 값을 치를 서버가 없기 때문입니다. 실제 한계는 사용자의 기기입니다. 몇 메가바이트짜리 XML은 괜찮고, 아주 긴 문서에서는 키보드를 두고 다투는 대신 입력이 멎기를 기다렸다가 다시 포맷합니다.

### 무료인가요? 가입해야 하나요?

무료이고 계정도, 로그인도, 체험 기간도 없으며 얼마나 붙여 넣든 제한이 없습니다. 이 사이트에는 광고가 있고 그것이 비용을 댑니다. 광고에는 텍스트에 관한 것이 아무것도 주어지지 않습니다.

### 오프라인에서도 되나요?

됩니다. 페이지를 한 번 불러온 뒤 인터넷을 끊어도 계속 동작합니다. 이것이 아무것도 업로드되지 않는다는 가장 간단한 증명이기도 합니다. XML을 포맷하러 밖으로 보내는 도구라면 선을 뽑는 순간 멈췄을 것입니다.

## 이 약속을 직접 확인하는 방법

- **붙여 넣은 것이 나갈 곳이 없습니다.** Content-Security-Policy에는 이 페이지가 연결할 수 있는 모든 주소가 적혀 있고, 그중 우리 것은 하나도 없습니다. 붙여 넣은 청구서가 수집될 만한 엔드포인트가 여기에는 없고, 설령 있다 해도 그리로 보낼 코드가 없습니다.
- **여기서는 아무것도 가져오지 않습니다.** `src/` 어디에도 `fetch`도, `XMLHttpRequest`도, `sendBeacon`도 없습니다. 파서와 프린터는 문자열을 받아 문자열을 돌려주는, 이 페이지 안의 함수입니다.
- **외부 엔티티는 결코 해석되지 않습니다.** 외부 엔티티가 들어 있는 `DOCTYPE`은 XML 파서를 구슬려 파싱 중인 그 기기의 파일을 읽게 만드는 방법이고, 이 형식의 가장 오래된 구멍입니다. `src/shared/parse-xml.js`는 엔티티 해석이 아예 들어 있지 않은, 손으로 쓴 리더입니다. 꺼 둔 것이 아니라 없는 것이고, 이 페이지는 사용자의 텍스트를 브라우저 자신의 `DOMParser`에 넘기지도 않습니다.
- **XML에서 나오는 모든 값은 문자열입니다.** `<port>8080</port>`은 그것이 숫자인지에 대해 아무 말도 하지 않으므로, JSON은 `"8080"`이라고 씁니다. 대신 정해 주는 것은, 마치 파일에 있었던 것처럼 계속 실려 다니게 될 정보를 지어내는 일입니다.
- **구글이 불러오는 것과, 구글에 주지 않는 것.** 광고와 측정 스크립트는 구글에서, 후원 버튼은 Buy Me a Coffee에서 옵니다. 그 어느 쪽에도 사용자의 텍스트는 한 글자도 건네지지 않습니다. 그것을 읽고 파싱하고 쓰는 모든 줄은 이 오리진에서 제공되며 저장소에 올라 있습니다.
- **오프라인에서 동작합니다.** 네트워크를 끊어도 이 도구는 그대로입니다. 애초에 네트워크 단계가 없었기 때문입니다. 가장 간단한 증명입니다.

**직접 확인해 보세요.** 위에 적은 어느 것도 그냥 믿으실 필요는 없습니다. 이 페이지는 저장소 안의 템플릿과 설정을 빌드 스크립트가 찍어 낸 결과이고, 그 스크립트는 누구나 읽고 직접 돌려 볼 수 있습니다. 결과물은 `dist` 브랜치에 그대로 올라가 있고요. 그래서 실제로 제공되는 파일과 소스를 빌드한 결과를 나란히 놓고 비교해 보실 수 있습니다: https://github.com/A-Box-of-Tools/website

먼저 읽어 볼 파일을 꼽자면, Content-Security-Policy는 `config/site.toml`, 어떤 태그가 끝내 닫히지 않았는지 알려 주는 파서는 `src/shared/parse-xml.js`를, XML에서 나오는 모든 값이 왜 짐작되지 않고 문자열로 남는지는 `src/convert.js`를 보세요.
