Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Ruby의 Zeitwerk와 모듈 자동 로딩(Autoloading) 완벽 이해하기

이 글에서는 Ruby에서 클래스와 모듈을 자동으로 불러오는 '자동 로딩(Autoloading)' 메커니즘에 대해 알아봅니다. 특히 Rails 6부터 도입된 새로운 자동 로딩 시스템인 Zeitwerk 젬(gem)까지 함께 다룹니다.

왜 자동 로딩이 필요할까요?

먼저 간단한 질문부터 시작해 보겠습니다.

여러 개의 소스 코드 파일을 하나로 합치려면 어떻게 해야 할까요?

답은 require를 사용하는 것입니다.

파일을 require하면 Ruby가 해당 파일을 읽고 그 안의 코드를 실행합니다. 하지만 프로젝트가 커지면 수십, 수백 개의 파일을 전부 require해야 할까요?

물론 아닙니다. 실제로는 모든 파일을 한 번에 로드할 필요가 없습니다. 적어도 애플리케이션 시작 시점에는 말이죠.

그렇다면 파일을 필요한 순간에만 로드해서 애플리케이션을 더 빠르게 시작하려면 어떻게 해야 할까요?

바로 이럴 때 필요한 것이 자동 로딩(Autoloading)입니다!

Ruby 기본 자동 로딩 사용법

Ruby에는 자동 로딩 기능이 내장되어 있습니다.

이 기능은 상수(Constant)를 기반으로 동작합니다.

Coconut 같은 상수가 코드에서 처음 발견되면, Ruby는 해당 상수와 연결된 파일을 찾아 로드하려고 시도합니다.

하지만 그 전에 먼저 해야 할 일이 있습니다.

상수와 파일 이름을 미리 등록해 주어야 합니다.

다음과 같이 말이죠:

autoload :Coconut, "lib/coconut.rb"

이렇게 등록해 두면 이후 Coconut 클래스나 모듈을 사용하고 싶을 때마다 Ruby가 어디서 찾아야 하는지 알게 됩니다.

그런데 더 좋은 방법이 있다면 어떨까요?

상수를 일일이 수동으로 등록하지 않아도 Ruby가 모든 것의 위치를 정확히 파악한다면 얼마나 편리할까요?

실제로 Rails가 바로 그렇게 동작합니다. 하지만 Rails 6 이전 버전에서는 이 autoload 메커니즘을 사용하지 않았습니다.

Rails 6 이전의 자동 로딩 방식

Rails는 autoload 대신 const_missing 훅(hook)을 활용해 프로젝트 내의 파일을 자동으로 찾아 로드했습니다.

이게 왜 유용할까요?

Rails 프로젝트에서 일일이 require를 작성하지 않아도 되기 때문입니다.

바로 '관습은 설정보다 우선한다(Convention over Configuration)'는 철학이죠.

규칙만 잘 지키면 모든 과정이 자동으로 처리됩니다.

그 규칙은 다음과 같습니다:

  1. 파일을 찾을 수 있는 위치에 둡니다 (ActiveSupport::Dependencies.autoload_paths에 정의된 경로)
  2. 한 파일당 하나의 클래스 또는 모듈만 정의합니다
  3. 클래스 이름을 따라 파일 이름을 스네이크 케이스(underscore)로 지정합니다 (UsersControllerusers_controller.rb)

이렇게만 하면 코드에서 해당 클래스 이름을 참조하는 순간 Rails가 어디서 파일을 찾아야 하는지 알아냅니다.

정말 편리한 기능이죠.

Rails 6 이후의 자동 로딩: Zeitwerk

Rails 6에서는 완전히 새로운 상수 자동 로딩 메커니즘이 도입되었습니다.

바로 Zeitwerk입니다.

기존의 '클래식' Rails 자동 로딩과 무엇이 다를까요?

구버전 Rails가 사용한 const_missing 방식은 모듈 중첩(nesting) 정보가 부족해 여러 가지 제약이 있었습니다.

반면 Zeitwerk는 Ruby의 autoload를 직접 활용합니다.

새 구현 역시 비슷한 파일 명명 규칙을 따릅니다.

흥미로운 점은:

Zeitwerk는 파일 이름으로부터 클래스 이름을 추론합니다. 즉, users_controllerUsersController처럼요.

기존 방식과 정확히 반대되는 접근입니다.

참고로 autoload_paths는 여전히 사용할 수 있지만, "/lib/**" 같은 세부 경로가 아니라 "/lib"처럼 기본(base) 경로만 추가하는 것이 좋습니다.

그 이유가 무엇일까요?

네임스페이스와 중첩 구조가 처리되는 방식 때문입니다.

  • "lib"를 autoload 경로에 추가했다면, Fruit::Orange 클래스를 정의할 때 파일 경로는 반드시 "lib/fruit/orange.rb"여야 합니다.
  • 반대로 "lib/fruit"를 autoload 경로에 추가하면, 같은 클래스를 위해 "lib/fruit/fruit/orange.rb"라는 중복된 폴더 구조를 만들어야 합니다.

가장 좋은 점은 무엇일까요?

이 새로운 기능은 Rails에 종속되어 있지 않다는 것입니다. 따라서 Rails를 사용하지 않는 순수 Ruby 프로젝트에서도 Zeitwerk를 활용할 수 있습니다!

정리

지금까지 Ruby의 모듈 자동 로딩이 정확히 무엇이고, 어떻게 동작하는지 살펴보았습니다.

이제 이 개념을 이해했으니, Ruby 프로젝트를 가장 효율적인 방식으로 구조화할 수 있을 것입니다.

이제 여러분 차례입니다. 에디터를 열고 직접 실험해 보며 즐거운 코딩 시간을 보내세요!