본문 바로가기

Development/TIL

patch 알고리즘 수정 (feat. </p> 태그 지옥)

현재 진행중인 프로젝트인 무비위키의 버전 관리 기능은 크게 두 부분으로 나뉜다.

두 문서의 변경 사항을 추적하여 데이터화 하는 diff 알고리즘과

원본 문서와 변경 사항 데이터를 더해서 최신 버전 문서를 만들어내는 patch 알고리즘이다.

그런데 기능 테스트 중 오류를 발견했다.

 

 

한산 : 리덕스 (moviId : 3721)의 버전 기록

버전 6의 content에 버전 7의 diff를 더하면 버전 7의 content가 나와야하는데

변경 사항 데이터인 diff가 제대로 반영이 되지 않는다.....

{
    "postId": 148,
    "userId": 13,
    "content": "<p>이순신 3부작의 2탄!</p><p>노량의 기록을 깰 수 있을 것인가</p><p>최종 스코어 720만</p><p>박해일 버전 이순신은 뭔가 냉철한 느낌이네요</p><p>가슴이 웅장해지네요...</p><p>거북선 폼 미쳤어요!!</p>",
    "comment": "원본의 다섯번째 수정",
    "createdAt": "2023-06-13T02:42:48.556Z",
    "version": 6
}



 {
    "postId": 149,
    "userId": 13,
    "content": "<p>이순신 3부작의 2탄!</p><p>제작비 312억억</p><p>최종 스코어 720만</p><p>박해일 버전 이순신은 뭔가 냉철한 느낌이네요</p><p>가슴이 웅장해지네요...</p><p>거북선 폼 미쳤어요!!</p>",
    "comment": "원본의 여섯번째 수정",
    "createdAt": "2023-06-13T02:44:18.153Z",
    "version": 7,
    "diff": [
      {
        "type": "remove",
        "value": "<p>노량의 기록을 깰 수 있을 것인가",
        "idx": 1
      },
      {
        "type": "add",
        "value": "<p>제작비 312억억",
        "idx": 1
      },
      {
        "type": "remove",
        "value": "<p>박해일 버전 이순신은 뭔가 냉철한 느낌이네요",
        "idx": 3
      },
      {
        "type": "remove",
        "value": "<p>거북선 폼 미쳤어요!!",
        "idx": 4
      },
      {
        "type": "remove",
        "value": "<p>가슴이 웅장해지네요...",
        "idx": 5
      }
    ]
  }

 

 

 

현재 무비위키 프로젝트의 patch 알고리즘 코드

generateModifiedArticle = (originalArticle, diff) => {
    let modifiedArticle = originalArticle;

    for (const change of diff) {
      if (change.type === 'remove') {
        const sentenceToRemove = change.value;
        modifiedArticle = modifiedArticle.replace(sentenceToRemove, '');
      } else if (change.type === 'add') {
        const sentenceToAdd = change.value;
        const insertIndex = change.idx;
        modifiedArticle = this.insertSentence(modifiedArticle, sentenceToAdd, insertIndex);
      }
    }

    // 연속된 '</p>' 정리
    modifiedArticle = this.cleanUpConsecutiveTags(modifiedArticle, '</p>');

    // 마지막에 '</p>' 추가
      modifiedArticle += '</p>';

    return modifiedArticle;
  }

  // </p> 태그 단위로 나누고 다시 더함
  insertSentence = (article, sentence, index) => {
    const sentences = article.split('</p>');
    const modifiedSentences = [
      ...sentences.slice(0, index),
      sentence,
      ...sentences.slice(index)
    ].filter(Boolean);

    return modifiedSentences.join('</p>');
  }

  // 가공 마지막에 </p> 태그 중복값 제거
  cleanUpConsecutiveTags = (article, tag) => {
    const consecutiveTagsRegex = new RegExp(`${tag}+`, 'g');
    return article.replace(consecutiveTagsRegex, tag);
  }

 

현재 patch 알고리즘 코드를 살펴보니 두가지 문제가 있었다!

첫번째 remove 데이터의 경우

diff 객체에서 type이 remove일 경우 value값을 원본 데이터인 modifiedArticle에서 찾아서 replace 메서드를 이용해

지워주는데 diff에는 value: '<p>문장'  형태로 되어있고 modifiedArticle(원본문서)에는 '<p>문장1</p><p>문장2</p><p>문장3</p>' 형태로 되어있어서 remove 문장을 검색하지 못한 것이다.

그래서 </p> 태그를 붙여서 지울 문장을 찾도록 수정했다.

if (change.type === 'remove') {
        const sentenceToRemove = change.value + '</p>';
        modifiedArticle = modifiedArticle.replace(sentenceToRemove, '');
      }

 

 

두번째 add 데이터의 경우

diff 객체에서 type이 add일 경우  '<p>문장1</p><p>문장2</p><p>문장3</p>'의 형태로 되어 있는 원본 문서를

</p> 단위로 split하여 배열로 만든 다음, diff 데이터의 idx를 참고하여 추가된 문장을 삽입한다.

그리고 마지막으로 </p>로 join을 해주는데 마지막 문장에 </p>가 붙지 않아 온전한 문장이 되지 않는 것이었다.

그래서 join을 해주고 나서 문자열 제일 끝에 </p>를 붙여주도록 하였다.

  // </p> 태그 단위로 나누고 다시 더함
  insertSentence = (article, sentence, index) => {
    const sentences = article.split('</p>');
    const modifiedSentences = [
      ...sentences.slice(0, index),
      sentence,
      ...sentences.slice(index)
    ].filter(Boolean);

    return modifiedSentences.join('</p>')+'</p>';
  }

 

 

수정 후 정상 작동하는 것을 확인하였다.

같은 문장으로 테스트하였는데 diff 데이터도 달라진 것을 보니

반복된 수정으로 인해 잘못된 diff데이터가 쌓인 것으로 추측된다.

  {
    "postId": 244,
    "userId": 13,
    "content": "<p>이순신 3부작의 2탄!</p><p>제작비 312억억</p><p>최종 스코어 720만</p><p>박해일 버전 이순신은 뭔가 냉철한 느낌이네요</p><p>가슴이 웅장해지네요...</p><p>거북선 폼 미쳤어요!!</p>",
    "comment": "버전7",
    "createdAt": "2023-06-15T05:12:06.007Z",
    "version": 7,
    "diff": [
      {
        "type": "remove",
        "value": "<p>노량의 기록을 깰 수 있을 것인가",
        "idx": 1
      },
      {
        "type": "add",
        "value": "<p>제작비 312억억",
        "idx": 1
      }
    ]
  },
  {
    "postId": 243,
    "userId": 13,
    "content": "<p>이순신 3부작의 2탄!</p><p>노량의 기록을 깰 수 있을 것인가</p><p>최종 스코어 720만</p><p>박해일 버전 이순신은 뭔가 냉철한 느낌이네요</p><p>가슴이 웅장해지네요...</p><p>거북선 폼 미쳤어요!!</p>",
    "comment": "버전6",
    "createdAt": "2023-06-15T05:11:23.017Z",
    "version": 6
}

'Development > TIL' 카테고리의 다른 글

Nest.js 클러스터링  (0) 2023.06.21
비교 단위별 Diff 및 Patch 코드 동작 속도 측정  (0) 2023.06.16
DP, LCS, MED, SES 개념정리  (1) 2023.06.15
문서의 문장 단위 변경 추적  (0) 2023.06.13
git의 diff 알고리즘  (0) 2023.06.10