HOW TO CODE BROWNIE

Syntax Highlighting을 추가했다

Cover Image for Syntax Highlighting을 추가했다

개발 블로그이니 Syntax Highlighting이 지원되는 Code Block 기능을 우선 추가해보았다. Syntax Highlighting이 어떻게 동작하는 것인지 몰랐던 지라 기능을 붙이는 데 헤맸다.

크게 세 가지를 구성해야 한다.

Sanity 플러그인

먼저 할 일은 에디터 환경에 Code Block 기능을 추가하는 일이다. CMS로 Sanity를 사용하고 있어서 Sanity의 플러그인을 설치한 다음, Code Block을 스키마에 추가해야 한다.

스키마는 각자가 정의하는 바에 아주 다를 것이라서 예시가 별로 의미가 없겠지만, 내 경우에는 이렇게 했다. array 타입의 content 아래에 code 타입의 codeBlock이라는 스키마를 추가했다.

// schemas/post.ts
defineField({
      name: 'content',
      title: 'Content',
      type: 'array',
      of: [
        { type: 'block' },
       // ...
        { type: 'code', name: 'codeBlock', title: 'Code' },
      ],

Code Block 컴포넌트

codeBlock 데이터에 대해 사용할 리액트 컴포넌트를 만들어준다. 플러그인에서 소개된 react-refractor 를 사용했다.

prism이 코드를 하이라이팅된 html로 변환해준다면, refractorprism을 이용해서 코드를 AST 객체로 변환해준다. react-refractorrefractor를 사용해서 코드를 리액트 컴포넌트로 만들어준다.

컴파일러 수업을 들었다면 AST가 뭔지 더 잘 이해했겠지만 넘어가본다. 어쨌든 아래와 같이 CodeBlock이라는 컴포넌트를 만든다.

// components/CodeBlock.tsx
import { CodeInputValue } from '@sanity/code-input'
import Refractor from 'react-refractor'
import js from 'refractor/lang/javascript'

Refractor.registerLanguage(js)

export function CodeBlock(props: CodeInputValue) {
  return (
    <Refractor
      language={props.language}
      value={props.code}
      markers={props.highlightedLines}
    />
  )
}

그리고 CodeBlock 컴포넌트를 codeBlock 데이터에 대해 사용하도록 연결해준다.

// components/PostBody.tsx
import { PortableText, PortableTextReactComponents } from '@portabletext/react'

import { CodeBlock } from './CodeBlock'
import styles from './PostBody.module.css'
import { SanityImage } from './SanityImage'

const myPortableTextComponents: Partial<PortableTextReactComponents> = {
  types: {
    image: ({ value }) => {
      return <SanityImage {...value} />
    },
    codeBlock: ({ value }) => {
      return <CodeBlock {...value} />
    },
  },
}

Prism 테마

렌더링 결과물을 보면 코드 syntax에 맞게 DOM 객체들이 예쁘게 잘 만들어져있는데, CSS가 없어서 하이라이팅이 안된다.

Prism의 CDN 링크를 추가했다.

// pages/_app.tsx

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Head>
        <link
          rel="stylesheet"
          href="https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.min.css"
          integrity="sha512-/mZ1FHPkg6EKcxo0fKXF51ak6Cr2ocgDi5ytaTBjsQZIH/RNs6GF6+oId/vPe3eJB836T36nXwVh/WBl/cWT4w=="
          crossorigin="anonymous"
          referrerpolicy="no-referrer"
        />
      </Head>
      <Component {...pageProps} />
    </>
  )
}

마치며

이게 뭐라고 구구절절 튜토리얼처럼 적었다.

Sanity의 Next.js용 스타터 템플릿으로 시작했는데, Next.js의 동작과 구성을 몰라 수정이 쉽지 않다.

다음에 하고 싶은 작업은, 커버 이미지를 너무 강조하는 디자인을 수정하는 것. 그리고 태그와 카테고리 기능을 추가해야 한다. 이것은 스키마를 새로 만들어야 해서 문서를 읽어가며 차근차근 해봐야하겠다.


More Stories

bug fix (feat. claude code)

브라우니
Coding Brownie

헤더를 약간 수정하는 작업

브라우니
Coding Brownie