IT 블로거들은 프로그래밍 언어들을 표시할 때, [**코드 하이라이터**]를 쓰곤 합니다.
코드 하이라이터는 표시된 코드들을 종류별로 구분해서 다른 색깔로 표시해주는 것으로 독자가 코드를 읽을 때 가독성을 좋게 해주는 효과가 있습니다.
실제로 저의 블로그에도 아래 사진과 같은 [**코드 하이라이터**]가 적용되어 있습니다.
코드 하이라이터는 보통 플러그인이나 외부 라이브러리로 불러다 쓰시는 분들이 많습니다. 대표적으로 티스토리에선 [**Syntax Highlight**]라는 플러그인을 제공하기도 합니다.
다만 개인적으론 플러그인에 의존하는 걸 싫어하기도 하거니와, 제가 쓰는 건 javascript/css/html 3종류에 불과한데 여러 언어 기능이 탑재된 플러그인을 쓰는 건 용량 낭비 같아서 직접 제작하기로 했습니다.
의외로 방법 자체는 간단합니다.
[팁*초보를 위해서 마지막 부분에 css/javascript를 올리겠습니다. 그걸 복사해서 블로그/홈페이지에 넣으시면 정상 작동할 겁니다.*]
코드 하이라이터의 원리
원리를 요약하자면 이렇습니다.
- 코드하이라이터로 쓸 태그를 찾아냅니다.
- 찾아낸 태그에서 텍스트를 추출해냅니다.
- 정규식으로 특정 텍스트를 span으로 감싸줍니다. 이 때 span의 클래스는 원하는 글씨색으로 지정해줍니다.
- 특정 텍스트를 span으로 감싸줬다면 이걸 다시 코드하이라이터에 주입합니다.
써놓다보니 복잡해보이지만 실제로는 매우 간단합니다.
실제 예제를 통해 단계적으로 시도해보겠습니다.
code,pre태그를 css로 꾸며주기
첫 단계는 다른 코드하이라이터처럼 꾸미기를 시도하는 겁니다.
CSS로 작업해주면 됩니다.
티스토리의 경우 코드블럭을 사용하면 <pre><code></code></pre>로 지정되기 때문에 선택자는 태그로 하면 됩니다.
이를테면 아래와 같이 해주면 되지요.
pre code{
color:#fff;word-break: break-all;
order:3;
width:100%;
padding:7px;
line-height:43px;
overflow-x: auto;
position:relative;
}
pre:before,pre:after{
z-index:5;
top:-30px;
position:absolute;
}
pre:after{
left:5px;
content: '★';
color: gold;
transform-origin: center center;
display: inline-block;
-webkit-animation: starstar 3s linear infinite;
}
pre:before{
content: ' 'attr(data-ke-language);
background:#000;
height:30px;
color:#fff;
width:100%;
}
pre {
margin:50px 0 20px 0;
position:relative;
min-height:56px;
color:#fff;
display:flex;
display : -webkit-flex;
display : -ms-flexbox;
width:100%;
background: #272822;
-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;
}
꾸미는 것은 어디까지나 취향의 영역이기 때문에 유저들이 식별하기 좋게 꾸며주는 정도면 충분합니다.
원하는 대로 꾸미세요.
어렵지 않을 겁니다. 보통 코드하이라이터는 어둡게 하는 게 대세입니다.
코드 블럭을 찾아 텍스트 추출하기
꾸며줬다면 이제 본격적으로 태그에 따라 색깔을 바꿔주거나 효과를 넣어줘야 합니다.
그러기 위해선 자바스크립트로 제어할 필요가 있습니다.
이 자바스크립트는 다음과 같은 순서로 진행되야 합니다.
- 본문에서 CODE 태그를 찾아내야 합니다.
- CODE 태그 내부에 있는 텍스트를 추출해야 합니다.
- 정규식으로 태그별로 클래스 이름을 붙여주며 감싸주며 태그를 주입합니다.
- CSS로 클래스 이름에 해당하는 효과를 넣어줍니다.
여기서 주의해야할 점은 코드하이라이터가 본문에 여러개가 있을 수도 있기 때문에 getElementsByTagName으로 수집한다음에 for문을 써야한다는 겁니다.
function codehigh () {
const Content = document.querySelector('.entry-content');//본문선택
if (!Content) {return !1}//본문이 없다면 작업 끝내기
const code = Content.getElementsByTagName('code');//본문내에 있는 CODE태그 선택
if (!code) {return !1} // 본문 내에 code태그가 없다면 작업 끝내기
const codeLength = code.length;//코드태그의 갯수
for (var i =0; i<codeLength; i++) {
const codeText = code[i].innerHTML;//각 코드태그에 있는 text를 추출하기
}
}
제 스킨을 기준으로 보면 본문을 나타내는 건 entry-content라는 Class입니다.
그걸 querySelector로 선택하고, 그 컨텐츠에서 code 태그를 추출한 후 각 code에 있는 텍스트를 가져오는 거지요.
설명은 달아놨습니다.
이제 다음 단계로 넘어갈 차례입니다.
정규식으로 치환해주기
이제 남은 것은 정규식을 통해 텍스트를 분류하는 겁니다.
이 때 사용되는 게 정규식의 경계문자 기능입니다.
표기는 [**\b**]로 해줍니다.
이 기능을 이용해야 정상적으로 필요한 단어만 잘라올 수 있습니다.
위의 사진을 보시면 이해가 되실 겁니다.
이 기능을 이용하면 정확히 사용된 코드의 언어만을 감지해줄 수 있습니다.
그 외에도 단어만 감지해주는 \w이나, 특정 문자 사이를 감지해주는 (.*?)이나, 후방탐색을 해서 원하는 만큼의 양을 골라내는 ?=등을 활용하면 됩니다.
일일히 다 설명하기엔 너무 길므로, 제가 실제로 사용 중인 정규식을 뽑아내보겠습니다.
var strReg1 = /"(.*?)"/g, // 따옴표 사이
strReg2 = /'(.*?)'/g, // 작은따옴표 사이
specialReg = /\b(new|var|if|do|function|while|switch|for|foreach|in|continue|break)(?=[^\w])/g, //new var if등 앞에 선언하는 단어
specialJsGlobReg = /\b(document|window|Array|String|Object|Number|\$)(?=[^\w])/g, //array,string등 자바스크립트에 사용되는 단어
specialJsReg = /\b(getElementsBy(TagName|ClassName|Name)|getElementById|typeof|instanceof|classList|forEach|(remove|get|set)Attribute|append(Child|)|replaceChild|preventDefault|addEventListener|querySelector(All|)|dataset|inner(HTML|Text)|push|join|concat|pop|shift)(?=[^\w])/g,
specialMethReg = /\b(indexOf|match|replace|toString|length)(?=[^\w])/g,
specialPhpReg = /\b(define|echo|print_r|var_dump)(?=[^\w])/g,
specialCommentReg = /(\/\*.*\*\/)/g, //주석1
inlineCommentReg = /(\/\/.*)/g; // 주석2
var htmlTagReg = /(<[^\&]*>)/g; // <>를 감지
var sqlReg = /\b(CREATE|ALL|DATABASE|TABLE|GRANT|PRIVILEGES|IDENTIFIED|FLUSH|SELECT|UPDATE|DELETE|INSERT|FROM|WHERE|ORDER|BY|GROUP|LIMIT|INNER|OUTER|AS|ON|COUNT|CASE|TO|IF|WHEN|BETWEEN|AND|OR)(?=[^\w])/g;
다른 언어를 사용중이시라면 저 언어의 행태를 보고 적절한 단어를 | |사이에 넣어주시면 됩니다.
설명을 생략한 것은 말 그대로 적혀있는 단어를 보고 골라내는 정규식이기 때문입니다.
이제 다음으로 뽑아낸 텍스트에 정규식을 사용해 치환할건데 이럴 때엔 역시 [**replace**]를 사용해주는 게 좋습니다.
또한 정확히 치환만 해주려면 정규식으로 캡쳐된 부분만을 가져오는 $를 활용해줘야 합니다.
이 활용법을 이용한다면 이렇습니다.
var parsed = string.replace(strReg1,'<span class="string">"$1"</span>');
parsed = parsed.replace(strReg2,"<span class=\"string\">'$1'</span>");
parsed = parsed.replace(specialReg,'<span class="special">$1</span>');
parsed = parsed.replace(specialJsGlobReg,'<span class="special-js-glob">$1</span>');
parsed = parsed.replace(specialJsReg,'<span class="special-js">$1</span>');
parsed = parsed.replace(specialMethReg,'<span class="special-js-meth">$1</span>');
parsed = parsed.replace(htmlTagReg,'<span class="special-html">$1</span>');
parsed = parsed.replace(sqlReg,'<span class="special-sql">$1</span>');
parsed = parsed.replace(specialPhpReg,'<span class="special-php">$1</span>');
parsed = parsed.replace(specialCommentReg,'<span class="special-comment">$1</span>');
parsed = parsed.replace(inlineCommentReg,'<span class="special-comment">$1</span>');
parsed라는 변수를 선언해주고, 이걸 차례대로 정규식으로 치환해줍니다.
그러면 parsed엔 최종적으로 필요한 태그마다 <span>으로 둘러싸인 완성물이 튀어나옵니다.
이걸 다시 code에 innerHTML로 불어넣으면 끝입니다.
그러면 최종 완성이지만 한 가지 남은 게 바로 css입니다.
예를 들어 <span class="string">으로 둘러진 글씨 색을 변경해야 하기 때문입니다.
이 css들은 다음과 같습니다.
pre code .string {
color:#A1E46D;
}
pre code .special {
color:#D6665D;
}
pre code .special-js {
color:#6DE4D1;
}
pre code .special-js-glob {
color:#A1E46D;
font-weight:bold;
}
pre code .special-comment{
color:#aaa;
}
pre code .special-js-meth {
color:#E46D8A;
}
pre code .special-html {
color:#E4D95F;
}
pre code .special-sql {
color:#1D968C;
}
pre code .special-php{
color:#597EA7;
}
innerHTML을 배제하기
제가 위에 올린 코드엔 한 가지 약점이 있습니다.
[**innerHTML**]을 2번 사용했다는 겁니다.
innerHTML은 자바스크립트 사용시 보안/속도 문제 때문에 가급적 지양해야 합니다.
대체할 수 있으면 대체하는 게 옳지요.
저는 약간의 꼼수를 활용해서 해결했습니다.
먼저 아래의 정규식을 사용해서 innerHTML로 추출하건, textContent로 추출하건 같은 결과가 나오도록 만들었습니다.
var htmlTagReg = /(<[^\&]*>)/g;
이제 이 정규식을 사용해 replace를 가장 먼저 해주면 끝입니다.
이걸로 첫번째 innerHTML은 textContent로 대체할 수 있게 되었습니다.
그리고 두 번째 innerHTML은 꼼수를 동원했습니다.
code.textContent = "";
code.insertAdjacentHTML('afterbegin', parsed);
code에 들어 있는 글자들을 모두 지운 후에, innerHTML대신 insertAdjacentHTML을 활용해 집어넣는 겁니다.
insertAdjacentHTML의 설명은 이 글을 참고하시면 될 듯 합니다.
이렇게 해서 innerHTML을 배제해서 코드 하이라이터를 완성했습니다.
최종 완성본 실제 모습
위에서 설명한 것들을 종합해 코드하이라이터를 만들었습니다.
여기 적힌 css/javascript 부분만을 따가면 티스토리나 홈페이지에서도 사용 가능하십니다.
커스터마이징이 더 필요하겠지만 뼈대는 될 겁니다.
관련글
'그외 > 유용한 정보' 카테고리의 다른 글
이미지 파일을 압축하거나 webp와 반응형으로 최적화 하는 방법 (2) | 2021.02.20 |
---|---|
중고 그래픽카드 구매할 때 주의, 체크해야할 점과 제품 시세 정보 (0) | 2021.02.18 |
RTX 3070 가격 변동 추이 - 얼마에 사야할까? (7) | 2021.02.02 |
엑박패드 4세대 리시버 없이 블루투스로 PC에 연결하기 (0) | 2020.12.14 |
RTX-3060ti가 출시! 종류별 성능 등급표와 3070과 가격 비교. (0) | 2020.12.03 |