Development/TIL

jsCodeShift를 활용한 코드 자동 변환

highcastlee 2024. 1. 30. 01:49

문제 상황

사내 디자인 시스템에서 제공하는 Modal의 property 명칭을 onCancel에서 onClose로 고친 동료가 있었습니다.

하지만, 이미 해당 모달과 프로퍼티를 사용하고 있는 코드가 수백 개인 상황.

단순 onCancel을 검색하니 Modal이 아닌 곳에서의 onCancel까지 검색되어 하나씩 확인해보는 수 밖에 없었는데,

그 많은 코드를 직접 수정하는 모습을 보며 드는 생각 하나! "아 저거 딱 원하는 코드만 수정되게 자동화할 수 있으면 좋겠다"

문제 발견

 

Modal의 onCancel을 onClose로 수정해보자.

 

vscode 내에서 제공하는 바꾸기(cmd + shift + H) 기능은 텍스트 검색만 가능합니다. 위에서 언급했던 예시처럼, onCancel로 검색하면 의도했던 Modal의 onCancel이 아닌 프로젝트의 모든 onCancel이 검색에 걸리게 되는 것이죠.

onCancel이 어떤 컴포넌트의 prop인지 하나씩 확인해야하는 번거로움

// 1번
<ConfirmationModal isOpen onCancel={onClose} /> 

// 2번
<OtherComponent onCancel={onClose}/>

 

1번 코드가 수십개 있고, 2번 코드도 수십개 있다고 칩시다.

<ConfirmationModal /> 에서 쓰이는 onCancelonClose로 바꾸려면,

방법 1) onCancel 검색 -> 수많은 onCancel 중 <ConfirmationModal />의 인터페이스인지 확인하고 → onClose로 바꾸기
방법 2) <ConfirmationModal 검색 → onCancel 마우스로 선택 → onClose로 타이핑 or 복사하기로 바꾸기

 

즉, 한 줄의 텍스트가 아닌 여러 조건을 가진 코드 변환이 필요할 경우, 일괄 바꾸기 작업에 시간과 노동이 선형적으로 증가하게 됩니다.

 


해결 방안

JSCodeShift라는 도구를 활용해서 정확히 원하는 조건만을 충족하는 코드를 찾아 바꾸는 작업을 자동화하기

 

1. JSCodeShift가 무엇인가요?

유사한 상황에서 쓰이는 단어로 codemod가 있는데요. 두 용어에 대한 개념은 아래와 같습니다.

  • codemod : "code modification"의 줄임말로, 소프트웨어 코드를 자동으로 수정하거나 변환하는 도구나 프로세스를 가리킵니다.
  • jscodeshift : facebook(현 Meta)에서 만든 javascript/typescript 코드 변환 도구입니다. 주로 codemod 패턴을 구현하기 위해 jscodeshift를 사용합니다.

Q. jscodeshift는 어떻게 텍스트보다 더 세밀한 조건의 코드를 찾아낼 수 있을까요?

- 단순 문자열이 아닌, 특정 코드의 AST 구조를 기준으로 탐색하기 때문입니다.

 

+ AST(Abstract Syntax Tree, 추상 구문 트리)란?

AST는 컴파일러가 사용할 프로그램의 소스 코드 구조를 나타내는 데 사용됩니다.

 

기본적으로 javascript &rarr; AST &rarr; bytecode 순으로 변환되어 실행됩니다.&nbsp; (JIT compiler 내용 생략)

코드의 문자들을 읽어서 정해진 룰에 따라 토큰으로 만들어 합치는 렉시컬 분석 후,

토큰을 트리 구조로 만드는 신택스 분석 단계를 거치면 AST가 만들어 집니다.

(참고) 특정 코드의 AST 구조를 쉽게 보는 방법 : AST explorer

 

주의) AST 분석을 위해 사용하는 parser는 라이브러리마다 다른 parser를 사용할 수 있습니다. (크게 다르진 않음)

  • JSCodeShift가 사용하는 기본 parser는 @babel/parser입니다.
  • eslint는 @babel/eslint-parser @typescript-eslint/parser 등을 씁니다.

미리 알고 있으면 좋을 내용
It’s important to know that root.find() returns a collection of node-paths.
(jscodeshift는 node의 path를 활용하기 때문에 default export된 모듈을 다른 이름으로 import해도 찾을 수 있다.)

즉, import OtherName from ‘@my-lib'; 와 같이 호출된 모듈 import도 AST로 다 찾을 수 있다.
- 단, jscodeshift 내부에서 사용하는 replaceWith() 메서드는 nodePath가 아닌 node를 반환해야한다.

 

3. 연습 예제

 

jscodeshift를 설치해봅니다.

npm i -g jscodeshift

 

요구사항 : "프로젝트 내에 존재하는 모든 console.log()를 지워라"

  • AST explorer를 통해 console의 AST 구조 보기

  • callExpression 중 MemberExpression인 console을 찾아서 제거하는 코드
// remove-console.js
export default (fileInfo, api) => {
  const j = api.jscodeshift;

  return j(fileInfo.source)
    .find(j.CallExpression, {
      callee: {
        type: 'MemberExpression',
        object: { type: 'Identifier', name: 'console' },
      },
    })
    .remove()
    .toSource();
};

 

 

4. 실전 예제

 

요구사항 : "ConfirmationModal의 onCancel prop을 onClose로 변환하라."

  1) ConfirmationModal onCancel의 AST 구조 파악하기

  2) codemod 만들기

 

jscodeshift의 findJSXElements() 메서드는 탐색하는 파일 내 해당 이름을 가진 JSXElements를 찾아줍니다.

우리가 찾는 요구사항의 조건은 다음과 같습니다.

  • {name : ‘ConfirmationModal’} 인 JSXElement 찾기
  • (위 조건 충족 대상 중) JSXAttribute 중에 name.type이 JSXIdentifier이고, name.name이 onCancel 인 노드
  • (위 조건 충족 대상) 노드들의 JSXIdentifier name을 onClose로 대체하기
/* eslint-disable @typescript-eslint/no-unused-vars */
export default (fileInfo, { jscodeshift }) => {
  const root = jscodeshift(fileInfo.source);

  const ConfirmationModalComponents = root.findJSXElements('ConfirmationModal');

  return ConfirmationModalComponents.find(jscodeshift.JSXAttribute, {
      name: {
        type: 'JSXIdentifier',
        name: 'onCancel',
      },
      value: {
        type: 'JSXExpressionContainer',
      },
    })
    .forEach((path) => {
      jscodeshift(path).replaceWith(
        jscodeshift.jsxAttribute(jscodeshift.jsxIdentifier('onClose'), path.node.value)
      );
    })
    .toSource();
};

 

 

  3) jscodeshift 실행하기

이 때, jscodeshift 실행하면 코드 스타일이 살짝 다르게 적용될 수 있는데, 추가로 prettier를 실행해주면 프로젝트 설정에 맞게 수정됩니다.

// jscodeshift --parser={파싱할 대상 파일 파서} -t {codemod 파일} {대상 파일} -p

jscodeshift --parser=tsx -t replace-oncancel.js ../app/src/**/*.tsx -p

 

(팁1) ChatGPT 혹은 copilot에 대략적인 요구사항 구조를 만들어서 jscodeshift 용도로 쓸 AST 구문 달라고하면 잘 줍니다.

  - 물론, 답변이 항상 정확하지는 않으니, 실제 AST 구문과 비교하며 정확도를 높여줍니다.

 

(팁2) VSCode에서 Extension으로 AST 바로 보기 - vscode-ast-explorer

 


[추가 내용]

ESLint에서도 AST를 통해 문법 등을 검사합니다.

- eslint-plugin 만들 때도 AST 구조를 기반으로 코드를 찾아 규칙을 설정할 수 있습니다.


jsx-ast-utils

- JSX 문법에 맞게 AST를 평가해주는 유틸리티 모듈

- 주어진 유틸리티 함수로 간단하게 찾자

import { hasProp } from 'jsx-ast-utils';
// OR: var hasProp = require('jsx-ast-utils').hasProp;
// OR: const hasProp = require('jsx-ast-utils/hasProp');
// OR: import hasProp from 'jsx-ast-utils/hasProp';

module.exports = context => ({
  JSXOpeningElement: node => {
    const onChange = hasProp(node.attributes, 'onChange');

    if (onChange) {
      context.report({
        node,
        message: `No onChange!`
      });
    }
  }
});

 

NextJS Codemod

- nextjs에서 제공하는 codemod

- jscodeshift(v0.13.1) 사용하고 있음

 

Putout

- 특정 코드 패턴을 원하는 방식으로 자동 변환해주는 도구

- eslint-plugin-putout을 활용하면, 특정 패턴을 강제로 바꿀 수 있다.

- 강한 규칙이 필요한 경우 활용 가능할 듯

- 인터페이스 변경 등 일시적으로 바꾸는 상황에서는 적합하지 않아 보임

 


[참고 문서]

 

GitHub - facebook/jscodeshift: A JavaScript codemod toolkit.

A JavaScript codemod toolkit. Contribute to facebook/jscodeshift development by creating an account on GitHub.

github.com

 

 

GitHub - jsx-eslint/jsx-ast-utils: AST utility module for statically analyzing JSX

AST utility module for statically analyzing JSX. Contribute to jsx-eslint/jsx-ast-utils development by creating an account on GitHub.

github.com

 

 

Write Code to Rewrite Your Code: jscodeshift | Toptal®

How many times have you used the find-and-replace functionality (or RegEx) across a directory to make changes to JavaScript source files? Up your refactoring game by using codemods, scripts used to rewrite other scripts. In this article, Toptal Freelance D

www.toptal.com

 

 

JSCodeShift로 기술 부채 청산하기

기술 부채는 개발할수록 쌓여만 갑니다. 프론트엔드 챕터가 JSCodeShift를 이용하여 순식간에 100개 서비스의 기술 부채를 해결한 경험을 소개합니다.

toss.tech

 

 

1) AST를 이용한 코드 변환

## 1. 자동화된 코드 변환이 등장한 이유 프런트엔드 개발 스택에서는 코드를 변환해주는 툴이 많이 있다. 가령, babel이 리액트의 JSX 문법을 아래와 같이 변환한다. 또…

wikidocs.net

 

 

GitHub - Bogdan-Lyashenko/js-code-to-svg-flowchart: js2flowchart - a visualization library to convert any JavaScript code into b

js2flowchart - a visualization library to convert any JavaScript code into beautiful SVG flowchart. Learn other’s code. Design your code. Refactor code. Document code. Explain code. - GitHub - Bogd...

github.com

 

 

Upgrading: Codemods | Next.js

Use codemods to upgrade your Next.js codebase when new features are released.

nextjs.org