Remarkable.js로 md 파일 렌더링하기

이종현·2023년 3월 19일
0

JavaScript

목록 보기
2/2
post-thumbnail

1. 사건의 발단

SPA_Router 프로젝트가 시작되고, 우리는 toss.tech 사이트를 벤치마킹해서 전체적인 틀은 거의 유지하되, 프로젝트에서 가장 중요한 부분인 SPA 방식으로 웹페이지를 개발하면서 라우터 기능을 넣으려고 하는 부분에 집중하기로 했다. 그래서 메인페이지인 index.html 파일에서 새로운 컨텐츠로 이동하는 부분은 최대한 간략하게 마크업하고 싶었다.

그래서 생각했던 방법이 블로그나 노션에 작성해놓았던 파일을 md파일로 내보내기해서 그 파일 전체를 html에서 자바스크립트를 이용해 로드해서 렌더링할 수 있다면 하나의 컨텐츠페이지를 쉽게 제작할 수 있을 것 같았다.

2. 라이브러리 사용

첫 번째로 시도했던 방법은 marked.js다. 일단 CDN으로 로드해서 시도했는데 안되서 로컬에다 파일을 가져와서 시도했지만 이 마저 되지 않았다.

그 다음 방법으로 시도했던 건 remarkable.js다.
이번에도 CDN 주소로 먼저 시도했다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="normalize.css" />
    <link rel="stylesheet" href="contents.css" />
	  <script src="https://cdn.jsdelivr.net/npm/remarkable@2.0.0/dist/remarkable.min.js"></script>
    <title>contents</title>
  </head>
  <body>
    <div class="container">
      <div class="container-inner">
        <div class="imgWrap">
          <img src="./images/drag-drop-icon.jpeg" alt="dragImage" />
        </div>
        <h1>Web API_Drag & Drop</h1>
        <section class="writer">
          <img src="./images/jh_profile.jpeg" alt="" />
          <div class="writer-info">
            <div class="writer-info__nameFiled">
              <span class="writer-info__name">이종현</span>
              <span class="writer-info__filed">, Frontend Developer</span>
            </div>
            <div class="writer-info__date">2023.03.15</div>
          </div>
        </section>
        <div class="contentsWrap">
          <div class="contentsWrap-inner">
            <div id="drag"></div>
          </div>
        </div>
      </div>
    </div>
    <script>
			const md = new Remarkable();
	    const markdownContent = `# Hello!`;
	    const html = md.render(markdownContent);
	    document.getElementById("drag").innerHTML = html;
    </script>
  </body>
</html>

<script src="https://cdn.jsdelivr.net/npm/remarkable@2.0.0/dist/remarkable.min.js"></script>
</head>

일단 간단한 문장부터 로드 되는지 확인하기 위해 # Hello 로 간단하게 작성해서 시도했다.

이번에도 실패했다. 로컬에 파일을 전부 가져와서 시도를 다시 해보았다. 또 다시 실패..

계속 Remarkable이 정의되어 있지 않다고 에러가 발생했다.

script 태그의 위치가 문제일까? body태그 아래에 있는 script태그를 먼저 로드해오기 때문에 그랬을까?

이 부분은 알아보니까 문제요소가 아니었다.

일반적으로 CDN(Content Delivery Network)에 호스팅된 스크립트를 head 태그에 정의하면, 
해당 스크립트는 웹 페이지가 로드될 때 바로 다운로드되어 해석되고 실행됩니다. 
그러므로, 해당 스크립트를 사용하는 어떤 script 태그도 head 태그에 있는 CDN 스크립트보다 먼저 
실행되지 않습니다.

즉, head 태그에 CDN 스크립트를 정의하면 웹 페이지에서 해당 스크립트를 사용하는 어떤 script 태그도 
head 태그에 정의된 스크립트보다 먼저 실행되지 않습니다. 
따라서 body 태그 안에 script 태그를 정의하더라도 head 태그에 CDN 스크립트를 정의한 경우 먼저 
받아올 수 있습니다.

그럼 이번에는 혹시 버전 문제일까 해서, 이번에는 버전을 바꿔봤다. 간혹 최신버전이 안되는 경우가 있기 때문에 버전을 낮춰서 시도해봤다.

2.0.0 에서 1.7.1로 낮춰서 진행해봤다.

<script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.min.js"></script>

결과는 성공이었다. 그럼 로컬에서도 가능할까하여 복사해서 로컬에 가지고 왔는데 이번에는 로컬에서도 정상적으로 동작했다. 결국은 버전문제였다.
하지만 로컬에 가지고 와서 사용하는 방법은 CDN 서버에서 파일을 업데이트하는 경우 로컬에 저장된 파일과 일치하지 않게 되므로, 업데이트된 파일을 사용하지 못하게 됩니다.

또한 CDN 파일은 보안 및 안정성을 위해 특별히 유지보수되므로, 파일을 복사해서 사용하는 경우에는 해당 보안 및 안정성을 보장할 수 없습니다. 그러므로, CDN 파일은 웹 페이지에서 직접 사용하는 것이 좋습니다.

마무리로 md파일 자체를 받아와야해서 파일을 읽어오는 XMLHttpRequest 객체를 생성해서 경로를 지정한 뒤 ‘GET’으로 받아왔다.

const xhr = new XMLHttpRequest()
const url = './mdFiles/Drap&Drop.md'
xhr.open('GET', url, true)
xhr.send()

XMLHttpRequest 객체는 비동기적으로 서버와 상호작용할 수 있는 API로, Ajax 기술에 핵심적인 역할을 합니다. Ajax란 웹 페이지에서 동적으로 서버와 데이터를 교환하는 기술을 말하며, 이를 통해 사용자 경험을 향상시킬 수 있습니다.

XMLHttpRequest 객체를 사용하면 JavaScript 코드에서 서버로 데이터를 보낼 수 있습니다. 저의 경우에는 로컬에 있는 폴더가 서버라고 생각하면 됩니다. 이를 통해 서버로부터 데이터를 비동기적으로 받아와서 웹 페이지의 특정 부분을 업데이트하거나, 새로운 페이지를 로드하지 않고도 데이터를 동적으로 보여줄 수 있습니다.

따라서 로컬에서 파일을 읽어오기 위해 XMLHttpRequest 객체를 생성하여 파일을 요청하고, 요청이 완료되면 해당 파일의 내용을 받아와 remarkable.js로 렌더링하는 것이 가능합니다.

xhr.onreadystatechange = function () {
  if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
    const md = new Remarkable()
    const markdownContent = xhr.responseText
    const html = md.render(markdownContent)
    document.getElementById('drag').innerHTML = html
  }
}

XMLHttpRequest 객체를 생성하고, onreadystatechange 프로퍼티에 콜백 함수를 할당하여 서버로부터 데이터를 받을 때마다 이를 처리합니다.

xhr.readyState 속성은 XMLHttpRequest 객체의 상태를 나타내며, xhr.status 속성은 HTTP 응답 코드를 나타냅니다.

xhr.readyStateXMLHttpRequest.DONE이면, 서버로부터 응답을 받은 것입니다. 이때 xhr.status200이면, 서버가 성공적으로 응답했다는 것을 나타냅니다.

이후에는 Remarkable() 생성자 함수를 사용하여 Remarkable.js 객체를 생성하고, xhr.responseText를 사용하여 서버로부터 받은 Markdown 형식의 텍스트를 가져옵니다. md.render() 메서드를 사용하여 Markdown을 HTML로 변환하고, document.getElementById("drag").innerHTML 속성을 사용하여 해당 HTML을 페이지의 DOM에 삽입합니다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="normalize.css" />
    <link rel="stylesheet" href="contents.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.min.js"></script>
    <title>contents</title>
  </head>
  <body>
    <div class="container">
      <div class="container-inner">
        <div class="imgWrap">
          <img src="./images/drag-drop-icon.jpeg" alt="dragImage" />
        </div>
        <h1>Web API_Drag & Drop</h1>
        <section class="writer">
          <img src="./images/jh_profile.jpeg" alt="" />
          <div class="writer-info">
            <div class="writer-info__nameFiled">
              <span class="writer-info__name">이종현</span>
              <span class="writer-info__filed">, Frontend Developer</span>
            </div>
            <div class="writer-info__date">2023.03.15</div>
          </div>
        </section>
        <div class="contentsWrap">
          <div class="contentsWrap-inner">
            <div id="drag"></div>
          </div>
        </div>
      </div>
    </div>
    <script>
      const xhr = new XMLHttpRequest()
      const url = './mdFiles/Drap&Drop.md'
      xhr.open('GET', url, true)
      xhr.send()

      xhr.onreadystatechange = function () {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
          const md = new Remarkable()
          const markdownContent = xhr.responseText
          const html = md.render(markdownContent)
          document.getElementById('drag').innerHTML = html
        }
      }
    </script>
  </body>
</html>

이로써 Markdown(md)파일을 불러와 HTML로 변환하여 화면에 렌더링하는 부분을 해결해 보았습니다.


제가 작성한 내용에 대해 혹시 부적절한 부분이나 더 나은 대안이 있다면 언제든지 자유롭게 말씀해주세요.
겸허한 마음으로 수용하겠습니다.


Reference

ChatGPT

profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 8월 31일

지린당!! 감사합니다!

답글 달기