hsunny study blog

Glob을 사용해서 원하는 확장자 파일 안에 있는 문자열들을 치환하기 (+@ 정규식) 본문

카테고리 없음

Glob을 사용해서 원하는 확장자 파일 안에 있는 문자열들을 치환하기 (+@ 정규식)

헤써니 2023. 2. 19. 22:01

프로젝트 내에 특정 파일들을을 일정한 규칙이 있는 문자열로 모두 변경해야 할 때가 있다.

이때 스크립트를 작성하여 실행하면 간편한 수정이 가능하다.

누군가에게 도움일 될거라 생각하며! 작업 과정의 일부를 공유한다.

 

작업을 위한 선행과정으로 before, after를 구분할 수 있는 JSON 파일을 만든다.

포스팅을 위한 예시 데이터

나의 경우 before을 path 로, after를 to-be로 이름지었다.

 

원하는 확장자의 파일만 수정할 수 있도록 'glob' 라이브러리를 사용한다. 

glob이 실제 프로젝트에 쓰인다면 package.json에 목적에 맞게 넣으면 되지만, 나의 경우에는 치환 작업만 진행할 것이라 작업을 완료하고 삭제하였다.

npm i glob

 

내가 변경할 내용을 담은 스크립트를 작성한다.

실행에 필요한 일부 코드들을 공유한다.

함수 구조

const path = require('path');
const fs = require('fs');
const glob = require('glob');

function convertString() {
  function explodeFolder() { } // 타겟 폴더 안에 타겟 확장자 파일을 탐색함
  
  function convertTarget() { } // 실제 변경을 진행하는 곳
  
  function generateFileIncludeAffectedFileList() { } // 변경내역을 확인하려는 목적으로 파일을 생성함
}

function getBaseFile() { } // 가장 처음에 만든 JSON 파일을 불러오기 위해 사용

function convertToRegPattern() { } // 밑에서 설명

function addWrapRegPattern() { } // 밑에서 설명

function replacePattern() { // 개인 작업 특이사항을 진행하기 위해 작성
	function replacer() { }  
}

function modifyFile() { } // 파일을 수정함**


exports.changeString = convertString; // 스크립트 실행을 위해 export

 

일부 과정을 상세 소개해보자면..

 

탐색을 시작할 경로를 지정한다.

const rootDir = path.join(__dirname, '../');

 

지정한 경로를 시작으로 내가 설정한 폴더 안에 있는 파일들을 탐색한다.

fs.readdirSync(rootDir) // 폴더 리스트를 배열 형태로 반환 (동기)
      .filter(folder => folder === 'projects' || folder === 'src') // projects, src 폴더만 탐색
      .forEach((value) => {
        explodeFolder(rootDir + value, version); // 폴더 내에 있는 파일을 모두 탐색
      });

 

explodeFolder() 메소드로 들어온 값을 확인하여, 파일인 경우 원하는 변경 작업을 진행한다.

function explodeFolder(path, version) {
  let dirList = fs.readdirSync(path);
  for (const file of dirList) {
    let pullPath = path + '/' + file;
    const isDir = fs.statSync(pullPath).isDirectory();

    if (isDir) {
      explodeFolder(pullPath, version); // 폴더인 경우 재귀
      const fileList = glob.sync(pullPath + '/' + '*.ts'); // typescript 파일들만 배열 형태로 반환
      fileList.forEach(file => convertTarget(file, version)); // 문자열 변경 진행
    }
  }
}

 

convertTarget() 메소드에서 문자열 변경을 진행하고, 변경사항을 반영한다.

sheetData = getBaseFile(); // 처음에 만들어둔 JSON 파일

...

function convertTarget(pullPath, version) {
  let fileContent = fs.readFileSync(pullPath, 'utf-8'); // 파일 내용 반환 (동기)

  sheetData = sheetData.reduce((acc, v, i) => {
    const regPattern = convertToRegPattern(v.path); // **
    const matchList = getMatchedString(fileContent, regPattern); // ** 변경된 파일을 요소로 포함하는 배열을 반환
   
    ...

    fileContent = replacePattern(fileContent, regPattern, version);

    acc.push(v);
    return acc;
  }, []);

  modifyFile(fileContent, pullPath); // fs.writeFileSync(filePath, fileContent);
}

 

 

 

Regular expression()

실제 치환작업을 위해서는 정규식을 사용했고, 작성한 정규식은 아래와 같다.

치환을 할 대상을 선정하고, 자신에게 맞게 정규식을 변경하면 된다!

function convertToRegPattern(path) {
  let fullPattern = path;
  const regex = /:[a-z]+[_]?[a-z]+/g;
  const searchRegex = '("\\s?\\+\\s?|\\$(?=\\{)\\{)[\\w.]+(\\s?\\+\\s?"|})';
  fullPattern = fullPattern.replace(regex, searchRegex);
  fullPattern = addWrapRegPattern(fullPattern);
  return new RegExp(fullPattern, 'g');
}

function addWrapRegPattern(regPattern) {
  return `(path:\\s?)?(\'|\"|\`|\})\\s?(${regPattern}[\'\"\`\{\?])`;
}

 

convertToRegPattern()

찾아야했던 형태

:p_target, :id, ${p.id}, ${target}, " + target + "

  • +: 한 번 이상 나올 수 있음을 의미
  • \$(?=\{): 무조건 { 앞에 $가 있어야 함을 의미
  • \w. : 모든 문자열을 의미 (숫자 제외)
  • \: 특수문자의 경우 앞에 \를 붙여 특수문자임을 알려줌
    • 정규식에서 사용하는 의미있는 기호($, + 등)를 특수문자 자체로 쓰기 위해서는 꼭 필요
  • ?: 앞에 해당하는 정규식은 있어도 되고 없어도 됨을 의미
  • \s: 공백문자를 의미

 

addWrapRegPattern()

return `(path:\s?)?(\'|\"|\`|\})\s?(${regPattern}[\'\"\`\{\?])`;
  • ()로 묶은 것: 하나의 그룹으로 취급 (순서대로 $1, $2, $3,,,)
  • []: 내부에 있는 문자는 각각 대응
    • 여러 케이스에 대응하기 위한 문자열을 넣을 때.. () 안에서는 or(|)이 필요하지만 [] 안에서는 필요 없음

 

 

addWrapRegPattern()에서 그룹화된 결과물은 아래와 같은 방식으로 처리하는데 사용했다.

function replacer(match, p1, p2, p3) {
  // 'path:'를 포함하는 경우 'path:'가 p1이 되고, 없는 경우 p1은 undefined
  if (p1) {
    return `${p1}${p2}${p3}`;
  } else {
    return `${p2}/v1${p3}`;
  }
}

 

 

나의 경우는 걸프를 사용하고 있어, package.json에 아래와 같이 스크립트를 추가했다.

"scripts": {
...
"change-string": "gulp changeString --gulpfile build-config/gulpfile.js"
}
npm run change-tring

이렇게 작성한 파일을 돌리면 내가 설정한 타겟들이 정규식에 맞추어 수정된다.

 

 

참고사이트

더보기

정규식을 만드는데 정말 큰 도움이 된 사이트! https://regexr.com/ 

Tests 활용을 많이 했다.