highlight.js를 이용하여 코드 전송하는 기능 만들기

halang·2022년 10월 16일
3

모도코 MODOCO

목록 보기
9/12
post-thumbnail
post-custom-banner

개요

현재 소프트웨어 마에스트로에서 모도코라는 홈페이지를 제작중입니다. 각 방에서 사람들과 채팅을 할 수 있는 기능을 만들었는데 아무래도 개발자들이 주로 쓰는 사이트다보니 Syntax highlighting 기능이 추가로 필요했습니다. hilight.js를 이용하여 코드 보내는 기능을 구현해보겠습니다.

highlight.js

자동으로 언어를 감지해주는 기능이 있고 바로 적용하기가 간단하여 highlight.js 라이브러리를 사용하였습니다. 스크립트 파일과 css 파일로 적용하며 다양한 언어와 테마를 제공합니다.

아래는 실제 적용한 화면입니다.

적용화면

구현

CodeModal.tsx

...

export default function CodeModal({
  toggle,
  codeModalType,
}: {
  toggle: (_type) => void;
  codeModalType: string;
}) {
  const closeModalBackground = (event: React.MouseEvent<HTMLDivElement>) => {
    event.preventDefault();
    toggle(codeModalType);
  };
  const { setCode } = codeChatStore();

  useEffect(() => {
    return () => {
      setCode('');
    };
  }, []);

  return (
    <ModalPortal>
      <Screen onClick={closeModalBackground}>
        <Container onClick={(e) => e.stopPropagation()}>
          <Header toggle={toggle} />
          <Contents toggle={toggle} codeModalType={codeModalType} />
        </Container>
      </Screen>
    </ModalPortal>
  );
}

... 
생략

우선 code를 볼 수 있는 모달창에 대한 코드입니다. codeModalType을 인자값으로 받는 이유는 자신이 코드를 보내고 있는것인지 아니면 다른 사용자가 보낸 코드를 확인하는 것인지 구별하기 위함입니다. 또한 useEffect에서 return 함수를 이용하여 사용자가 해당 코드 modal을 끌 경우 code를 초기화해주었습니다.

Contents.tsx

...

export default function Contents({
  toggle,
  codeModalType,
}: {
  toggle: (_type) => void;
  codeModalType: string;
}) {
  const { code } = codeChatStore();
  const newSocket = roomSocket.socket;
  
  // 첫 화면은 입력
  const [isInput, setIsInput] = useState(codeModalType === 'SEND');

  const onSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
    event.preventDefault();
    if (code.trim() === '') return;
    newSocket.emit('chatMessage', ...); // socket 으로 채팅 보내기
	...
  };

  return (
    <Component>
      {codeModalType === 'SEND' && ( // SEND일 경우 입력과 코드 선택하는 부분이 보임
        <TypeComponent>
          <Type
            type="button"
            onClick={() => setIsInput(true)}
            isClick={isInput}
          >
            입력
          </Type>
          <Type
            type="button"
            onClick={() => setIsInput(false)}
            isClick={!isInput}
          >
            코드
          </Type>
        </TypeComponent>
      )}
      {isInput ? <Input /> : <Code codeModalType={codeModalType} />}
      {codeModalType === 'SEND' && (
        <Button onClick={onSubmit}>코드 보내기</Button>
      )}
    </Component>
  );
}

...

모달창 안의 contents에 대한 component입니다. 코드 보내기 버튼을 누를때 소켓으로 채팅이 보내집니다.

Code.tsx

import React, { useEffect } from 'react';
import styled from 'styled-components';
import hljs from 'highlight.js';
import 'highlight.js/styles/vs2015.css';
import codeChatStore from '../../../stores/room/codeChatStore';

export default function Code({ codeModalType }: { codeModalType: string }) {
  const { code } = codeChatStore();

  useEffect(() => {
    hljs.highlightAll();
  }, []);

  return (
    <Component codeModalType={codeModalType}>
      <pre>
        <code>{code}</code>
      </pre>
    </Component>
  );
}
...

실제 highlight.js를 적용한 부분입니다. 해당 라이브러리에서 다양한 테마를 지원하고 있습니다. 저는 vs2015테마를 적용했는데 사이트에서 더 많은 테마를 확인해볼 수 있습니다. 한가지 주의할 부분은 import 'highlight.js/styles/vs2015.css'; 처럼 stylesheet을 꼭 import 해야 한다는 것입니다.

useEffect를 이용해 첫 렌더링에만 hljs.highlightAll(); 을 해주면 자동으로 페이지에 포함된 <pre><code> 를 찾아 구문강조를 적용해줍니다.

마무리

이렇게 하여 코드 보내는 기능을 완성하였습니다. codeMirror라는 또 다른 라이브러리가 있는데 해당 라이브러리보다 config 설정이 간단하여 쉽게 적용할 수 있었던 것 같습니다:)

+) 여담
기능 구현 후 실제로 사용한 유저가 엄청 편하다는 피드백을 줘서 뿌듯했습니다ㅎㅎ😊

post-custom-banner

0개의 댓글