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가 데이터베이스 트랜잭션과 파일 스토리지 작업을 안전하고 효율적으로 조율하는 정교한 방식을 보여줍니다.