본문 바로가기

Development/TIL

데이터 형상 관리 (특정 버전으로 롤백)

이번주는 증분식 데이터 형상관리 학습과 구현으로 대부분의 시간을 보냈다.

나는 최신 버전 전체 스냅샷과 특정 버전 스냅샷으로 롤백하는 기능 구현을 맡았다.

전체 스냅샷 응답은 스냅샷 테이블에서 특정 영화의 id 값을 기준으로 찾아서 보여주면

되는 것이기 때문에 어려움 없이 구현할 수 있었다.

반면 롤백 기능 구현에 많은 시간을 할애했다.

 

롤백은 post 테이블에 쌓인 버전별 변경사항 데이터와 snapshot 테이블에 10개의 버전 단위로 쌓인 전체 스냅샷 데이터를

movieId와 version 값을 기준으로 찾아와서 합치는 것이 기본적인 로직이다.

오늘 애를 먹었던 부분은 post 테이블에서 여러 버전의 변경 사항 데이터들을 가져와

원본 문서와 더해주는 함수에 집어 넣는 것이다.

 

아래는 repository 계층의 버전 값의 범위 사이에 있는 변경 사항 데이터들을 찾아서 가져오는 함수다.

마지막에 map 메서드를 이용하여 배열 형태로 service 계층에 넘겨준다.

  async findPostByVersion(movieId: number, version: number) {
    const snapshotVersion = (Math.floor(version / 10) * 10) + 1;

    const posts = await this.createQueryBuilder('post')
      .leftJoinAndSelect('post.movie', 'movie')
      .where('movie.movieId = :movieId', { movieId })
      .andWhere('post.version >= :minVersion AND post.version <= :maxVersion', {
        minVersion: snapshotVersion + 1,
        maxVersion: version,
      })
      .getMany();

    const diffs = posts.map((post) => post.content);
    return diffs;
  }

 

service 계층의 롤백 함수는 아래와 같이 구현되어있다.

diffs에 repository 계층에서 넘겨받은 변경 사항 데이터들이 배열 형태로 담겨있고

그걸 applyDiff에 인자로 넣어주면 원본 문서에 변경 사항 데이터를 더해 원하는 버전의 전체 스냅샷 문서가 만들어진다.

 async revertPost(
    movieId: number,
    version: number
  ) {
    try {
      const original = await this.snapshotRepository.findSnapshotByVersion(movieId, version);
      const diffs = await this.postRepository.findPostByVersion(movieId, version);

      const applyDiff = (original, diffs) => {
        let modified = original;

        for (let i = 0; i < diffs.length; i++) {
          const { type, value, idx } = diffs[i];
          if (type === "add") {
            modified = modified.slice(0, idx) + value + modified.slice(idx);
            for (let j = i + 1; j < diffs.length; j++) {
              if (diffs[j].type === "remove") {
                diffs[j].idx += value.length;
              }
            }
          } else if (type === "remove") {
            modified = modified.slice(0, idx) + modified.slice(idx + value.length);
            for (let j = i + 1; j < diffs.length; j++) {
              if (diffs[j].type === "add") {
                diffs[j].idx -= value.length;
              }
            }
          }
        }

        return modified;
      }

      return applyDiff(original, diffs);

 

하지만 문제는 applyDiff 함수의 제일 바깥 for문에서 type, value, idx 요소가 구조 분해 할당이 안되는 것이다.

repo 계층과 service 계층에서 diffs를 console.log로 찍어봤을 때는 데이터가 정상적으로 확인되었다.

그래서 applyDiff 함수에 문제가 있나 싶어 original과 diffs 값에 명시적으로 데이터를 넣어서 시험해봤는데

원하는 결과가 나왔다.

 

그래서 repo 계층에서 넘겨받은 데이터에 문제가 있다는 결론을 내리고 우선 service 계층에서 타입 체크를 해보았다.

diffs 데이터의 타입은 object로 확인되었다. applyDiff 함수에는 배열 형태의 데이터가 들어가야하지만

자바스크립트의 typeof 메서드는 배열도 객체로 나오니 isArray 메서드를 사용하여 체크해본 결과 true가 나왔다.

 

배열이 맞는데 applyDiff 함수에서 작동하지 않는 것이 이상해서 함수 내부에서 구조 분해 할당된 데이터들을

콘솔에 찍어보았다. 어떻게 된 일인지 undifined가 나왔다. 변경 사항 데이터를 읽지 못하고 있는 것이었다.

 

그래서 repo 계층에서 변경 사항 데이터의 배열 요소 하나를 찍어보니 string으로 확인되었다.

터미널에 보이기엔 객체 배열형태였지만 결국 ' '에 감싸진 상태였던 것이다.

그래서 데이터를 받아 map을 돌려주면서 아래와 같이 하나 하나 JSON 형태로 파싱을 해주었다.

  async findPostByVersion(movieId: number, version: number) {
    const snapshotVersion = (Math.floor(version / 10) * 10) + 1;

    const posts = await this.createQueryBuilder('post')
      .leftJoinAndSelect('post.movie', 'movie')
      .where('movie.movieId = :movieId', { movieId })
      .andWhere('post.version >= :minVersion AND post.version <= :maxVersion', {
        minVersion: snapshotVersion + 1,
        maxVersion: version,
      })
      .getMany();

    const diffs = posts.map((post) => JSON.parse(post.content));

    console.log('postRepoDiff : ' + diffs);
    console.log(typeof diffs);
    return diffs;
  }

 

그래서 객체 형태의 배열 데이터로 가공을 할 수 있었다.

그리고 다음으로 service 계층에서 아래와 같이 반복문을 사용하여 diffs 배열의 요소 하나씩 applyDiff 함수에 넣어

특정 버전의 전체 스냅샷을 완성해줄 수 있었다.

      const diffUtil = new DiffUtil();
      let result = original;
      for (let i = 0; i < diffs.length; i++) {
        result = diffUtil.applyDiff(result, diffs[i]);
      }

 

 

데이터 타입에 대한 이해가 부족하다는 것을 깨닳았다.

기본적인 것이라 간과하고 넘어가는 부분이 더 많은 것 같다.

 

더불어 문제를 바라보는 시각이 너무 좁아서 해결하는 방법을 찾는 것도 시간이 오래 걸리고

다른 사람의 도움을 받아야할 때가 많다.

상황을 깊이 있게 분석하고 차근차근 짚어가는 자세가 필요하다는 것을 다시 한번 깨닫는다.