본문 바로가기
Tips

Syntaxhighlighter 수정해서 티스토리 코드블럭 인식시키기 후기

by lovey25 2019. 4. 18.
반응형

2021-05-24 update log: 본 블로그에서는 더이상 Syntaxhighlighter를 사용하지 않습니다. highlight.js + alikong 님이 개발하신 커스텀 코드를 사용하고 있습니다.
2020-05-06 update log: Syntaxhighlighter의 부가 기능을 사용할 수 없었던 문제가 있어서 코드 수정하였습니다.
2020-05-05 update log: <code> 태그를 제거하는 중요한 부분의 언급이 누락되어 추가하였습니다.


티스토리 에디터에 새로생긴 코드블럭이라는 기능을 처음 봤을때 엄청 편할거 같아서 기대가 컸었습니다. 그런데 실제로 쓰고보니 불편한 점도 있고 아쉬운점도있고 고민해야 할 부분도 있었습니다.

그래서 그런 부분들을 개선해서 포스팅을 할때 온전히 글에만 집중할 수 있도록 고민한 흔적들을 남겨봅니다.

그리고 이번 포스팅의 일련의 고민이 낳은 결과물은 이전 포스팅에 공개를 하였습니다. 결과물만 필요하신 분은 다음 글로 바로 이동하시면 되겠습니다.

 

티스토리 코드블럭용 Syntaxhighlighter 수정버전 (뉴에디터의 코드블럭 및 기존 Syntaxhighlighter 태그 모두사용가능)

티스토리의 새로운 에디터와 같이 출시된 참신한 기능인 "코드블럭"을 편리하게 사용하면서 Syntaxhighlilghter의 다양한 기능을 사용할 수 있는 방법 공유합니다. 그리고 기존의 Syntaxhighlighter용으로 작성해..

kwonkyo.tistory.com

코드블럭의 단점

코드블럭은 에디터에서 소스코드를 넣을 때 상당히 편리하도록 만들어져 있습니다. 그러나 어디까지나 에디터 단계에서의 개선이며 최종결과물은 하이라이터가 입혀지지 않은 일반 텍스트로 나타나게 됩니다. 그래서 기존에 사용하던 제3의 소스코드 하이라이터를 이용해야하는것이죠. 이런식으로 에디터만 개선을 하고 하이라이터는 기존방식을 그대로 둔것이 사용자 입장에서 자유도를 열어두기 위함이라고 티스토리측은 얘기하고 있습니다. 사용자들이 원하는 스타일이 있을테니 맞는 결정인것 같습니다. 그런데 여기서 문제가 있습니다.

1. 코드블럭을 인식하는 태그

티스토리의 코드블럭은 "<pre><code> </code></pre>"의 2중 태그의 형태로 구성되어 있습니다. 그리고 이 태그의 형태는 고정입니다. 저같은 경우는 소스코드 하이라이터로 "Syntaxhighlighter"를 사용해 왔었는데요. Syntaxhighlighter는 <pre> </pre>의 1단계 태그로만 이루어져 있습니다. 이와같이 소스코드를 인식하는 방식이 하이라이터마다 다르기 때문에 그에 맞는 하이라이터를 써야하고 티스토리 코드블럭을 사용한다면 "Highlightjs"라는 티스토리 코드블럭을 하이라이터를 사용해야 합니다.

2. Highlightjs의 기능적인 제한

그럼 Highlightjs를 쓰면 되지 뭐가 문제냐?! 할텐데, 문제는 이 Highlightjs가 소스코드 하이라이터 본연의 기능에만 집중하고 있어서 다른 부가기능이 전혀 없다는 점입니다. 잡다한 기능을 배제했기때문에 속도면에서 유리한 측면이 있을 수 있는데 제가 생각했을 때 속도의 차이는 미미하기 때문에 시인성을 높이는데 필요한 가능한한 모든기능을 사용할 수 있는것이 더 이득이라고 생각합니다. 그래서 전 원래 사용해 왔던 Syntaxhighlighter를 계속 쓰고 싶었습니다.

아래 두 하이라이터의 차이점 참고해주시구요.

Highlightjs vs Syntaxhighlighter

분류 Highlightjs Syntaxhighlighter
태그 형태 <pre>
 <code> </code>
</pre>
<pre>
</pre>
언어인식기준 언어명 키워드 brush 키워드 + 언어명 키워드
행번호 X O
행강조 X O
기타등등 X O

3. 기존 포스팅과의 호환성

그리고 지금와서 Highlightjs로 하이라이터를 바꾸는것도 문제입니다. 이미 여러 글들을 Syntaxhighlighter를 사용해서 작성을 해놨는데 그것들을 일일히 고쳐주는 것도 상당히 귀찮은 일이되어버릴 테니까요.

4. 티스토리 코드블럭의 오지랍

그럼 Syntaxhighlighter를 그냥 쓰지 뭐가 불만이냐 하실수 있는데, 여기서 골때리는 일이 발생합니다.

앞에서 나열했던 문제점들로 인해서 Syntaxhighlighter를 사용하는 경우 코드블럭을 사용할 수 없기 때문에 다음과 같이 서식을 만들어 놓고 필요할때마다 불러와서 쓰는 방식을 취하게 되는데요.

<pre class="brush: cpp;">
	//[CPP 코드]
</pre>

그런데 이 서식을 불러오면 티스토리 에디터가 <pre>라는 태그를 발견하는 즉시 내부에 소스코드같은게 있는걸로 판단하고 사용자의 동의도 없이 html코드를 아래와 같이 변경시켜 버립니다.

<pre class="brush: cpp; json">
	<code>
		//[CPP 코드]
	</code>
</pre>

소스코드 앞뒤로 자동으로 <code>라는 태그가 들어가서 최종적으로 발행을 누르기 전에 지워주어야 하는 불편함이 따릅니다. 

겨우 그거 하나지우는게 뭐가 불편해?! 하시면 곤란합니다 왜냐? 맘대로 변경되어버린 코드를 다시 Syntaxhighlighter가 잘 인식하도록 코드를 고쳐서 저장을 했다가 나중에 다시 편집을 하려고 전에 작성했던 글을 불러오면 불러오는 그 순간 다시 원래 티스토리 코드블럭으로 변경시켜 버립니다.

한마디로 편집을 할때마다 소스코드를 변경해주어야 한다는거죠. 이정도 불편함도 감수하시겠다는 분은 아마 안계시겠죠?

해결책

그래서 어줍잖은 실력이지만 Syntaxhighlighter를 조금 고쳐서 티스토리 코드블럭을 그대로 이용할 수 있는 방법을 찾아봤습니다. (코드블럭 자체의 장점을 별도로 말씀 안드리겠습니다. 한번이라도 사용해 보셨으면 옛날 에디터로 돌아가겠다는 얘기를 못할겁니다.)

먼저 어디서 손대야 할지 티스토리 코드블럭의 형태를 유심히 살펴봐야겠죠. 주로 제가 사용하는 언어들이 아래 5개 정도 되어서 이것만 추렸습니다.

//HTML
<pre id="code_1554962373933" class="html xml" data-ke-type="codeblock"><code>HTML</code></pre>

//CSS
<pre id="code_1554962390495" class="css" data-ke-type="codeblock"><code>CSS</code></pre>

//JavaScript
<pre id="code_1554962404805" class="javascript" data-ke-type="codeblock"><code>JS</code></pre>

//Python
<pre id="code_1554962417248" class="python" data-ke-type="codeblock"><code>PY</code></pre>

//CPP
<pre id="code_1554962521626" class="c++ cpp" data-ke-type="codeblock"><code>CPP</code></pre>

이 코드를 Syntaxhighlithter코드와 비교해 볼까요? CPP코드를 예를 들어 비교해보겠습니다.

<pre class="brush: cpp;">CPP</pre>

가장 큰 차이점은 앞에서 얘기했듯 <code>태그의 유무겠고 그외에 티스토리 코드블럭에는 <pre>태그안에 속성이 "class"말고도 "id"와 'data-ke-type"이란게 있는데 별로 중요하지 않은것 같았습니다. 다행히 이 두가지 하이라이터가 모두 "class"라는 매개변수의 속성값을 파싱해서 소스코드를 인식하는 것 같았습니다.

저는 자바스크립트란 언어를 전혀 모르는 상태였기 때문에 코드의 흐름을 이해하기 위해서 무식하게 크롬 개발자도구를 열어서 디버깅모드에서 소스를 한줄한줄 따라가는 방법을 썼습니다. 여러번의 시행착오를 거쳐가며 소스코드의 "class"속성을 파싱하는 부분을 찾았습니다.

shcore.js파일안에 parseParams()란 함수가 있는데 바로 여기서 인식된 <pre>태그의 내부에 class 속성값을 파싱하는 일을 합니다. Syntaxhighlighter는 "brush:cpp"라는 형태로 ":"을 중심으로 뒤쪽에 나오는 "cpp"라는 키워드를 브러시 파일에 정의된 이름과 일치하는지를 여부를 판단해서 반환하는 그런 함수입니다. 

function parseParams(str)
{
	var match, 
		result = {},
		arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
		regex = new XRegExp(
			"(?<name>[\\w-]+)" +
			"\\s*:\\s*" +
			"(?<value>" +
				"[\\w-%#]+|" +		// word
				"\\[.*?\\]|" +		// [] array
				'".*?"|' +			// "" string
				"'.*?'" +			// '' string
			")\\s*;?",
			"g"
		)
		;

	while ((match = regex.exec(str)) != null) 
	{
		var value = match.value
			.replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
			;
		
		// try to parse array value
		if (value != null && arrayRegex.test(value))
		{
			var m = arrayRegex.exec(value);
			value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
		}
		
		result[match.name] = value;
	}
	
	return result;
};

그래서 이함수를 과감히 아래와 같이 수정했습니다.에 아래와 같이 추가 로직을 삽입하였습니다(31~50행). "brush:"라는 키워드가 있어야 소스코드의 언어종류를 인식할 수 있기 때문에 "brush:" 키워드를 사용하지 않는 티스토리 코드블럭에서도 적용할 수 있도록 하였습니다. 제가 사용할 언어는 정해져 있기 때문에 브러시로 선언되어 있는 키워드 cpp, xml, css, js, py가 상황에 따라서 반환될 수 있도록 하였습니다.

function parseParams(str) {
	var match,
		result = {},
		arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
		regex = new XRegExp(
			"(?<name>[\\w-]+)" +
			"\\s*:\\s*" +
			"(?<value>" +
			"[\\w-%#]+|" +		// word
			"\\[.*?\\]|" +		// [] array
			'".*?"|' +			// "" string
			"'.*?'" +			// '' string
			")\\s*;?",
			"g"
		)
		;

	while ((match = regex.exec(str)) != null) {
		var value = match.value
			.replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
			;

		// try to parse array value
		if (value != null && arrayRegex.test(value)) {
			var m = arrayRegex.exec(value);
			value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
		}

		result[match.name] = value;
	}
	/*** 티스토리 코드블럭을 위한 추가 로직 ***/
	if (str.indexOf("cpp") != -1) {
		result["brush"] = "cpp";
	}
	else if (str.indexOf("c++") != -1) {
		result["brush"] = "cpp";
	}
	else if (str.indexOf("xml") != -1) {
		result["brush"] = "xml";
	}
	else if (str.indexOf("css") != -1) {
		result["brush"] = "css";
	}
	else if (str.indexOf("javascript") != -1 | str.indexOf("js") != -1) {
		result["brush"] = "js";
	}
	else if (str.indexOf("python") != -1 | str.indexOf("py") != -1) {
		result["brush"] = "py";
	}
	/*** 티스토리 코드블럭을 위한 추가 로직 ***/
	return result;
};

이렇게 해주면, 코드블럭으로 손쉽게 작성한 코드도 인식이 되면서 기존에 Syntaxhighlighter용으로 포스팅 했던 글들까지 모두 인식이 됩니다. 비교적 간단한 방법으로 해결할 수 있어서 천만 다행입니다.

[추가내용입니다.]

이제 사용 언어에 대한 키워드 파싱을 완료했으면 마지막으로 <code></code> 태그를 제거해 주어야 합니다. 이 부분은 syntaxhighlighter가 구문강조를 위해서 분석을 할 순수한 코드의 텍스트를 처리하기 직전에 개입을 해서 코드 앞에 붙어있는 <code>와 뒤에 붙어있는 </code>를 지워주는 과정을 거치도록 하였습니다. 이 부분은 syntaxhighlighter에서 <pre> 태그가 아니라 <script>태그를 이용할 때 태그를 지워주는 로직이 있어서 그걸 이용해서 만들었습니다.

code = target[propertyName];
			    
// remove CDATA from <SCRIPT/> tags if it's present
if (conf.useScriptTags)
    code = stripCData(code);
code = stripCodeTag(code); // 여기서 필요다하면 <code> 태그 제거필요
// Inject title if the attribute is present
if ((target.title || '') != '')
    params.title = target.title;

shCore.js 내용중에 위와 같은 부분이 있는데 여기서 6번째 줄이 <code>태그 제거를 위해 주가된 부분입니다. 그리고 아래 함수가 stripCodeTag 입니다.

/**
 * Strips <code> tag
 */
function stripCodeTag(original) {
    var left = '<code>',
        right = '</code>',
        // for some reason IE inserts some leading blanks here
        copy = trim(original),
        changed = false,
        leftLength = left.length,
        rightLength = right.length
        ;

    if (copy.indexOf(left) == 0) {
        copy = copy.substring(leftLength);
        changed = true;
    }

    var copyLength = copy.length;

    if (copy.indexOf(right) == copyLength - rightLength) {
        copy = copy.substring(0, copyLength - rightLength);
        changed = true;
    }

    return changed ? copy : original;
};

마무리

티스토리 공지에서 사용자들이 원하는 스타일을 자유롭게 사용할 수 있도록 하이라이터부분은 사용자의 몫으로 남겨두었다는 그런 언급이있었습니다. 만약 코드블럭이 다양한 하이라이터를 지원할 수 있도록 유연성을 가진 형태로 출시되었다면 그말이 맞았을 겁니다. 실제로는 Highlightjs라는 하이라이터만 사용하도록 강제하는 꼴이어서 자유도는 어디로 갔는가 하는 의문이 들었습니다. 그리고 다른 하이라이터용으로 만들어 놓은 기존 코드마져도 마음대로 수정해버리는 오지랍은 더욱이 이해할 수 없었죠. 그래서 고객센터에 문의도 했었습니다. 이런 문제가 있으니 유연성을 가질 수 있도록 이러이런 옵션을 추가하는 방향을 고려하는게 어떻겠냐고. 답변은 단호박!!

우선 문의하신 2건의 내역 모두 확인하였으며 종합하여 답변 드리는 점 참고 부탁드리겠습니다.
'pre', 'code' 는 코드 블럭을 나타내는 html 마크업입니다. 
'pre'만 사용할 경우 의미상 코드 블럭이라 할 수는 없어 에디터에서 옵션을 제공하기는 어렵습니다.
번거로우시더라도 사용하시는 highlighter의 옵션을 변경해주시길 부탁드리겠습니다.

고객센터에서 말씀하시는 highlighter의 옵션을 변경하라는게 제가 했던 이런 걸 말씀하시는 걸까요? 저같은 초보인에게는 결코 쉽지않은 작업들이었습니다. Javascript를 손대는 방법 말고 다른방법 있나요?? 그런데 저렇게 쉽게 답을 해주시니 한편으로는 다들 이정도는 간단히 해결하는건가, 나만 너무 무지한건가 하는 자괴감들고 막 그러는 그런 작업이었습니다. 긴글 읽어주셔서 감사합니다.

 

끝!

반응형

댓글