개발(Web)/VCS

[Git/GitHub] Jekyll 기반의 GitHub Page 생성(2-5) post목차 설정

shinyelee 2021. 3. 7. 23:25

python-table-of-contents.html

_includes 폴더 하위에 파일 생성(강의에서는 python-table-of-contents.html로 만들었지만 나는 아이유로 바꿈)

<span class="table-of-contents-list">Python 강좌는 여러 절로 구성되어 있습니다. </span>
<ul class="table-of-contents-list">
    <li><a href="./python-basic">Python 강좌(1) - Python 기본</a></li>
    <li><a href="./python-control-statement">Python 강좌(2) - Python 제어문</a></li>
</ul>

연관 포스트 작성

강의와 내용이 상이하니 강사님 블로그와 유튜브를 참고하세요
아이유 3월 25일에 컴백함 많관부


custom.css

assets/css/custom.css 생성해 아래 코드 복붙(스압 주의)

/* Search 결과화면을 위한 CSS 설정 시작 */
ul.mylist li, ol.mylist li {
    padding: 5px 0px 5px 5px;
    margin-bottom: 5px;
    border-bottom: 1px solid #efefef;
    font-size: 12px;
}

ul.mylist li:last-child,
ol.mylist li:last-child {
    border-bottom: 0px;
}

ul.mylist li,
ol.mylist li {
    -webkit-transition: background-color 0.3s linear;
    -moz-transition: background-color 0.3s linear;
    -ms-transition: background-color 0.3s linear;
    -o-transition: background-color 0.3s linear;
    transition: background-color 0.3s linear;
}

ul.mylist li:hover,
ol.mylist li:hover {
    background-color: #f6f0b6;
}
/* Search 결과화면을 위한 CSS 설정 끝 */

/* 링크뒤쪽에 FontAwesome표현을 위한 custom CSS */
.post-full-content a[target="_blank"]::after {
    content: '\f08e';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 2rem;
    font-weight: normal;
    font-style: normal;
}


/* 강좌 table of contents(목차)를 위한 custom css*/
span.table-of-contents-list {
    margin: 0px;
    padding: 0px;
    max-width: 250px;
    width: 100%;
    border-bottom: 1px solid #efefef;
    font-size: 18px;
    font-weight: bold;
    color: #ce180b;
    font-family: Georgia, 'Nanum Gothic', serif !important;
}

ul.table-of-contents-list, ol.table-of-contents-list {
    list-style: none;
    margin: 0px;
    padding: 0px;
    max-width: 250px;
    width: 100%;
    font-family: Georgia, 'Nanum Gothic', serif !important;
}

ul.table-of-contents-list li, ol.table-of-contents-list li {
    padding: 0px 0px 5px 5px;
    margin-bottom: 5px;
    font-size: 15px;
}

ul.table-of-contents-list li a, ol.table-of-contents-list li a {
    text-decoration: none;
    color: color(var(--midgrey) l(-25%));
    font-weight: bold;
}

ul.table-of-contents-list li a:hover, ol.table-of-contents-list li a:hover {
    color: #c2255c;
    text-decoration: none;
    font-weight: bold;
}


ul.table-of-contents-list li:last-child,
ol.table-of-contents-list li:last-child {
    border-bottom: 0px;
}

ul.table-of-contents-list li:before,
ol.table-of-contents-list li:before {
    content: '\f14d';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 2rem;
    font-weight: normal;
    font-style: normal;
    vertical-align: middle;
    padding: 5px 5px 6px 0px;
}


/* 가로방향의 리스트를 위한 custom css*/
ul.font-awesome-list, ol.font-awesome-list {
    list-style: none;
    margin: 0px;
    padding: 0px;

    max-width: 250px;
    width: 100%;
}

ul.font-awesome-list li, ol.font-awesome-list li {
    padding: 5px 0px 5px 5px;
    margin-bottom: 5px;
    border-bottom: 1px solid #efefef;
    font-size: 18px;
}

ul.font-awesome-list li span.icon-name,
ol.font-awesome-list li span.icon-name {
    padding: 0px 10px;
}

ul.font-awesome-list li span.icon-value,
ol.font-awesome-list li span.icon-value {
    padding: 0px 10px;
    color: #c2255c;
    font-size: 15px;
}


ul.font-awesome-list li,
ol.font-awesome-list li {
    -webkit-transition: background-color 0.3s linear;
    -moz-transition: background-color 0.3s linear;
    -ms-transition: background-color 0.3s linear;
    -o-transition: background-color 0.3s linear;
    transition: background-color 0.3s linear;
}

ul.font-awesome-list li:hover,
ol.font-awesome-list li:hover {
    background-color: #f6f6f6;
}

/* tl; dr 이미지를 위한 custom style. 각 글의 첫 이미지로 등장 - 사용하지 않음 */
.myTLDRStyle {
    display: inline !important;
    margin-bottom: -0.5em !important;
    width: 100px;
}

/* 각 포스트의 시작 Introduction을 위한 fontAwesome */
strong.post_introduction:before {
    content: '\f086';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 3rem;
    font-weight: normal;
    font-style: normal;
    vertical-align: middle;
    padding: 0px 5px 6px 0px;
    color: teal;
}

/* 포스트의  subtitle을 위한 fontAwesome */
strong.subtitle_fontAwesome:before {
    content: '\f0a4';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 5rem;
    font-weight: normal;
    font-style: normal;
    vertical-align: middle;
    padding: 0px 5px 6px 0px;
    color: teal;
}

/* 포스트의  subtitle안에 단락제목을 위한 fontAwesome */
strong.subtitle2_fontAwesome:before {
    content: '\f13a';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 3rem;
    font-weight: normal;
    font-style: normal;
    vertical-align: middle;
    padding: 0px 5px 6px 0px;
    color: #f92672;
}


/* 강좌 최 하단 Reference를 위한 custom css*/
span.lecture-reference {
    margin: 0px;
    padding: 0px;
    max-width: 250px;
    width: 100%;
    border-bottom: 0px solid #efefef;
    font-size: 18px;
    font-weight: bold;
    color: #0e16ce
}

/* All Tags 화면을 위한 폰트 크기 설정 custom css */
.alltags {
    font-size: 18px;
    font-weight: bold;
    color: #ce112a
}


ul.lecture-reference {
    list-style: none;
    margin: 0px;
    padding: 0px;

    max-width: 250px;
    width: 100%;
}

ul.lecture-reference li {
    padding: 0px 0px 5px 5px;
    margin-bottom: 5px;
    font-size: 15px;
    font-weight: bold;
}

ul.lecture-reference li a {
    text-decoration: none;
    color: color(var(--midgrey) l(-25%));
}

ul.lecture-reference li a:hover {
    color: #c2255c;
    text-decoration: none;
    font-weight: bold;
}


ul.lecture-reference li:last-child {
    border-bottom: 0px;
}

ul.lecture-reference li:before {
    content: '\f02b';
    display: inline-block;
    margin: 0 2px 0 4px;
    line-height: 1;
    font-family: "FontAwesome";
    font-size: 2rem;
    font-weight: normal;
    font-style: normal;
    vertical-align: middle;
    padding: 5px 5px 6px 0px;
}

/* 이미지 출처를 표현하기 위한 custom class */
.img-reference {
    font-size: 1.5rem !important;
    text-align: center;
    font-weight: bold;
    margin-top: -40px;
    margin-bottom: 30px;
}

/* 메뉴 표현을 위한 CSS */

/*
   DL Menu In/Out
   ========================================================================== */
@-webkit-keyframes MenuAnimOut {
    100% {
        -webkit-transform: translateZ(300px);
        opacity: 0; } }
@-moz-keyframes MenuAnimOut {
    100% {
        -moz-transform: translateZ(300px);
        opacity: 0; } }
@keyframes MenuAnimOut {
    100% {
        transform: translateZ(300px);
        opacity: 0; } }
@-webkit-keyframes MenuAnimIn {
    0% {
        -webkit-transform: translateZ(300px);
        opacity: 0; }
    100% {
        -webkit-transform: translateZ(0px);
        opacity: 1; } }
@-moz-keyframes MenuAnimIn {
    0% {
        -moz-transform: translateZ(300px);
        opacity: 0; }
    100% {
        -moz-transform: translateZ(0px);
        opacity: 1; } }
@keyframes MenuAnimIn {
    0% {
        transform: translateZ(300px);
        opacity: 0; }
    100% {
        transform: translateZ(0px);
        opacity: 1; } }
@-webkit-keyframes SubMenuAnimIn {
    0% {
        -webkit-transform: translateZ(-300px);
        opacity: 0; }
    100% {
        -webkit-transform: translateZ(0px);
        opacity: 1; } }
@-moz-keyframes SubMenuAnimIn {
    0% {
        -moz-transform: translateZ(-300px);
        opacity: 0; }
    100% {
        -moz-transform: translateZ(0px);
        opacity: 1; } }
@keyframes SubMenuAnimIn {
    0% {
        transform: translateZ(-300px);
        opacity: 0; }
    100% {
        transform: translateZ(0px);
        opacity: 1; } }
@-webkit-keyframes SubMenuAnimOut {
    0% {
        -webkit-transform: translateZ(0px);
        opacity: 1; }
    100% {
        -webkit-transform: translateZ(-300px);
        opacity: 0; } }
@-moz-keyframes SubMenuAnimOut {
    0% {
        -moz-transform: translateZ(0px);
        opacity: 1; }
    100% {
        -moz-transform: translateZ(-300px);
        opacity: 0; } }
@keyframes SubMenuAnimOut {
    0% {
        transform: translateZ(0px);
        opacity: 1; }
    100% {
        transform: translateZ(-300px);
        opacity: 0; } }


.dl-menuwrapper {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1000;
    -webkit-perspective: 1000px;
    -moz-perspective: 1000px;
    perspective: 1000px;
    -webkit-perspective-origin: 50% 200%;
    -moz-perspective-origin: 50% 200%;
    perspective-origin: 50% 200%;
    /* Hide the inner submenus */ }
@media only screen and (min-width: 48em) {
    .dl-menuwrapper {
        position: fixed;
        max-width: 175px;
        top: 25px;
        left: 25px; } }
.dl-menuwrapper.dl-menuopen {
    width: 100%;
    height: 100%; }
.dl-menuwrapper button {
    top: 0;
    left: 0;
    background: #222222;
    border: none;
    width: 48px;
    height: 45px;
    text-indent: -900em;
    overflow: hidden;
    position: relative;
    cursor: pointer;
    outline: none;
    border-radius: 0 0 3px 0;
    opacity: 0.6;
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
    transition: 0.4s ease-in-out; }
.dl-menuwrapper button:hover {
    opacity: 1; }
@media only screen and (min-width: 48em) {
    .dl-menuwrapper button {
        border-radius: 3px; } }
.dl-menuwrapper button:hover,
.dl-menuwrapper button.dl-active,
.dl-menuwrapper ul {
    background: #aaa; }
.dl-menuwrapper button:after {
    content: '';
    position: absolute;
    width: 68%;
    height: 5px;
    background: #fff;
    top: 10px;
    left: 16%;
    box-shadow: 0 10px 0 #fff,  0 20px 0 #fff; }
.dl-menuwrapper ul {
    padding: 0;
    list-style: none;
    -webkit-transform-style: preserve-3d;
    -moz-transform-style: preserve-3d;
    transform-style: preserve-3d; }
.dl-menuwrapper li {
    position: relative; }
.dl-menuwrapper li h4 {
    margin: 0;
    padding: 15px 20px 0;
    color: rgba(255, 255, 255, 0.9); }
.dl-menuwrapper li p {
    margin: 0;
    padding: 15px 20px;
    font-size: 14px;
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.8);
    font-weight: 300; }
.dl-menuwrapper li p a {
    display: inline;
    padding: 0;
    font-size: 14px;
    /*font-size: 0.875rem;*/
}
.dl-menuwrapper li a {
    display: block;
    position: relative;
    padding: 15px 20px;
    font-size: 14px;
    /*font-size: 0.875rem;*/
    line-height: 20px;
    font-weight: 400;
    color: #fff;
    outline: none; }
.dl-menuwrapper li.dl-back > a {
    padding-left: 30px;
    background: rgba(0, 0, 0, 0.0); }
.dl-menuwrapper li.dl-back:after,
.dl-menuwrapper li > a:not(:only-child):after {
    position: absolute;
    top: 0;
    line-height: 50px;
    font-family: "fontawesome";
    color: #fff;
    speak: none;
    -webkit-font-smoothing: antialiased;
    content: "\f105"; }
.dl-menuwrapper li.dl-back:after {
    left: 10px;
    color: rgba(212, 204, 198, 0.5);
    -webkit-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    transform: rotate(180deg); }
.dl-menuwrapper li > a:after {
    right: 10px;
    color: rgba(0, 0, 0, 0.15); }
.dl-menuwrapper .dl-menu {
    margin: 5px 0 0 0;
    position: relative;
    width: 100%;
    max-height: 90%;
    overflow-y: auto;
    overflow-x: hidden;
    opacity: 0;
    pointer-events: none;
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.4);
    -webkit-transform: translateY(10px);
    -moz-transform: translateY(10px);
    transform: translateY(10px);
    -webkit-backface-visibility: hidden;
    -moz-backface-visibility: hidden;
    backface-visibility: hidden;
    z-index: inherit; }
@media only screen and (min-width: 48em) {
    .dl-menuwrapper .dl-menu {
        border-radius: 3px; } }
.dl-menuwrapper .dl-menu.dl-menu-toggle {
    -webkit-transition: all 0.3s ease;
    -moz-transition: all 0.3s ease;
    transition: all 0.3s ease; }
.dl-menuwrapper .dl-menu.dl-menuopen {
    opacity: 1;
    pointer-events: auto;
    background-color: #062c33;
    -webkit-transform: translateY(0px);
    -moz-transform: translateY(0px);
    transform: translateY(0px); }
.dl-menuwrapper .dl-submenu {
    border-radius: 3px;
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.0); }
.dl-menuwrapper .dl-submenu .btn, .dl-menuwrapper .dl-submenu .read-more-header a, .read-more-header .dl-menuwrapper .dl-submenu a, .dl-menuwrapper .dl-submenu #goog-wm-sb {
    margin-bottom: 0; }
.dl-menuwrapper li .dl-submenu {
    display: none; }

/*
When a submenu is opened, we will hide all li siblings.
For that we give a class to the parent menu called "dl-subview".
We also hide the submenu link.
The opened submenu will get the class "dl-subviewopen".
All this is done for any sub-level being entered.
*/
.dl-menu.dl-subview li,
.dl-menu.dl-subview li.dl-subviewopen > a,
.dl-menu.dl-subview li.dl-subview > a {
    display: none;
    background-color: #062c33;}

.dl-menu.dl-subview li.dl-subview,
.dl-menu.dl-subview li.dl-subview .dl-submenu,
.dl-menu.dl-subview li.dl-subviewopen,
.dl-menu.dl-subview li.dl-subviewopen > .dl-submenu,
.dl-menu.dl-subview li.dl-subviewopen > .dl-submenu > li {
    display: block;
    background-color: #062c33;}

/* Animation classes for moving out and in */
.dl-menu.dl-animate-out {
    -webkit-animation: MenuAnimOut 0.4s ease;
    -moz-animation: MenuAnimOut 0.4s ease;
    animation: MenuAnimOut 0.4s ease; }

.dl-menu.dl-animate-in {
    -webkit-animation: MenuAnimIn 0.4s ease;
    -moz-animation: MenuAnimIn 0.4s ease;
    animation: MenuAnimIn 0.4s ease; }

.dl-menuwrapper > .dl-submenu.dl-animate-in {
    -webkit-animation: SubMenuAnimIn 0.4s ease;
    -moz-animation: SubMenuAnimIn 0.4s ease;
    animation: SubMenuAnimIn 0.4s ease; }

.dl-menuwrapper > .dl-submenu.dl-animate-out {
    -webkit-animation: SubMenuAnimOut 0.4s ease;
    -moz-animation: SubMenuAnimOut 0.4s ease;
    animation: SubMenuAnimOut 0.4s ease; }

/* No Touch Fallback */
.no-touch .dl-menuwrapper li a:hover {
    background: rgba(255, 248, 213, 0.1); }

/* No JS Fallback */
.no-js .dl-trigger {
    display: none; }
.no-js .dl-menuwrapper {
    position: initial; }
@media only screen and (min-width: 48em) {
    .no-js .dl-menuwrapper {
        position: absolute; } }
.no-js .dl-menuwrapper .dl-menu {
    position: relative;
    opacity: 1;
    pointer-events: auto;
    -webkit-transform: none;
    -moz-transform: none;
    transform: none; }
.no-js .dl-menuwrapper li .dl-submenu {
    display: block; }
.no-js .dl-menuwrapper li.dl-back {
    display: none; }
.no-js .dl-menuwrapper li > a:not(:only-child) {
    background: rgba(0, 0, 0, 0.1); }
.no-js .dl-menuwrapper li > a:not(:only-child):after {
    content: ''; }
.no-js .dl-menu {
    max-height: 100%; }
.no-js .dl-menu li {
    display: block; }

.dl-menuwrapper button:hover,
.dl-menuwrapper button.dl-active,
.dl-menuwrapper ul {
    background: #222222; }

.dl-menu li {
    display: none; }

.dl-menuopen li {
    display: block; }


/* Tags Archive 화면을 위한 CSS 처리 */

.entry-meta {
    font-size: 12px;
    font-size: 0.75rem;
    text-transform: uppercase;
    color: rgba(187, 187, 187, 0.8); }
.entry-meta a {
    color: rgba(187, 187, 187, 0.8); }
.entry-meta .vcard:before {
    content: " by "; }
.entry-meta .tag {
    display: inline-block;
    margin: 4px;
    color: #fff;
    border-radius: 3px;
    background-color: rgba(162, 162, 162, 0.8); }
.entry-meta .tag span {
    float: left;
    padding: 2px 6px;
    color: black;
    font-family: inherit;
    background-color: aliceblue;
}
.entry-meta .tag .count {
    background-color: rgba(136, 136, 136, 0.8);
    border-radius: 0 3px 3px 0; }
.entry-meta .tag:hover {
    background-color: rgba(136, 136, 136, 0.8); }
.entry-meta .entry-reading-time {
    float: right; }

.inline-list {
    list-style: none;
    margin-left: 0;
    padding-left: 0; }
.inline-list li {
    list-style-type: none;
    display: inline; }

#post-index.feature .header-title {
    height: 400px; }
#post-index .entry-image {
    min-height: 400px; }
#post-index .entry-image img {
    min-height: 400px; }

#post-index #main {
    margin: 40px 2px 20px 2px; }
@media only screen and (min-width: 48em) {
    #post-index #main {
        margin-left: 20px;
        margin-right: 20px; } }
@media only screen and (min-width: 62.5em) {
    #post-index #main {
        max-width: 800px;
        margin-top: 50px;
        margin-left: auto;
        margin-right: auto; } }
#post-index article {
    background-color: #fff;
    box-shadow: 0 0 0 0, 0 6px 12px rgba(34, 34, 34, 0.1);
    border-radius: 3px;
    margin-bottom: 20px;
    padding: 25px 15px; }
@media only screen and (min-width: 48em) {
    #post-index article {
        padding: 30px; } }
@media only screen and (min-width: 62.5em) {
    #post-index article {
        margin-bottom: 30px;
        padding: 50px 80px; } }

.entry-title {
    font-size: large;
}

node.js 설치(이미 설치했으므로 아래 게시물로 대체함)

 

[따라하며 배우는 노드, 리액트 시리즈] 기본 강의 01-02. Node.js 설치

Node.js 다운로드 Node.js Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. nodejs.org 참고 : 폴더 생성 참고 :

shinye0213.tistory.com


gulpfile.js 수정

코드 전체 삭제 후 아래 코드 복붙 → 편집기(VSCode) 재실행

var gulp = require('gulp');

// gulp plugins and utils
var gutil = require('gulp-util');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var imagemin = require('gulp-imagemin');

// postcss plugins
var autoprefixer = require('autoprefixer');
var colorFunction = require('postcss-color-function');
var cssnano = require('cssnano');
var customProperties = require('postcss-custom-properties');
var easyimport = require('postcss-easy-import');

gulp.task('images', function() {
    return gulp.src('assets/images/*')
        .pipe(imagemin())
        .pipe(gulp.dest('assets/built/images/'))
});

gulp.task('css', function () {
    var processors = [
        easyimport,
        customProperties,
        colorFunction(),
        autoprefixer({browsers: ['last 2 versions']}),
        cssnano()
    ];

    return gulp.src('assets/css/*.css')
        .pipe(sourcemaps.init())
        .pipe(postcss(processors))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('assets/built/'))
});

package.json 수정

얘도 아래 코드 복붙

{
    "name": "blog",
    "description": "",
    "version": "1.0.0",
    "engines": {
        "ghost": ">=1.2.0"
    },
    "license": "MIT",
    "devDependencies": {
        "autoprefixer": "^7.2.6",
        "cssnano": "^3.10.0",
        "graceful-fs": "^4.1.11",
        "gulp": "^3.9.1",
        "gulp-imagemin": "^4.1.0",
        "gulp-postcss": "^7.0.1",
        "gulp-sourcemaps": "^2.6.5",
        "gulp-util": "^3.0.8",
        "minimatch": "^3.0.4",
        "postcss-color-function": "^4.0.1",
        "postcss-custom-properties": "^6.3.1",
        "postcss-easy-import": "^3.0.0"
    },
    "config": {
        "posts_per_page": 25
    }
}

npm install

터미널에서 npm install 입력하고 엔터
node_modules라는 폴더가 생기면 설치가 끝난 것


gulp css 설치

npm install 끝났으면 이번엔 gulp css

 

gulp 에러(gulp Error) gulp : 'gulp' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이

방법 1. 터미널 변경 [오류모음] npm : 'npm' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 VSCode와 Node.js 를 설치하고 간단한 프로젝트를 생성하기 위해 npm init을 쳤는데

shinye0213.tistory.com

 

Gulp 에러(Gulp Error) ReferenceError: primordials is not defined

gulp 에러(gulp Error) gulp : 'gulp' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이 방법 1. 터미널 변경 [오류모음] npm : 'npm' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있..

shinye0213.tistory.com

 

Gulp 에러(gulp Error) Replace Autoprefixer browsers option to Browserslist config.

Gulp 에러(Gulp Error) ReferenceError: primordials is not defined gulp 에러(gulp Error) gulp : 'gulp' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이 방법 1. 터미널 변경 [오류모음]..

shinye0213.tistory.com

결론 : css minify 포기


html파일 포스트에 include

내맘대로 게시글을 수정한 후 {% include iu-table-of-contents.html %} 코드 추가


bundle exec jekyll serve
이제 게시글을 클릭하면
목차가 같이 뜸

 

출처 : 

 

Jekyll 기반의 GitHub Page 생성(2) - 블로그 수정 & Publishing

Jekyll 기반의 GitHub Page 생성은 여러 절로 구성되어 있습니다. Jekyll 기반의 GitHub Page 생성(1) - 환경설정 Jekyll 기반의 GitHub Page 생성(2) - 블로그 수정 & Publishing Jekyll 기반의 GitHub Page 생성(3) - 웹 폰트

moon9342.github.io

반응형