S3 또는 Cloudflare R2를 업로드에 사용하고 동시에 커스텀 CDN URL을 설정한 경우, 커스텀 이모지 업로드는 CDN 설정을 무시하고 원본 버킷 URL에서 직접 로드하려 합니다.
문제
관리자가 커스텀 이모지를 업로드하면 업로더가 upload 레코드를 생성하고 데이터베이스에 원본 버킷 URL(예: //my-bucket.s3.amazonaws.com/... 또는 //my-bucket.r2.cloudflarestorage.com/...)을 저장합니다. 이는 표준 Discourse 동작입니다.
그러나 app/models/emoji.rb가 /site.json을 위한 이모지 캐시를 생성할 때 upload.url을 emoji 객체에 직접 전달합니다:
e.url = emoji.upload&.url
CDN 헬퍼를 건너뛰기 때문에 프론트엔드는 원본 버킷 URL을 받게 됩니다. 따라서 버킷의 액세스 정책이 얼마나 엄격한지에 따라 이미지가 깨지거나 Discourse가 이모지를 내부 avatar_proxy를 통해 라우팅하도록 강제됩니다.
해결책
Discourse.store.cdn_url()로 URL 할당을 감싸도록 PR을 열었습니다. 이를 통해 커스텀 이모지 로더가 표준 게시물 이미지와 아바타가 라우팅되는 방식과 일치하도록 합니다.
임시 해결책
PR이 검토되고 병합될 때까지 기다리는 동안, 커스텀 이모지가 DOM에 렌더링되기 직전에 원본 버킷 URL을 올바른 CDN URL로 교체하는 경량 테마 컴포넌트를 만들었습니다(게시물과 채팅 모두에서 작동합니다).
사이트에서 이 버그를 경험하고 있다면, 이 컴포넌트를 설치하고 테마 관리자 설정에서 S3 문자열을 구성하여 깨진 커스텀 이모지를 수정할 수 있습니다:
참고: 이미 업로드한 커스텀 이모지가 현재 깨져 있는 경우, 컨테이너에서 discourse remap "//my-raw-bucket-url.com" "https://my-cdn.com"를 실행하면 데이터베이스의 기존 이모지가 수정되며, 테마 컴포넌트는 PR 수정이 코어에 병합될 때까지 새로 업로드된 이모지를 수정합니다.
와, 정말 험난한 여정이었다. 결국 Cloudflare R2 객체 스토리지와 Discourse 인스턴스를 전체적으로 재구성했는데, 이 버그가 R2 관련인 것 같아. Cloudflare DNS 레코드를 수정하고 인스턴스를 재빌드해서 DISCOURSE_CDN_URL이 실제로 해당 주소를 가리키도록 했더니, 번역 문자열 같은 다른 여러 가지가 깨지고 콘솔에 CORS 오류를 포함한 여러 오류가 발생했다. 오늘 하루 종일 엉뚱한 방향으로 빠져들었다. 그래서 DISCOURSE_CDN_URL 사용이 Cloudflare R2와 호환되지 않는 것 같다. (이게 정말 이상했는데, 원래 DNS 항목을 설정할 때 cdn.mysite.com DNS 레코드를 잘못 입력해서 cdn.mysite.com.mysite.com으로 해석되게 되어 있었다.) DISCOURSE_CDN_URL을 올바르게 설정하는 것이 Cloudflare R2 객체 스토리지와 호환되지 않는 것으로 보인다. 여기서 내가 완전히 이해하지 못하는 부분이 더 있을 수도 있다.
어쨌든, 내 PR 브랜치로 재빌드하면 Discourse.store.cdn_url()로 할당을 감싸서 커스텀 이모지 업로드가 표준 게시물 이미지 업로드와 동일한 S3 CDN 라우팅 및 폴백 로직을 따르도록 했기 때문에 모든 것이 정상으로 돌아온다.
PR을 다시 열고 설명을 수정했다. 하지만 Discourse 팀이 이를 병합하지 않기로 결정해도 괜찮다. 왜냐하면 테마 컴포넌트가 클라이언트 레벨에서 문제를 해결해주기 때문이다. 참고로 PR 수정 사항은 커스텀 이모지를 위한 R2 객체 스토리지 설정에만 영향을 미치며, AWS 같은 다른 일반적인 S3 호환 환경에는 영향을 미치지 않는다.