1. CSST란 무엇인가
CSST의 유의어는 SASS, PostCSS 같은 것이 아니라 JSONP입니다. 즉, JSONP가 할 수 있는 일을 (CSS3 환경에서) CSST로도 할 수 있다는 뜻입니다.
Ajax는 교차 출처(Cross-origin) 요청이 불가능하지만, JSONP는 Ajax의 교차 출처 패치 역할을 합니다. (물론 다른 교차 출처 방법도 있지만, JSONP가 가장 널리 사용됩니다.)
CSST의 가장 큰 제한은 CSS3를 지원하는 환경에서만 작동한다는 점입니다. 하지만 우리가 이것을 실제로 사용하느냐보다는 그 아이디어와 원리가 꽤 흥미롭습니다.
2. 구현 원리
1. 클라이언트(브라우저 JS)
- 요청 전송
head에 <link>를 삽입하고 href 속성을 통해 요청을 보냅니다.
- 응답 수신 준비
보이지 않는 span을 생성하고 해당 요소의 animationstart 이벤트를 감시합니다 (따라서 CSS3 환경이 필요합니다).
2. 서버 측(node/PHP...)
- 스타일 반환 (ID를 통해 숨겨진 span에 적용됨)
스타일 내용은 두 부분으로 나뉩니다: 1. content (CSS 속성 content를 사용하여 비즈니스 로직에서 반환할 문자열을 실어 보냄); 2. animation (응답이 도착했음을 클라이언트에 알림).
서버는 이 작업만 수행하며, 클라이언트는 응답을 받은 후 span의 content 내용을 추출하여 텍스트 전송을 완료합니다.
3. 세부 문제 (기술적 수단)
<link>로드 완료를 어떻게 감지하나요?
온라인 자료를 수집해 본 결과, 일반적인 해결책은 타이머를 사용하거나
onpropertychange또는DOMAttrModified를 사용하는 것이었습니다.
CSS3 환경임을 고려하여, 애니메이션 시작(
animationstart) 이벤트를 활용해 이를 캡처하는 기교를 발휘합니다.
onpropertychange와 DOMAttrModified 이벤트, 그리고 더 최신인 MutationObserver는 호환성 면에서 많은 문제가 있지만, animationstart 이벤트는 상대적으로 호환성이 좋습니다(Android 2.3 이하 및 IE6-IE9 미지원). 애니메이션 이벤트는 CSS3 애니메이션 모듈 명세의 일부이므로, CSS3 애니메이션을 지원하는 UA(사용자 에이전트)는 해당 애니메이션 이벤트도 지원해야 하기 때문입니다.
- 특수 문자("、'、\、\n、\r、\t)는 어떻게 전송하나요?
Chrome과 Safari는
content스타일 속성의 문자 파싱 방식이 일치하지 않습니다.
알 수 없는 파싱 규칙의 영향을 피하기 위해 일관되게 Base64 인코딩을 사용합니다.
브라우저 환경에서 Base64를 인코딩/디코딩하는 것은 비교적 쉬우며, 특수 문자를 지원해야 하는 다른 상황에도 적용 가능합니다.
3. 장단점
장점: 만약 API가 해킹당하더라도 JSONP보다 피해가 약간 적습니다 (link 태그는 script 태그보다 안전합니다. script는 주입된 코드가 즉시 실행되지만, link는 스타일에만 영향을 미칩니다).
P.S. 만약 API 자체가 뚫렸다면, 이런 작은 방어책이 과연 결정적인 역할을 할 수 있을까요?
단점:
-
CSS3 환경만 지원함
-
JSONP보다 DOM 조작이 많음 (요청마다 link 하나와 span 하나를 추가/삭제해야 합니다. 반면 JSONP는 script 하나만 추가/삭제하면 됩니다. 또한 숨겨진 span의 추가/삭제 및 스타일 업데이트는 페이지의 일부 리플로우(reflow)를 유발할 수 있습니다).
따라서 CSST 사용을 권장하지 않으며, JSONP가 훨씬 더 편리하고 실용적입니다. 이 글은 단지 이러한 접근 방식과 원리가 알아둘 가치가 있다는 점을 말하고자 함입니다.
만약 굳이 사용해야 한다면, CSS3를 지원하지 않거나 부분적으로 지원하는 UA의 상황도 고려해야 하며, 기능 감지(feature detection)를 보완해야 합니다 (원본 프로젝트에는 아직 추가되지 않았습니다):
var animation = false,
animationstring = 'animation',
keyframeprefix = '',
domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
pfx = '',
elm = document.createElement('div');
if( elm.style.animationName !== undefined ) { animation = true; }
if( animation === false ) {
for( var i = 0; i < domPrefixes.length; i++ ) {
if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) {
pfx = domPrefixes[ i ];
animationstring = pfx + 'Animation';
keyframeprefix = '-' + pfx.toLowerCase() + '-';
animation = true;
break;
}
}
}
(위 코드는 Detecting CSS animation support - CSS | MDN에서 발췌했습니다)
더 나은 호환성을 위해서는 코드를 수동으로 재구성해야 하므로, 그냥 이런 것이 있다는 정도로만 알아두면 충분합니다.
참고 자료
-
mobilebone.js-mobile移动web APP单页切换骨架: animationstart 이벤트의 호환성을 참고했습니다 (이번 주 번역 문제에 도움을 주신 zxx 선배님께도 감사의 말씀을 전합니다..)
아직 댓글이 없습니다