Rino.js
언어

발행 시간: 2025-04-15T01:21:00.000Z

프로젝트 구조

Rino는 프로젝트 디렉토리와 파일 구조를 기반으로 웹사이트를 빌드합니다. 이는 표준 HTML, CSS, Javascript, Node.js를 사용하여 웹사이트 제작을 쉽게 학습하고 구축할 수 있다는 의미입니다.

- pages
- components
- mds
- public
- scripts
    - export
- styles
    - export
- rino-config.js
- contents
- content-theme
- backoffice.bat&backoffice.sh

/pages

/pages 디렉토리는 기본 HTML 파일을 넣는 공간입니다. Rino는 이 디렉토리를 기준으로 웹 페이지를 빌드합니다. 파일 및 디렉토리 구조는 그대로 유지됩니다.

/components

/components 디렉토리는 컴포넌트 HTML 파일을 넣는 공간입니다. 컴포넌트는 다른 컴포넌트를 포함할 수 있습니다. 그러나 동일한 컴포넌트를 순환 호출하여 무한 루프 오류를 발생시키지 않도록 주의하세요. 컴포넌트는 아래와 같은 문법으로 사용할 수 있습니다:

<component @path="" @tag="" />
  • @path: 컴포넌트를 불러오기 위해 필요한 속성입니다. 예: @path="/header" 또는 @path="/ko/footer"

  • @tag: 선택 속성입니다. 컴포넌트 태그에 HTML 속성을 사용하려면 HTML 태그 이름을 지정해야 합니다. 예: @tag="div" 또는

<component @path="/button" @tag="button" onclick="myFunction()" />

/mds

/mds 디렉토리는 마크다운 파일을 넣는 공간입니다. Rino는 이 디렉토리의 모든 마크다운을 로드하고 요청 시 HTML로 변환합니다.

<script @type="md" @path="" @tag="" type="text/markdowns"></script>
  • @type: 마크다운을 사용하려면 이 속성에 md 또는 markdown을 지정해야 합니다.
  • @path: 마크다운을 불러오기 위한 경로를 지정합니다.
  • @tag: 선택 속성입니다. HTML 속성을 사용하려면 HTML 태그 이름을 지정해야 합니다.

/public

/public 디렉토리는 이미지, 외부 CSS, 자바스크립트 라이브러리 등 정적 파일을 넣는 공간입니다.

/scripts

/scripts 디렉토리는 자바스크립트 라이브러리를 작성하는 공간입니다. npm 패키지를 사용할 수 있으며, /scripts/export/ 디렉토리에 자바스크립트 파일을 넣으면 Rino가 빌드하여 /scripts 디렉토리에 출력합니다. HTML에서 일반적으로 사용하는 방식으로 사용할 수 있습니다. 타입스크립트도 지원됩니다.

스크립트는 import/export 모듈 방식으로 처리됩니다. 파일 이름이 스크립트 인스턴스 이름이 되며, 예를 들어 hello.js 또는 hello.ts인 경우, 외부에서는 hello.FunctionName()으로 호출합니다.

<script src="/scripts/example.js" />

/styles

/styles 디렉토리는 CSS 라이브러리를 작성하는 공간입니다. /styles/export/ 디렉토리에 CSS 파일을 넣으면 Rino가 빌드하여 /styles 디렉토리에 출력합니다. HTML에서 일반적으로 사용하는 방식으로 사용할 수 있습니다.

<link rel="stylesheet" href="/styles/example.css" />

로컬 CSS 파일을 불러오는 예시:

@import "../header.css";
@import "../sidebar.css";
@import "../footer.css";            

rino-config.js

rino-config.js는 Rino의 설정 파일입니다. 배포 디렉토리 변경, 개발 서버 포트 변경, 사이트 URL 설정 및 사이트맵 추가 등을 설정할 수 있습니다.

contents

/contents는 블로그용 마크다운 콘텐츠를 저장하는 디렉토리입니다. 파일 경로 및 URL 구조는 /contents/theme/category/markdownfile.md 형식을 따릅니다. 마크다운 파일 상단에는 아래와 같이 HTML 주석 형태로 JSON 데이터를 작성할 수 있습니다:

<!--
{
  "title": "Project Structure",
  "time": "2025-04-15T01:21:00.000Z",
  "description": "Project Structure Rino build website based on project directory and file structure. Which means it is easy to learn and build a website with standard HTML, CSS, Javascript and Node.js. pages components mds public scripts export styles export contents contenttheme rinoconfig.js backoffice.bat & backoffice.sh..."
}
-->

이 데이터는 콘텐츠 테마 페이지에서 {{ content.title }} 등의 문법으로 사용할 수 있습니다.

content-theme

/content-theme는 콘텐츠 템플릿 페이지를 저장하는 디렉토리입니다. 파일 경로 및 URL 구조는 /content-theme/theme/content.html/content-theme/theme/content-list.html을 따릅니다. /contents 디렉토리 안의 같은 테마 이름의 콘텐츠 파일들과 매칭됩니다.

예: 아래는 해당 웹사이트에서 사용하는 템플릿 페이지입니다:

  • /content-theme/en/content.html:
<!DOCTYPE html>
<html>

<head>
  <component @path="/en/head"></component>
  <meta name="description" content="{{content.description}}" />
  <meta property="og:title" content="{{ content.title }}" />
  <meta property="og:description" content="{{ content.description }}" />
  <meta property="og:type" content="article" />
  <meta property="og:url" content="https://rinojs.org{{ content.urlPath }}" />
  <link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml">
  <link rel="alternate" type="application/atom+xml" title="Atom" href="/atom.xml">
  <title>{{ content.title }}</title>
</head>

<body>
  <div class="top">
    <component @path="/en/sidebar"></component>
    <div class="top-right">
      <component @path="/en/header"></component>
      <main>
        <div class="content-container">
          <div class="content" id="content">
            <article>
              <p class="published-time">
                Published:
                <span id="published-time" data-raw-time="{{ content.time }}">
                  {{ content.time }}
                </span>
              </p>
              {{ content.body }}
            </article>
            <hr />
            <h3>Content Navigation:</h3>
            <nav class="content-navigation">
              <script @type="js" type="text/javascript">
                const args = process.argv;
                const contentDataString = args[args.length - 1];
                const contentData = JSON.parse(contentDataString);

                if (Array.isArray(contentData.nearby))
                {
                  const nearby = contentData.nearby;
                  const currentIndex = nearby.findIndex(post => post.link === contentData.urlPath);
                  let start = 0;
                  let end = nearby.length;

                  if (currentIndex !== -1)
                  {
                    const maxItems = 5;
                    const before = Math.min(2, currentIndex);
                    const after = Math.min(2, nearby.length - currentIndex - 1);
                    let total = 1 + before + after;

                    if (total < maxItems)
                    {
                      const extra = maxItems - total;
                      const moreBefore = Math.min(extra, currentIndex - before);
                      const moreAfter = Math.min(extra - moreBefore, nearby.length - currentIndex - 1 - after);

                      start = currentIndex - (before + moreBefore);
                      end = currentIndex + 1 + after + moreAfter;
                    }
                    else
                    {
                      start = currentIndex - before;
                      end = currentIndex + 1 + after;
                    }

                    const sliced = nearby.slice(start, end);
                    console.log(`<ul>`);
                    sliced.forEach(post =>
                    {
                      if (post.link === contentData.urlPath)
                      {
                        console.log(`<li><span>${post.title}</span></li>`);
                      }
                      else
                      {
                        console.log(`<li><a href="${post.link}">${post.title}</a></li>`);
                      }
                    });
                    console.log(`</ul>`);
                  }
                }
              </script>
            </nav>
          </div>
          <div class="tab2-content tab2-content--invisible" id="tab2-content">
            <component @path="/en/tab2-content"></component>
          </div>
        </div>
      </main>
    </div>
  </div>
  <component @path="/en/footer"></component>
</body>

</html>
  • /content-theme/en/content-list.html:
<!DOCTYPE html>
<html>

<head>
  <component @path="/en/head"></component>
  <meta name="description" content="This is a page for navigating contents." />
  <meta property="og:title" content="Content List" />
  <meta property="og:description" content="This is a page for navigating contents." />
  <meta property="og:type" content="website" />

  <title>Content List</title>
</head>

<body>
  <div class="top">
    <component @path="/en/sidebar"></component>
    <div class="top-right">
      <component @path="/en/header"></component>
      <main>
        <div class="content-container">
          <div class="content" id="content">
            <article>
              <ol>
                <script @type="js" type="text/javascript">
                  var args = process.argv;
                  var contentListDataString = args[args.length - 1];
                  var contentListData = JSON.parse(contentListDataString);
                  var contentList = contentListData.contentList;

                  for (let i = 0; i < contentList.length; i++)
                  {
                    const item = contentList[i];
                    if (item?.title && item?.link)
                    {
                      console.log(`<li><a href="${item.link}">${item.title}</a></li>`);
                    }
                  }
                </script>
              </ol>
            </article>
          </div>
          <div class="tab2-content tab2-content--invisible" id="tab2-content">
            <component @path="/en/tab2-content"></component>
          </div>
          <hr />
          <nav class="content-list-navigation">
            <script @type="js" type="text/javascript">
              var args = process.argv;
              var contentListDataString = args[args.length - 1];
              var contentListData = JSON.parse(contentListDataString);
              var pagination = contentListData.pagination;

              if (pagination.prevLink)
              {
                console.log(`
                  <div>
                    <a href="${pagination.prevLink}">Previous List</a>
                  </div>
                `);
              }
              if (pagination.nextLink)
              {
                console.log(`
                  <div>
                    <a href="${pagination.nextLink}">Next List</a>
                  </div>
                `);
              }
            </script>
          </nav>
        </div>
      </main>
    </div>
  </div>
  <component @path="/en/footer"></component>
</body>

</html>

backoffice.bat & backoffice .sh

backoffice.batbackoffice.sh는 콘텐츠 관리 및 이미지 압축을 위한 백오피스 서버를 실행하는 스크립트입니다. 이 파일을 더블 클릭하면 npm run backoffice 명령을 실행하게 되어 웹사이트 관리자가 편리하게 웹사이트를 유지 관리할 수 있도록 돕습니다.


콘텐츠 네비게이션: