Gatsby에서 Medium, Dev.to 및 Hashnode로 기사 교차 게시
이 모든 것을 통해 더 많은 청중에게 다가갈 수 있는 기회를 잡는 것이 중요합니다. 한 가지 해결책은 다양한 배포 플랫폼에 콘텐츠를 교차 게시하는 것입니다.
소프트웨어 개발자를 위해 글을 쓰기 때문에 자주 가는 곳은 Medium.com , Hashnode.com 입니다.
일반적으로 이것은 2단계 프로세스입니다.
파헤쳐보자!
GatsbyJS에서 RSS 피드 게시
Start by installing one of Gatsby's plugins:
npm install gatsby-plugin-feed
Once installed, add it to your gatsby-config.js
file:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
query: ``,
setup(options) {
return {};
},
feeds: [
{
title: "RSS feed",
output: '/rss.xml',
query: ``,
serialize: ({ query: {} }) => {},
},
],
},
},
],
};
The configuration above is not enough to generate a usable feed.
Let me break it down, option-by-option.
Note: when I first implemented the RSS feed on my site, I struggled to find examples that explained how I could achieve everything I wanted. I hope this article will help others avoid my experience.
사이트 메타데이터 검색
First, each feed will need to access the site's metadata. The format of your site's metadata can be different, but generally, the GraphQL query to select it will look as follows. If it doesn't work, you can always start your GatsbyJS server in development mode (gatsby develop
) and debug the query using the GraphiQL explorer .module.exports = {
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
query: `
{
site {
siteMetadata {
title
description
author {
name
}
siteUrl
}
}
}
`,
},
},
],
};
설정
The gatsby-plugin-feed
relies on a few pre-defined (but not well-documented) field names to build the resulting RSS.
Below, you can see a snippet with line-by-line explanations:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
setup(options) {
return Object.assign({}, options.query.site.siteMetadata, {
// make the markdown available to each feed
allMarkdownRemark: options.query.allMarkdownRemark,
// note the <generator> field (optional)
generator: process.env.SITE_NAME,
// publish the site author's name (optional)
author: options.query.site.siteMetadata.author.name,
// publish the site's base URL in the RSS feed (optional)
site_url: options.query.site.siteMetadata.siteUrl,
custom_namespaces: {
// support additional RSS/XML namespaces (see the feed generation section below)
cc:
'http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html',
dc: 'http://purl.org/dc/elements/1.1/',
media: 'http://search.yahoo.com/mrss/',
},
});
},
},
},
],
};
RSS 피드에 포함할 기사 선택
This and the next sections are highly specific to your site and are based on how you set up your posts' frontmatter. You will likely need to customize these, but they should hopefully serve as an example of how to generate a feed will all the necessary article data.
The provided GraphQL query filters articles with includeInRSS: true
in their frontmatter and sorts the results by publishing date ( frontmatter___date
), most recent first.
Since articles on my site support a featured (cover) image ( featuredImage {...}
), we want to select and include it in the feed, along with the alt
and title
fields.
We also select several other fields from frontmatter and some made available by allMarkdownRemark
.
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
feeds: [
{
query: `
{
allMarkdownRemark(
filter: { frontmatter: { includeInRSS: { eq: true } } }
sort: { order: DESC, fields: [frontmatter___date] },
) {
nodes {
excerpt
html
rawMarkdownBody
fields {
slug
}
frontmatter {
title
description
date
featuredImage {
image {
childImageSharp {
gatsbyImageData(layout: CONSTRAINED)
}
}
imageAlt
imageTitleHtml
}
category {
title
}
tags
}
}
}
}
`,
},
],
},
},
],
};
필요한 데이터 직렬화
The last step in the generation process is to merge all the available data and generate complete feed entries. This is achieved during serialization:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
feeds: [
{
serialize: ({ query: { site, allMarkdownRemark } }) => {
// iterate and process all nodes (articles)
return allMarkdownRemark.nodes.map((node) => {
// store a few shorthands that we'll need multiple times
const siteUrl = site.siteMetadata.siteUrl;
const authorName = site.siteMetadata.author.name;
// populate the canonical URL
const articleUrl = `${siteUrl}${node.fields.slug}`;
// retrieve the URL (src=...) of the article's cover image
const featuredImage =
siteUrl +
node.frontmatter.featuredImage?.image
.childImageSharp.gatsbyImageData.images.fallback
.src;
// augment each node's frontmatter with extra information
return Object.assign({}, node.frontmatter, {
// if a description isn't provided,
// use the auto-generated excerpt
description:
node.frontmatter.description || node.excerpt,
// article link, used to populate canonical URLs
link: articleUrl,
// trick: you also need to specify the 'url' attribute so that the feed's
// guid is labeled as a permanent link, e.g.: <guid isPermaLink="true">
url: articleUrl,
// specify the cover image
enclosure: {
url: featuredImage,
},
// process local tags and make them usable on Twitter
// note: we're publishing tags as categories, as per the RSS2 spec
// see: https://validator.w3.org/feed/docs/rss2.html#ltcategorygtSubelementOfLtitemgt
categories: node.frontmatter.tags
.map((tag) => makeTwitterTag(tag))
// only include the 5 top-most tags (most platforms support 5 or less)
.slice(0, 5),
custom_elements: [
// advertise the article author's name
{ author: site.siteMetadata.author.name },
// supply an image to be used as a thumbnail in your RSS (optional)
{
'media:thumbnail': {
_attr: { url: featuredImage },
},
},
// specify your content's license
{
'cc:license':
'https://creativecommons.org/licenses/by-nc-sa/4.0/',
},
// advertise the site's primary author
{
'dc:creator': renderHtmlLink({
href: siteUrl,
title: process.env.SITE_NAME,
text: authorName,
}),
},
// the main article body
{
'content:encoded':
// prepend the feature image as HTML
generateFeaturedImageHtml({
src: featuredImage,
imageAlt:
node.frontmatter.featuredImage?.imageAlt,
imageTitleHtml:
node.frontmatter.featuredImage?.imageTitleHtml
}) +
// append the content, fixing any relative links
fixRelativeLinks({
html: node.html,
siteUrl: site.siteMetadata.siteUrl,
}),
},
],
});
});
},
},
],
},
},
],
};
There are a few caveats here:
I'm using a few custom functions I wrote, here is the source code:
// Generates HTML for the featured image, to prepend it to the node's HTML
// so that sites like Medium/Dev.to can include the image by default
function generateFeaturedImageHtml({
src,
imageAlt,
imageTitleHtml,
}) {
const caption = imageTitleHtml
? `<figcaption>"${imageTitleHtml}"</figcaption>`
: '';
return `<figure><img src="${src}" alt="${imageAlt}" />${caption}</figure>`;
}
// Takes a tag that may contain multiple words and
// returns a concatenated tag, with every first letter capitalized
function makeTwitterTag(tag) {
const slug = tag
.replaceAll(/[^\w]+/g, ' ')
.split(/[]+/)
.map((word) => upperFirst(word))
.join('');
if (slug.length === 0) {
throw new Error(
`Invalid tag, cannot create empty slug from: ${tag}`,
);
}
return slug;
}
// Prepends the siteURL on any relative links
function fixRelativeLinks({ html, siteUrl }) {
// fix static links
html = html.replace(
/(?<=\"|\s)\/static\//g,
`${siteUrl}\/static\/`,
);
// fix relative links
html = html.replace(/(?<=href=\")\//g, `${siteUrl}\/`);
return html;
}
Most of the extra tags are optional but can be helpful if your RSS gets redistributed further, in which case you'd want users to have as much metadata about you and your site as possible!
We prepend the cover image usinggenerateFeaturedImageHtml
, because Medium's API은 표지 이미지를 필드로 지원하지 않습니다. 그러나 제출된 원시 콘텐츠의 첫 번째 이미지를 구문 분석하여 표지로 저장합니다. 이것은 '트릭'에 해당합니다 😊.GatsbyJS는 React 경로를 사용하기 때문에 모든 내부 링크는 RSS에서 상대 링크로 끝납니다.
fixRelativeLinks
함수를 사용하여 이러한 링크(이미지 포함)에 사이트 URL을 추가하여 이 문제를 해결합니다. (이 문제를 해결하는 방법을 문서화한 사람Mark Shust에게 먼저 외쳐주세요!)표지에 태그를 지정할 때 가장 관련성이 높은 태그를 맨 위에 정의하십시오. 예를 들어 Medium은 5개의 태그만 지원하고 DevTo는 4개의 태그를 지원하므로 RSS에 포함된 태그(카테고리)의 수를 자릅니다.
다양한 배포 플랫폼에 기사 업로드
개발
Dev.to supports . This is very easy to set up once you've done all the above. They will parse your RSS feed and create draft articles from new content. You can then review and publish these in the DEV community!
미디엄닷컴
Medium does not support reading RSS feeds, so in this case, we have to rely on Zapier to crosspost .여기서 같은 내용을 반복하지 않겠습니다. 구성 방법에 대한 자세한 내용은 위에 링크된 자습서를 참조하고Zapier 무료로 콘텐츠를 교차 게시하도록 하십시오!
해시노드닷컴
The more recent platform I'm crossposting to is Hashnode.com .잠시 미디엄용 Zapier를 사용하다보니 Zapier integration 빌드를 해보면 재미있을 것 같다는 생각이 들었습니다. 나는 그들의 UI로 시작했고 나중에 코드를 내보내고 완성했습니다. 그래서 everyone can use it!
개발자라면 자유롭게 복제하거나 포크my Zapier integration하고 계정에 배포하고 Zapier.com에서 교차 게시하는 데 사용하세요.
불행히도 Zapier는 공식적으로 게시된 통합을 대상 플랫폼에서 지원해야 합니다(이 경우 Hashnode ). 나는 그들에게 이 코드를 인수하여 커뮤니티에서 사용할 수 있게 해달라고 요청했지만 지금까지 답장을 받지 못했습니다.
결론
I hope this helps GatsbyJS users properly define RSS feeds and successfully crosspost their articles to reach a wider audience. If you follow this tutorial and struggle, please reach out via !
Thanks!
If you liked this article and want to read more like it, please subscribe to my newsletter ; 몇 주에 한 번씩 보내드립니다!
Reference
이 문제에 관하여(Gatsby에서 Medium, Dev.to 및 Hashnode로 기사 교차 게시), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/mihaibojin/crossposting-articles-from-gatsby-to-medium-dev-to-and-hashnode-4m13텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)