최근 Discourse 설치를 서브폴더로 이동했습니다. 그렇게 한 후 “전체 게시물 보기” 버튼이 작동하지 않습니다 – 버튼을 클릭하면 내용이 확장되어야 하지만, 전체 게시물이 로드되지 않습니다.
WP Discourse 설정은 변경한 것이 없습니다.
브라우저에서 임베드 URL을 직접 접근하면 404 오류가 반환됩니다:
최근 Discourse 설치를 서브폴더로 이동했습니다. 그렇게 한 후 “전체 게시물 보기” 버튼이 작동하지 않습니다 – 버튼을 클릭하면 내용이 확장되어야 하지만, 전체 게시물이 로드되지 않습니다.
WP Discourse 설정은 변경한 것이 없습니다.
브라우저에서 임베드 URL을 직접 접근하면 404 오류가 반환됩니다:
이것은 관련이 없습니다. 이 라우트는 application/json 콘텐츠 타입으로만 응답합니다. https://tecnoblog.net/comunidade/posts/483289/expand-embed.json 은 다음을 반환하고 있습니다.
"<div><div></div></div>\n<hr>\n<small>Este é um tópico de discussão auxiliar para a entrada original em <a href='https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix'>https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix</a></small>\n"
<div><div></div></div> 가 콘텐츠여야 합니다.
혹시 블로그 URL도 변경하셨나요?
원박스(Onebox) 표시도 이상하게 느껴집니다. 캐시된 절단된 콘텐츠가 있어야 할 것 같아서, 위의 조건문에서 body.present? 가 false라고 가정하고 있습니다.
Rails 콘솔에 진입하여 TopicEmbed.where(topic_id: 157441).pick(:embed_url) 이 올바른 블로그 콘텐츠 URL을 표시하는지 확인해 주시겠어요?
https://tecnoblog.net/comunidade/logs 에서 관련 오류를 발견할 수 있나요?
아, 그렇군요!
게시물 URL을 반환합니다:
discourse(prod)> TopicEmbed.where(topic_id: 157441).pick(:embed_url)
=> “``https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix”
로그에 관련 에러가 있는 것 같지 않습니다.
아니요! 블로그 URL은 항상 `tecnoblog.net`이었습니다.
또한 서버 IP가 CF 방화벽에서 우회(bypass)되고 있다는 점도 언급할 가치가 있습니다:
이 문제를 몇 번이나 이렇게 디버깅해야 해서 복잡합니다. 이해해 주세요.
아래 스크립트를 실행하고 출력을 여기에 공유해 주세요
# 디버깅하려는 토픽 ID 또는 URL로 교체하세요
topic_id = 386983
# 1. TopicEmbed의 존재 여부와 콘텐츠 확인
te = TopicEmbed.find_by(topic_id: topic_id)
puts "TopicEmbed 존재 여부: #{te.present?}"
puts "임베드 URL: #{te&.embed_url}"
puts "콘텐츠 캐시 존재 여부: #{te&.embed_content_cache.present?}"
puts "콘텐츠 캐시 길이: #{te&.embed_content_cache&.length || 0}"
puts "콘텐츠 SHA1: #{te&.content_sha1}"
# 2. 실제 캐시된 콘텐츠 확인 (첫 500자)
puts "\n--- 캐시된 콘텐츠 미리보기 ---"
puts te&.embed_content_cache&.truncate(500)
# 3. 원격 URL에서 가져오기 시도
if te&.embed_url.present?
puts "\n--- 원격 가져오기 시도 중 ---"
begin
response = TopicEmbed.find_remote(te.embed_url)
puts "원격 가져오기 성공 여부: #{response.present?}"
puts "원격 본문 존재 여부: #{response&.body.present?}"
puts "원격 본문 길이: #{response&.body&.length || 0}"
puts "원격 제목: #{response&.title}"
puts "원격 본문: #{response&.body&.truncate(500)}"
rescue => e
puts "원격 가져오기 실패: #{e.message}"
end
end
# 4. expanded_for가 반환할 내용 확인
if te.present?
puts "\n--- expanded_for 테스트 ---"
post = Post.find(te.post_id)
# 캐시 초기화로 새로운 가져오기 강제
Discourse.cache.delete("embed-topic:#{topic_id}")
begin
expanded = TopicEmbed.expanded_for(post)
puts "확장된 콘텐츠 존재 여부: #{expanded.present?}"
puts "확장된 콘텐츠 길이: #{expanded&.length || 0}"
rescue => e
puts "expanded_for 실패: #{e.message}"
end
end
# 5. 관련 설정 확인
puts "\n--- 사이트 설정 ---"
puts "embed_truncate: #{SiteSetting.embed_truncate}"
puts "allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}"
puts "blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}"
이것은 https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462?u=falco 가 왜 실패하는지 보여줄 것입니다
discourse(prod)> # 디버깅 중인 토픽 ID 또는 URL로 교체하세요
discourse(prod)> topic_id = 386983
discourse(prod)>
discourse(prod)> # 1. TopicEmbed의 존재 여부와 내용을 확인합니다
discourse(prod)> te = TopicEmbed.find_by(topic_id: topic_id)
discourse(prod)> puts “TopicEmbed 존재 여부: #{te.present?}”
discourse(prod)> puts “임베드 URL: #{te&.embed_url}”
discourse(prod)> puts “콘텐츠 캐시 존재 여부: #{te&.embed_content_cache.present?}”
discourse(prod)> puts “콘텐츠 캐시 길이: #{te&.embed_content_cache&.length || 0}”
discourse(prod)> puts “콘텐츠 SHA1: #{te&.content_sha1}”
discourse(prod)>
discourse(prod)> # 2. 실제 캐시된 내용을 확인합니다 (첫 500자)
discourse(prod)> puts “\n— 캐시된 콘텐츠 미리보기 —”
discourse(prod)> puts te&.embed_content_cache&.truncate(500)
discourse(prod)>
discourse(prod)> # 3. 원격 URL에서 가져오기를 시도합니다
discourse(prod)* if te&.embed_url.present?
discourse(prod)* puts “\n— 원격 가져오기 시도 중 —”
discourse(prod)* begin
discourse(prod)* response = TopicEmbed.find_remote(te.embed_url)
discourse(prod)* puts “원격 가져오기 성공 여부: #{response.present?}”
discourse(prod)* puts “원격 본문 존재 여부: #{response&.body.present?}”
discourse(prod)* puts “원격 본문 길이: #{response&.body&.length || 0}”
discourse(prod)* puts “원격 제목: #{response&.title}”
discourse(prod)* puts “원격 본문: #{response&.body&.truncate(500)}”
discourse(prod)* rescue => e
discourse(prod)* puts “원격 가져오기 실패: #{e.message}”
discourse(prod)* end
discourse(prod)> end
discourse(prod)>
discourse(prod)> # 4. expanded_for가 반환할 내용을 확인합니다
discourse(prod)* if te.present?
discourse(prod)* puts “\n— expanded_for 테스트 중 —”
discourse(prod)* post = Post.find(te.post_id)
discourse(prod)*
discourse(prod)* # 캐시를 지워 새로 가져오도록 강제합니다
discourse(prod)* Discourse.cache.delete(“embed-topic:#{topic_id}”)
discourse(prod)*
discourse(prod)* begin
discourse(prod)* expanded = TopicEmbed.expanded_for(post)
discourse(prod)* puts “확장된 콘텐츠 존재 여부: #{expanded.present?}”
discourse(prod)* puts “확장된 콘텐츠 길이: #{expanded&.length || 0}”
discourse(prod)* rescue => e
discourse(prod)* puts “expanded_for 실패: #{e.message}”
discourse(prod)* end
discourse(prod)> end
discourse(prod)>
discourse(prod)> # 5. 관련 설정을 확인합니다
discourse(prod)> puts “\n— 사이트 설정 —”
discourse(prod)> puts “embed_truncate: #{SiteSetting.embed_truncate}”
discourse(prod)> puts “allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}”
discourse(prod)> puts “blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}”
TopicEmbed 존재 여부: false
임베드 URL:
콘텐츠 캐시 존재 여부: false
콘텐츠 캐시 길이: 0
콘텐츠 SHA1:
— 캐시된 콘텐츠 미리보기 —
— 사이트 설정 —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil
discourse(prod)>
![]()
이것이 올바른 토픽 ID인지 확인해 주시겠어요? https://tecnoblog.net/comunidade/t/-/386983은 404 오류로 연결됩니다.
아, 맞다. 제가 링크한 주제는 실제로는 157462입니다.
실수했습니다!
올바른 토픽 ID에 대한 결과가 다음과 같습니다.
TopicEmbed 존재 여부: true
Embed URL: https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento
콘텐츠 캐시 존재 여부: true
콘텐츠 캐시 길이: 22
콘텐츠 SHA1:
— 캐시된 콘텐츠 미리보기 —
<div><div></div></div>
— 원격 가져오기 시도 중 —
원격 가져오기 성공: true
원격 본문 존재 여부: true
원격 본문 길이: 22
원격 제목:
원격 본문:
— expanded_for 테스트 중 —
확장된 콘텐츠 존재 여부: true
확장된 콘텐츠 길이: 309
— 사이트 설정 —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil
Cloudflare 우회가 성공했나요? https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento의 본문이 22자뿐이며, 제목 태그가 없는 것으로 보입니다.
이것은 쉽게 테스트할 수 있습니다. 다음을 시도해 보세요
url = "https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/"
response = TopicEmbed.find_remote(url)
puts "Remote fetch success: #{response.present?}"
puts "Remote body present: #{response&.body.present?}"
puts "Remote body length: #{response&.body&.length || 0}"
puts "Remote title: #{response&.title}"
puts "Remote body: #{response&.body&.truncate(500)}"
동작하는 것 같습니다:
discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 3776
Remote title: Governo renova app da CNH para baratear obtenção do documento • Tecnoblog
Remote body:
<figure><img src="https://files.tecnoblog.net/wp-content/uploads/2025/12/cnh-brasil-app-1060x596.jpg">
<figcaption>Aplicativo CNH do Brasil (imagem: Emerson Alecrim/Tecnoblog)</figcaption></figure>
</div>
<details>
Resumo
<div><ul>
<li>App CNH do Brasil substitui CDT e passa a oferecer recursos para obtenção da CNH, em especial, aulas teóricas gratuitas;</li>
<li>Aulas práticas continuam obrigatórias, mas a carga horária mínima foi reduzida de ...
=> nil
여기서는 끝부분의 슬래시가 없습니다:
discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body:
=> nil
구글 포스트에서도 포스트 슬러그가 변경된 경우 동일한 오류가 발생합니다.
예를 들어, 이 포스트의 URL은 이전에는 다음과 같았습니다:
https://tecnoblog.net/486925/o-que-e-pirataria-digital/
이제 다음과 같이 변경되었습니다:
겉보기에 이것이 주요 문제인 것 같습니다. Embed Discourse comments on another website via Javascript를 사용할 경우 이 부분은 파라미터로 제어할 수 있어 수정하기가 매우 쉽습니다.
WP-Discourse가 이를 어떻게 결정하는지에 대해서는 잘 모르겠습니다. 게시물 정본(canonical)을 사용해야 하는데, 확신은 없습니다. @angus 님, 혹시 아이디어가 있으신가요?
카테고리의 모든 임베드 URL을 최종 목적지까지 추적하여 Discourse가 이를 업데이트하도록 강제하는 방법이 있을까요?
본격적인 프로덕션 환경에서 사용할 수 있을 때, 제가 테스트해 온 그 완전한 임베드 방식인 embed discourse로 마이그레이션할 계획입니다. 하지만 임베드 URL이 일치하지 않으면, 각 게시물마다 새로운 토픽이 생성되고 댓글이 유실될 가능성이 높습니다…
다음 코드를 실행해 보세요:
te = TopicEmbed.find_by(topic_id: 157462)
te.embed_url = te.embed_url + "/"
te.save
이 방법으로 https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462 의 문제가 해결되나요?
동작합니다!
하지만 이런 경우에 대한 수정 방법이 있을까요?
Gemini는 이 코드를 제안했습니다:
# 설정
CATEGORY_SLUG = 'tb'
category = Category.find_by(slug: CATEGORY_SLUG)
unless category
puts "오류: 카테고리 '#{CATEGORY_SLUG}'를 찾을 수 없습니다."
exit
end
puts "'#{category.name}' 카테고리에서 URL 전체 스캔을 시작합니다..."
puts "토픽의 양과 사이트 응답에 따라 시간이 걸릴 수 있습니다..."
count_updated = 0
count_errors = 0
count_ok = 0
Topic.where(category_id: category.id).find_each do |topic|
current_url = topic.custom_fields["embed_url"]
# embed_url이 없으면 건너뜀
next unless current_url.present?
begin
# 리다이렉트를 따르는 GET 요청을 수행합니다
response = Faraday.get(current_url)
final_url = response.env.url.to_s
# 요청이 성공한 경우 (200 OK)
if response.status == 200
# 최종 URL이 데이터베이스에 저장된 URL과 다른지 확인합니다
# 필요 시 미묘한 차이를 무시할 수 있지만, 여기서는 정확한 문자열 비교를 수행합니다
if final_url != current_url
puts "\n[업데이트] 토픽 ##{topic.id}:"
puts " 이전: #{current_url}"
puts " 이후: #{final_url}"
topic.custom_fields["embed_url"] = final_url
topic.save_custom_fields(true)
count_updated += 1
else
# print "." # 시각적 진행 상황(점)을 보려면 주석을 해제하세요
count_ok += 1
end
else
puts "\n[HTTP 오류 #{response.status}] 토픽 ##{topic.id} - URL: #{current_url}"
count_errors += 1
end
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
puts "\n[연결 실패] 토픽 ##{topic.id} - URL: #{current_url} - #{e.message}"
count_errors += 1
rescue StandardError => e
puts "\n[일반 오류] 토픽 ##{topic.id} - #{e.message}"
count_errors += 1
end
# 선택 사항: WordPress 서버에 과부하가 걸리지 않도록 짧은 휴지
# sleep 0.1
end
puts "\n\n최종 요약:"
puts "------------------------------------------------"
puts "확인된 토픽 (정상): #{count_ok}"
puts "업데이트된 토픽: #{count_updated}"
puts "발견된 오류: #{count_errors}"
puts "------------------------------------------------"
드디어 진전이 있네요 ![]()
그런 스크립트는 좋은 아이디어이지만, 실행하기 전에 반드시 백업을 만들어 두세요.
이 작은 테이블만이라도 백업해 두면 좋겠습니다.
좋아요! 팀이 퇴근하는 대로 나중에 실행해 볼게요.
여러분, 다시 트레일링 슬래시(trailing slash) 문제가 발생한 것 같네요 ![]()
[트레일링 슬래시가] 주요 문제인 것으로 보입니다. JavaScript를 사용하여 다른 웹사이트에 Discourse 댓글을 임베드할 때는 이를 파라미터로 제어할 수 있으며, 수정하기가 매우 쉽습니다.
참고로 Discourse의 모든 토픽 임베드는 embed_url에서 트레일링 슬래시를 제거합니다. TopicEmbed.normalize_url을 참조하세요. JavaScript 임베드와 WP Discourse 임베드가 교차하는 별도의 사례로 인해, 우리는 두 임베드 방식 모두에서 이 처리 방식을 표준화했습니다. Apply TopicEmbed url normalisation to embed urls inserted in the PostCreator - Pull Request #30641%EC%9D%84 - discourse/discourse - GitHub 참조하세요.
@Thiago_Mobilon 이 과정에서 Discourse도 업데이트하셨나요? Discourse 업데이트가 서브폴더 설치로 이전한 시점과 동시에 이루어졌기 때문에, WP Discourse 임베드에 대한 embed_url 정규화 표준화가 여기에도 적용되고 있는 것으로 보입니다. 현재 실행 중인 Discourse 버전은 무엇인가요? (이전 버전이 무엇이었는지 아신다면 그것도 알려주세요.)
참고로 최신 버전의 Discourse에서 로컬로 이 두 명령어를 실행하면 동일한 결과를 얻습니다. 즉, 아티클의 HTML 본문이 반환됩니다.
# 트레일링 슬래시가 있는 경우
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/")
# 트레일링 슬래시가 없는 경우
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento")
# 동일한 결과를 생성
혹시 WordPress 쪽에서 변경 사항을 적용하셨나요?
** 수정 이 토픽을 좀 더 자세히 읽어보니, 문제가 Discourse를 서브폴더 설치로 이전하거나 트레일링 슬래시 때문이 아니라, WordPress URL을 마이그레이트한 것 때문일 수 있습니다. 즉,
예를 들어, 이 게시물에서 사용된 URL은 이전에는 다음과 같았습니다:
https://tecnoblog.net/486925/o-que-e-pirataria-digital/
이제 다음과 같이 변경되었습니다:
따라서 문제는 topic_embeds.embed_url에 구식 URL 구조가 저장되어 있고, FinalDestination이 어떤 이유에서인지(즉, 리다이렉트를 따를 수 없어서) 새 URL을 해결하지 못하는 것일 수 있습니다.
그 경우, 구식 블로그 URL이 새 블로그 URL로 리다이렉트되도록 하거나 topic_embeds.embed_url을 마이그레이션해야 합니다. 마이그레이션 측면에서, 여러분의 스크립트는 부정확합니다. 예를 들어 topic.custom_fields["embed_url"]은 embed_url이 저장되는 곳이 아닙니다.
리다이렉트 대신 마이그레이션 경로를 선택하시려면 다음을 제안합니다. 먼저 topic_embeds.embed_url의 잘못된 블로그 URL 형식이 문제임을 확인하기 위해 예를 확인하세요. 예: TopicEmbed.find_by(topic_id: 157441). 그런 다음, 해당 컬럼에 구식 URL 형식이 저장되어 있음을 확인하면, 특정 카테고리의 모든 구식 형식 embed_url을 업데이트하기 위해 다음을 실행하세요:
category_id = # 여기에 카테고리 ID 입력
TopicEmbed.joins(:topic).where(topics: { category_id: category_id }).find_each do |embed|
new_url = embed.embed_url.sub(%r{/\d+/}, "/responde/")
embed.update!(embed_url: new_url) if new_url != embed.embed_url
end
구식 형식에서 새 형식으로의 정규식 치환(sub(%r{/\d+/}, "/responde/"))은 제공하신 예시에 기반한 추측일 뿐입니다. 실제 URL에 대한 효과를 여기서 테스트할 수 있습니다: https://regex101.com/
안녕하세요, Angus!
아니요, 이것은 서로 다른 문제입니다. 서브폴더로 옮긴 시점에 트레일링 슬래시 문제가 시작되었지만, 몇 년 전의 다른 슬러그를 가진 오래된 URL들도 있습니다.
설치를 다시 구축해야 했기 때문에, 네, 아마도 이 새로운 표준이 원인일 것 같습니다.
이 문제를 해결하기 위한 제 제안은 다음과 같습니다: Discourse가 데이터를 가져오기 위해 적어도 한두 번의 리디렉트를 따를 수 있지 않을까요? 그러면 트레일링 슬래시 문제도 해결되고, 향후 발생할 수 있는 URL 변경에도 대비하여 웹사이트의 안정성을 높일 수 있습니다.
또한, 오래된 토픽을 업데이트하기 위해 스크립트를 실행할 필요가 없으므로 더 안전합니다. 그러한 스크립트가 데이터베이스에 손상을 줄 수도 있기 때문입니다.