텍스트 비교와 패치(Patch)를 위한 강력한 솔루션, java-diff-utils 심층 분석 및 활용 방안
오늘은 밸둥의 https://www.baeldung.com/java-diff-utils-intro 문서를 읽어보고 포스팅을 작성하게 되었다.
1. java-diff-utils란 무엇인가?
java-diff-utils는 두 텍스트 데이터 간의 차이점을 계산하고, 그 차이점을 '패치(Patch)' 형태로 생성하거나 기존 텍스트에 패치를 적용하는 기능을 제공하는 순수 자바 라이브러리이다 [1]. Git과 같은 버전 관리 시스템에서 사용하는 'diff' 기능의 핵심 로직을 자바 애플리케이션 내에서 손쉽게 구현할 수 있도록 돕는다.
이 라이브러리는 유진 마이어스(Eugene Myers)의 차분 알고리즘(diff algorithm)에 기반하여 효율적으로 텍스트 간의 차이점을 찾아낸다. 따라서 대용량 텍스트 비교 시에도 준수한 성능을 보장한다.
2. 핵심 메커니즘 및 주요 기능
java-diff-utils의 동작은 크게 'Patch 생성'과 'Patch 적용' 두 단계로 나뉜다. 이 과정의 중심에는 Patch, AbstractDelta, Chunk라는 핵심 개념이 있다.
개념 (Concept) | 설명 (Description) |
Patch | 두 텍스트(원본과 수정본) 간의 전체 변경 사항 묶음을 담는 컨테이너이다. |
AbstractDelta | 단일 변경 단위를 나타낸다. 변경(CHANGE), 삭제(DELETE), 삽입(INSERT) 유형이 있다. |
Chunk | Delta 내에서 실제로 변경되거나, 삭제/삽입된 실제 텍스트 라인들의 묶음이다. |
위 그림처럼, 원본 텍스트와 수정본 텍스트를 DiffUtils.diff() 메서드에 전달하면, 라이브러리는 두 텍스트를 비교하여 변경된 부분들을 Delta로 식별하고, 이 Delta들을 모아 하나의 Patch 객체를 반환한다.
3. Gradle 설정 방법
우리 팀은 주로 Gradle을 사용하므로, Maven이 아닌 Gradle 기준으로 의존성을 추가한다. build.gradle 파일에 아래와 같이 의존성을 추가하면 바로 사용할 수 있다. Baeldung 사이트에는 Maven으로 나와 있지만 우리 환경에 맞게 변환했다.
//Gradle dependencies { implementation 'io.github.java-diff-utils:java-diff-utils:4.12' } |
(※ 현재 기준 최신 버전은 4.12이며, 프로젝트 상황에 맞게 버전을 관리하도록 한다.)
4. 실제 사용 예제 (코드로 이해하기)
백문이 불여일견이다. 실제 코드를 통해 라이브러리의 사용법을 알아보자.
원본 데이터와 수정본 데이터 준비
//Java import io.github.java.diff.DiffUtils; import io.github.java.diff.patch.Patch; import io.github.java.diff.patch.PatchFailedException; import java.util.Arrays; import java.util.List; public class DiffUtilExample { public static void main(String[] args) { // 1. 원본 텍스트와 수정본 텍스트를 줄 단위로 List<String>으로 준비한다. final List<String> original = Arrays.asList("apple", "banana", "orange"); final List<String> revised = Arrays.asList("apple", "grape", "orange", "mango"); System.out.println("--- Patch 생성 ---"); // 2. DiffUtils.diff()를 사용하여 Patch 객체를 생성한다. final Patch<String> patch = DiffUtils.diff(original, revised); // 생성된 Delta들을 확인한다. //생성된 Patch에 포함된 Delta(변경점)들을 실제로 출력한다. // Delta의 toString() 메서드가 오버라이드 되어 있어 보기 좋은 형태로 출력된다. for (AbstractDelta<String> delta : patch.getDeltas()) { System.out.println(delta); } System.out.println("\n--- Patch 적용 ---"); try { // 3. 원본 텍스트에 생성된 Patch를 적용하여 수정본 텍스트를 얻는다. List<String> result = DiffUtils.patch(original, patch); System.out.println(result); // [apple, grape, orange, mango] } catch (PatchFailedException e) { // Patch 적용이 불가능한 경우 예외 처리 System.err.println("Patch 적용 실패: " + e.getMessage()); } } } |
실행 결과
--- Patch 생성 --- [ChangeDelta, position: 1, lines: [banana] to [grape]] [InsertDelta, position: 3, lines: [mango]] --- Patch 적용 --- [apple, grape, orange, mango] |
결과에서 보듯, banana가 grape로 변경(ChangeDelta)되고, 마지막에 mango가 추가(InsertDelta)된 것을 정확히 감지해낸다. 이 patch 객체만 있으면 언제든지 원본을 수정본으로 변환할 수 있다.
5. 결론 및 실제 서비스 도입 제안
java-diff-utils는 단순하지만 매우 명확한 목적을 가진 라이브러리로, 텍스트 데이터의 버전을 관리하거나 변경 사항을 추적해야 하는 모든 곳에 적용할 수 있다.
향후 발전 가능성 및 장점:
- 안정성 및 성숙도: 라이브러리는 오랫동안 개발되어 왔으며 매우 안정적이다. 복잡한 텍스트 구조에서도 신뢰도 높은 결과를 제공한다.
- 경량성: 별도의 무거운 의존성 없이 순수 자바로 구현되어 있어 어느 프로젝트에나 부담 없이 추가할 수 있다.
- 명확한 API: diff, patch 등 직관적인 API를 제공하여 팀원 누구나 쉽게 학습하고 사용할 수 있다.
어떤 서비스의 기능에 도입 되면 좋을까:
- 콘텐츠 관리 시스템(CMS) 개선: 사용자가 작성하는 게시물이나 문서의 수정 이력을 관리하고, 이전 버전과의 차이점을 시각적으로 보여주는 기능(Side-by-Side Diff)을 구현할 때 핵심 로직으로 활용할 수 있다.
- 사용자 입력 데이터 변경분 분석: 사용자가 프로필 정보나 자기소개 등 장문의 텍스트를 수정했을 때, 전체를 저장하는 것이 아니라 변경된 Patch 정보만 저장하여 데이터 저장 효율을 높이고, 변경 패턴을 분석하는 데 활용할 수 있다.