diff는 무엇을 계산하나: 가장 긴 공통 부분열(LCS)
두 텍스트의 차이를 찾는다는 것은 "원본을 수정본으로 바꾸려면 어떤 줄을 지우고 어떤 줄을 넣어야 하는가"를 가장 적은 편집으로 답하는 일입니다. 이 문제는 두 목록에서 순서를 유지한 채 공통으로 뽑을 수 있는 가장 긴 열, 즉 LCS(Longest Common Subsequence)를 구하는 문제와 같습니다. LCS에 속한 항목은 "변하지 않은 것"이고, 나머지는 원본에만 있으면 삭제, 수정본에만 있으면 추가입니다. 그래서 편집 횟수(삭제 + 추가)는 원본 길이 + 수정본 길이 − 2 × LCS 길이로 정해집니다.
이 도구는 동적계획법으로 LCS 표를 채운 뒤 역추적하는 고전적 방식을 씁니다. 시간과 메모리가 두 길이의 곱에 비례하므로, 앞뒤에서 똑같은 부분을 먼저 잘라 내어 비교 범위를 줄이고, 그래도 항목 수의 곱이 너무 크면 안내 메시지를 띄웁니다. 널리 쓰이는 Git의 diff는 Eugene Myers가 제안한 알고리즘(O(ND), 차이 D가 작을수록 빠름)을 변형해 쓰는데, 둘 다 최소 편집을 찾는다는 목표는 같고 동점일 때 어떤 해를 고르느냐에 따라 결과가 조금 다르게 나올 수 있습니다.
손으로 따라 해 보는 예시
다음은 LCS 알고리즘 설명에 자주 쓰이는 문자열 ABCABBA(원본)와 CBABAC(수정본)의 표입니다. 각 칸은 "그 위치부터 끝까지의 LCS 길이"이고, 맨 왼쪽 위 칸이 전체 LCS 길이입니다. 이 값은 이 페이지를 만들 때 코드로 계산한 결과입니다.
| 원본\수정본 | C | B | A | B | A | C | 끝 |
|---|---|---|---|---|---|---|---|
| A | 4 | 4 | 3 | 2 | 2 | 1 | 0 |
| B | 4 | 4 | 3 | 2 | 1 | 1 | 0 |
| C | 4 | 3 | 3 | 2 | 1 | 1 | 0 |
| A | 3 | 3 | 3 | 2 | 1 | 0 | 0 |
| B | 3 | 3 | 2 | 2 | 1 | 0 | 0 |
| B | 2 | 2 | 2 | 2 | 1 | 0 | 0 |
| A | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| 끝 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
결과: LCS 길이는 4, 원본 7개와 수정본 6개이므로 최소 편집 수는 7 + 6 − 2×4 = 5입니다. 한 줄 단위로 바꾸어 보면 더 와닿습니다. 원본이 "사과 / 바나나 / 포도", 수정본이 "사과 / 딸기 / 포도 / 귤"일 때 LCS는 "사과, 포도"로 길이2이고, 편집 수는 3 + 4 − 2×2 = 3입니다. 즉 "바나나" 삭제 1줄, "딸기"와 "귤" 추가 2줄이 나옵니다. 이 도구에 그대로 넣어 보면 같은 결과를 확인할 수 있습니다.
줄·단어·글자 단위의 차이
줄 단위는 코드, 설정 파일, 목록, 계약서 조항처럼 줄이 의미 단위인 문서에 맞습니다. 속도가 빠르고 결과가 간결하지만 줄 안에서 단어 하나만 바뀌어도 그 줄 전체가 삭제와 추가로 표시됩니다. 그래서 이 도구는 삭제 줄과 추가 줄이 서로 짝지어질 때 줄 안에서 실제로 바뀐 단어를 진하게 강조합니다. 단어 단위는 문장이 길게 한 줄로 이어진 글(보고서, 기사 초안)에서 어느 어구가 고쳐졌는지 볼 때 좋고,글자 단위는 오탈자, 숫자 한 자리 차이, 계정·계좌처럼 한 글자 오류가 치명적인 값을 대조할 때 적합합니다. 다만 글자 단위는 길이가 긴 문서에서 처리량이 급증하고, 우연히 맞아떨어지는 글자 때문에 사람이 읽기 어려운 조각난 결과가 나올 수 있으니 짧은 구절에 쓰세요.
| 단위 | 잘 맞는 문서 | 장점 | 주의점 |
|---|---|---|---|
| 줄 | 코드, 설정, 목록, 조항 | 빠르고 결과가 짧음 | 한 단어 수정도 줄 전체가 바뀐 것으로 보임(줄 안 강조로 보완) |
| 단어 | 긴 문장, 보고서 | 고친 어구가 보임 | 한국어는 조사 포함 어절 단위로 나뉨 |
| 글자 | 숫자·이름·URL 대조 | 한 글자 차이도 포착 | 긴 글은 느리고 결과가 잘게 쪼개짐 |
공백·대소문자 옵션은 언제 쓰나
내용은 같은데 들여쓰기만 바뀌거나 줄 끝에 공백이 붙어 모든 줄이 "다름"으로 나올 때는 앞뒤 공백 무시를 켜면 실제 변경만 남습니다. 워드에서 복사한 글처럼 스페이스가 두 번 들어간 경우에는 연속 공백을 하나로가 맞습니다. 대소문자 무시는 영문 문서의 대문자 표기 변경이나 코드가 아닌 일반 글 비교에 유용하지만, 변수명처럼 대소문자가 의미를 갖는 코드에서는 꺼 두어야 합니다. 옵션을 켜도 화면에는 원문 그대로 표시되고, 비교할 때만 해당 차이를 무시합니다. 줄바꿈 문자가 윈도우(CRLF)와 리눅스(LF)로 달라서 모든 줄이 다르게 나올 수 있는 문제는 입력 시점에 줄바꿈을 통일해 방지합니다. 줄 끝에 남은 CR 때문에 같은 내용이 다르다고 표시되는 일은 없습니다.
활용 사례와 읽는 법
초록은 수정본에만 있는 부분(추가), 빨강은 원본에만 있는 부분(삭제)입니다. 계약서나 약관 개정 전후를 비교할 때는 줄 단위로 크게 훑은 뒤 바뀐 줄만 단어 단위로 다시 확인하면 효율적입니다. 코드나 설정 파일은 줄 단위에 공백 무시를 켜서 의미 있는 변경만 보고, JSON이라면 먼저JSON 포맷터로 두 쪽의 들여쓰기와 키 순서를 맞춘 뒤 비교하면 불필요한 차이가 사라집니다. 자기소개서를 고쳐 쓴 뒤에는 글자수 세기로 분량 변화를 함께 확인하고, 문자열 인코딩 때문에 겉보기엔 같은데 다르다고 나오는 값은 텍스트 바이트 계산기의 바이트 수나 유니코드 문자 정보로 눈에 보이지 않는 문자를 찾아보세요. 불필요한 공백이 많다면 공백 정리 도구를 먼저 거치는 것도 방법입니다.
이 도구가 비교하는 순서: 입력부터 화면까지
결과가 왜 그렇게 나왔는지 따져 보려면 내부 단계를 아는 것이 도움이 됩니다. 이 페이지의 스크립트는 다음 순서로 일합니다.
- 줄바꿈 통일: 두 입력에서 CRLF와 단독 CR을 모두 LF로 바꿉니다.
- 토큰 나누기: 줄 모드는 LF 기준으로 자르고, 단어 모드는 "공백 덩어리"와 "공백 아닌 덩어리"를 번갈아 토큰으로 만들며(띄어쓰기도 하나의 토큰), 글자 모드는 유니코드 글자 하나씩 자릅니다. 빈 입력은 토큰이 0개입니다.
- 비교용 키 만들기: 공백 옵션과 대소문자 옵션을 적용한 값을 비교에만 사용합니다. 화면에 보이는 글은 원문입니다. 같다고 판정된 부분은 수정본 쪽 글자가 표시됩니다.
- 앞뒤 잘라내기: 처음부터 끝까지 똑같은 앞부분과 뒷부분은 표를 만들기 전에 제외합니다. 긴 문서에서 일부만 고쳤다면 이 단계에서 계산량이 크게 줄어듭니다.
- 크기 확인: 남은 범위의 (원본 개수 + 1) × (수정본 개수 + 1)이 25,000,000칸을 넘으면 계산하지 않고 안내 문구를 보여 줍니다.
- 표 채우기와 역추적: 칸마다 부호 없는 32비트 정수(4바이트)를 저장하는 표로 LCS를 구한 뒤, 앞에서부터 따라가며 같은 항목은 유지하고 갈림길에서는 삭제를 먼저 선택합니다(동점이면 삭제 우선). 그래서 "x"를 "y"로 바꾸면 삭제 한 줄 다음에 추가 한 줄이 나옵니다.
- 줄 안 강조: 줄 모드에서 연속된 삭제 줄과 추가 줄은 위에서부터 한 쌍씩 짝지은 뒤, 각 쌍을 단어 단위로 다시 비교해 바뀐 단어에 표시를 합니다. 삭제 줄이 3개이고 추가 줄이 5개라면 앞의 3쌍만 줄 안 강조가 적용되고 나머지 2줄은 통째로 추가로 표시됩니다.
한도: 몇 줄·몇 글자까지 비교할 수 있나
양쪽 개수가 같다고 가정하고 25,000,000칸 한도에 맞춰 계산한 표입니다. 앞뒤가 같아 잘려 나가는 부분은 개수에서 빠지므로 실제 여유는 이보다 큽니다. 글자 모드는 글자 하나가 토큰 하나이므로 한글 5,000자 정도가 사실상 한계이고, 같은 글이라도 줄 모드로 비교하면 수십~수백 줄이어서 한도와 거리가 멉니다.
| 양쪽 토큰 수 | 표 칸 수 | 필요 메모리(대략) | 비교 가능 여부 |
|---|---|---|---|
| 500개 | 251,001 | 1.0 MB | 가능 |
| 1,000개 | 1,002,001 | 3.8 MB | 가능 |
| 2,000개 | 4,004,001 | 15.3 MB | 가능 |
| 4,000개 | 16,008,001 | 61.1 MB | 가능 |
| 4,999개 | 25,000,000 | 95.4 MB | 가능 |
| 5,000개 | 25,010,001 | 95.4 MB | 안내 문구 표시 |
같은 두 글, 비교 단위에 따라 달라지는 숫자
요약줄의 "유사도"는 일치한 항목 수의 두 배를 (일치 × 2 + 추가 + 삭제)로 나눈 백분율입니다. 즉 양쪽을 합친 항목 중 일치하는 비율이고, 어떤 단위로 세느냐에 따라 값이 크게 달라집니다. 아래 예문(원본 3줄, 수정본 4줄)으로 세 모드를 계산한 값입니다. 원본은 "서비스 이용료는 월 9,900원입니다 / 해지는 마이페이지에서 가능합니다 / 환불은 7일 이내 요청해야 합니다"이고, 수정본은 이용료를 11,900원, 환불 기한을 14일로 고치고 "문의는 고객센터로 하세요" 한 줄을 더한 글입니다.
| 비교 단위 | 일치 | 삭제 | 추가 | 유사도 |
|---|---|---|---|---|
| 줄 | 1 | 2 | 3 | 약 29% |
| 단어(공백 토큰 제외) | 10 | 2 | 5 | 약 74% |
| 글자 | 58 | 2 | 19 | 약 85% |
줄 모드에서는 세 줄 중 두 줄이 숫자 일부만 고쳐졌어도 통째로 다른 줄이 되어 유사도가 29%로 낮게 나오지만, 글자 모드에서는 85%입니다. 따라서 유사도를 "얼마나 많이 바뀌었나"의 절대 지표로 보지 말고, 같은 단위로 여러 번 비교할 때의 상대적 감각 정도로만 쓰세요. 한편 표시된 숫자와 눈으로 본 차이를 맞춰 볼 때는 줄 모드는 "줄 수", 단어 모드는 "공백을 뺀 단어 수", 글자 모드는 "공백·줄바꿈 포함 글자 수"를 센다는 점을 기억하면 헷갈리지 않습니다.
상황별 사용 시나리오
1. 약관·공지 개정안에서 숫자만 바뀐 곳 찾기
앞의 예문처럼 금액과 기한이 섞인 짧은 공지를 비교할 때는 줄 모드로 시작해 보세요. 두 줄이 삭제·추가 쌍으로 나오고, 쌍 안에서 "9,900원입니다."와 "11,900원입니다.", 그리고 "7일"과 "14일"이 강조됩니다. 단어 모드로 바꾸면 같은 변경이 삭제 2개, 추가 5개로 집계되는데, 추가 중 일부는 새로 들어간 안내 문장의 단어입니다. 마침표가 붙은 어절은 하나의 단어로 취급되므로 "9,900원입니다."처럼 문장부호까지 통째로 바뀐 것으로 보이는 점을 감안하세요. 숫자만 정확히 보려면 글자 모드로 해당 문구만 따로 붙여 넣는 것이 낫습니다.
2. 계좌·주문번호 두 개가 정말 같은지 대조하기
110-234-567890와 110-243-567890를 글자 모드로 비교하면 눈으로는 놓치기 쉬운 자리 바뀜이 드러납니다. 결과는 일치 13글자, 삭제 1글자, 추가 1글자(유사도 약 93%)로, 가운데 "34"가 "43"으로 뒤집혔음을 삭제 한 글자와 추가 한 글자로 알려 줍니다. 이런 용도에서는 유사도가 높아도 의미가 없습니다. 다른 글자가 단 하나라도 있으면 값이 틀린 것이므로 요약줄의 "같습니다" 문구가 나오는지만 확인하세요. 대소문자 무시 옵션은 영문이 섞인 코드(주문번호, 쿠폰 코드)에서는 꺼 두어야 오탐을 막습니다.
3. 서식 때문에 전부 다르게 나오는 설정 파일
탭을 공백으로 바꾸거나 들여쓰기 폭을 바꾼 설정 파일은 줄 모드에서 거의 모든 줄이 다르게 나옵니다. 이때 공백 옵션을 "연속 공백을 하나로"로 두면 줄 안의 여러 공백과 탭이 한 칸으로 간주되어 내용이 달라진 줄만 남습니다. 반대로 YAML이나 파이썬처럼 들여쓰기가 문법인 파일은 공백이 곧 의미이므로 먼저 "그대로 비교"로 보고, 그 뒤 공백 무시로 바꿔 두 결과의 차이가 곧 들여쓰기 변경임을 확인하는 순서가 안전합니다. 또 한 가지, 줄 끝에 줄바꿈이 있는 파일을 붙여 넣으면 마지막에 빈 줄 하나가 추가 토큰으로 생기므로 한쪽만 끝에 엔터가 있으면 빈 줄 하나가 차이로 나옵니다.
비교 결과를 믿기 전에 확인할 것
- 어느 쪽이 원본인지: 삭제(빨강)는 "원본 텍스트" 칸에만, 추가(초록)는 "수정본 텍스트" 칸에만 있는 내용입니다. 반대로 붙였다면 "원본 ↔ 수정본 바꾸기" 버튼으로 뒤집어 확인하세요.
- 옵션이 켜져 있는지: 대소문자나 공백 무시 상태에서 "같습니다"가 나왔다면 그 옵션 기준으로만 같다는 뜻입니다. 최종 제출 문서라면 옵션을 모두 끄고 한 번 더 비교하세요.
- 복사한 값의 눈에 안 보이는 문자: 웹페이지에서 복사한 글에는 줄바꿈 없는 공백(NBSP)이나 제로폭 문자가 섞일 수 있습니다. 같아 보이는데 차이로 나오면 글자 모드로 바꿔 어느 위치인지 찾고, 필요하면 유니코드 문자 정보 도구에서 코드를 확인합니다.
- 입력 분량: 위 표의 한도에 가까운 입력은 계산에 시간이 걸릴 수 있습니다. 글자 모드는 일부 구절만 따로 붙여 넣으세요.
- 복사한 차이의 용도: 복사 결과는 사람이 읽기 위한 표기이고 Git 패치 파일(@@ 구간 표시 포함)이 아닙니다. 패치 적용이 목적이면 Git 같은 전용 도구를 쓰세요.
한계와 이 도구를 쓰면 안 되는 경우
diff는 "이동"을 이해하지 못합니다. 문단을 통째로 다른 위치로 옮기면 한쪽에서 삭제, 다른 쪽에서 추가로 표시됩니다. 표나 서식, 이미지가 들어간 문서(워드, 한글, PDF)의 서식 변경은 비교하지 못하며, 순수 텍스트만 대상으로 합니다. 이 도구의 줄 안 강조는 가까이 있는 삭제·추가 줄을 순서대로 짝지어 비교하므로 줄 순서가 크게 뒤섞인 경우 짝이 맞지 않을 수 있습니다. 3자 병합(merge)이나 버전 관리는 지원하지 않으므로 팀 협업 코드는 Git 같은 도구를 쓰세요. 입력이 매우 길어(항목 수의 곱이 수천만을 넘는 경우) 브라우저가 느려질 수 있어 안내 메시지가 나오면 나누어 비교하세요. 법적 효력이 있는 문서의 최종 대조는 반드시 사람이 원문으로 다시 확인해야 합니다. 입력한 텍스트는 모두 브라우저 안에서만 처리되며 서버로 전송되지 않습니다.
자주 묻는 질문
한 줄에서 단어 하나만 바뀌었는데 줄 전체가 빨강/초록으로 나와요.
줄 단위 비교의 기본 동작입니다. 삭제 줄과 추가 줄이 짝지어지면 그 안에서 실제로 바뀐 단어를 더 진하게 강조하니 진한 부분을 보세요. 줄 전체가 아니라 어구만 보고 싶다면 비교 단위를 단어로 바꾸세요.
"앞뒤 공백 무시"와 "연속 공백을 하나로"는 뭐가 다른가요?
앞뒤 공백 무시는 각 단위의 양 끝 공백만 없애고 비교합니다. 연속 공백을 하나로는 중간에 있는 여러 공백이나 탭을 한 칸으로 보고 비교하므로 띄어쓰기를 두 번 한 차이까지 무시합니다.
내용이 같은데 다르다고 나오는 이유는?
눈에 안 보이는 공백(전각 공백, 제로폭 문자), 줄 끝 공백, 한글의 NFC/NFD 차이, 대소문자 차이가 흔한 원인입니다. 옵션을 바꿔 보고, 글자 단위로 비교하면 어느 글자가 다른지 확인할 수 있습니다.
줄이 옮겨진 것도 감지하나요?
아니요. 위치가 바뀐 줄은 한쪽에서 삭제, 다른 쪽에서 추가로 표시됩니다. 이는 LCS 기반 diff의 일반적인 특성입니다.
Git의 diff와 결과가 다를 수 있나요?
같은 최소 편집 수라도 동점일 때 선택하는 경로가 달라 삭제·추가 줄의 배치가 조금 다를 수 있습니다. Git은 Myers 계열 알고리즘과 가독성 휴리스틱을 쓰기 때문입니다.
차이 복사 버튼은 어떤 형식으로 복사하나요?
줄 단위는 변경 없는 줄 앞에 공백 2칸, 삭제 줄에 "- ", 추가 줄에 "+ "를 붙인 텍스트로 복사합니다. 단어·글자 단위는 삭제를 [-...-], 추가를 [+...+]로 표시합니다.
유사도 퍼센트는 어떻게 계산되나요?
일치한 항목 수에 2를 곱한 값을 (일치 × 2 + 추가 + 삭제)로 나누어 반올림합니다. 두 텍스트가 완전히 같으면 100%이고, 둘 다 비어 있으면 100%로 처리합니다. 비교 단위가 달라지면 같은 글이라도 값이 달라집니다.
입력한 글이 서버로 전송되나요?
이 페이지의 비교 계산은 브라우저 안의 스크립트가 처리하며 입력 내용을 저장하거나 서버로 보내는 코드는 없습니다. 입력 내용은 페이지를 닫거나 새로 고치면 사라집니다. 그래도 비밀번호나 주민등록번호 같은 민감한 값은 공용 컴퓨터에서 붙여 넣지 않는 것이 안전합니다.
어느 정도 길이까지 비교할 수 있나요?
앞뒤의 같은 부분을 뺀 뒤 (원본 개수 + 1) × (수정본 개수 + 1)이 25,000,000을 넘으면 비교하지 않습니다. 개수는 줄 모드에서는 줄, 단어 모드에서는 단어와 공백 토큰, 글자 모드에서는 글자입니다. 안내 문구가 나오면 줄 모드로 바꾸거나 문서를 나누어 붙여 넣으세요.
마지막에 빈 줄 하나가 차이로 나오는 이유는?
줄 모드는 줄바꿈 문자를 기준으로 자르기 때문에, 끝에 엔터가 하나 있는 글은 마지막에 빈 줄이 하나 더 있는 것으로 계산됩니다. 한쪽에만 끝 엔터가 있으면 그 빈 줄이 추가 또는 삭제로 표시됩니다. 끝 엔터를 지우고 다시 비교하면 사라집니다.
한글 자모나 이모지는 글자 모드에서 어떻게 세어지나요?
글자 모드는 자바스크립트의 문자열 순회 단위(유니코드 코드 포인트)로 자릅니다. 완성형 한글 한 글자는 하나로 세지만, 자모가 분리된 형태(NFD)로 저장된 글은 자모마다 따로 세어져 같은 글자로 보여도 다르게 나올 수 있습니다. 이모지도 여러 코드 포인트가 결합된 것은 여러 글자로 나뉠 수 있습니다.