Active Storage `has_one_attached` 내부 동작: DSL, 프록시, 변경 객체 패턴

Active Storage Internals: How has_one_attached DSL Works

발행일
2025년 10월 18일

핵심 요약

  • 1 Active Storage의 `has_one_attached`는 Rails 모델에 동적으로 첨부 기능을 추가하며, 프록시 객체와 변경 객체 패턴을 활용하여 복잡한 파일 관리를 추상화합니다.
  • 2 파일 첨부 과정은 `after_save`에서 데이터베이스 연관 관계를 설정하고, `after_commit`에서 실제 파일을 스토리지에 업로드하여 트랜잭션 안전성과 데이터 일관성을 보장합니다.
  • 3 `ActiveStorage::Attached::One` 프록시와 `CreateOne`, `DeleteOne`, `DetachOne`, `PurgeOne` 같은 변경 객체들은 파일 첨부, 분리, 삭제 및 스토리지 정리 동작을 캡슐화하고 조율합니다.

도입

Rails 개발자들이 흔히 사용하는 `has_one_attached` API는 모델에 단일 파일 첨부 기능을 선언하는 강력한 도구입니다. 이 글은 해당 API가 단순히 한 줄의 코드 이상의 의미를 가지며, Active Storage의 복잡한 내부 메커니즘을 모델에 어떻게 통합하는지 심층적으로 탐구합니다. 특히 DSL을 통한 코드 생성, `ActiveStorage::Attached::One` 프록시 객체의 역할, 그리고 파일 첨부/삭제/정리(purge)를 조율하는 "변경 객체" 패턴에 초점을 맞춰 Rails의 핵심 디자인 패턴과 라이프사이클 콜백 활용법을 분석합니다.

has_one_attached 메서드는 Rails 모델에 동적으로 여러 기능을 추가합니다. :avatar 예시의 경우, avatar_attachment, avatar_blob 연관 관계, avatar 읽기/쓰기 메서드 및 after_save, after_commit 콜백이 생성됩니다. 특히 generated_association_methods 모듈을 통해 동적 메서드가 정의되어 모델 클래스에 포함되는 방식은 Rails 메타프로그래밍의 핵심을 보여줍니다.

모델의 avatar 읽기 메서드는 ActiveStorage::Attached::One 프록시 객체를 반환합니다. 이 프록시는 attach, attached?, purge, detach와 같은 상위 레벨 API를 제공하며, 실제 파일 관리 로직은 내부적으로 “변경 객체”에 위임됩니다. 예를 들어, attach는 레코드의 영속성 및 변경 여부에 따라 즉시 저장하거나 다음 저장으로 연기할 수 있습니다.

avatar= 쓰기 메서드는 할당 값에 따라 attachment_changes 해시에 ActiveStorage::Attached::Changes::CreateOne 또는 ActiveStorage::Attached::Changes::DeleteOne 인스턴스를 저장합니다. 이 “변경 객체”들은 파일 첨부/삭제/정리(purge)의 구체적인 동작을 캡슐화합니다.

  • CreateOne (첨부): 생성 시 블롭의 메타데이터(체크섬, 콘텐츠 타입, 바이트 크기)를 미리 식별합니다. after_save 콜백에서 첨부 및 블롭 레코드를 데이터베이스에 저장하며, 실제 파일 업로드는 after_commit 콜백에서 발생합니다. 이는 데이터베이스 트랜잭션 롤백 시 파일만 업로드되는 “고아 업로드”를 방지합니다. 블롭 생성 과정에서 “unfurl”(메타데이터 추출)과 “upload”(실제 파일 전송)를 분리하여 유연성을 확보합니다.

  • DeleteOne (해제): user.avatar = nil 또는 "" 할당 시 사용되며, after_save에서 avatar_attachment 연관 관계를 nil로 설정합니다. 블롭의 실제 삭제는 :dependent 옵션에 따릅니다.

  • DetachOne (분리): user.avatar.detach 호출 시 첨부 레코드만 삭제하고 블롭 및 스토리지 서비스의 파일은 유지합니다.

  • PurgeOne (정리): user.avatar.purge 또는 purge_later 호출 시 첨부 레코드와 블롭 레코드를 모두 삭제하고, 스토리지 서비스에서 실제 파일을 제거합니다(다른 첨부와 공유되지 않는 경우).

이러한 패턴들은 Active Storage가 데이터베이스 트랜잭션과 파일 스토리지 작업을 안전하고 효율적으로 조율하는 정교한 방식을 보여줍니다.

결론

본 글은 Rails Active Storage의 `has_one_attached` API가 DSL, 프록시 객체, 그리고 변경 객체 패턴을 통해 모델에 강력한 파일 첨부 기능을 어떻게 제공하는지 심층적으로 분석했습니다. `avatar=`를 통한 변경 스테이징, `after_save`를 통한 트랜잭션 내 영속성 확보, `after_commit`을 통한 안전한 파일 업로드, 그리고 `detach`와 `purge`를 통한 유연한 파일 관리 방식은 Active Storage 설계의 핵심 원칙을 명확히 보여줍니다. 이러한 내부 메커니즘에 대한 이해는 Rails 애플리케이션의 견고성과 성능을 향상시키는 데 기여할 것입니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!