Codemod?
Codemod란?
codemod는 코드베이스의 일괄적인 변환 및 리펙토링을 자동화하는 도구. 주로 대규모 코드베이스에서 API 변경, 라이브러리 업데이트, 코드 스타일 통일 등의 작업을 할 때 사용되며 시간 소모적인 수작업을 줄이고, 변환의 일관성을 유지 할 수 있게 해줍니다.
codemod 툴로 주로 사용되는 것들은 다음과 같습니다.
- jscodeshift: Facebook에서 만든 도구로, JavaScript 및 TypeScript 코드를 변환하는 데 널리 사용됩니다.
- Recast: JavaScript 코드의 AST(Abstract Syntax Tree)을 생성하고, 이를 기반으로 코드를 변환한 후 다시 코드로 변환합니다.
- Babel Codemod: Babel 플러그인을 활용하여 AST 변환을 수행합니다.
위 언급한 툴 중 jscodeshift 사용법에 대해서만 알아보겠다. 우선 작업을 위해
jscodeshift
설치하는 것외에 아래 도구들도 추가적으로 필요합니다.- AST 탐색기: codemod를 테스트하고 AST 탐색을 위한 도구
- ast-type 정의들: 새로운 AST 노드를 만들기 위한 API
*여기서 AST란?:
컴퓨터 과학에서 추상 구문 트리(abstract syntax tree, AST), 또는 간단히 구문 트리(syntax tree)는 프로그래밍 언어로 작성된 소스 코드의 추상 구문 구조의 트리이다. 이 트리의 각 노드는 소스 코드에서 발생되는 구조를 나타낸다. 구문이 추상적이라는 의미는 실제 구문에서 나타나는 모든 세세한 정보를 나타내지는 않는다는 것을 의미한다. 예를 들어, 그룹핑을 위한 괄호는 암시적으로 트리 구조를 가지며, 분리된 노드로 표현되지는 않는다. 마찬가지로, if-condition-then 표현식과 같은 구문 구조는 3개의 가지에 1개의 노드가 달린 구조로 표기된다. - wikipedia
설명이 복잡합니다… 그냥 간단하게 말하자면 아래 코드를 AST로 변환하면 다음과 같은 트리 구조를 가지게 된다는 것입니다.
TMIㅎ: eslint도 내부적으로 AST를 이용하여 규칙을 정의하고 적용한다고 합니다. 다만 espree라고 하는 것을 이용해 코드를 파싱하고 처리한다고 합니다.
이제 실제 코드를 변환하는 작업을 진행해보겠습니다.
jscodeshift
를 global로 설치변환 스크립트 작성
jscodeshift 공식문서와 예제를 참고하여 어떤 방식으로 코드를 작성하면 될지 준비를 합니다.
아래는 스크립트 기본 구조 입니다.
주요 메서드
- j.functionDeclaration(id,params,body,generator,async): 함수 선언을 나타내는 AST 노드
id
: 함수 이름 (예:j.identifier('myFunction')
)params
: 함수 매개변수의 배열 (예:[j.identifier('param1'), j.identifier('param2')]
)body
: 함수 본문을 나타내는 블록 문 (예:j.blockStatement([j.returnStatement(j.literal('result'))])
)generator
(선택적): 제너레이터 함수 여부 (기본값:false
)async
(선택적): 비동기 함수 여부 (기본값:false
)
- j.variableDeclaration(kind,declarations): 변수선언을 나타냄
kind
: 변수 선언 유형 ('var'
,'let'
,'const'
)declarations
: 변수 선언자들의 배열 (예:[j.variableDeclarator(j.identifier('myVar'), j.literal('value'))]
)
- j.variableDeclarator(id,init): 변수 선언자 노드
id
: 변수 이름 (예:j.identifier('myVar')
)init
: 초기값 표현식 (예:j.literal('value')
)
- j.literal(value): 리터럴 값을 나타내는 AST 노드
value
: 리터럴 값 (예:'someValue'
,42
,true
)
- j.identifier(name): 식별자(변수, 함수 이름등)을 나타내는 AST노드
name
: 식별자 이름 (예:'myIdentifier'
)
- j.memberExpression(object, property, computed): 객체의 속성 접근을 나타내는 AST노드
object
: 객체 표현식 (예:j.identifier('object')
)property
: 속성 표현식 (예:j.identifier('property')
)computed
(선택적): 계산된 속성 여부 (기본값:false
)
- j.blockStatement(body): 여러 statement를 블록으로 묶는 AST노드
body
: 문(statement)의 배열[j.expressionStatement(j.callExpression(j.identifier('console.log'), [j.literal('Hello, world!')]))]
)
- j.expressionStatement: 표현식을 나타내는 AST 노드
expression
: 표현식- (예:
j.callExpression(j.identifier('myFunction'), [j.literal('arg')])
)
- j.returnStatement(argument): 반환문을 나타내는 AST 노드
argument
: 반환할 표현식 (예:j.literal('returnValue')
)
- j.jsxElement: JSX요소와 속성을 생성하고 변환하는 데 사용되는 메서드
- j.importDeclaration(specifiers, source): 새로운
ImportDeclaration
노드를 생성 specifiers
: 가져온 모듈의 변수 또는 객체- j.importDefaultSpecifier(local): 기본 가져오기 (예:
import myModule from 'my-module'
) - j.importSpecifier(imported, local): 명명된 가져오기 (예:
import { namedImport } from 'my-module'
) source
: 모듈의 경로를 나타내는 문자열 리터럴, (예:j.literal('my-module')
)
메서드들을 참고 및 활용하여 위에서 제공했던 예제 함수
myFunction
를 a+b
를 반환하는 counter
함수로 수정하는 코드를 아래와 같이 작성할 수 있습니다.해당 코드를 AST Explore 에서 테스트를 해보면 기존 함수가 counter 함수로 변경이 되는것을 확인 할 수 있게 됩니다.
이후
jscodeshift
cli를 통해 다음과 같은 형식의 명령어를 통해 코드를 실행하면 코드를 수정 할 수 있게 됩니다.일괄 수정이 필요한 코드들에 대해서 에디터의
find & replace
기능을 사용하고 그랬다면 jscodeshift
를 통해 자동화하고 좀 더 안전하게 코드의 일관성을 맞추고 변경할 수 있어 필요하다면 도입을 고민해보는것도 좋을거 같습니다.