Skip to content
블로그로 돌아가기
튜토리얼

온라인 텍스트 비교 완전 가이드: LCS/Myers 알고리즘과 6 사례 | Go Tools

두 텍스트를 온라인에서 비교합니다. side-by-side 와 unified diff 뷰, LCS/Myers 알고리즘, text vs JSON diff 선택 기준을 정리.

14 분 소요

온라인 텍스트 비교 완전 가이드: LCS/Myers 알고리즘과 6 사례

온라인 텍스트 비교 도구는 한 가지 질문에 빠르게 답합니다. 버전 A 와 버전 B 사이에서 실제로 무엇이 바뀌었는가? 두 덩어리 텍스트를 붙여 넣으면 도구가 Longest Common Subsequence 알고리즘을 실행하고, 모든 삽입·삭제·수정 내용을 좌우 대조 또는 unified 뷰로 보여 줍니다. 보통 1 밀리초도 걸리지 않습니다.

이 가이드는 코드 리뷰를 하는 개발자, 로그 구간을 비교하는 SRE, 계약서를 redline 처리하는 법무 담당자, 편집본을 검토하는 작가를 위한 글입니다. 알고리즘(LCS, Myers, Patience), 두 가지 표준 뷰, “전부 바뀐 것처럼 보인다”는 불만의 95%를 해결하는 ignore 옵션, JSON diff 를 대신 써야 할 때, 복사해서 바로 쓰는 6 가지 사례, 알고리즘 자체가 설명해 주는 함정을 다룹니다.

지금 바로 두 텍스트를 비교하고 싶다면 Text Diff 를 여세요. 모든 처리는 브라우저 안에서 실행되며 업로드되지 않습니다.

1. 텍스트 비교란?

텍스트 비교란 한 텍스트를 다른 텍스트로 바꾸는 가장 작은 삽입·삭제 집합이며, 각 행을 추가·제거·변경 없음으로 표시합니다. 현대 diff 는 여기에 한 번 더, 단어 또는 문자 단위 비교를 덧붙입니다. 그래서 한 글자만 바꿔도 전체 행 대신 해당 토큰만 강조됩니다.

1.1 문자 일치(===) 비교만으로는 부족한 이유

200 행짜리 설정 파일 맨 위에 한 행을 삽입해 보세요. 단순 문자 비교는 삽입 지점 이후의 모든 바이트를 다르다고 보고합니다. 텍스트 자체는 그대로이고 위치만 옮겨졌을 뿐인데도 말입니다. diff 알고리즘은 “이후 199 행은 여전히 같은 행이고 한 칸씩 밀렸을 뿐”이라는 사실을 인식해 삽입 한 건으로 보고해야 합니다. 이 인식이 바로 LCS 가 제공하는 능력이며, git, GitHub, 그리고 모든 코드 리뷰 도구가 LCS 를 기본으로 채택한 이유입니다.

1.2 Side-by-side vs unified diff

좌우 대조 뷰는 두 버전을 나란히 두 열로 배치하고 셀에 색을 입힙니다. 추가는 녹색, 삭제는 빨강, 수정은 노랑입니다. Unified diff 는 GNU diff 시절부터 쓰던 더 오래된 텍스트 형식으로, 한 열에 -+ 마커를 쓰고 변경 부위 주위로 3 행씩 문맥을 보여 줍니다. 같은 비교지만 표현 방식이 다른 두 가지 뷰입니다. 4 장에서 각각 언제 쓰는지 다룹니다.

1.3 텍스트 비교가 쓰이는 곳

GitHub 와 GitLab 의 코드 리뷰. 로컬 git diff 출력. Slack 에 붙여 넣은 패치. 계약서 redline. 번역 검수. +/- 출력으로 실패하는 CI 스냅숏 테스트. 장애 타임라인 조사. 두 개의 .env 파일 비교. 두 텍스트 덩어리를 행 단위로 맞춰 봐야 하는 모든 상황입니다.

Text Diff 를 열고 두 텍스트를 붙여 넣어 이 모든 것을 직접 확인하세요. 모든 비교는 브라우저 안에서 실행됩니다.

2. 텍스트 비교의 알고리즘 (LCS + Myers + Patience)

2.1 Longest Common Subsequence

두 행 시퀀스 A 와 B 가 주어졌을 때, Longest Common Subsequence 는 양쪽에 같은 순서로 등장하는(인접할 필요는 없는) 가장 긴 행 목록입니다. LCS 를 구하고 나면 diff 는 단순합니다. A 에 있지만 LCS 에 없는 행은 제거, B 에 있지만 LCS 에 없는 행은 추가, LCS 에 있는 행은 변경 없음입니다.

고전 LCS 는 N × M 크기의 동적 계획법(DP) 표로 동작합니다. 셀 (i, j) 는 A 의 첫 i 행과 B 의 첫 j 행의 LCS 길이를 담습니다. 표를 좌→우, 위→아래로 채운 뒤 우하단 셀에서 거꾸로 거슬러 편집 스크립트를 재구성합니다. 시간과 공간 모두 O(N×M) 입니다. 2000 행짜리 파일 두 개는 무난하지만 10 만 행짜리 로그에는 느립니다.

2.2 Myers (1986)

Eugene Myers 의 1986 년 논문 “An O(ND) Difference Algorithm and Its Variations” 는 문제를 편집 그래프 위의 최단 경로로 재정의했습니다. 노드는 두 입력의 위치 (i, j), 수평 이동은 삭제, 수직 이동은 삽입, 대각선 이동은 일치입니다. 최단 경로가 곧 최소 편집 스크립트입니다.

Myers 는 O((N+M)D) 로 동작하며 여기서 D 는 편집 스크립트의 크기입니다. 두 텍스트가 비슷할 때, 즉 diff 의 일반적인 경우 D 가 작아서 알고리즘은 사실상 선형입니다. git diff, GNU diff, GitHub PR 렌더러의 기본값입니다. 웹 입력의 99% 에 대해서는 이것이 정답입니다.

2.3 Patience diff (Bram Cohen, 2005)

Patience diff 는 접근법이 다릅니다. 각 입력에 정확히 한 번만 등장하는 행(“고유 앵커 행”)을 찾아 짝지은 뒤, 앵커 사이의 빈 구간에서 재귀적으로 처리합니다. 수학적으로는 더 지저분합니다(최악의 경우는 여전히 나쁨). 그러나 결과물의 가독성은 코드 위에서 훨씬 좋습니다.

왜 그럴까요? Myers 는 편집 거리를 최소화합니다. 수학적으로는 최적이지만, 그 최적 정렬이 무관한 중괄호나 빈 줄을 가로지르면 시각적으로는 읽기 어려워집니다. Patience 는 흔한 보일러플레이트(모든 파일에 있는 } 행, 모든 파일에 있는 빈 줄)에서는 정렬을 거부합니다. 그래서 함수 경계가 온전히 유지됩니다. Bram Cohen 은 이를 Bazaar 를 위해 만들었고, Git 은 git diff --patience 로 제공합니다. 비슷한 Histogram 알고리즘(git diff --histogram)은 결과 품질이 비슷하면서 약간 더 빠릅니다.

같은 파일의 두 버전에서 함수 하나가 위치를 옮겼다고 상상해 보세요. Myers 는 함수 A 의 닫는 중괄호와 함수 B 의 닫는 중괄호를 정렬해 본문이 완전히 다르다고 보고할 수 있습니다. Patience 는 고유한 함수 이름을 앵커로 잡아 깔끔한 이동으로 보고합니다. 같은 입력, 완전히 다른 리뷰 경험입니다.

2.4 알고리즘 비교

속성Myers (기본)PatienceHistogram
시간 복잡도O((N+M)D)일반적으로 ~O(N log N)Patience 와 유사
최적 편집 거리예, 최단 스크립트아니요, 더 길 수 있음아니요, 더 길 수 있음
코드에서 자연스러운 가독성중괄호와 빈 줄이 어긋날 때가 있음매우 좋음 — 고유 행 앵커매우 좋음
사용처git 기본, GNU diff, GitHub UIgit diff --patience, Bazaargit diff --histogram
적합한 용도대다수 입력에 대한 속도와 정확성코드 리뷰, 리팩터링 diffPatience 와 동일, 약간 더 빠름

2.5 이 도구의 동작 방식

Text Diff 는 고전 DP 기반 LCS 에 두 가지 적극적인 최적화를 더했습니다. 공통 prefix/suffix 트리밍, 그리고 행 내 단어 단위 diff 를 위한 토큰 수준의 두 번째 LCS 패스입니다. 한 행만 바뀐 2000 행 설정 파일 두 개의 diff 는 트리밍 후 1×1 DP 표로 축소되어 1 밀리초 이내에 렌더링됩니다. 전형적인 웹 입력에서는 Myers 와 DP 의 차이가 보이지 않습니다. 둘 다 브라우저가 결과를 그리는 것보다 더 빠르게 끝나기 때문입니다.

3. 행 내 단어 단위 diff — 한 글자만 바꿔도 행 전체가 강조되는 이유

한 식별자만 바꿨는데 전체 행이 빨강과 녹색으로 물듭니다. 버그일까요? 아닙니다, 설계입니다.

diff 는 먼저 행 수준에서 LCS 를 실행합니다. “14 행이 교체되었음.” 그런 다음 교체된 모든 쌍에 대해 토큰 수준에서 두 번째 LCS 를 실행합니다. 토큰은 Unicode 단어 경계로 분리해 만듭니다. 글자와 숫자의 연속체는 함께 묶이고, 공백 문자와 구두점은 각각 독립적인 토큰이 됩니다. 두 번째 LCS 가 그 행 안의 최소 토큰 수준 편집 스크립트를 만들어 냅니다.

렌더러는 행 전체를 강조 색으로 그려서 눈에 띄게 만든 다음, 바뀐 토큰만 밝은 배경으로 칠합니다. 그 주변의 변경 없는 토큰은 같은 색의 어둡게 처리된 버전을 입혀, 표시는 되지만 시각적으로는 조용히 머무릅니다. 시선이 정확한 편집 위치에 머무릅니다.

예시 1: 식별자 이름 변경. function getUser(id)function getUser(userId) 로 바뀝니다. 행 전체가 수정으로 표시됩니다. 행 안에서는 id(빨강에 취소선)와 userId(밝은 녹색)만 inline 강조를 받습니다. 나머지는 어둡게 표시됩니다.

예시 2: 로그 지연 시간 변화. POST /api/orders 201 88msPOST /api/orders 201 4200ms 가 됩니다. 행은 수정 상태입니다. 행 안에서는 884200 만 밝게 표시됩니다. 경로, 메서드, 상태 코드는 어둡게 유지됩니다. 장애 타임라인을 읽는 사람이 필요로 하는 그대로입니다.

너무 많은 토큰이 바뀌면 단어 수준 강조는 오히려 잡음이 됩니다. 그럴 때 도구는 짝지어진 remove + add 표현으로 후퇴합니다. 원래 행을 제거로, 새 행을 추가로 보여 주고 행 내 색칠은 하지 않습니다. 임계값은 대략 “토큰의 절반 이상이 다를 때” 입니다.

요약하면 행 수준 diff 는 어느 행이 바뀌었는지 알려 주고, 단어 수준 diff 는 그 행의 어느 문자가 변경을 담고 있는지 알려 줍니다. Text Diff 안에서 Sample 을 클릭해 같은 입력으로 두 뷰를 비교해 보세요.

4. Side-by-side vs unified diff — 한 diff, 두 가지 뷰

4.1 Side-by-side 뷰

두 열입니다. 왼쪽이 원본, 오른쪽이 수정본입니다. 일치하는 행은 수평으로 정렬됩니다. 추가된 행은 오른쪽 열에만 녹색 배경으로 나타나고, 제거된 행은 왼쪽 열에만 빨간 배경으로 나타나며, 수정된 쌍은 노란색 거터를 사이에 두고 나란히 자리하고 행 내 단어 강조가 덧붙습니다.

좌우 대조는 사람이 diff 를 읽을 때 쓰세요. PR 리뷰, 교육, 데모, 비기술자에게 계약서 변경을 짚어 줄 때 적합합니다. 눈을 위한 뷰입니다.

단점은 이동성입니다. 좌우 대조 렌더링은 Slack 에 붙여 넣어도 누군가 적용할 수 없습니다. patch 로 파이프 할 수도 없습니다. 공유하고 적용하려면 unified 가 필요합니다.

4.2 Unified diff 형식

Unified diff 는 GNU diff 가 정의하고 POSIX 가 표준화한 50 년 묵은 일반 텍스트 형식입니다. 완전한 예시:

--- original
+++ modified
@@ -1,3 +1,4 @@
 1. The service is provided as-is.
 2. Either party may terminate with 30 days notice.
+2a. Termination notice must be in writing.
 3. Disputes are resolved in California courts.

처음 두 행은 원본 파일을 명명합니다. @@ -L,C +L,C @@ 행은 hunk 헤더입니다. -L,C 는 원본의 L 행부터 시작해 C 행이 관련됨을 의미하고, +L,C 는 수정본에 대해 같은 의미입니다. hunk 안에서 공백으로 시작하는 행은 문맥(변경 없음)이고, - 는 제거, + 는 추가입니다.

각 변경의 위아래로 3 행씩 문맥을 두는 것이 GNU 기본값입니다. 대부분의 도구는 -U n 으로 바꿀 수 있습니다. 문맥을 없애려면 diff -U0, 10 행으로 늘리려면 diff -U10 입니다. hunk 헤더가 선택한 값을 따라갑니다.

Text Diff 에서 Unified 탭을 클릭해 뷰를 전환하거나 Copy unified diff 를 눌러 패치를 클립보드에 담으세요.

4.3 Unified diff 의 이식성

Unified diff 는 이동합니다. 텍스트 변경의 만국 공통 화폐입니다.

대상unified diff 수용?방법
GNU patchpatch -p1 < diff.patch
git applygit apply diff.patch
GitHub PR 리뷰 댓글예 (```diff 블록 안)색상까지 렌더링
GitLab MR 댓글같은 펜스 블록
Bitbucket / Azure DevOps PR같은 펜스 블록
Slack / Discord 붙여넣기부분코드 블록 안에서 텍스트로 렌더링, 색상 없음
VS Code “Open Patch”Source Control 에서 패치 적용
Jira / Linear 이슈 본문부분코드 블록에서 동작, 적용 버튼 없음

같은 9 행의 ---/+++/@@ 텍스트가 patchgit apply 에 적용되고, 세 가지 PR 플랫폼에서 렌더링되며, Slack 붙여넣기에서도 살아남습니다. 다른 어떤 diff 형식도 이 정도 도달 범위를 갖지 못합니다.

4.4 어느 쪽을 고를까

리뷰는 side-by-side, 공유와 적용은 unified 입니다. 본인이 diff 를 읽는 중이라면 두 열이 더 빠릅니다. 다운스트림에서 누군가 또는 어떤 도구(리뷰어, 도구, patch 명령)가 그것을 소비해야 한다면 unified 형식으로 복사하세요.

5. Ignore 옵션 — 공백 문자, 대소문자, 빈 줄, 줄바꿈

“전부 바뀐 것처럼 보인다”는 불만의 대부분은 잡음입니다. 네 가지 토글이 그 중 95%를 해결합니다.

  1. Ignore caseAa 로 매핑합니다. git diff -i 와 동등합니다. 환경 변수 비교, SQL 키워드 스타일 감사처럼 관습은 대문자 또는 소문자로 갈리지만 의미가 같은 상황에 사용하세요.
  2. Ignore all whitespace 는 비교 전에 모든 공백, 탭, 줄바꿈을 축소합니다. git diff -w 와 동등합니다. 탭 ↔ 공백 재서식, 들여쓰기 재작성, 행 수를 망가뜨리는 “Prettier 로 전환했어요” 류 diff 의 해독제입니다. 이런 변경에 ignore-whitespace diff 를 적용하면 보통 87 개 수정에서 4 개로 줄어듭니다.
  3. Ignore trailing spaces and tabs 는 행 끝의 공백 문자만 제거합니다. git diff -b 와 동등합니다. Windows 와 Unix 사이를 오가며 복사한 뒤 발생하는 CRLF 잡음의 해독제입니다. 행 끝 \r 문자가 걸러지고 실제 내용이 정렬됩니다.
  4. Ignore blank lines 는 diff 전에 빈 줄을 떨어뜨립니다. 산문 diff 에서 “한 단락 사이에 빈 줄을 하나 추가했더니 12 번 단락이 완전히 다르게 보입니다” 류 문제의 해독제입니다.

“87 개 수정”으로 보고되는 200 행 설정 파일은 Ignore all whitespace 를 켠 뒤 보통 “4 개 수정”으로 떨어집니다. 모든 행이 표시되는 Windows-to-Unix 복사본은 Ignore trailing spaces 로 0 개가 됩니다. 각 토글은 독립적이며 세션 사이에 유지됩니다.

CRLF vs LF. Windows 줄바꿈은 \r\n, Unix 는 \n, 고전 Mac 은 \r 입니다. 정규화하지 않는 Unix 편집기로 Windows 파일을 열면 행 끝 \r 이 남습니다. 모든 행이 “내용은 일치하지만 끝에 \r 이 있음”으로 diff 됩니다. Ignore trailing spaces 가 실제 변경은 놓치지 않고 이 잡음을 잠재웁니다.

한 가지 주의. Ignore 옵션은 양날의 검입니다. Ignore case 를 켜면 LOG.errorlog.Error 로 바꾸는 리팩터링이 같아 보입니다. Ignore all whitespace 를 켜면 Python 들여쓰기 버그가 보이지 않게 됩니다. 묻고 싶은 질문에 맞춰 토글을 고르고, 끝나면 다시 끄세요.

6. Text diff vs JSON diff vs Git diff — 결정 매트릭스

텍스트 비교는 구조에 대한 이해 없이 행과 단어를 맞추는 작업입니다. 산문에는 바로 그 점이 좋고, JSON 에는 바로 그 점이 나쁩니다.

6.1 결정 매트릭스

입력 유형Text diffJSON diffGit diff
산문 / Markdown / 계약서최적잘못된 도구부분 (추적된 파일만)
코드 스니펫 (단일 파일 붙여넣기)최적잘못된 도구부분 (저장소 필요)
저장소 내 코드 (다중 파일)부분잘못된 도구최적
API JSON 응답잘못된 도구 (키 순서로 인한 오탐)최적잘못된 도구
YAML / TOML 설정부분 (키 순서로 인한 오탐)최적 (변환 후)부분
CSV 행 단위부분잘못된 도구잘못된 도구
로그 / heredoc최적잘못된 도구잘못된 도구
바이너리 파일잘못된 도구잘못된 도구git diff --binary

6.2 텍스트 비교가 적절하지 않을 때

전형적인 실수 세 가지입니다.

키 순서가 뒤바뀐 JSON. {"a":1,"b":2}{"b":2,"a":1} 은 같은 JSON 문서입니다. 텍스트 비교는 모든 행이 바뀌었다고 보고합니다. 진짜로 다른 행이기 때문입니다. JSON Diff 를 쓰세요. JSON 키가 순서 없음을 이해합니다.

재서식된 YAML 설정. 값 하나를 바꾸고 포매터를 돌리면 들여쓰기, 키 순서, 따옴표가 모두 옮겨갑니다. 텍스트 비교는 전체 재작성으로 보고합니다. 두 파일을 먼저 JSON 으로 변환한 다음 JSON Diff 로 비교하세요.

이름 변경이 포함된 다중 파일 리팩터링. Git 은 이름 변경을 추적하지만 텍스트 비교는 그렇지 않습니다. 두 트리를 파일 연결한 한 덩어리로 비교하면 모든 파일 간 이동이 제거 + 추가로 나타납니다. 대신 git diff(또는 git diff --find-renames=80%)를 쓰세요.

6.3 텍스트 비교가 정확히 들어맞을 때

산문. 어디선가 붙여 넣은 코드 스니펫. 계약서 redline. 로그 구간. 자연어 문장을 맞춰 보는 번역 검수. 셸이 위에서 아래로 읽기 때문에 순서가 중요한 .env 파일. 행 자체가 의미를 담는 모든 상황입니다.

JSON diff 에서 잡음(타임스탬프, 요청 ID, 자동 생성된 UUID)을 걸러내는 심층 가이드는 JSON Diff에서 타임스탬프와 ID를 무시하는 방법 을 읽어 보세요.

7. 6 가지 실전 사례 (복사해서 바로 쓰는 입력)

7.1 코드 리뷰 스니펫 — 함수 이름 변경

PR 을 리뷰 중입니다. 작성자가 iduserId 로 바꾸고 가드 절을 추가했습니다. 두 버전을 붙여 넣으세요.

// Original
function getUser(id) {
  const u = db.users.find(x => x.id === id);
  return u;
}
// Modified
function getUser(userId) {
  if (!userId) return null;
  const u = db.users.find(x => x.id === userId);
  return u;
}

diff 는 세 행의 수정과 한 행의 추가를 보여 줍니다. inline 단어 강조가 모든 iduserId 토큰을 표시하고, 새 가드 절은 녹색 배경으로 등장합니다. Ignore 옵션은 모두 꺼 두세요. Text Diff 에서 시도해 보고 unified 출력을 복사해 리뷰 댓글로 남기세요.

7.2 계약서 또는 정책 redline — 한 조항 삽입

50 단락짜리 계약서에 조항 하나가 끼어들었습니다. 어제 버전을 왼쪽에, 오늘 버전을 오른쪽에 붙여 넣으세요.

1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
3. Disputes are resolved in California courts.
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
2a. Termination notice must be in writing.
3. Disputes are resolved in California courts.

diff 는 변경 없는 49 행과 추가된 한 행(+2a. Termination notice must be in writing.)을 렌더링합니다. unified diff 를 내보내 법무 리뷰 흔적으로 남기세요.

7.3 로그 타임라인 조사

지연 시간 회귀가 의심됩니다. 장애 직전과 장애 중의 액세스 로그 구간을 가져오세요.

GET /api/users 200 14ms
POST /api/orders 201 88ms
GET /api/orders/42 200 21ms
GET /api/users 200 14ms
POST /api/orders 201 4200ms
GET /api/orders/42 500 21ms

inline 강조가 884200(50 배 지연 점프)과 200500(주문 상세 엔드포인트의 실패 시작)을 표면화합니다. 필드 추출, 엔드포인트별 그룹화, 백분위 계산 같은 더 깊은 로그 작업이 필요하다면 로그가 JSON 일 때 diff 를 jq 치트시트 와 함께 쓰세요.

7.4 번역 검수 — placeholder 보존

새 번역 회사에 일을 맡겼고, 새 카피의 구조가 기존과 일치하는지 확인하고 싶습니다. 이전 번역을 왼쪽에, 새 번역을 오른쪽에 붙여 넣으세요. 번역가가 문자열 끝에 공백을 무심코 붙이는 일이 흔하므로 Ignore trailing spaces / tabs 를 켜세요.

diff 는 모든 {username}, {count}, %s placeholder 가 제자리에 있는지 확인해 줍니다. 자연어 텍스트만 바뀌고 placeholder 가 누락되면 inline diff 에서 제거된 토큰으로 표시됩니다. 출시 전에 잡힙니다. placeholder 형식 자체를 비교해야 한다면 정규표현식 치트시트\{\w+\} 와 유사 패턴을 다룹니다. Text Diff 에서 시도해 보세요.

7.5 설정 또는 .env 감사 — 프로덕션 vs 스테이징

두 개의 .env 파일을 비교하세요. 단락 형태의 그룹화가 섹션을 잘못 정렬하지 않도록 Ignore blank lines 를 켜세요. diff 는 어느 키의 값이 다른지, 어느 키가 한 환경에만 존재하는지, 어디서 주석이 동기화에서 벗어났는지 보여 줍니다. 5 분이면 “스테이징에서는 되는데 프로덕션에서는 안 됩니다” 류 디버깅 세션을 사전에 막을 수 있습니다.

7.6 산문 또는 초고 수정

편집자가 초고를 돌려보냈습니다. 원본을 왼쪽에, 편집본을 오른쪽에 붙여 넣으세요. inline 단어 diff 가 어느 문장이 다시 쓰였고 어느 문장은 그대로이며 어느 단락이 끼어들었는지 정확히 보여 줍니다. 변경 사항을 하나씩 수락하거나 거절하면 됩니다. Track Changes 기능도, Word 파일도, 독점 형식도 필요 없습니다.

8. 자주 마주치는 함정과 증상으로 읽는 법

알고리즘의 동작 방식이 대부분의 사용자 불편을 설명합니다. 흔한 다섯 가지 불만과 그 의미입니다.

Pitfall 1: “Windows-to-Unix 복사 뒤 모든 행이 빨강입니다.” 증상: 내용은 같아 보이는데 diff 의 모든 행이 변경됨으로 나옵니다. 원인: CRLF 줄바꿈의 행 끝 \r 문자. 수정: Ignore trailing spaces / tabs 를 켜세요. diff 가 실제 변경만 남기고 떨어집니다.

Pitfall 2: “JSON 을 붙여 넣었더니 100% 가 다릅니다.” 증상: 동등해야 할 두 JSON 객체가 완전히 다르게 표시됩니다. 원인: 키 순서 변경. 텍스트 비교는 행 순서를 중요하게 보지만 JSON 은 그렇지 않습니다. 수정: 어떤 JSON 입력이든 JSON Diff 를 쓰세요.

Pitfall 3: “탭 ↔ 공백 재서식이 diff 를 폭파시켰습니다.” 증상: 87 개 수정, 전부 들여쓰기입니다. 원인: 포매터가 모든 행의 선두 공백 문자를 바꿔 놓았습니다. 수정: Ignore all whitespace 가 잡음을 축소하고 실제 의미 변경을 표면화합니다.

Pitfall 4: “diff 는 동일하다는데 cmp 는 다르다고 합니다.” 증상: diff 는 차이가 없다고 보고하는데 바이트 수준 비교는 파일이 다르다고 합니다. 원인: 이전 세션에서 켜 두었던 ignore 옵션이 실제 변경을 가립니다. 수정: Ignore 옵션 패널을 열어 모든 토글을 끄고 다시 diff 하세요.

Pitfall 5: “짧은 한 편집이 remove + add 로 나옵니다.” 증상: 작은 변경이 inline 강조 대신 따로 떨어진 제거 행과 추가 행으로 나타납니다. 원인: 바뀐 토큰의 비율이 inline 임계값을 넘어 렌더러가 짝지어진 행 표현으로 후퇴했습니다. 버그가 아니라 설계입니다. patch 도구가 기대하는 고전적 -/+ 쌍을 보려면 Unified 뷰로 전환하세요.

9. 프라이버시, 성능, 그리고 명령줄로 옮겨야 할 때

Text Diff 의 모든 비교는 브라우저 안의 JavaScript 로 실행됩니다. 업로드도, 임시 파일도, 서버 로그도, 붙여 넣은 텍스트에 대한 분석도 없습니다. 독점 코드, 내부 계약서, 비공개 로그처럼 제3자 서버에 붙여 넣고 싶지 않은 텍스트에 안전합니다.

실용적 한계: 한쪽당 약 5,000 행 또는 1 MB. 합계가 200 KB 를 넘으면 라이브 diff 가 비활성화되고 수동 Diff 버튼으로 전환되어, 입력 중에 페이지가 멈추지 않게 합니다. 5,000 행을 넘으면 입력이 잘리고 경고가 표시됩니다. 이 한계가 존재하는 이유는 diff 가 메인 스레드에서 실행되기 때문입니다(웹 워커 없음). 워커로 넘기고 직렬화하는 비용이 작은 입력에서는 diff 자체보다 더 큽니다.

입력이 브라우저의 능력을 넘어서면 명령줄로 내려가세요.

# 두 파일 간 unified diff
diff -u a.txt b.txt

# 같은 동작이지만 git 의 diff 엔진 사용 (Patience, Histogram, 색상)
git diff --no-index a.txt b.txt
git diff --no-index --patience a.txt b.txt

# 거대한 파일을 위한 스트리밍 diff 뷰어 (Rust, 좌우 대조, 구문 인식)
delta a.txt b.txt

수 메가바이트짜리 로그, 바이너리 파일, 다중 파일 저장소 diff, delta 같은 구문 인식 색상이 필요한 작업, 또는 diff 출력을 다른 도구로 파이프해야 하는 경우에는 명령줄로 전환하세요.

10. Unicode, CJK, RTL — 국제화 텍스트 비교 노트

토크나이저는 Unicode 단어 경계를 세 범주로 나누어 분리합니다. 단어 연속체(\p{L} 글자와 \p{N} 숫자), 단어가 아닌 구두점, 그리고 공백 문자입니다. 각 범주는 자체 토큰을 생성하므로 hello, world!hello, ,, , world, ! 의 다섯 토큰이 됩니다.

CJK 콘텐츠(중국어, 일본어, 한국어)에서는 각 표의문자나 가나가 독립된 토큰입니다. 중국어 문장에서 한 글자만 바꾸면 그 글자만 inline 강조를 받고 행의 나머지는 어둡게 유지됩니다. 단락 수준 구조는 여전히 행 기반이므로, 줄바꿈을 추가하는 문장 재작성은 토큰 수준이 아니라 행 수준 편집으로 나타납니다.

RTL 언어(아랍어, 히브리어)에서는 diff 가 논리적 CSS 방향(ml-, mr- 대신 ms-, me-)을 사용합니다. RTL locale 에서는 거터와 행 열이 자연스럽게 뒤집힙니다. 각 diff 셀 안에서 텍스트 방향은 내용을 따르므로 아랍어 문자열은 오른쪽에서 왼쪽으로 렌더링되고 +- 마커는 시작 거터에 정렬을 유지합니다.

줄바꿈 정규화는 \r\n(Windows), \n(Unix), 그리고 단독 \r(고전 Mac OS 9 이전)을 모두 인식합니다. 세 가지 모두 별개의 행으로 분리되므로, 한 플랫폼에서 다른 플랫폼으로 변환된 파일이 하나의 거대한 행으로 뭉개지지 않습니다.

11. FAQ

온라인 텍스트 비교는 어떻게 동작하나요?

텍스트 비교는 두 입력을 행으로 분할한 뒤 Longest Common Subsequence 알고리즘(보통 Myers 의 O((N+M)D))을 실행해 가장 작은 삽입·삭제 집합을 찾고, 추가(녹색), 제거(빨강), 변경 없음(회색) 행을 강조합니다. 두 번째 토큰 수준 LCS 가 수정된 각 행 안의 바뀐 단어를 표시합니다. Text Diff 는 비교 전체를 브라우저 안에서 로컬로 실행합니다.

텍스트 diff 와 JSON diff 의 차이는 무엇인가요?

텍스트 비교는 행 단위 비교입니다. 산문, 코드, 로그, 계약서에 적합합니다. JSON Diff 는 JSON 의 데이터 모델을 이해합니다. 키 순서는 무관하고, 타입은 엄격하며(1"1"), 배열을 키로 매칭할 수 있습니다. 텍스트 비교에 JSON 을 붙여 넣으면 JSON Diff 가 무시하는 키 순서 변경이나 공백 문자가 변경으로 표면화됩니다. 비구조화 콘텐츠에는 텍스트 비교, API 응답과 설정에는 JSON Diff 를 쓰세요.

한 단어만 바꿨는데 왜 행 전체가 변경된 것으로 보이나요?

그렇지 않습니다. 행에 무언가가 바뀌었기 때문에 행이 강조되지만, 강조 안에서는 바뀐 토큰만 밝은 배경을 받습니다(추가는 녹색, 제거는 빨강에 취소선). 이것이 행 내 단어 단위 diff 입니다. 행 문맥은 읽기 쉽게 유지하면서 시선이 정확한 편집 위치에 머무릅니다. 단어 수준 강조가 유용하지 않을 만큼 행이 많이 바뀌었을 때는 diff 가 별도의 제거 + 추가 쌍으로 후퇴해 구조를 깔끔하게 유지합니다.

diff 에서 공백 문자, 대소문자, 빈 줄을 무시하려면 어떻게 하나요?

Ignore 옵션 패널을 토글하세요. Ignore case 는 Aa 를 같게 만듭니다. Ignore all whitespace 는 모든 공백, 탭, 줄바꿈을 축소합니다. git diff -w 와 동등합니다. Ignore trailing spaces and tabs 는 git diff -b 를 반영해 CRLF 잡음을 잠재웁니다. Ignore blank lines 는 빈 줄을 떨어뜨려 단락 간격 변경이 diff 를 어긋나게 하지 않습니다. 각 옵션은 독립적이며 세션 사이에 유지됩니다.

Unified diff 형식이 무엇인가요?

Unified diff 는 1980 년대 후반에 GNU diff 가 도입한 ---/+++/@@ -L,C +L,C @@ 텍스트 형식이며 git, GitHub, GitLab, 그리고 Unix patch 명령이 사용합니다. 각 hunk 는 변경 주위로 세 행의 문맥을 보여 주고 - 는 제거, + 는 추가입니다. unified 출력을 PR 댓글에 복사하거나 git apply 에 붙여 넣거나 patch -p1 < diff.patch 를 실행하면 깔끔히 적용됩니다.

Myers vs Patience: 코드 리뷰에는 어느 diff 알고리즘이 더 좋은가요?

Myers 는 git diff 와 GNU diff 의 기본값입니다. 빠르고 수학적으로 최소이지만 무관한 빈 줄이나 닫는 중괄호를 정렬해 “이상하게 읽히는” diff 를 만들 때가 있습니다. Patience(Bram Cohen, 2005)는 각 입력에 정확히 한 번 등장하는 행을 앵커로 잡아 그 사이에서 재귀하므로 함수 경계가 온전히 유지됩니다. 리팩터링을 리뷰할 때 git diff --patience(또는 결과가 비슷하고 약간 더 빠른 --histogram)를 사용하세요.

붙여 넣은 텍스트가 서버로 전송되나요?

아닙니다. Text Diff 의 모든 비교는 브라우저 안의 JavaScript 로 로컬 실행됩니다. 텍스트는 업로드되거나 기록되거나 디스크에 저장되거나 제3자에게 전송되지 않습니다. UI 환경설정(뷰 모드와 ignore 옵션 토글)만 다음 방문 때 기억하려고 localStorage 에 저장됩니다. 텍스트는 저장되지 않습니다. DevTools → Network 로 확인해 보세요. Diff 를 클릭해도 요청이 한 건도 발생하지 않습니다.

두 입력은 얼마나 클 수 있나요?

실용적 한계는 한쪽당 약 5,000 행 또는 1 MB 입니다. 합계가 200 KB 를 넘으면 라이브 diff 가 비활성화되고 수동 Diff 버튼으로 전환됩니다. 5,000 행을 넘으면 입력이 잘리고 경고가 표시됩니다. 수 메가바이트 파일은 diff -u a.txt b.txt, git diff --no-index a.txt b.txt, 또는 delta 로 전환하세요. 스트리밍 방식이라 기가바이트도 처리합니다.