# Gatsby
- **React** 기반의 프레임워크이다.
- 정적인 웹사이트(유저들이 데이터를 올리지 않는 경우) 제작에 특화되어 있다.
- **Gastby**로 만든 대부분의 웹사이트는 **빌드, 렌더링** 될 때 거의 모든 데이터를 한 번에 가져와 웹사이트를 만든다.
- **react(create react app)는** User의 **JS**가 비활성화 된 상태이거나 **인터넷 연결 상태가 좋지 않을 경우**, 페이지에는 아무 것도 뜨지 않는다.
- **Gatsby**(develop 모드가 아닌)의 경우에는 빌드 되는 과정에서 소스 코드가 콘텐츠의 모든 내용을 이미 알고 있기 때문에 J**S가 비활성화된 상태**나 **인터넷 연결 상태가 좋지 않은 경우**라도 문제 없이 실행된다.
Gatsby는 엄청나게 빠른 웹사이트와 앱을 구축할 수 있도록 도와주는 React 기반의 무료 오픈 소스 프레임워크입니다.
동적으로 렌더링되는 사이트의 제어 및 확장성과 정적 사이트 생성 속도를 결합하여 완전히 새로운 가능성의 웹을 만듭니다.
## Install
```bash
# Gatsby CLI 설치
npm i -g gatsby-cli
# Gatsby 버전 확인
gatsby --version
# Gatsby 설치
gatsby new
```
## Run
```bash
# develop 모드로 사이트 열기
npm run develop
# 배포 모드로 build 하기
npm run build
# 배포된 버젼 사이트 열기
npm run serve
```
# Gatsby Rules
### Router
- 실제 브라우저에 보여줄 page는 무조건 **pages** 폴더 안에 있어야 한다.
- 따로 라우터를 쓰지 않아도 `/페이지명` 으로 해당 **page**에 접근할 수 있다.
![[pages.png]]
- 만약 경로에 해당하는 **page**가 없으면 자동으로 `404.tsx` page로 넘어간다.
- 만약 **pages** 폴더 안에 다른 폴더를 만들고 그 폴더의 **page**를 **route** 시키고 싶다면 `index.tsx` 파일 이름을 지정하면 자동으로 **폴더의 이름**과 **매칭**이 된다.
## Layout
- **Layout 컴포넌트를** 만들어서 **다른 컴포넌트**가 사용하게끔 하는 것도 가능하다.
```jsx
**// Layout.tsx
import { Link } from "gatsby";
import React from "react";
interface LayoutProps {
children: React.ReactNode;
title: string;
}
export default function Layout({ children, title }: LayoutProps) {
return (
<div>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about-us">About Us</Link></li>
<li><Link to="/blog">Blog</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</nav>
<h1>{title}</h1>
<main>{children}</main>
</div>
);
}**
```
```jsx
export default function AboutUs() {
return (
<Layout title="About Us">
<div>
<h1>About Us</h1>
</div>
</Layout>
);
}
```
## Header
- 어떤 컴포넌트든 안에 **Head** 컴포넌트가 있으면 무조건 최상단에 해당 **Head** 컴포넌트를 띄운다.
```jsx
export const Head = () => {
return <title>Blog</title>;
};
```
- 이를 하나의 **컴포넌트 형태**로 만들어서 **다른 컴포넌트**가 사용하게끔 하는 것도 가능하다.
```jsx
// Seo.tsx
import React from "react";
interface SeoProps {
title: string;
}
export default function Seo({ title }: SeoProps) {
return <title>{title}</title>;
}
```
```jsx
// Other component
export const Head = () => {
return <Seo title="Devstickers!" />;
};
```
## gatsby-browser
- browser의 CSS 스타일을 적용하기 위해선 최상단 위치에 `gatsby-browser.ts or js` 을 만들고 적용할 스타일을 import 해주면 된다.
![[gatsby-browser.png]]
```jsx
import "./src/styles.css";
```
💡Pico CSS 아주 가볍지만 나름 이쁜(?) CSS 프레임워크이다.
```jsx
@import url('<https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css>');
```
# Data Fetching
- **Gatsby**는 **Build** 과정에서 모든 데이터를 가져와 **html**로 바꾼다.
- 데이터를 가져오는 과정은 **GraphQL**을 통해 수행된다.
## GraphQL
- **Query Language** 중 하나로 클라이언트가 필요한 데이터를 정확하게 요청하고 서버는 요청된 데이터만 응답하여 효율적인 데이터 관리를 할 수 있게 한다.
- **Facebook**에서 개발했다.
- Query Example
```graphql
query {
allPlanets {
totalCount
planets {
name
population
}
}
}
query {
film(id:"ZmlsbXM6MQ==") {
title
producers
planetConnection {
planets {
name
}
}
speciesConnection {
speices {
name
}
}
}
}
```
- **npm start**를 통해 `localhost:8000`으로 build 됐다면 [http://localhost:8000/___graphql을](http://localhost:8000/___graphql%EC%9D%84) 통해 **GraghQL**의 **Explorer**로 진입할 수 있다.
- **useStaticQuery**를 통해 **react**에서 **GraphQL**를 사용할 수 있다.
```jsx
export default function Seo({ title }: SeoProps) {
const data = useStaticQuery<Queries.SeoQueryQuery>(graphql`
query SeoQuery {
site {
siteMetadata {
title
}
}
}
`);
return <title>{title} | {data.site?.siteMetadata?.title}</title>;
}
```
💡 **siteMetadata**는 **gatsby-config.ts**에서 수정 및 추가할 수 있다.
💡 **graphQL**를 쓰게 되면 **gatsby**에서 자동으로 불러온 **쿼리의 타입을** 지정해준다. (*경로 : gatsby-types.d.ts)
- 같은 **프레임** 안이라면 퀴리를 외부에 선언하고 데이터를 **파라미터로** 받을 수도 있다.
```jsx
function Blog({ data }: PageProps<Queries.BlogQueryQuery>) {
console.log(data);
return (
<Layout title="Blog">
<ul>
{data.allFile?.nodes?.map((node) => (
<li key={node?.name}>{node?.name}</li>
))}
</ul>
</Layout>
);
}
export const query = graphql`
query BlogQuery {
allFile {
nodes {
name
}
}
}
`
```
# Gatsby Plugin Library
- Gatsby 사이트나 앱을 커스텀할 수 있는 기능을 제공하는 라이브러리이다.
- 라이브러리 사이트 주소 : [https://www.gatsbyjs.com/plugins](https://www.gatsbyjs.com/plugins)
## gatsby-source-filesystem
- **Local File System**에서 **Gatsby** 어플리케이션으로 데이터를 **sourcing**하기 위한 플러그인
```bash
npm install gatsby-source-filesystem
```
- **gatsby-config.ts**에서 어떤 데이터들을 가져올 것인지 **폴더 경로**를 **지정**해주어야 한다.
```jsx
plugins: [
{
resolve: "gatsby-source-filesystem",
options: {
path: `${__dirname}/blog-posts`,
}
}
],
```
💡 **GraphQL** 을 통해 가져온 데이터를 확인 할 수 있다.
```graphql
query {
allFile {
nodes {
name
}
}
}
```
## gatsby-plugin-mdx
- **MDX**를 사용하면 마크다운 안에 **포함된 JSX**를 작성할 수 있고 **gatsby-plugin-mdx**에서 **MDX**를 사용할 수 있다.
```bash
npm install gatsby-plugin-mdx gatsby-source-filesystem @mdx-js/react
```
- **source-filesystem**과 마찬가지로 따로 **gatsby-config.ts**에서 설정을 해주어야 한다.
```jsx
plugins: [
`gatsby-plugin-mdx`,
]
```
- **mdx file**을 생성한 뒤 `---` 안에 특정 값들을 넣으면 해당 값들을 따로 분류하고 가져올 수 있다.
```jsx
---
title: Hello
category: personal
date: "2020-10-29"
---
## Hello everyone!
Welcome to my blog post. I'm very happy to you all here with me on this special ocasion.
```
💡**GraphQL**에서 해당 값들을 가져올 수 있다.
```graphql
query {
allMdx {
nodes {
frontmatter {
title
category
date(formatString:"YYYY.MM.DD")
}
excerpt(pruneLength:50)
}
}
}
```
# **Dynamic Pages**
- **{mdx.frontmatter__slug}.tsx** 등 **`mdx.frontmatter`** 형식을 이용해 **여러가지 컴포넌트**들을 생성할 수 있다
![[Dynamic Pages.png]]
- 이렇게 생성된 컴포넌트들은 각각 **해당 형식**으로 **지정된 값**을 파라미터로 가지고 있는데**GraphQL**을 통해 해당 mdx를 읽어서 띄우는 것도 가능하다.
```graphql
export default function BlogPost({ data, children }: PageProps<Queries.BlogPostQuery>) {
return <Layout title={data.mdx?.frontmatter?.title || ""}>
{children}
</Layout>;
}
export const query = graphql`
query BlogPost($frontmatter__slug: String!) {
mdx(frontmatter: {slug: {eq: $frontmatter__slug}}) {
body
frontmatter {
title
category
date(formatString:"YYYY.MM.DD")
author
slug
}
}
}
`;
```
💡 **children** 파라미터로 **mdx**에서 **markdown**의 값 또한 가져올 수 있는데, 값을 가져올 때 **자동**으로 **html**로 변환되어 가져온다!
## gatsby-plugin-image
- 다양한 크기와 형식의 이미지 생성해주는 플러그인이다.
```bash
npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-source-filesystem gatsby-transformer-sharp
```
💡공식 라이브러리 사이트에서 **image, shape, filesystem**, **transformer-sharp**가 동시에 설치하는 것을 권장한다.
💡**gatsby-plugin-sharp**
- **Sharp**한 이미지 처리 방식을 제공하는 플러그인이다.
- **Sharp** : 일반적인 형식의 큰 이미지를 더 작고 웹 친화적인 **JPEG**, **PNG**, **WebP**, **GIF** 및 다양한 크기의 **AVIF** 이미지로 변환한 것이다.
- **config.ts 설정**
```jsx
plugins: [
`gatsby-plugin-mdx`,
`gatsby-plugin-image`,
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
]
```
### **Static Image**
- **Gatsby**가 **Page**를 빌드 할 때, **Static Image**가 있으면 해당 **Image**의 **URL**로 가서 이미지를 가져온 뒤 **webp**형식으로 변환해준다.
- 이를 통해 User는 원본 사진이 아닌 크기가 조정된 사진을 가져올 수 있게 된다.
```jsx
const IndexPage: React.FC<PageProps> = () => {
return (
<Layout title="Hello welcome to Devstickers">
<StaticImage height={500} src="<https://images.unsplash.com/photo-1625768376503-68d2495d78c5?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1450&q=80>" alt="Stickers on the wall" />
</Layout>
)
}
```
### Dynamic Image(**GatsbyImage**)
- **GatsbyImage**를 통해 이미지를 **GraphQL** 통해 동적으로 띄우는 것도 가능하다.
```jsx
import { GatsbyImage, getImage } from "gatsby-plugin-image";
export default function BlogPost({ data, children }: PageProps<Queries.BlogPostQuery>) {
const image = getImage(data.mdx?.frontmatter?.headerImage?.childImageSharp?.gatsbyImageData!);
return <Layout title={data.mdx?.frontmatter?.title || ""}>
{image && <GatsbyImage image={image} alt={data.mdx?.frontmatter?.title || ""} />}
{children}
</Layout>;
}
export const query = graphql`
query BlogPost($frontmatter__slug: String!) {
mdx(frontmatter: {slug: {eq: $frontmatter__slug}}) {
body
frontmatter {
title
category
date(formatString:"YYYY.MM.DD")
author
slug
headerImage {
childImageSharp {
gatsbyImageData(height : 450, placeholder :TRACED_SVG)
}
}
}
}
}
`;
```
💡
**gatsbyImageData**의 다양한 옵션을 추가할 수 있다.
**placeholder :** 이미지가 나타나기 전의 임시 이미지를 지정 가능하다.
- **TRACED_SVG :** 흑백 svg 이미지를 생성하여 보여준다.
- **BLURRED** : 블러 svg 이미지를 생성하여 보여준다.
# Contentful
- **Contentful**은 컨텐츠 관리 시스템으로 개발자가 아닌 일반 사용자들이 **Gatsby**에 컨텐츠를 쉽게 관리할 수 있게 해준다.
- 개발자들이 해당 **Contentful**에서 생성된 데이터를 가져와 사이트에 올려놓도록 만들면 회사의 개발 담당이 아니더라도 (ex 마케팅 팀) 쉽게 사이트의 게시물을 만들 수 있다.
## **gatsby-source-contentful**
- **gatsby**에서 **contentful** 데이터를 가져오기 위해서는 **gatsby-source-contentful** 플러그인이 필요하다.
```bash
npm install gatsby-source-contentful gatsby-plugin-image
```
- 플러그인 설정
```jsx
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `your_space_id`,
// Learn about environment variables: <https://gatsby.dev/env-vars>
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
},
},
]
```
💡 **spaceId**와 **accessToken**은 **contentful**애서 발급 받은 것을 연결하면 된다.
- **GraphQL**의 **allContentfulStickerPack(예시)**을 통해 **Sticker**의 **데이터**들을 가져올 수 있다.
```jsx
const IndexPage = ({ data }: PageProps<Queries.StickersQuery>) => {
return (
<Layout title="Hello welcome to Devstickers">
{data.allContentfulStickerPack.nodes.map((sticker) => (
<article key={sticker.name}>
<GatsbyImage image={sticker.preview?.gatsbyImageData!} alt={sticker.name} />
<h2>{sticker.name}</h2>
<h4>{sticker.price}</h4>
</article>
))}
</Layout>
)
}
export const query = graphql`
query Stickers {
allContentfulStickerPack {
nodes {
name
price
preview {
gatsbyImageData(placeholder : BLURRED)
}
}
}
}
`
```
# Question
### `sticker.preview?.gatsbyImageData!`에서 !는 왜 붙이는가?
- **TypeScript**에서는 **value**가 **undefined** 또는 **null**이 될 수 없음을 강제로 지정할 때 `!` 을 사용하지만, 컴파일러 오류만 피할 뿐 실제로 **undefined**과 **null**이 올 수 있기 때문에 권장하지 않는다.
## Slug가 무엇일까?
- **slug**는 **URL**의 한 부분으로 **사람이 읽기 쉬운 키워드**로 페이지를 구분하게 해주는 키워드 이다.
- **slug**은 자동 생성되거나 직접 입력되는데, 그 간결성을 유지하기 위해서 다음과 같은 몇가지 조건을 가진다.
- 너무 긴 제목은 가지지 않는다.
- 전체가 소문자인 영문자(Latin script)이다.
- 각 단어는 하이픈 `-` 혹은 언더스코어 `-`로 연결한다.
- 구두점 `.` 을 사용하지 않으며 접속사처럼 없어도 되는 단어는 생략한다.
- **Gatsby**에서 **Slug**를 통해 **Dynamic Page**를 만들 때는 `__` 을 사용하고 Slug 이외에는 `.` 을 사용한다.
# **Framework vs Library**'
| | | |
|---|---|---|
||**Framework**|**Library**|
|개념|개발자가 소프트웨어를 개발함에 있어 코드를 구현하는 개발 시간을 줄이고, 코드의 재사용성을 증가 시키기 위해 일련의 **클래스 묶음**이나 **뼈대**, **틀**을 라이브러리 형태로 제공되는 것을 말한다.|개발자가 만든 **클래스**들의 **나열**로, 다른 프로그램들에서 사용할 수 있도록 제공하는 방식이다.|
|비유|프레임워크는 "**틀**", “**집**”|라이브러리는 "**부품**”, “**가구**”|