Ruby/Rails의 코드 로딩 문제
-
전역 네임스페이스와 상수 로딩: Ruby는 전역 네임스페이스를 가지며, 상수(클래스, 모듈, CONSTANT)는 전역 싱글턴입니다. 코드가 상수를 정의할 때 Ruby는 해당 상수의 모든 것(클래스 본문, 속성 등)을 평가하며, 이 과정에서 참조되는 모든 상수와 그 의존성도 로드되고 평가됩니다. 이는 애플리케이션 부팅이 느려지는 주된 원인입니다.
-
‘적게 로드’ 전략: 개발 환경에서는 단일 테스트 실행, 특정 라우트 탐색, CLI 실행 등 필요한 코드만 로드하는 것이 중요합니다. 상수를 로드하지 않으면 해당 파일과 그 의존성 로딩을 지연시킬 수 있습니다.
-
문자열 기반 지연 로딩: Rails는 `to: “mycontroller
index”와 같은 라우트 정의나 class_name: “MyModel”`과 같은 ActiveRecord 관계 정의에서 상수 대신 문자열을 사용하여 로딩을 지연시키는 전략을 사용합니다. 그러나 이러한 접근 방식은 때때로 상수 참조와 문자열 참조가 혼용되어 일관성이 부족하며, 개발 성능 개선을 위한 추가 작업이 요구됩니다.
Hanami의 접근 방식: 문자열 키와 의존성 주입
-
문자열 키 기반 참조: Hanami는 거의 모든 애플리케이션 컴포넌트를 ‘키(key)’라는 문자열로 참조 가능하게 만듭니다. 객체는 의존하는 키에 따라 구성되며, 프레임워크에 의해 주입됩니다.
-
Deps를 통한 객체 주입:class MyClass; include Deps["api_client"]; end와 같이Deps를 사용하여 객체를 주입받으며, 이는ApiClient.new처럼 상수를 직접 로드하는 대신 사용됩니다. -
dry-system활용: 키는 전역적이며, 해당 키의 객체가 로드되지 않았다면 실행 시점에 문자열에서 실제 객체로 변환됩니다. 이 메커니즘은dry-system라이브러리에 의해 구동됩니다. -
선택적 키 사용: 모든 것에 키가 필요한 것은 아닙니다. 데이터 구조를 나타내는 클래스(Hanami의 Structs)는 앱 컨테이너에 등록되지 않아 키가 없습니다.
-
Provider를 통한 외부 컴포넌트 관리:ApiClient와 같이 Hanami 외부 또는 비 Hanami Gem에서 오는 기능적 컴포넌트는Provider를 통해 키를 부여하고 애플리케이션 내 라이프사이클을 정의할 수 있습니다. -
일관성과 이점: Hanami의 ‘문자열에서 객체로의 변환’ 방식은 일관되게 적용되며, 의존성 오버로딩과 같은 이점을 제공합니다. 또한, 모든 것을 프레임워크에 맞추는 명시적인 인터페이스를 제공합니다.
기타 특징 및 미래 방향
-
Provider의 역할:Provider는 Rails의 이니셜라이저와 유사하지만, 더 강력한 기능을 제공합니다. 컴포넌트를 컨테이너에 등록하고, 리소스 관리를 위한 라이프사이클 훅(prepare, start, stop)을 가집니다. 지연 로딩되며 관련 컴포넌트 구성을 위한 네임스페이스 기능도 제공합니다. -
Slices를 통한 모듈화: Hanami는 Rails의 플랫한 구조보다 네임스페이스를 장려하며,Slices는 Rails Engines처럼 애플리케이션 모듈화를 위한 1급 지원을 제공합니다. 각 슬라이스는 자체 컨테이너와 프로바이더를 가질 수 있어 경계 지어진 컨텍스트를 생성합니다. -
코드 로딩 및 개발 서버: Hanami는
Zeitwerk를 코드 로딩에 사용하며, 개발 서버는Guard를 사용하여 Puma를 재시작합니다. 이는 모듈화된 구조 덕분에 충분히 효율적입니다. -
지연 로딩 vs. 사전 로딩: 개발 환경에서는 코드가 지연 로딩되지만, 프로덕션 환경에서는 완전히 사전 로드됩니다.
-
플러그인 시스템 제안: Hanami Discord에서 논의된 플러그인 시스템 제안은 Railties 및 ActiveSupport의 지연 로드 훅과 유사한 형태로, Hanami의 확장성을 더욱 강화할 것으로 예상됩니다.