소스 코드를 분석해서 모든 메서드가 어디에 정의되어 있는지, 그리고 어떤 인자를 받는지 찾아내고 싶다고 가정해 봅시다.
어떻게 하면 이 작업을 수행할 수 있을까요?
첫 번째로 떠오르는 아이디어는 정규표현식(regexp)을 작성하는 것일 수 있습니다.
하지만 더 좋은 방법이 있지 않을까요?

네, 있습니다!
정적 분석(Static Analysis)은 소스 코드 자체에서 정보를 추출해야 할 때 사용할 수 있는 기법입니다.
이 과정은 소스 코드를 토큰 단위로 변환하는 것(파싱)부터 시작됩니다.
그럼 바로 시작해 보겠습니다!
parser 젬 사용하기
Ruby는 표준 라이브러리에 Ripper라는 파서를 포함하고 있습니다. 하지만 Ripper의 출력 결과는 다루기가 까다롭기 때문에, 저는 훨씬 편리한 parser 젬을 사용하는 것을 선호합니다. 실제로 유명한 코드 분석 도구인 Rubocop도 이 젬을 활용해 마법 같은 일을 해냅니다.
이 젬에는 코드를 직접 파싱하고 그 결과로 생성된 파스 트리(parse tree)를 확인할 수 있는 실행 파일도 함께 포함되어 있습니다.
다음은 그 예시입니다:
ruby-parse -e '%w(hello world).map { |c| c.upcase }'
출력 결과는 다음과 같습니다:
(block
(send
(array
(str "hello")
(str "world")) :map)
(args
(arg :c))
(send
(lvar :c) :upcase))
이 출력은 Ruby가 특정 코드를 어떻게 해석하는지 이해하려고 할 때 매우 유용합니다. 하지만 직접 분석 도구를 만들고 싶다면, 소스 파일을 읽고 파싱한 뒤 생성된 트리를 순회(traverse)하는 과정을 거쳐야 합니다.
예시:
require 'parser/current'
code = File.read('app.rb')
parsed_code = Parser::CurrentRuby.parse(code)
파서는 여러분의 코드에 대한 AST(추상 구문 트리, Abstract Syntax Tree)를 반환합니다. 이름이 다소 어려워 보일 수 있지만, 실제로는 들리는 것만큼 복잡하지 않으니 겁먹지 않아도 됩니다 🙂
AST 순회하기
parser 젬으로 코드를 파싱했다면, 이제 생성된 AST를 순회해야 합니다.
이 작업은 AST::Processor를 상속받는 클래스를 만들면 됩니다.
예시:
class Processor < AST::Processor end
그런 다음 이 클래스의 인스턴스를 생성하고 .process 메서드를 호출합니다:
ast = Processor.new ast.process(parsed_code)
여기서 on_으로 시작하는 메서드들을 몇 가지 정의해야 합니다. 이 메서드들은 AST의 노드 이름과 대응됩니다.
어떤 메서드들을 정의해야 하는지 알아내려면 Processor 클래스에 handler_missing 메서드를 추가하면 됩니다. 또한 on_begin 메서드도 필요합니다.
class Processor < AST::Processor
def on_begin(node)
node.children.each { |c| process(c) }
end
def handler_missing(node)
puts "missing #{node.type}"
end
end
현재까지의 진행 상황:
이제 Ruby AST와 기본적인 프로세서가 준비되었습니다. 이 코드를 실행하면 AST에 포함된 노드 타입들이 화면에 출력되는 것을 확인할 수 있습니다.
이제 남은 작업:
사용하고자 하는 on_ 메서드들을 모두 구현하면 됩니다. 예를 들어, 모든 인스턴스 메서드의 이름과 해당 라인 번호를 얻고 싶다면 다음과 같이 작성할 수 있습니다:
def on_def(node)
line_num = node.loc.line
method_name = node.children[0]
puts "Found #{method_name} at line #{line_num}"
end
프로그램을 실행하면 발견된 모든 메서드 이름이 출력될 것입니다.
마무리
Ruby 정적 분석 도구를 만드는 일은 생각보다 어렵지 않습니다. 더 완성도 높은 예시를 보고 싶다면 class_indexer 젬을 참고해 보세요. 이제 여러분 차례입니다. 직접 나만의 분석 도구를 만들어 보세요!
이 글이 도움이 되었다면 주변에 공유해 주세요! 🙂