GitHub Next는 새로운 기술을 연구하고 프로토타이핑하는 연구조직 같은 팀이다. 여기서 새로운 아이디어를 구현해서 테스트하고 피드백 받으면서 잘 다듬어지면 제품으로 만들기 때문에 GitHub Next의 사이트를 보면 프로젝트마다 제품이 되었는지 아직 프로토타이핑 단계인지 등이 표시되어 있고 GitHub Copilot도 GitHub Next의 프로젝트 중 하나였다.
GitHub Blocks는 현재 제한된 사람만 사용할 수 있는 테크니컬 프리뷰 상태라 신청해 놓고 허용되어야 사용할 수 있다. 몇 주 전에 Blocks 접근 허용을 받아서 사용을 해봤다. 아래 설명에서도 blocks.githubnext.com
주소들이 나오는데 홈페이지 외에는 권한을 받아야만 접근할 수 있다.
저장소에 있는 각 파일의 추가 기능을 제공해서 목적에 맞게 더 풍부한 기능을 제공할 수 있게 하는 기능이다. 홈페이지에 나온 "저장소를 다시 상상해 봐라"라는 말대로 사람들이 Blocks를 만들어서 공유하고 이를 저장소에 사용하면 저장소의 기능을 확장할 수 있다. 예를 들어 저장소에 올린 코드 외에 이미지 파일이나 SVG 파일을 GitHub에서 열어보면 텍스트 파일로 보이는 게 아니라 이미지가 그대로 보이거나 SVG를 렌더링해서 볼 수 있게 된다. 이는 GitHub에서 해당 파일에 대해서 기능을 제공하는 것인데 이런 기능을 사람들이 직접 만들 수 있다고 생각하면 된다. 직접 보면 이해하기 쉽다.
Blocks 살펴보기
GitHub Blocks에서 시작하기를 누르고 로그인하면 githubnext/blocks 저장소의 githubnext.com 페이지로 이동하게 된다.
코드 위에 보면 기존 저장소에는 없던 Block이라는 메뉴가 보이는데 이를 눌러보면 사용할 수 있는 Block 목록을 볼 수 있다.
선택한 Blocks의 기능에 따라 코드가 나오는 화면에 Blocks가 적용된 화면이 나오게 된다. 마크다운 프리뷰는 항상 보던 화면이므로 githubnext/blocks에서 제공하는 예제를 살펴보자. 스크린샷 만으로는 동작을 확인하기 어려울 것 같아서 (용량은 좀 크지만) 영상으로 만들어 봤다.
지금은 테스트고 실제로 서비스에 적용되게 된다면 정확히 어떤 모습일지 더 궁금하긴 하지만 Blocks로 어떤 일을 할 수 있는지 어느 정도 이해했겠다고 생각한다. 범용적으로도 확장할 부분이 많지만 회사내에서는 정해진 형식을 사용할 가능성이 더 높기 때문에 이런 부분의 Blocks를 만들어서 GitHub의 활용도를 높일 수 있을 걸로 보인다. 다만 Blocks를 선택하는 건 좀 귀찮긴 하다.
Blocks 만들기
Blocks는 직접 만들 수 있고 나중에는 마켓플레이스가 열려서 자신이 만든 Blocks를 올려서 공유할 수 있게 될 것으로 보인다. 아직 문서가 충실하진 않지만 githubnext/blocks에서 개발 문서를 제공하고 있다.
Blocks 개발에 참고할 수 있게 템플릿 저장소와 예제 저장소를 제공하고 있고 Blocks 개발을 쉽게 할 수 있는 모듈이 포함된 blocks-dev가 있다.
Blocks를 만들려면 먼저 템플릿 저장소를 포크해서 저장소를 만든 다음에 로컬에 클론 받는다. Blocks 저장소에 github-blocks
태그를 지정하도록 권장하고 있다.
yarn
으로 의존성을 설치하고 yarn start
로 개발 서버를 로컬에서 실행한다.
$ yarn
$ yarn start
Starting the development server at http://localhost:4000
개발 환경은 흥미롭게 구성되어 있는데 http://localhost:4000
에 접근하면 https://blocks.githubnext.com/?devServer=http%3A%2F%2Flocalhost%3A4000%2F
로 이동해서 개발할 수 있게 한다.
처음 살펴볼 때처럼 Let's get started 버튼을 누르면 아까 데모를 blocks.githubnext.com
에서 확인한 것처럼 똑같이 이동하지만 https://blocks.githubnext.com/githubnext/blocks/blob/main/README.md?devServer=http%3A%2F%2Flocalhost%3A4000%2F
처럼 로컬 개발 서버와 연결하는 devServer
쿼리스트링가 붙어있게 된다.
하지만 githubnext/blocks
저장소는 GitHub Next의 저장소이므로 수정 권한이 없어서 Blocks 개발을 제대로 할 수 없다. 앞에서 템플릿 저장소를 포크하면서 만든 자신의 저장소로 이동해야 한다. 예를 들어 나처럼 저장소 주소가 github.com/outsideris/blocks-demo
라면 blocks.githubnext.com/outsideris/blocks-demo
에 접속해야 푸시도 하고 수정도 해보면서 테스트할 수 있다. 그리고 여기서는 로컬 개발 서버와 연결해서 테스트해야 하므로 https://blocks.githubnext.com/outsideris/blocks-demo?devServer=http%3A%2F%2Flocalhost%3A4000%2F
처럼 쿼리스트링에 devServer
를 붙여준다.
이 저장소에서 Block 메뉴를 열어보면 "Example File Block"이라는 내 저장소의 Blocks를 볼 수 있다.(템플릿에 미리 만들어져 있던 Blocks다.) 이는 devServer
쿼리스트링으로 로컬에 실행한 개발 서버와 연결되어 로컬에서 작성된 Blocks를 불러온 것이다. 이를 통해 blocks.githubnext.com
에서 테스트해보면서 Blocks를 개발할 수 있게 된다.
Blocks는 정의파일과 Blocks 코드로 구성되어 있다.
blocks.config.json
저장소 루트 디렉터리에 blocks.config.json
파일로 Blocks를 정의한다. 템플릿에 포함되어 있던 blocks.config.json
는 다음과 같이 생겼다.
[
{
"type": "file",
"id": "file-block",
"title": "Example File Block",
"description": "A basic file block",
"entry": "blocks/example-file-block/index.tsx",
"matches": ["*"],
"example_path": "https://github.com/facebook/react/blob/main/packages/react-dom/index.js"
},
{
"type": "folder",
"id": "folder-block",
"title": "Example Folder Block",
"description": "A basic folder block",
"entry": "blocks/example-folder-block.tsx",
"matches": ["*"],
"example_path": "https://github.com/githubocto/flat"
}
]
아까 봤던 Example File Block
과 Example Folder Block
2개의 Blocks가 정의된 것을 알 수 있다. 이 Blocks 정의의 형식은 다음과 같다.
interface BlockDefinition {
type: "file" | "folder";
id: string;
title: string;
description: string;
entry: string;
matches?: string[];
}
type
: 이 Blocks를 파일에 적용할지 폴더에 적용할지를 지정id
: Blocks의 식별자 문자열로 프로젝트 내에서 유일해야 한다.title
: 표시할 Block 이름description
: 표시할 Block 설명entry
: Blocks의 진입점 파일을 프로젝트 루트에서 상대 경로로 지정한다.matches
: 해당 Blocks가 적용되어야 할 파일을 globs 배열로 지정한다.["*.json"]
는 JSON 파일에만 적용되고["*"]
는 모든 파일에 적용된다.example_path
: 이름으로 보면 예제 링크를 지정하는 거 같은데 문서에 없어서 어디 표시되는지는 모르겠다.
Blocks 코드
코드는 TypeScript로 작성하고 blocks/
디렉터리에 위치한다.
폴더를 선택하고 Block에서 Example Folder Block
을 선택하면 다음과 같이 해당 폴더 안에 있는 각 파일의 내용이 정리된 것을 볼 수 있다.
이 Blocks의 코드는 blocks/example-folder-block.tsx
에 있다.
import { FolderBlockProps } from "@githubnext/blocks";
import { Box } from "@primer/react";
export default function ExampleFolderBlock(props: FolderBlockProps) {
return (
<Box p={4}>
<Box
borderColor="border.default"
borderWidth={1}
borderStyle="solid"
borderRadius={6}
overflow="hidden"
>
<Box
bg="canvas.subtle"
p={3}
borderBottomWidth={1}
borderBottomStyle="solid"
borderColor="border.default"
>
This is the folder content.
</Box>
<Box p={4}>
<table style={{ textAlign: "left" }}>
<thead>
<tr>
<th className="p-1">Path</th>
<th className="p-1">Size</th>
<th className="p-1">Type</th>
</tr>
</thead>
<tbody>
{props.tree.map((item, index) => (
<tr key={index}>
<td className="p-1">{item.path}</td>
<td className="p-1">{item.size}</td>
<td className="p-1">{item.type}</td>
</tr>
))}
</tbody>
</table>
</Box>
</Box>
</Box>
);
}
React에 익숙하다면 어렵지 않게 이해할 수 있는 코드이다. 폴더 타입의 Blocks이므로 FolderBlockProps
를 받아서 이 정보를 이용해서 화면을 그려주는 코드이고 반드시 export default
로 Block 컴포넌트를 노출해야 한다.
이번엔 좀 더 복잡한 Example File Block
을 살펴보자. 해당 파일 경로와 파일의 언어를 보여주고 아래는 파일의 내용을 보여준다. 중간에는 메타데이터 예시가 있다.
이 Blocks는 blocks/example-file-block/example-folder-block.tsx
에 있다.
import { FileBlockProps, getLanguageFromFilename } from "@githubnext/blocks";
import { Button, Box } from "@primer/react";
import "./index.css";
export default function ExampleFileBlock(props: FileBlockProps) {
const { context, content, metadata, onUpdateMetadata } = props;
const language = Boolean(context.path)
? getLanguageFromFilename(context.path)
: "N/A";
return (
<Box p={4}>
<Box
borderColor="border.default"
borderWidth={1}
borderStyle="solid"
borderRadius={6}
overflow="hidden"
>
<Box
bg="canvas.subtle"
p={3}
borderBottomWidth={1}
borderBottomStyle="solid"
borderColor="border.default"
>
File: {context.path} {language}
</Box>
<Box p={4}>
<p>Metadata example: this button has been clicked:</p>
<Button
onClick={() =>
onUpdateMetadata({ number: (metadata.number || 0) + 1 })
}
>
{metadata.number || 0} times
</Button>
<pre className="mt-3 p-3">{content}</pre>
</Box>
</Box>
</Box>
);
}
@githubnext/blocks가 Blocks에 쓰기 편한 기능을 제공해 주므로 getLanguageFromFilename
로 파일에서 언어를 추출했다. FileBlockProps
에서 파일의 내용 등을 가져와서 아까처럼 페이지를 그려주었다.
메타데이터 관리
위 Example File Block
에는 메타데이터라는 부분이 있었다. Blocks는 기능을 제공하는데 상황에 따라서는 상태를 저장해 두어야 할 수 있으므로 메타데이터는 Blocks를 사용하는 곳에서 상태를 저장할 수 있게 하는 기능이 나온다. 메타데이터 업데이트는 onUpdateMetadata()
를 사용하는데 앞의 코드를 보면 버튼을 클릭할 때 number
의 값을 1씩 증가시켰다.
onUpdateMetadata({ number: (metadata.number || 0) + 1 })
앞에서 보았던 "0 times"라는 버튼을 클릭하면 아래와 같이 커밋 화면이 나온다.
그리고 이제 0
이 1
로 바뀐걸 볼 수 있다.
위에 커밋 화면이 나온 것처럼 Blocks를 사용하는 저장소에서 .github/blocks/
안에 저장되는데 블록마다 메타데이터는 따로 관리되며 폴더/파일 단위로 저장된다. 여기서는 blocks/example-file-block/index.css
파일에 대해서 outsideris
계정의 blocks-demo
저장소의 file-block
Blocks의 메타데이터이므로 .github/blocks/file/outsideris__blocks-demo__file-block/blocks%2Fexample-file-block%2Findex.css.json
라는 파일이 생기고 여기에서 JSON으로 데이터가 관리된다.
GitHub Blocks는 GitHub에서 기능을 제공해 주지 않아도 직접 커스텀한 기능을 추가할 수 있다는 면에서 매력적으로 느껴진다. 마켓플레이스가 열리면 많은 사람이 좋은 기능을 만들어 줄 것 같다.
Comments