JKUN

Welcome To The Jkun.net

블로그 포스트 검색결과


Development 에 해당되는 글 233개가 검색 되었습니다.

  1. 2017.06.21 [Codeigniter] 코드이그나이터 넷빈즈로 개발환경 구성
  2. 2017.05.22 자바스크립트 오버로딩
  3. 2016.09.23 A4 용지 출력
  4. 2016.06.19 [BootStrap 3] ScrollSpy
  5. 2016.06.19 [BootStrap 3] popover Example
  6. 2016.06.19 [BootStrap 3] popover live
  7. 2016.06.19 jQuery PlugIn - 커스텀 스크롤 라이브러리
  8. 2016.06.19 [BootStrap] Tooltip Styling
  9. 2016.05.17 Include File List
  10. 2016.03.09 디자인 패턴 종류
  11. 2016.03.09 [펌] 자바스크립트에서 strict mode를 사용해야 하는 이유
  12. 2016.03.09 [펌] +function() 의 원리
  13. 2016.03.09 지식공유 서비스 - SlimWiki
  14. 2016.02.29 프로젝트 환경 [ Taiga + HipChat + BitBucket + SourceTree + Vagrant (NgineX/PHP/MariaDB) + IDE(PHPStorm) ] (1)
  15. 2016.01.21 jquery attribute (속성 존재여부)
  16. 2015.12.14 [Study][Node.Js] 1. Hello World 와 웹서버 만들기.
  17. 2015.12.10 JBOSS 외부접근 설정
  18. 2015.11.26 반응형 웹과 적응형 웹
  19. 2015.09.25 Visual Studio Code 를 이용한 node.js 환경 구성 (1)
  20. 2015.09.23 [펌] node.js 장단점
  21. 2015.09.23 [펌] 브라우저는 어떻게 동작하는가?
  22. 2015.09.22 [펌] git 파일 대소문자 변경하기
  23. 2015.09.21 FormData Object 확인 (1)
  24. 2015.06.24 GIT 참고링크
  25. 2015.06.24 GIT 명령어
  26. 2015.06.16 조엘 온 소프트웨어
  27. 2015.05.27 JMeter - 웹사이트 성능 테스트 (스트레스 테스트)
  28. 2015.05.08 IE 10 이상으로 셀렉트박스 아이콘 숨기기
  29. 2015.04.27 replaceArray - 배열간 문자열 탐색변경
  30. 2015.04.13 $.when()

Blog

[Codeigniter] 코드이그나이터 넷빈즈로 개발환경 구성

2017.06.21 14:49 Development/PHP


회사에서 일을 하며 울며 겨자먹기식으로 어쩔 수 없이,

NetBeans PHP 로 IDE 환경구성하며 프로젝트를 진행하고 있습니다.


이에 어떻게하다 보니 그럭저럭 쓸만하게 PHP & CodeIgniter 환경구성을 하게되어 포스팅하게 됩니다.

넷빈즈 다운로드는 다음과 같이 링크합니다.



먼저 필요한 파일은 첨부파일을 다운로드 받아 넷빈즈 플러그인 디렉토리 또는 개별적으로 설정하신

디렉토리에 압축해제 하세요.


netbeans-codeIgniter-modules.zip

ㄴ org-nbphpcouncil-modules-php-ci-0.6.0.nbm

ㄴ org-nbphpcouncil-modules-php-ci-repository-0.6.0.nbm


그리고 코드이그나이터 공식웹사이트에 접속하셔서 코드이그나이터를 다운로드 받으시기 바랍니다.

일단은 스피드한 포스팅 전개를 위해 첨부합니다. 버전은 3.0.6 입니다.


CodeIgniter-3.0.6.zip


링크 : 코드이그나이터 다운로드



이제 준비는 끝났습니다. 넷빈즈에 대한 순차적인 설정입니다.


1. 먼저 넷빈즈의 Tools > plugins 를 실행합니다.



2. 그리고 다운로드된 플러그인을 설치하는 탭으로 이동합니다.


저는 윈도우즈 USERS/계정/AppData/... 로 시작하는 경로에 압축을 풀었습니다.

이건 특별한 제한은 없지 싶습니다.


C:\Users\JKUN\AppData\Roaming\NetBeans\8.2\plugins


하여 위 두 파일들을 차례대로 선택하여 "열기" 클릭!

그럼 아마 인스톨하든지 할겁니다. 여기는 재량껏 Next! Next! Next!

그럼 이제 프레임워크 정보에 코드이그나이터 추가된 것을 확인하실 수 있습니다.

위 경로는 Tools > Options > PHP (Tab) > Framework & Tools (Tab) > CodeIgniter 입니다.


3. 이제 Base Files 에 다운로드 받은 코드이그나이터를 적용시켜주는 단계입니다.


아 참 저 Base Files 를 선택하면 나오는 목록에 Name 은 Edit 버튼을 이용하여 이름을 변경하였습니다.


4. 이제 프로젝트 속성창에서 프레임워크를 코드이그나이터의 Enabled 를 체크합니다



 그럼 프로젝트 옆에 CI 불꽃 아이콘이 나타납니다.


그리고 코드상에서 한번 테스트해서 보면은~~



이야~ ㅋㅋㅋ 되지요?

다들 즐코 PHP/CI 하시길 바랍니다.


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

자바스크립트 오버로딩

2017.05.22 10:59 Development/JavaScript


출처 : http://webclub.tistory.com/404


진작에 구글링 할걸...ㅡㅡ;;;

엄밀히 말하자면 비스무리흉내 ㅋㅋ 그래도 너무 편하다;;


function sum() {
    var result = 0;
    for (var i = 0; i < arguments.length; i++) {
	result += arguments[i];
    }
    return result;
}
console.log(sum(10, 20));// 30
console.log(sum(10, 20, 30));// 60 
console.log(sum(10, 20, 30, 40)); // 100 
console.log(sum(10, 20, 30, 40, 50)); //150

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

A4 용지 출력

2016.09.23 11:06 Development/HTML / XHTML


웹페이지에서 프린트 출력할때 유용합니다.

HTML

<page size="A4">
	<h1>A4 용지 세로 1 이어서</h1>
</page>
<page size="A4">
	<h1>A4 용지 세로 2 이어서</h1>		
</page>
<hr is-divide="true">
<page size="A4" is-divide="true">
	<h1>A4 용지 세로 1 분리</h1>
</page>		
<page size="A4" is-divide="true">
	<h1>A4 용지 세로 2 분리</h1>		
</page>
<page size="A4" layout="portrait">
	<h1>A4 용지 가로</h1>		
</page>
<page size="A5">
	<h1>A5 용지 세로</h1>		
</page>
<page size="A5" layout="portrait">
	<h1>A5 용지 가로</h1>		
</page>
<page size="A3">
	<h1>A3 용지 세로</h1>		
</page>
<page size="A3" layout="portrait">
	<h1>A3 용지 가로</h1>				
</page>


CSS

body {
	background: rgb(204,204,204); 
}
page {
	  background: white;
	  display: block;
	  margin: 0 auto;
	  margin-bottom: 0.5cm;
	  box-shadow: 0 0 0.5cm rgba(0,0,0,0.5);
}
page[size="A4"] {  
	  width: 21cm;
	  height: 29.7cm; 
}
page[size="A4"][layout="portrait"] {
	  width: 29.7cm;
	  height: 21cm;  
}
hr[is-divide="true"] {
	height:0; margin:0; padding:0; border:none !important;
}
[is-divide="true"] {
	page-break-after: always;
}
page[size="A3"] {
	  width: 29.7cm;
	  height: 42cm;
}
page[size="A3"][layout="portrait"] {
	  width: 42cm;
	  height: 29.7cm;  
}
page[size="A5"] {
	  width: 14.8cm;
	  height: 21cm;
}
page[size="A5"][layout="portrait"] {
	  width: 21cm;
	  height: 14.8cm;  
}
@media print {
	  body, page {
		margin: 0;
		box-shadow: 0;
	  }
}


JAVASCRIPT

window.onload = function() {
	window.print();
};

Inline HTML Full Code

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<style type="text/css">
		body {
		  background: rgb(204,204,204); 
		}
		page {
		  background: white;
		  display: block;
		  margin: 0 auto;
		  margin-bottom: 0.5cm;
		  box-shadow: 0 0 0.5cm rgba(0,0,0,0.5);
		}
		page[size="A4"] {  
		  width: 21cm;
		  height: 29.7cm; 
		}
		page[size="A4"][layout="portrait"] {
		  width: 29.7cm;
		  height: 21cm;  
		}
		hr[is-divide="true"] {
			height:0; margin:0; padding:0; border:none !important;
		}
		[is-divide="true"] {
			page-break-after: always;
		}
		page[size="A3"] {
		  width: 29.7cm;
		  height: 42cm;
		}
		page[size="A3"][layout="portrait"] {
		  width: 42cm;
		  height: 29.7cm;  
		}
		page[size="A5"] {
		  width: 14.8cm;
		  height: 21cm;
		}
		page[size="A5"][layout="portrait"] {
		  width: 21cm;
		  height: 14.8cm;  
		}
		@media print {
		  body, page {
			margin: 0;
			box-shadow: 0;
		  }
		}
		</style>
		<title>Page Print Example</title>
	</head>
	<body>
		<page size="A4">
			<h1>A4 용지 세로 1 이어서</h1>
		</page>
		<page size="A4">
			<h1>A4 용지 세로 2 이어서</h1>		
		</page>
		<hr is-divide="true">
		<page size="A4" is-divide="true">
			<h1>A4 용지 세로 1 분리</h1>
		</page>		
		<page size="A4" is-divide="true">
			<h1>A4 용지 세로 2 분리</h1>		
		</page>
		<page size="A4" layout="portrait">
			<h1>A4 용지 가로</h1>		
		</page>
		<page size="A5">
			<h1>A5 용지 세로</h1>		
		</page>
		<page size="A5" layout="portrait">
			<h1>A5 용지 가로</h1>		
		</page>
		<page size="A3">
			<h1>A3 용지 세로</h1>		
		</page>
		<page size="A3" layout="portrait">
			<h1>A3 용지 가로</h1>				
		</page>	
		<script type="text/javascript">
			window.onload = function() {
				window.print();
			};
		</script>
	</body>
</html>


아 구글링 중에 설명을 깔끔하게 잘 해주신 분입니다.

http://noveloper.github.io/blog/css/2015/04/12/how-to-divide-print-space.html


추가적으로 확인해야 하는 부분은

프린트도 크로스 브라우징 검증 작업은 필요합니다.

인터넷 익스플로러도 감안해서 폰트는 포인트(pt) 로 작성하시는게 정신건강상.. ㅎㅎ

그리고 인터넷 익스플로러에서는 상황에 따라 페이지 설정을 브라우저 기능 설정단에서 바꾸어줘야

하는 경우가 있습니다.





저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[BootStrap 3] ScrollSpy

2016.06.19 18:19 Development/WebUI FrameWork | BootStrap


부트스트랩 스크롤 스파이 엘리먼트에 적용

링크 : http://tutsme-webdesign.info/bootstrap-3-scrollspy/

<body>
<div class="container">
    <nav id="target_nav" class="col-md-3">
        <ul class="nav list-group">
            <li class="list-group-item"><a href="#one">One</a></li>
            <li class="list-group-item"><a href="#two">Two</a></li>
            <li class="list-group-item"><a href="#three">Three</a></li>
            <li class="list-group-item"><a href="#four">Four</a></li>
        </ul>
    </nav>
    <div id="scroll-able" class="col-md-6">
        <section><h4 id="one">One</h4><p>Pellentesque habitant morbi tristique senectus et netus ... metus</p></section>
        <section><h4 id="two">Two</h4><p>Pellentesque habitant morbi tristique senectus et netus ... metus</p></section>
        <section><h4 id="three">Three</h4><p>Pellentesque habitant morbi tristique senectus et netus ... metus</p></section>
        <section><h4 id="four">Four</h4><p>Pellentesque habitant morbi tristique senectus et netus ... metus</p></section>
   </div>
</div>
 
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script>
  $('div#scroll-able').scrollspy({ target: '#target_nav' });
</script>
</body>


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[BootStrap 3] popover Example

2016.06.19 18:16 Development/WebUI FrameWork | BootStrap


링크1 : http://www.w3schools.com/bootstrap/bootstrap_ref_js_popover.asp

링크2 : http://www.w3schools.com/bootstrap/tryit.asp?filename=trybs_ref_js_popover_methods&stacked=h


<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
  <style>
  a {
      font-size: 25px;
  }
  </style>
</head>
<body>

<div class="container">
  <h3>Popover Methods</h3>
  <a href="#" data-toggle="popover" title="Popover Header" data-content="Some content inside the popover">Popover Example</a>
  <div>
    <p>Click on the buttons to manually control the popover above:</p>
    <button type="button" class="btn btn-primary">Show</button>
    <button type="button" class="btn btn-warning">Hide</button>
    <button type="button" class="btn btn-success">Toggle</button>
    <button type="button" class="btn btn-danger">Destroy</button>
  </div>
</div>

<script>
$(document).ready(function(){
    $(".btn-primary").click(function(){
        $("[data-toggle='popover']").popover('show');
    });
    $(".btn-warning").click(function(){
        $("[data-toggle='popover']").popover('hide');
    });
    $(".btn-success").click(function(){
        $("[data-toggle='popover']").popover('toggle');
    });
    $(".btn-danger").click(function(){
       $("[data-toggle='popover']").popover('destroy');
    });
});
</script>

</body>
</html>


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[BootStrap 3] popover live

2016.06.19 18:12 Development/WebUI FrameWork | BootStrap


부트스트랩 팝오버 컴포넌트를 사용하려다 보면, live / on 으로 이벤트를 바인딩 해야할 경우가 있다.

jsFiddle 에 누가 올려놓음. 링크


그래서~ 아래와 같이


HTML

<div id="popover-triggers">
    <a class="btn has-popover" data-content="Hello world">Initial trigger</a>
</div>
<a class="btn btn-orange add-one">Add new Popover trigger</a>
$('body').popover({
    selector: '.has-popover',
    trigger: 'hover'
});

$('.add-one').on('click', function () {
    $('#popover-triggers').append("Added trigger")
;});


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

jQuery PlugIn - 커스텀 스크롤 라이브러리

2016.06.19 18:09 Development/JavaScript | jQuery-PlugIn



저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[BootStrap] Tooltip Styling

2016.06.19 18:06 Development/WebUI FrameWork | BootStrap


부트스트랩 툴팁에 대한 스타일링~


HTML

<a href="#" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="Tooltip on bottom" class="red-tooltip">Tooltip on bottom</a>


CSS


.red-tooltip + .tooltip > .tooltip-inner {background-color: #f00;}
.red-tooltip + .tooltip > .tooltip-arrow { border-bottom-color:#f00; }

JS


$(document).ready(function(){
    $("a").tooltip();
});
저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

Include File List

2016.05.17 10:42 Development/PHP


	$includeFiles = get_included_files();
	echo '<h1>Include File List</h1>';
	$i = 1;
	foreach ($includeFiles as $if)
	{
		echo " <p> $i : $if </p> ";
		$i++;
	}


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

디자인 패턴 종류

2016.03.09 17:38 Development/Design Pattern


Gof 디자인 패턴

디자인 패턴의 종류

저작자 표시
신고

'Development > Design Pattern' 카테고리의 다른 글

디자인 패턴 종류  (0) 2016.03.09
디자인 패턴 블로그 링크 모음  (0) 2014.02.27
디자인 패턴  (0) 2014.02.27
현재 0 개의 댓글이 있습니다.
Comment

Blog

[펌] 자바스크립트에서 strict mode를 사용해야 하는 이유

2016.03.09 15:21 Development/JavaScript


출처 : http://blog.aliencube.org/

이 포스트는 Stack Overflow의 What does “use strict” do in JavaScript, and what is the reasoning behind it?의 질문과 답변을 번역한 내용입니다.

자바스크립트에서 use strict는 뭘 하는 것이고, 왜 그걸 써야 하나요?

질문:

최근에 내가 짰던 자바스크립트 코드를 크록포드의 JSLint를 통해 실행시켰더니 아래와 같은 에러가 나타났습니다:

Problem at line 1 character 1: Missing “use strict” statement.

검색을 좀 해봤는데, 몇몇 사람들이 "use strict;" 라인을 그들의 자바스크립트 코드에 추가했다는 것을 알아챘습니다. 저도 이것을 따라서 추가해 봤더니, 위의 에러가 더이상 나타나지 않네요. 구글에서 검색을 해 봤는데, 이것을 추가하는 이유에 대해서 딱히 찾을 수가 없더라구요. 분명히 이것을 통해 브라우저가 자바스크립트를 해석하는 데 영향을 주는 것 같은데, 이걸 사용하면 나타날 수 있는 효과에 대한 것에 대한 것을 전혀 모르겠습니다.

"use strict";가 도대체 무엇이고, 이것이 의미하는 것은 무엇이며, 필요하긴 한 건가요?

현재 쓰이고 있는 브라우저들이 이 "use strict"; 문자열에 대응하는지요, 아니면 향후에 쓰일 것에 대한 대비인가요?


답변: (가장 추천수가 높은 것만을 번역했습니다: 역자 주)

이 문서가 도움이 될 겁니다: John Resig – ECMAScript 5 Strict Mode, JSON, and More

인상적인 부분을 살짝 인용하자면:

Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a “strict” operating context. This strict context prevents certain actions from being taken and throws more exceptions.

Strict Mode는 ECMAScript 5 버전에 있는 새로운 기능으로써, 당신의 프로그램 또는 함수를 엄격한 운용 콘텍스트 안에서 실행시킬 수 있게끔 합니다. 이 엄격한 콘텍스트는 몇가지 액션들을 실행할 수 없도록 하며, 좀 더 많은 예외를 발생시킵니다.

이와 더불어:

Strict mode helps out in a couple ways:

  • It catches some common coding bloopers, throwing exceptions.
  • It prevents, or throws errors, when relatively “unsafe” actions are taken (such as gaining access to the global object).
  • It disables features that are confusing or poorly thought out.

Strict Mode는 몇가지 면에서 도움이 되는데:

  • 흔히 발생하는 코딩 실수를 잡아내서 예외를 발생시킵니다.
  • 상대적으로 안전하지 않은 액션이 발생하는 것을 방지하거나 그럴 때 예외를 발생시킵니다. 예를 들자면 전역객체들에 접근하려 한다거나 하는 것들이겠지요.
  • 혼란스럽거나 제대로 고려되지 않은 기능들을 비활성화시킵니다.

이 strict mode는 파일 전체에 적용시킬 수도 있고, 아니면 특정한 함수 안에서만 적용시킬 수도 있습니다.

위와 같은 방식으로 한다면, 예전의 레거시 코드와 새 코드가 한 파일 안에 섞여 있을 때 도움이 될 것입니다.

아마도 이 "use strict";는 왠지 Perl 에서 온 것 같기도 하네요. 이것을 사용함으로써 오류가 발생할 수 있는 좀 더 많은 부분을 검사할테니, 훨씬 더 적은 에러를 만들 수 있을 겁니다.


역자 추가:

위의 내용과 더불어 Can I use ECMAScript 5 Strict Mode? 페이지를 보면 지원하는 브라우저의 버전을 제공하고 있는데, IE는 이strict mode를 버전 10부터 지원한다. 그렇다고 해서 낮은 버전의 IE를 위해서 쓰지 말아야 하는가 하면 그렇지도 않다. 위에 언급한John Resig의 포스트를 다시 인용하자면:

This means that you can turn strict mode on in your scripts – today – and it’ll have, at worst, no side effect in old browsers.

strict mode를 지금 당장 활성화 시켜야 한다는 것을 의미하고, 이전 브라우저에서는 최악의 경우에라도 아무런 부작용이 없습니다.

따라서, 기존의 자바스크립트 코드에 대해 좀 더 엄격한 검사를 실행시키고 싶다면 문서의 첫 줄에 "use strict";를 추가하고, 기존의 것은 그대로 놔두고, 새로운 코드에 대해서만 추가하고 싶다면 각각의 함수 블록 처음에 추가하는 것이 좋겠다.

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[펌] +function() 의 원리

2016.03.09 15:18 Development/JavaScript


출처 : http://blog.coderifleman.com

오픈소스나 레거시 읽다 보면 종종 +function(){}() 같은 코드를 마주하게됩니다. 이 코드가 혼란스러운 분들을 위해 +function(){}()이 무엇인지 그리고 어떻게 동작하는 것인지 간략히 정리해보겠습니다.

엔진이 함수를 실행하는 방법

함수를 실행하기 위해서는 이름(식별자)이 필요합니다. 이름이 있어야 스코프에서 값을 참조할 수 있기 때문입니다.

예를 들어 function foo(){}를 정의하면 foo(); 구문을 이용해 함수를 실행할 수 있습니다.

함수를 실행하는 흐름<그림 1 함수를 실행하는 흐름>

엔진이 함수 선언문을 만나면 식별자를 관리하는 특별한 집합(EnviromentRecord)에 함수의 이름을 식별자로 넣고 함수 객체를 생성하여 참조합니다. 그리고 함수 실행 구문 중 foo를 만나면 값을 스코프를 통해 가져옵니다. 그 다음 구문이 () 이므로 실행 가능하다면 실행합니다.

만약 스코프에서 식별자를 찾지 못했다면 참조 에러(ReferenceError)를 출력하고, 식별자는 찾았지만 실행할 수 없는 타입이라면 타입 에러(TypeError)를 출력합니다.

not(); // ReferenceError: not is not defined

var foo = 'some';
foo(); // TypeError: foo is not a function

익명함수를 선언하는 방법

function(){} 구문은 이름 없는 “익명함수” 이므로 엔진이 스코프를 통해 값을 가져올 수 있는 방법이 없습니다. 따라서 이 문법을 실행하면 함수의 이름이 필요하다고 문법 오류를 출력합니다.(이 오류 메시지는 브라우저 마다 다릅니다.)

function(){} // SyntaxError: function statement requires a name

이름 없는 함수를 선언할 수 있는 유일한 경우는 함수를 값으로 사용(전달, 대입, 반환, 연산)할 때 입니다.

var func = function(){console.log('ok');}() // ok
some(function(){console.log('ok');})() // ok
return function(){console.log('ok');}() // ok
(function(){console.log('ok');})(); // ok
+function(){console.log('ok');}() // ok
!function(){console.log('ok');}() // ok

자바스크립트 엔진은 단항연산자(-, +, ~, !)를 만나게 되면 function(){}을 값으로 평가합니다. 쉽게 말해 연산을 위해 함수 객체를 생성하게 되고 최종적으로 () 구문을 이용해 실행할 수 있는 것입니다.

+function(){}은 함수 객체를 + 하려고 했으므로 결과로 NaN이 출력됩니다.

+function(){} // NaN

결론

결국 +function(){}()은 익명 함수를 즉시 실행시키기 위해 엔진의 원리를 이용해 만든 편법입니다.

이 원리를 이용한 즉시 실행 함수 중 가장 대중적인 방식은 (function(){})()입니다. ()는 구문 평가를 하는데 평가된 결과가 함수이니 함수 객체를 만들고 이어서 () 구문으로 즉시 실행하는 방식입니다.

(function(){}) // function() 객체
(function(){})() // 즉시 실행

남이 읽을때 혼란스럽지 않아야 좋은 코드라고 할 수 있겠죠. 따라서 비대중적인 +function(){}() 보다 (function(){})() 사용하여 코드를 읽는 개발자가 즉시 실행하는 함수 임을 쉽게 알 수 있도록 하는편이 좋겠습니다.

자바스크립트는 구문이 유연하기 때문에 자신만의 규칙이나 법칙을 만들기 쉽습니다. 하지만 이는 협업시 독이 될 수 있음을 명심해야합니다.

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

지식공유 서비스 - SlimWiki

2016.03.09 12:27 Development/INFRA


순수하게 팀간 지식만 공유할 수 있는게 없을까 하는 생각이 문득들었다.

같이 이야기 하고 지내는 사람들과 순간순간 메신저에서 지식을 공유하고는 했지만 그 내용을 다시 보려면

손가락 골절 스크롤 압박;;

그래서 무작정 구글링 중 Free Forever 란 고마운 문구를 보고 들어갔다. ㅎㅎ



https://slimwiki.com/


장점 - 콜렉션으로 만들어서 페이지를 만들어 해당 지식을 Private &  Public 으로 공유할 수 있다.

단점 - 파일은 이미지만 ~ 


그래도 스타트업이나 스터디 그룹간에는 좋은 서비스인것만은 맞는것 같다.

저작자 표시
신고

'Development > INFRA' 카테고리의 다른 글

지식공유 서비스 - SlimWiki  (0) 2016.03.09
현재 0 개의 댓글이 있습니다.
Comment

Blog

프로젝트 환경 [ Taiga + HipChat + BitBucket + SourceTree + Vagrant (NgineX/PHP/MariaDB) + IDE(PHPStorm) ]

2016.02.29 17:51 Development/PHP


이번에 진행중인 프로젝트에 대한 전체적인 인프라 환경을 구축한 히스토리를 포스팅합니다.

한번씩 테스트 해본 경우, 개인적으로는 굉장히 유익한 경험 이었습니다.

초기 스타트업 같은 경우 전반적인 인프라를 구축하면서도, 소요 비용은 항상 고민적인 요소입니다만,

아래 사항에서 PHPStorm 만 제외하고는 비용절감에 큰 효과를 줄 수 있을것 같습니다.


전체적인 구성은 다음과 같습니다.

 프로젝트 관리 (이슈관리 및 태스크 관리)

 Taiga (https://taiga.io/)

 커뮤니케이션

 HipChat (http://hipchat.com/)

 소스코드 관리 도구 및 저장소

 BitBucket (https://bitbucket.org)

 SourceTree (https://www.sourcetreeapp.com/)

 개발환경(VM)

 VirtualBox + Vagrant 

 개발환경 구성

 OS : Ubuntu 14.04 LTS

 NgineX, MariaDB, PHP ( CodeIgniter )

 IDE

 PHPStorm Or EclipsePDT + xDebug

위 목록 링크 에서부터 순서대로 포스팅 되어있습니다.

Taiga

taiga 는 기본적으로 애자일을 근본으로 합니다. 기본적으로 칸반보드와 스크럼을 선택하게 되어있습니다.


하지만 관리기능에서 모듈을 선택하여 백로그, 이슈 등을 추가할 수 있습니다.

해서 프로젝트를 생성하고 나면, 다음과 같이 대시보드 화면으로 진입합니다.


여기서는 User History + Sprint 등 해서 사용이 가능하도록 되어있습니다. 현재는 스크럼 방식이나 ,


위와 같이 칸반보드및 다른 모듈을 추가 (Enable) 할 수 있습니다.

일단은 자세한 기능은 직접 겪어서 사용해보시는 게 좋을 듯 합니다. JIRA 나 Redmine 을 사용하셨던 분들은 더욱 수월히

사용하실 수 있을거라 생각됩니다.^^;;


해서 이렇게 생성된 프로젝트를 기본으로 확장하면서 부터 빛을 발하는것 같습니다.

이제 HipChat, BitBucket 설정을 시작합니다.

(전 개인적으로 인터페이스 슬랙보다는 힙챗이 더 이쁘다고.. ㅎㅎㅎ)



좌측 아이콘 맨 아래 (ADMIN) 을 선택하시면 위와 같은 화면이 활성화 됩니다.

그럼 INTEGRATIONS 에서는 깃허브, 비트버켓 등의 저장소등과 연동하실 수 있으며, PLUGINS 에서는 슬랙이나 힙챗등을

연동하실 수 있습니다.


순서는 무관하나 먼저 힙챗과 연동시키는 순서입니다.


PLUGINS - HIPCHAT

먼저 힙챗에 접속해 계정이 없으면 만드시고, 있으면 로그인 합니다..



그럼 다음과 같이 웹 또는 앱으로 진행하실 수 있도록 초기화면이 나타납니다.



이제 위에 Launch the web app 버튼을 클릭하시면 다음과 같이 메신저 인터페이스로 전환됩니다.



이제부터 스피드한 포스팅으로..^^;;


최초 ROOM 을 개설합니다. ( Create a Room )


채팅룸이 개설한 후에는 힙챗의 Integrations 기능을 이용해야 합니다.

적용 루트는 대시보드와 채팅 인터페이스에서 모두 가능합니다.




이제 Integrations 설정 화면으로 이동하면, 여러 통합기능들을 확인하실 수 있습니다.


하지만 목록중에서 Taiga 는 등록되어있지 않습니다.

해서 Build your own integration 카드를 클릭해 주세요.


그럼 위와 같이 새로 추가되는 화면으로 전환되며, 여기서 적당한 별칭을 지정해주시면 됩니다.

일단은 Taiga 로 등록.. 


이제 다음과 같이 Taiga 와 힙챗이 연동될 수 있도록 포스팅 URL 및 정보가 나타납니다.


이제 위에서 포스팅 URL ( Send messages to this room by posting to this URL ) 을 복사하여,

다시 Taiga 로 이동하여 HipChat webhook url 에 붙여넣고 Save 해 주세요.



그리고 Test 버튼을 클릭하시면! 

이렇게 Taiga 에서 힙챗 채팅룸으로 테스트 메세지를 보낸 것을 확인하실 수 있습니다.

( 어휴~ 포스팅 빡셉니다;;; )


참 HipChat 은 스마트폰 앱으로도 있습니다. 여러모로 참 유용하죠.


이제 이렇게 까지 Taiga 와 HipChat 연동은 되었구요. 이제 추가 테스트로 Task 를 등록하고, 힙챗까지 Task 등록 메세지까지

자동 발송되는것을 확인합니다.





요만하면 지금까지만으로나 꽤나 쓸만하지요? ㅎㅎ

위에 예제에서와 같이 저장소를 등록하고, 소스트리등을 이용하여 소스코드를 Push 하면 위처럼 힙챗 채팅룸으로 메세지가 보내집니다.

그럼 하나의 작업으로 여겨졌던 불필요한 커뮤니케이션 시간도 단축시킬 수 있으며,

작업 히스토리를 더운 간편하게 확인할 수 있겠지요.


이제 저장소 (BitBucket) 을 연결하는게 남았습니다.

하지만 비트버켓에서 계정등록/저장소 생성까지 캡쳐포스팅은 오바에 가까운 생각이 들어서요.

일단은 패스합니다. 이미 구글링이나 아니면, 그냥 계정 생성하보시면 한눈에 아실 수 있을거란 생각이 들기도 합니다.


해서 힙챗과 연동하는것만요.^^;; 



BitBucket 에서 HipChat 연결. Connection HipChat 버튼을 누르면 계정이 연동되도록 힙챗 로그인 페이지로 이동합니다.



HipChat 에서 BitBucket Repository 선택


이제 프로젝트 관리와 저장소와 커뮤니케이션까지의 구성은 완료되었습니다.

Taiga 와 Asana 연동으로 일정관리까지 한눈에 보고 싶었으나 실패했습니다. ㅎㅎㅎ

만일 캘린더및 전체적인 일정까지 한눈에 보실 수 있도록 추가 PlugIn 및 Integration 연동 경험 공유해주시면 진심으로

감사드리겠습니다. ^^


이제 Virtual Box 와 Vagrant 로 Ubuntu 기반 NgineX, MariaDB, PHP ( CodeIgniter ) 환경구성을 포스팅하면,

맨 처음 언급한 전체적인 프로젝트 관리및 개발 환경은 완성 되었지 싶습니다.

추후 포스팅까진 힘들더라도 훌륭하신 개발자님들의 포스팅 링크로 대체할 수 도 있음을 미리.. ㅎㅎㅎㅎㅎ


초기 스타트 업 팀 및 이미 유지되고 있는 조직에서 프로젝트 관리 프로세스 및 개발환경 구성은 매우 중요한 요소라는

생각이 듭니다. 하지만 더욱 이와 맞물리게 더욱 중요하건 기획 과 프로젝트 관리 가 얼마나 정확하게 동기화 되고 있느냐가

핵심적인 요소가 아니지 싶네요. 짧은 식견에 말많은 포스팅이라 민망하네요.

다음은 JIRA 를 열심히 훑어봐서 아틀라시안 풀셋 연동 포스팅 한번 해봐야겠습니다. ㅎㅎㅎ


아;; 힘든 포스팅이었습니다. 댓글 부탁합니다. ㅎㅎㅎㅎㅎㅎㅎㅎㅎ


저작자 표시
신고
현재 1 개의 댓글이 있습니다.

XO 2016.03.18 20:10 신고

좋은 포스팅 감사합니당~


Comment

Blog

jquery attribute (속성 존재여부)

2016.01.21 18:16 Development/JavaScript | jQuery


Navtive

$(this)[0].hasAttribute('name');

jQuery

$(this).is('[name]');//boolean
$(this).filter('[name="jkun"]');

개인적으로는 is 메소드를 더 선호

저작자 표시
신고

'Development > JavaScript | jQuery' 카테고리의 다른 글

jquery attribute (속성 존재여부)  (0) 2016.01.21
$.when()  (0) 2015.04.13
노드 다루기  (0) 2014.12.29
jQuery Form Reset  (0) 2014.05.13
window.resize 크로스 브라우징  (0) 2013.11.15
현재 0 개의 댓글이 있습니다.
Comment

Blog

[Study][Node.Js] 1. Hello World 와 웹서버 만들기.

2015.12.14 17:55 Development/JavaScript | Node.js


기본적인 환경설정은 제외하고 스터디 포스팅을 합니다. 아주 스피드하게 스터디 하기위한 포스팅입니다.

가장 기본적인 예제가 된다.

1.1. Hello World

  1. app.js 파일을 만든다.
  2. 로그를 찍는 자바스크립트 코드를 작성한다.
    console.log('Hello World');

  3. 실행한다.
    node app.js


이렇게 아주 간단하게 nodejs 파일을 만들어 실행까지 시키는 과정이다.

다음은 웹서버를 만들어 구동까지 시켜보기로 한다.


1.2. createServer

  1. app.webserver.js 파일을 만든다.

    var http = require('http');
    
    var server = http.createServer(function(request, response){
            response.writeHead(200, { 'Content-Type' : 'text/plain' });
            response.end('Hello World');
    });
    
    server.listen(8000);


  2. 브라우저에서 localhost:8000 으로 확인한다.


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

JBOSS 외부접근 설정

2015.12.10 14:06 Development/Java


출처 : http://nockarm.blogspot.kr/2013/10/jboss-as-7.html


JBOSS_HOME/standalone/configuration/standalone.xml

<interface name="public">
    <inet-address value="${jboss.bind.address:127.0.0.1}"/>
</interface>

부분을 아래와 같이 변경한다.

<interface name="public">
    <any-address/>
</interface>

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

반응형 웹과 적응형 웹

2015.11.26 12:24 Development


단순 인지하고 지나치기엔 중요하게 고민해봐야하는 문제 인듯 싶어서,

오랜만에 포스팅을 한다.


반응형 웹과 적응형 웹.

"적응형 웹" 이란 용어는 어제 알았으나 이미 개발경험으로 인지하고 있었다. 그리고 이미 많은 IT엔지니어들이

급변하는 스마트 환경에 따른 대응책으로 이미 일선에서는 사용되고 있었던 방법으로 생각된다.

일전에 겪었던 데스크탑 브라우저 기준으로 개발하였으나, 모바일에서 표시할때는 반응하여 인터페이스를 변경하게 하는.

하지만 이미 데스크탑 기준과 동일한 데이터처리와 로딩을 끝낸 상태에서 변경하므로,

속도가 늦어짐에 매우 난감했었던 기억이 떠올랐다.


하지만 어느 순간부터 부트스트랩에 편리함에 익숙해지다 보니, 

모바일 퍼스트와 원소스 멀티가 유용하다는 생각으로만 "반응형 웹" 을 추구해야한다고만

생각했던 스스로에게 의문을 던지며 적재적소가 필요하단 생각을 하게 되었다.


먼저 목표는 동일하다.

웹서비스가 다양한 디바이스 해상도에서 원활한 경험을 제공하기 위함이다.

그러나 인터페이스 설계와 기획에서 많은 차이가 있을 수 있다. 또한 개념적으로 다르다.


다음은 내가 이해하고 있는 반응형과 적응형에 대한 개념이다.


반응형 웹 Responsive Web

미디어 쿼리를 이용하여 디바이스 화면의 크기를 확인하고 그리드를 기반으로 화면 크기의 변화에 따라 반응하게 된다.

단일 마크업이지만 복수의 스타일로

특정한 패턴을 가진 그리드로 반응하여 컨텐츠를

제공한다.

적응형 웹 Adaptive Web

서버나 클라이언트에서 서비스에 접근한 디바이스를

식별하여 해당 디바이스에 최적화된 마크업과 컨텐츠 정보를 제공한다.

또한 이에 걸맞는 동작이 적용되어진다.

이러한 특성으로 사용자는 보다 빠른 속도로 서비스를 이용할 수 있게 된다.



이제 다시 원점으로 위 와 같은 특징으로 개발중인 프로젝트에서 적용여부에 대한 문제로 다시 돌아 오자면,

프로젝트가 의미하고 가지는 성격이 제일 중요한 것 같다는 생각이 든다.


결국엔 "적재적소" 판단이 중요하다는 건데

가령 다음이나 네이버처럼 포털사이트에서는 "적응형" 이 걸맞다는 생각이 든다.

그 포털사이트 메인처럼 많은 양의 컨텐츠를 포함하고 있는데, 이 만큼을 동일한 양과 리소스를 로드하여 로딩이 끝난 후,

디바이스 반응하여 다르게 표시한다는게 단순하게만 생각해봐도 사용자 입장에서는 개거품물기 십상이란 생각이다.

이에 반해 많은 양의 컨텐츠를 포함하지 않고, 적당한 컨텐츠와 심플한 인터페이스라면 오히려 반응형이 더 어울릴것 같다.

해서 단순 테스트로 페이스북과 네이버, 다음등을 브라우저 창의 크기를 줄여 테스트하면 크기에 반응하지 않는 다는 것을

확인할 수 있다. 

아래는 창을 줄인 서비스의 캡쳐다.




다음은 반응형과 적응형에 대한 이해를 돕는 포스팅 들입니다.




저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

Visual Studio Code 를 이용한 node.js 환경 구성

2015.09.25 12:39 Development/JavaScript | Node.js




MEAN Stack 환경이 마이크로소프트에서 나온 Visual Studio Code 잘 맞는다는 이야기 귀동냥에 고대로 실행함 해본다.

일단 첫번째로는 Node.js 와 Express Framework 까지만 환경 설정하는걸로.

이 웹사이트의 매뉴얼대로만 해도 고대로 잘 됨.


설치환경 맥,맥OS,Mac OS X,맥 요시미테,Mac OS X Yosemite



순서

  1. node.js 설치 / Visual Studio Code 설치


  2. 익스프레스 설치
    npm install -g -express-generator
    express myExpressApp

    위 과정까지의 화면입니다.


    아 설치시에는 sudo 권한 필요합니다.

    cd myExpressApp
    npm install
    npm start


    아. 이것도 sudo 권한 필요합니다. ㅎㅎㅎ

    다음은 실행된 화면입니다.


    여기까지는 기본적인 Node.js 와 express 를 설치하는 과정이었습니다.
    이제 이 설치된 환경을 Visual Studio Code 에 적용시키는 과정입니다.^^


  3. TSD(TypeScript Definition Manager) 설치

    npm install -g tsd



    tsd query node --action install
    tsd query express --action install




    이제 전반적인 준비는 마쳤다. 


  4. Visual Studio Code 실행



  5. 이제 작업하면서 생성된 myExpressApp 디렉토리를 Visual Studio Code 에서 연결.





  6. 위와 같이 디렉토리를 선택하여 파일을 불러오면 이제 디버그 탭으로 이동하여 Lanch를 실행하여 줍니다.




  7. 실행화면




  8. 정상적으로 실행이 되었으나 디버깅 밑에 깔끔하게 아닌 느낌표가 나타나 있다.




    이제 이 부분은 디렉토리 설정 문제인데, app.js 파일 최 상단에 코드 한줄 추가 해준다.

    /// <reference path="./typings/node/node.d.ts"></reference>


이렇게 Visual Studio Code 에 node.js + express 환경을 설정하였다. 일단은 무작정 따라하기 였으므로,
추후 AngularJS 와 데이터베이스를 적용하는 것 까지 "시간봐서" 포스팅 해야겠다. ㅋㅋ



저작자 표시
신고
현재 1 개의 댓글이 있습니다.

ㅇㅅㅇ 2015.09.25 20:58 신고

좋은 포스트 감사합니다 회사에서 MEAN 세미나로 여럿이 모여 환경구성 같이 했네요.
다음 시간이 되실때 포스트가 궁금합니다. ㅎㅎ


Comment

Blog

[펌] node.js 장단점

2015.09.23 17:16 Development/JavaScript | Node.js


출처 : 조대협의 블로그 - node.js 의 장단점




Node.JS에 대해서.

Node.js는 구글의 크롬 V8 자바스크립트 엔진을 기반으로 한, 고성능 네트워크 서버이다.

Single Thread 기반의 Event Loop를 기반으로 하고 있으며, File, Network 등에 대해서 비동기 IO 처리를 하는 서버 미들웨어이다.

근래에 들어서 다시금 node.js 가 많은 주목을 받고 있는데, Paypal이나 Groupon과 같은 굴지의 서비스 기업들이 내부 서버 플랫폼을 node.js로 전환하는 것이 계기가 되고 있다.

Node.js의 장점

Node.js는 일반적으로 성능이 매우 빠른 고성능 서버로 알려져 있다. 이 고성능은 자바스크립트등에서 오는 것이 아니라, node.js의 기본적인 구조인 Single Thread기반의 비동기 IO 처리에서 온다. 하나의 쓰레드가 request를 받으면, 처리를 하고, File IO나 Network 처리 (데이타 베이스 접근)등이 있을 경우에는 IO 요청을 보내 놓고, 작업을 처리하다가, IO  요청이 끝나면 이벤트를 받아서 처리하는 이벤트 방식을 사용한다. 이로 인해서, CPU가 IO 응답을 기다리는 시간이 필요 없고, 대부분의 연산 작업에 사용되기 때문에 높은 효용성을 가질 수 있으며, 특히 하나의 Thread로 여러개의 요청을 처리하는 구조로 되어 있기 때문에, C10K 문제를 처리할 수 있는데 아주 최적화 되어 있다.

사실 node.js 자체의 구조로만 본다면 자바 보다 성능이 빠르다고 이야기할 수 없다. 이는 어디까지나 시나리오에 따라서이다. 상대적으로 CPU Intensive한 작업이 없고,많은 Connection을 동시에 처리해야 하는 시나리오 대해서는 아무래도  node.js의 성능이 압도적으로 높다.

아래의 그림을 보면 (참고: http://bcho.tistory.com/865)




데이타베이스 트랜젝션이 많은 애플리케이션의 경우 WAS(resin)+MySQL 구조가 nodejs + MySQL 구조보다 많이 빠름을 볼 수 있다.

 

그럼에도 불구하고 node.js가 인기가 있는 비결은 무엇일까? 이유는 javascript 에서 오는 생산성의 향상이라고 본다. HTML5 등으로 인해서 웹 개발에서 자바스크립트의 비중이 높아지게 되었고, 단순한 UI 수준만 필요하던 프론트앤드 개발자들에게도 수준 높은 개발 능력이 요구 되면서, 자바스크립트  기반의 프로그래밍 능력이 많이 향상되어 있던 차에, 백엔드에 대한 성역(?)에 대한 높은 접근 장벽이 있는 상태에서 node.js의 등장은 이러한 백엔드에 대한 진입 장벽을 깨버린 계기가 되었다. 자바스크립트 기술을 가지고 서버 백엔드를 개발할 수 있게 되었으며, 기존의 자바 프로그래밍등과 같은 서버 개발 언어보다 훨씬 높은 생산성을 보여준다 실제로 REST+DB API를 자바로 개발하였을 경우, 본인의 경우 이부분에 대해서 숙련이 되어 있음에도 불구하고, 30분~1시간정도가 소요되는 반면에, node.js를 공부해 가는 과정임에도 불구하고, 간단한 REST API의 경우 개발에 10분정도면 개발할 수 가 있었다.

Paypal도  node.js로 전환한 이유에 대해서 살펴보면, 프론트엔드와 백엔드 기술을 통합하여 생산성을 높이기 위함이라고 이야기 하고 있다.

본인이 생각하는 대단한 장점중의 하나는 node.js의 모듈중의 하나인 socket.io이다. 웹 개발이 점점 양방향이 되어가면서, 클라이언트와 connection을 맺고, 여기에 push를 보낼 수 있는 기술이 필요하다. HTML 5의 웹소켓이 좋은 기술이기는 하지만, 브라우져 호환성의 문제가 있는데 반해서, socket.io는 웹소켓을 포함한, AJAX 롤폴링등 여러개의 웹푸쉬를 abstraction하여, 브라우져에 상관 없이 개발자가 쉽게 socket.io API만 이용하면, 푸쉬를 구현할 수 있게 해주며, 싱글 쓰레드 기반의 멀티 플랙싱을 기반으로, 대용량 사용자에 대한 푸쉬 처리를 가능하게 해준다. (WAS는 Thread 수만큼 밖에 동시 connection 처리를 할 수 없기 때문에, node.js의 이 구조가 이런 대량 동시 Connection 처리에는 유리하다.)


또 하나의 장점을 들자면, 자바스크립트의 경우에는 멀티 쓰레드의 개념이 없다. 서버 프로그램에서 쓰레드간의 동기화 처리등이 중요하고 또한 복잡한점 중의 하나인데,이러한 문제 자체를 제거 함으로써 서버 프로그래밍 자체를 매우 단순하게 만들어 버린 것이다.

운영 관점에서도 node.js의 restart 시간이 1sec 미만이기 때문에, 빠른 배포나 업그레이드 작업이 가능하다.

Node.js를 사용할 때 주의할점

Node.js는 자바스크립트와 Single Thread 모델에서 오는 장점이 있는 반면 여기서 오는 단점 역시 만만하지 않다. 기본적으로 Single thread 모델이기 때문에, 하나의 작업 자체가 시간이 많이 걸리면, 전체 시스템의 성능이 아주 급격하게 떨어진다. 그래서, 가벼운 (CPU를 많이 사용하지 않는) 작업 위주로 개발이 되어야 하고, 자바스크립트에서 오는 문제점은 자바나 다른 언어에 비해서 명시성이 떨어지기 때문에, 코드의 가독성이 자바언어에 비해서 상대적으로 낮기 때문에 유지 보수가 어려워질 수 있으며,이벤트 Call back 을 형태를 기준으로 하기 때문에, 이러한 call back이 중첩될 경우 (이를 callback hell이라고 한다.) 코드의 가독성이 급격하게 떨어진다. (이를 해결하기 위한 프레임웍들이 있다.)

또한 자바스크립트와 같은 스크립트 언어의 특성상 해당 코드가 수행이 되어야 코드에서 에러가 나는지를 확인할 수 있고, 에러가 날 경우 프로세스 자체가 내려가기 때문에, node.js 를 사용하는 사람들에게 가장 많이 들려오는 단점이 “잘 죽어요.”이다.

물론 이부분은 watch dog이나 domain API 를 이용하면, 상당 부분 해결이 가능하지만, 이를 충분히 인지하고 설계에 사전 반영을 해야 하며, 발생가능한 에러를 잡아내기 위해서 개발 기간중에 테스트에 많은 집중을 해야한다.

다른 문제점으로는 single thread 모델이기 때문에, 멀티 코어 머신에서 CPU 사용을 최적화할 수 없다는 문제가 있다. 하나의 쓰레드는 하나의 물리적 코어밖에 사용하지 못하기 때문제 코어가 많은 시스템이라도 성능이 올라가지 않는다 그래서 설계시, Cluster 모듈등을 이용하여, 하나의 서버에서 여러개의 노드 프로세스를 사용하는 모델을 가지고 가야 하며, 또한, 세션등을 공유할 경우, 세션 공유용 redis와 같은 부가적인 인프라가 필요하다.

또한 V8 엔진을 기반으로 하는데, 이 V8 엔진은 Garbage collection  기반의 메모리 관리를 하기 때문에, GC시 CPU 사용률이 Spike를 치면서 순간적으로 서버를 멈추게할 수 있다는 문제점을 가지고 있다.

확장 모듈 관점에서도, Single Thread기반의 비동기 IO를 지원해야 하기 때문에, 노드 전용 모듈을 사용해야 하는데, 예를 들어 MySQL Connection Pool과 같은 경우에도 기존의 자바 기반의 dbcp와 같은 connection pool에 비해서 고급 기능이 적기 때문에 세밀한 High availability 구현등에는 한계를 가지고 있다.

그리고, node.js를 사용한 사람들에게서 들려오는 가장 많은 경험담은 자바스크립트 자체를 배우는 것보다는 프로그래밍 컨셉을 기존의 컨셉에서 Event 기반의 프로그래밍 컨셉으로 전환하는데 많은 시간이 걸렸다고 한다. 코드를 순차적으로 실행하는 것이 아니라, 비동기 방식으로 이벤트를 보내놓고, 그 응답에 대한 이벤트가 오면 핸들러를 통해서 처리 하는 형식이기 때문에, 기존 서버 프로그래밍 모델과는 많은 차이를 보인다.

[3/10 추가 내용] node.js의 async io 구현 부분은 os의 multiplexing library를 사용한다. 윈도우즈의 경우 iocp를, Unix 계열의 경우에는 select, epoll,kqueue등을 사용하는데, 각각의 장단점과 성능적인 차이를 가지고 있다. 그래서, 운영하고자 하는 OS에 따라서 OS 설정이 다리고, 또한 multiplexing 라이브러리에 따라서 성능이 차이가 나기 때문에, node.js 운영전에, 운영할 OS에서 테스트 및 튜닝을 해보기를 권장한다. 


개발 관점에서는 빠르고 쉬운 장점이 있지만, 반대로 운영 관점에서는 테스트, 장애 대응, 디버깅등에 대해서는 신경써야 할 부분이 훨씬 더 많다.

 

이런 단점에도 불구하고 탁월한 생산성과 이미 상당히 커진 커뮤니티와 에코 시스템에서 오는 장점 때문에, 널리 사용되고 있고 앞으로도 널리 사용될 것으로 보인다.

기존의 자바, 스프링,WAS와 같은 생태계가 node.js 기반으로 자바스크립트쪽에서 새롭게 생성되고 있는 느낌이라고나 할까?

 

지금 아직도 node.js를 공부중에 있습니다. 나중에 끝나면 전체적으로 한번 정리해서 튜토리얼을 올리도록 하겠습니다.


#1 – node.js의 소개와 내부 구조 http://bcho.tistory.com/881

#2 - 설치와 개발환경 구축 http://bcho.tistory.com/884

#3 - Event,Module,NPM  http://bcho.tistory.com/885

#4 - 웹 개발 프레임웍 Express 1/2 - http://bcho.tistory.com/887

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[펌] 브라우저는 어떻게 동작하는가?

2015.09.23 12:26 Development/Web Browser


출처 : 네이버 - 브라우저는 어떻게 동작하는가?


브라우저 작동방식은 아주 디테일까지는 아니더라도 포지션이 웹개발자라면 어느 정도는 알아야 한다고 생각한다.

해서 관련 자료 찾던중 네이버에서 아주 깔끔하게 정리되어있는 자료가 있어 스크랩한다.


이 글은 이스라엘 개발자 탈리 가르시엘(Tali Garsiel)이 html5rocks.com에 게시한 "How Browsers WorkBehindthe scenes of modern web browsers"를 번역한 글입니다. 탈리 가르시엘은 몇 년간 브라우저 내부와 관련된 공개 자료를 확인하고, C++ 코드 수백만 줄 분량의 WebKit이나 Gecko 같은 오픈소스 렌더링 엔진의 소스 코드를 직접 분석하면서 어떻게 브라우저가 동작하는지 파악했습니다.

소개

브라우저는 아마도 가장 많이 사용하는 소프트웨어일 것이다. 이 글을 통해 브라우저가 어떻게 동작하는지 설명하려고 한다. 이 글을 읽고 나면, 브라우저 주소 창에 naver.com을 입력했을 때 어떤 과정을 거쳐 네이버 페이지가 화면에 보이게 되는지 알게 될 것이다.

이 글에서 설명하는 브라우저

최근에는 인터넷 익스플로러, 파이어폭스, 사파리, 크롬, 오페라 이렇게 다섯 개의 브라우저를 많이 사용하지만 나는 파이어폭스, 크롬, 사파리와 같은 오픈소스 브라우저를 예로 들 것이다. 사파리는 부분적으로 오픈소스이다. StatCounter 브라우저 통계에 의하면 2012년 3월 현재 파이어폭스, 사파리, 크롬의 점유율은 62.57%에 달한다. 오픈소스 브라우저가 시장의 상당 부분을 차지하게 된 것이다.

브라우저의 주요 기능

브라우저의 주요 기능은 사용자가 선택한 자원을 서버에 요청하고 브라우저에 표시하는 것이다. 자원은 보통 HTML 문서지만 PDF나 이미지 또는 다른 형태일 수 있다. 자원의 주소는 URI(Uniform Resource Identifier)에 의해 정해진다.

브라우저는 HTML과 CSS 명세에 따라 HTML 파일을 해석해서 표시하는데 이 명세는 웹 표준화 기구인 W3C(World Wide Web Consortium)에서 정한다. 과거에는 브라우저들이 일부만 이 명세에 따라 구현하고 독자적인 방법으로 확장함으로써 웹 제작자가 심각한 호환성 문제를 겪었지만 최근에는 대부분의 브라우저가 표준 명세를 따른다.

브라우저의 사용자 인터페이스는 서로 닮아 있는데 다음과 같은 요소들이 일반적이다.

  • URI를 입력할 수 있는 주소 표시 줄
  • 이전 버튼과 다음 버튼
  • 북마크
  • 새로 고침 버튼과 현재 문서의 로드를 중단할 수 있는 정지 버튼
  • 홈 버튼

브라우저의 사용자 인터페이스는 표준 명세가 없음에도 불구하고 수 년간 서로의 장점을 모방하면서 현재에 이르게 되었다. HTML5 명세는 주소 표시줄, 상태 표시줄, 도구 모음과 같은 일반적인 요소를 제외하고 브라우저의 필수 UI를 정의하지 않았다. 물론 파이어폭스의 다운로드 관리자와 같이 브라우저에 특화된 기능도 있다.

브라우저의 기본 구조

브라우저의 주요 구성 요소는 다음과 같다.(1.1)

  1. 사용자 인터페이스 - 주소 표시줄, 이전/다음 버튼, 북마크 메뉴 등. 요청한 페이지를 보여주는 창을 제외한 나머지 모든 부분이다.
  2. 브라우저 엔진 - 사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어.
  3. 렌더링 엔진 - 요청한 콘텐츠를 표시. 예를 들어 HTML을 요청하면 HTML과 CSS를 파싱하여 화면에 표시함.
  4. 통신 - HTTP 요청과 같은 네트워크 호출에 사용됨. 이것은 플랫폼 독립적인 인터페이스이고 각 플랫폼 하부에서 실행됨.
  5. UI 백엔드 - 콤보 박스와 창 같은 기본적인 장치를 그림. 플랫폼에서 명시하지 않은 일반적인 인터페이스로서, OS 사용자 인터페이스 체계를 사용.
  6. 자바스크립트 해석기 - 자바스크립트 코드를 해석하고 실행.
  7. 자료 저장소 - 이 부분은 자료를 저장하는 계층이다. 쿠키를 저장하는 것과 같이 모든 종류의 자원을 하드 디스크에 저장할 필요가 있다. HTML5 명세에는 브라우저가 지원하는 ' 데이터 베이스'가 정의되어 있다.

brouser1

그림 1 브라우저의 주요 구성 요소

크롬은 대부분의 브라우저와 달리 각 탭마다 별도의 렌더링 엔진 인스턴스를 유지하는 것이 주목할만하다. 각 탭은 독립된 프로세스로 처리된다.

렌더링 엔진

렌더링 엔진의 역할은 요청 받은 내용을 브라우저 화면에 표시하는 일이다.

렌더링 엔진은 HTML 및 XML 문서와 이미지를 표시할 수 있다. 물론 플러그인이나 브라우저 확장 기능을 이용해 PDF와 같은 다른 유형도 표시할 수 있다. 그러나 이 장에서는 HTML과 이미지를 CSS로 표시하는 주된 사용 패턴에 초점을 맞출 것이다.

렌더링 엔진들

이 글에서 다루는 브라우저인 파이어폭스와 크롬, 사파리는 두 종류의 렌더링 엔진으로 제작되었다. 파이어폭스는 모질라에서 직접 만든 게코(Gecko) 엔진을 사용하고 사파리와 크롬은 웹킷(Webkit) 엔진을 사용한다.

웹킷은 최초 리눅스 플랫폼에서 동작하기 위해 제작된 오픈소스 엔진인데 애플이 맥과 윈도우즈에서 사파리 브라우저를 지원하기 위해 수정을 가했다. 더 자세한 내용은 webkit.org를 참조한다.

동작 과정

렌더링 엔진은 통신으로부터 요청한 문서의 내용을 얻는 것으로 시작하는데 문서의 내용은 보통 8KB 단위로 전송된다.

다음은 렌더링 엔진의 기본적인 동작 과정이다.

brouser2

그림 2 렌더링 엔진의 동작 과정

렌더링 엔진은 HTML 문서를 파싱하고 "콘텐츠 트리" 내부에서 태그를 DOM 노드로 변환한다. 그 다음 외부 CSS 파일과 함께 포함된 스타일 요소도 파싱한다. 스타일 정보와 HTML 표시 규칙은 "렌더 트리"라고 부르는 또 다른 트리를 생성한다.

렌더 트리는 색상 또는 면적과 같은 시각적 속성이 있는 사각형을 포함하고 있는데 정해진 순서대로 화면에 표시된다.

렌더 트리 생성이 끝나면 배치가 시작되는데 이것은 각 노드가 화면의 정확한 위치에 표시되는 것을 의미한다. 다음은 UI 백엔드에서 렌더 트리의 각 노드를 가로지르며 형상을 만들어 내는 그리기 과정이다.

일련의 과정들이 점진적으로 진행된다는 것을 아는 것이 중요하다. 렌더링 엔진은 좀 더 나은 사용자 경험을 위해 가능하면 빠르게 내용을 표시하는데 모든 HTML을 파싱할 때까지 기다리지 않고 배치와 그리기 과정을 시작한다. 네트워크로부터 나머지 내용이 전송되기를 기다리는 동시에 받은 내용의 일부를 먼저 화면에 표시하는 것이다.

동작 과정 예

brouser3

그림 3 웹킷 동작 과정

 brouser4

그림 4 모질라의 게코 렌더링 엔진 동작 과정(3.6)

웹킷과 게코가 용어를 약간 다르게 사용하고 있지만 동작 과정은 기본적으로 동일하다는 것을 그림 3과 그림 4에서 알 수 있다.

게코는 시각적으로 처리되는 렌더 트리를 "형상 트리(frame tree)"라고 부르고 각 요소를 형상(frame)이라고 하는데 웹킷은 "렌더 객체(render object)"로 구성되어 있는 "렌더 트리(render tree)"라는 용어를 사용한다. 웹킷은 요소를 배치하는데 "배치(layout)" 라는 용어를 사용하지만 게코는 "리플로(reflow)" 라고 부른다. "어태치먼트(attachment)"는 웹킷이 렌더 트리를 생성하기 위해 DOM 노드와 시각 정보를 연결하는 과정이다. 게코는 HTML과 DOM 트리 사이에 "콘텐츠 싱크(content sink)"라고 부르는 과정을 두는데 이는 DOM 요소를 생성하는 공정으로 웹킷과 비교하여 의미있는 차이점이라고 보지는 않는다.

파싱과 DOM 트리 구축

파싱 일반

파싱은 렌더링 엔진에서 매우 중요한 과정이기 때문에 더 자세히 다룰 필요가 있다. 파싱에 대한 간단한 소개로 시작한다.

문서 파싱은 브라우저가 코드를 이해하고 사용할 수 있는 구조로 변환하는 것을 의미한다. 파싱 결과는 보통 문서 구조를 나타내는 노드 트리인데 파싱 트리(parse tree) 또는 문법 트리(syntax tree)라고 부른다.

예를 들면 2+3-1과 같은 표현식은 다음과 같은 트리가 된다.

brouser5

그림 5 수학 표현식을 파싱한 트리 노드

문법

파싱은 문서에 작성된 언어 또는 형식의 규칙에 따르는데 파싱할 수 있는 모든 형식은 정해진 용어와 구문 규칙에 따라야 한다. 이것을 문맥 자유 문법이라고 한다. 인간의 언어는 이런 모습과는 다르기 때문에 기계적으로 파싱이 불가능하다.

파서-어휘 분석기 조합

파싱은 어휘 분석과 구문 분석이라는 두 가지로 구분할 수 있다.

어휘 분석은 자료를 토큰으로 분해하는 과정이다. 토큰은 유효하게 구성된 단위의 집합체로 용어집이라고도 할 수 있는데 인간의 언어로 말하자면 사전에 등장하는 모든 단어에 해당된다.

구문 분석은 언어의 구문 규칙을 적용하는 과정이다.

파서는 보통 두 가지 일을 하는데 자료를 유효한 토큰으로 분해하는 어휘 분석기(토큰 변환기 라고도 부름)가 있고 언어 구문 규칙에 따라 문서 구조를 분석함으로써 파싱 트리를 생성하는 파서가 있다. 어휘 분석기는 공백과 줄 바꿈 같은 의미 없는 문자를 제거한다.

brouser6

그림 6 문서 소스로부터 파싱 트리를 만드는 과정

파싱 과정은 반복된다. 파서는 보통 어휘 분석기로부터 새 토큰을 받아서 구문 규칙과 일치하는지 확인한다. 규칙에 맞으면 토큰에 해당하는 노드가 파싱 트리에 추가되고 파서는 또 다른 토큰을 요청한다.

규칙에 맞지 않으면 파서는 토큰을 내부적으로 저장하고 토큰과 일치하는 규칙이 발견될 때까지 요청한다. 맞는 규칙이 없는 경우 예외로 처리하는데 이것은 문서가 유효하지 않고 구문 오류를 포함하고 있다는 의미다.

변환

파서 트리는 최종 결과물이 아니다. 파싱은 보통 문서를 다른 양식으로 변환하는데 컴파일이 하나의 예가 된다. 소스 코드를 기계 코드로 만드는 컴파일러는 파싱 트리 생성 후 이를 기계 코드 문서로 변환한다.

brouser7

그림 7 컴파일 과정

파싱 예

그림 5에서는 수학 표현식을 파싱 트리로 만들어 보았다. 간단한 수학 언어를 정의하고 파싱 과정을 살펴 보자.

어휘: 수학 언어는 정수, 더하기 기호, 빼기 기호를 포함한다.

구문:

  1. 언어 구문의 기본적인 요소는 표현식, 항, 연산자이다.
  2. 언어에 포함되는 표현식의 수는 제한이 없다.
  3. 표현식은 "항" 뒤에 "연산자" 그 뒤에 또 다른 항이 따르는 형태로 정의한다.
  4. 연산자는 더하기 토큰 또는 빼기 토큰이다.
  5. 정수 토큰 또는 하나의 표현식은 항이다.

입력된 값 2+3-1을 분석해 보자.

규칙에 맞는 첫 번째 부분 문자열은 2이다. 규칙 5번에 따르면 이것은 하나의 항이다. 두 번째로 맞는 것은 2+3 인데 이것은 항 뒤에 연산자와 또 다른 항이 등장한다는 세 번째 규칙과도 일치한다. 입력 값의 마지막 부분까지 진행하면 또 다른 일치를 발견할 수 있다. 2+3은 항과 연산자와 항으로 구성된 하나의 새로운 항이라는 것을 알고 있기 때문에 2+3-1은 하나의 표현식이 된다. 2++은 어떤 규칙과도 맞지 않기 때문에 유효하지 않은 입력이 된다.

어휘와 구문에 대한 공식적인 정의

어휘는 보통 정규 표현식으로 표현한다. 예를 들면 언어는 다음과 같이 정의될 것이다.

INTEGER : 0|[1-9][0-9]*  
PLUS : +  
MINUS : -  

보시다시피 정수는 정규 표현식으로 정의한다.

구문은 보통 BNF 라고 부르는 형식에 따라 정의한다. 언어는 다음과 같이 정의될 것이다.

expression := term operation term  
operation := PLUS | MINUS  
term := INTEGER | expression  

문법이 문맥 자유 문법이라면 언어는 정규 파서로 파싱할 수 있다. 문맥 자유 문법을 쉽게 말하면 완전히 BNF로 표현 가능한 문법이다. 공식적인 정의는 위키백과의 문맥 자유 문법을 참조한다.

파서의 종류

파서는 기본적으로 하향식 파서와 상향식 파서가 있다. 하향식 파서는 구문의 상위 구조로부터 일치하는 부분을 찾기 시작하는데 반해 상향식 파서는 낮은 수준에서 점차 높은 수준으로 찾는다.

두 종류의 파서가 예제를 어떻게 파싱하는지 살펴보자.

하향식 파서는 2+3과 같은 표현식에 해당하는 높은 수준의 규칙을 먼저 찾는다. 그 다음 표현식으로 2+3-1을 찾을 것이다. 표현식을 찾는 과정은 일치하는 다른 규칙을 점진적으로 더 찾아내는 방식인데 어쨌거나 가장 높은 수준의 규칙을 먼저 찾는 것으로부터 시작한다.

상향식 파서는 입력 값이 규칙에 맞을 때까지 찾아서 맞는 입력 값을 규칙으로 바꾸는데 이 과정은 입력 값의 끝까지 진행된다. 부분적으로 일치하는 표현식은 파서 스택에 쌓인다.

스택입력 값
 2+3-1 
+3-1 
항 연산자3-1 
표현식-1 
표현식 연산자
표현식 

상향식 파서는 입력 값의 오른쪽으로 이동하면서(입력 값의 처음을 가리키는 포인터가 오른쪽으로 이동하는 것을 상상) 구문 규칙으로 갈수록 남는 것이 점차 감소하기 때문에 이동-감소 파서라고 부른다.

파서 자동 생성

파서를 생성해 줄 수 있는 도구를 파서 생성기라고 한다. 언어에 어휘나 구문 규칙 같은 문법을 부여하면 동작하는 파서를 만들어 준다. 파서를 생성하는 것은 파싱에 대한 깊은 이해를 필요로 하고 수동으로 파서를 최적화하여 생성하는 것은 쉬운 일이 아니기 때문에 파서 생성기는 매우 유용하다.

웹킷은 잘 알려진 두 개의 파서 생성기를 사용한다. 어휘 생성을 위한 플렉스(Flex)와 파서 생성을 위한 바이슨(Bison)이다. 렉스(Lex)와 약(Yacc)이라는 이름과 함께 들어본 적이 있을지도 모르겠다. 플렉스는 토큰의 정규 표현식 정의를 포함하는 파일을 입력 받고 바이슨은 BNF 형식의 언어 구문 규칙을 입력 받는다.

HTML 파서

HTML 파서는 HTML 마크업을 파싱 트리로 변환한다.

HTML 문법 정의

HTML의 어휘와 문법은 W3C에 의해 명세로 정의되어 있다. 현재 버전은 HTML4와 초안 상태로 진행 중인 HTML5 이다.

문맥 자유 문법이 아님

파싱 일반 소개를 통해 알게 된 것처럼 문법은 BNF와 같은 형식을 이용하여 공식적으로 정의할 수 있다. 

안타깝게도 모든 전통적인 파서는 HTML에 적용할 수 없다. 그럼에도 불구하여 지금까지 파싱을 설명한 것은 그냥 재미 때문은 아니다. 파싱은 CSS와 자바스크립트를 파싱하는 데 사용된다. HTML은 파서가 요구하는 문맥 자유 문법에 의해 쉽게 정의할 수 없다.

HTML 정의를 위한 공식적인 형식으로 DTD(문서 형식 정의)가 있지만 이것은 문맥 자유 문법이 아니다.

이것은 언뜻 이상하게 보일 수도 있는데 HTML이 XML과 유사하기 때문이다. 사용할 수 있는 XML 파서는 많다. HTML을 XML 형태로 재구성한 XHTML도 있는데 무엇이 큰 차이점일까?

차이점은 HTML이 더 "너그럽다"는 점이다. HTML은 암묵적으로 태그에 대한 생략이 가능하다. 가끔 시작 또는 종료 태그 등을 생략한다. 전반적으로 뻣뻣하고 부담스러운 XML에 반하여 HTML은 "유연한" 문법이다.

이런 작은 차이가 큰 차이를 만들어 낸다. 웹 제작자의 실수를 너그럽게 용서하고 편하게 만들어주는 이것이야 말로 HTML이 인기가 있었던 이유다. 다른 한편으로는 공식적인 문법으로 작성하기 어렵게 만드는 문제가 있다. 정리하자면 HTML은 파싱하기 어렵고 전통적인 구문 분석이 불가능하기 때문에 문맥 자유 문법이 아니라는 것이다. XML 파서로도 파싱하기 쉽지 않다. 

HTML DTD

HTML의 정의는 DTD 형식 안에 있는데 SGML 계열 언어의 정의를 이용한 것이다. 이 형식은 허용되는 모든 요소와 그들의 속성 그리고 중첩 구조에 대한 정의를 포함한다. 앞서 말 한대로 HTML DTD는 문맥 자유 문법이 아니다. 

DTD는 여러 변종이 있다. 엄격한 형식은 명세만을 따르지만 다른 형식은 낡은 브라우저에서 사용된 마크업을 지원한다. 낡은 마크업을 지원하는 이유는 오래된 콘텐츠에 대한 하위 호환성 때문이다. 현재의 엄격한 형식 DTD는www.w3.org/TR/html4/strict.dtd 에서 확인할 수 있다.

DOM

"파싱 트리"는 DOM 요소와 속성 노드의 트리로서 출력 트리가 된다. DOM은 문서 객체 모델(Document Object Model)의 준말이다. 이것은 HTML 문서의 객체 표현이고 외부를 향하는 자바스크립트와 같은 HTML 요소의 연결 지점이다. 트리의 최상위 객체는 문서이다.

DOM은 마크업과 1:1의 관계를 맺는다. 예를 들면 이런 마크업이 있다.

 <html>
  <body>
   <p>Hello World</p>
   <div><img src="example.png" /></div>
  </body>
</html>  

이것은 아래와 같은 DOM 트리로 변환할 수 있다.

brouser8

그림 8 예제 마크업의 DOM 트리

HTML과 마찬가지로 DOM은 W3C에 의해 명세(www.w3.org/DOM/DOMTR)가 정해져 있다. 이것은 문서를 다루기 위한 일반적인 명세인데 부분적으로 HTML 요소를 설명하기도 한다. HTML 정의는 www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109/idl-definitions.html에서 찾을 수 있다.

트리가 DOM 노드를 포함한다고 말하는 것은 DOM 접점의 하나를 실행하는 요소를 구성한다는 의미다. 브라우저는 내부의 다른 속성들을 이용하여 이를 구체적으로 실행한다.

파싱 알고리즘

앞서 말한대로 HTML은 일반적인 하향식 또는 상향식 파서로 파싱이 안되는데 그 이유는 다음과 같다.

  1. 언어의 너그러운 속성.
  2. 잘 알려져 있는 HTML 오류에 대한 브라우저의 관용.
  3. 변경에 의한 재파싱. 일반적으로 소스는 파싱하는 동안 변하지 않지만 HTML에서 document.write을 포함하고 있는 스크립트 태그는 토큰을 추가할 수 있기 때문에 실제로는 입력 과정에서 파싱이 수정된다.

일반적인 파싱 기술을 사용할 수 없기 때문에 브라우저는 HTML 파싱을 위해 별도의 파서를 생성한다.

파싱 알고리즘은 HTML 자세히 설명되어 있다. 알고리즘은 토큰화와 트리 구축 이렇게 두 단계로 되어 있다. 

토큰화는 어휘 분석으로서 입력 값을 토큰으로 파싱한다. HTML에서 토큰은 시작 태그, 종료 태그, 속성 이름과 속성 값이다.

토큰화는 토큰을 인지해서 트리 생성자로 넘기고 다름 토큰을 확인하기 위해 다음 문자를 확인한다. 그리고 입력의 마지막까지 이 과정을 반복한다.

brouser9

그림 9 HTML 파싱 과정(HTML5 명세에서 가져옴)

토큰화 알고리즘

알고리즘의 결과물은 HTML 토큰이다. 알고리즘은 상태 기계(State Machine)라고 볼 수 있다.  각 상태는 하나 이상의 연속된 문자를 입력받아 이 문자에 따라 다음 상태를 갱신한다. 그러나 결과는 현재의 토큰화 상태와 트리 구축 상태의 영향을 받는데. 이것은 같은 문자를 읽어 들여도 현재 상태에 따라 다음 상태의 결과가 다르게 나온다는 것을 의미한다. 알고리즘은 전체를 설명하기에 너무 복잡하니 원리 이해를 도울만한 간단한 예제를 한번 보자.

다음은 HTML 토큰화를 설명하기 위한 기본적인 예제이다.

 <html>
   <body>
      Hello world
   </body>
</html>  

초기 상태는 "자료 상태" 이다. < 문자를 만나면 상태는 "태그 열림 상태"로 변한다. a 부터 z까지의 문자를 만나면 "시작 태그 토큰"을 생성하고 상태는 "태그 이름 상태"로 변하는데 이 상태는 > 문자를 만날 때까지 유지한다. 각 문자에는 새로운 토큰 이름이 붙는데 이 경우 생성된 토큰은 html 토큰이다.

>문자에 도달하면 현재 토큰이 발행되고 상태는 다시 "자료 상태"로 바뀐다. 태그는 동일한 절차에 따라 처리된다. 지금까지 html 태그와 body 태그를 발행했고 다시 "자료 상태"로 돌아왔다.  Hello World의 H 문자를 만나면 문자 토큰이 생성되고 발행될 것이다. 이것은 종료 태그의 < 문자를 만날 때까지 진행된다. Hello World의 각 문자를 위한 문자 토큰을 발행할 것이다.

다시 "태그 열림 상태"가 되었다. / 문자는 종료 태그 토큰을 생성하고 "태그 이름 상태"로 변경 될 것이다. 이 상태는 > 문자를 만날 때까지 유지된다. 그리고 새로운 태그 토큰이 발행되고 다시 "자료 상태"가 된다. 또한 동일하게 처리될 것이다.

brouser10

그림 10 입력 예제의 토큰화

트리 구축 알고리즘

파서가 생성되면 문서 객체가 생성된다. 트리 구축이 진행되는 동안 문서 최상단에서는 DOM 트리가 수정되고 요소가 추가된다. 토큰화에 의해 발행된 각 노드는 트리 생성자에 의해 처리된다. 각 토큰을 위한 DOM 요소의 명세는 정의되어 있다. DOM 트리에 요소를 추가하는 것이 아니라면 열린 요소는 스택(임시 버퍼 저장소)에 추가된다. 이 스택은 부정확한 중첩과 종료되지 않은 태그를 교정한다. 알고리즘은 상태 기계라고 설명할 수 있고 상태는 "삽입 모드" 라고 부른다.

아래 입력 예제의 트리 생성 과정을 보자.

 <html>
   <body>
      Hello world
   </body>
</html>  

트리 구축 단계의 입력 값은 토큰화 단계에서 만들어지는 일련의 토큰이다. 받은 html 토큰은 "html 이전" 모드가 되고 토큰은 이 모드에서 처리된다. 이것은 HTMLHtmlElement 요소를 생성하고 문서 객체의 최상단에 추가된다.

상태는 "head 이전" 모드로 바뀌었고 "body" 토큰을 받았다. "head" 토큰이 없더라도 HTMLHeadElement는 묵시적으로 생성되어 트리에 추가될 것이다.

곧이어 "head 안쪽" 모드로 이동했고 다음은 "head 다음" 모드로 간다. body 토큰이 처리 되었고 HTMLBodyElement가 생성되어 추가됐으며 "body 안쪽" 모드가 되었다.

"Hellow world" 문자열의 문자 토큰을 받았다. 첫 번째 토큰이 생성되고 "본문" 노드가 추가되면서 다른 문자들이 그 노드에 추가될 것이다.

body 종료 토큰을 받으면 "body 다음" 모드가 된다. html 종료 태그를 만나면 "body 다음 다음" 모드로 바뀐다. 마지막 파일 토큰을 받으면 파싱을 종료한다.

brouser11

그림 11 예제 html 트리 구축

파싱이 끝난 이후의 동작

이번 단계에서 브라우저는 문서와 상호작용할 수 있게 되고 문서 파싱 이후에 실행되어야 하는 "지연" 모드 스크립트를 파싱하기 시작한다. 문서 상태는 "완료"가 되고 "로드" 이벤트가 발생한다. 보다 자세한 내용은 HTML5 토큰화 알고리즘과 트리 구축에서 볼 수 있다.

브라우저의 오류 처리

HTML 페이지에서 "유효하지 않은 구문" 이라는 오류를 본 적이 없을 것이다. 이는 브라우저가 모든 오류 구문을 교정하기 때문이다. 아래 오류가 포함된 HTML 예제를 보자.

<html>  
   <mytag></mytag>
   <div>
     <p>
   </div>
   Really lousy HTML
   </p>
</html>  

나는 일부러 여러 가지 규칙을 위반 했다. "mytag"는 표준 태그가 아니고 "p" 태그와 "div" 태그는 중첩 오류가 있다. 그러나 브라우저는 투덜거리지 않고 올바르게 표시하는데 이는 파서가 HTML 제작자의 실수를 수정했기 때문이다.

이런 오류 처리 행태는 브라우저에서 꽤나 일반적임에도 불구하고 HTML의 현재 명세가 아니라는 점이 놀라울 뿐이다. 북마크와 이전/다음 버튼처럼 수 년간 브라우저 안에서 구현된 것이다. 잘 알려진 HTML 오류를 많은 사이트에서 발견할 수 있지만 브라우저는 다른 브라우저들이 했던 것처럼 관습적으로 오류를 고치고 있다.

HTML5 명세는 이런 요구 사항 일부를 정의했다. 웹킷은 이것을 HTML 파서 클래스의 시작 부분에 주석으로 잘 요약해 두었다.

파서는 토큰화된 입력 값을 파싱하여 문서를 만들고 문서 트리를 생성한다. 규칙에 맞게 잘 작성된 문서라면 파싱이 수월하겠지만 불행하게도 형식에 맞지 않게 작성된 많은 HTML 문서를 다뤄야 하기 때문에 파서는 오류에 대한 아량이 있어야 한다.

파서는 적어도 다음과 같은 오류를 처리해야 한다.

  1. 어떤 태그의 안쪽에 추가하려는 태그가 금지된 것일 때 일단 허용된 태그를 먼저 닫고 금지된 태그는 외부에 추가한다.
  2. 파서가 직접 요소를 추가해서는 안된다. 문서 제작자에 의해 뒤늦게 요소가 추가될 수 있고 생략 가능한 경우도 있다. HTML, HEAD, BODY, TBODY, TR, TD, LI 태그가 이런 경우에 해당한다.
  3. 인라인 요소 안쪽에 블록 요소가 있는 경우 부모 블록 요소를 만날 때까지 모든 인라인 태그를 닫는다.
  4. 이런 방법이 도움이 되지 않으면 태그를 추가하거나 무시할 수 있는 상태가 될 때까지 요소를 닫는다.

웹킷이 오류를 처리하는 예는 다음과 같다.

<br> 대신 </br>

어떤 사이트는 <br> 대신 </br>을 사용한다. 인터넷 익스플로러, 파이어폭스와 호환성을 갖기 위해 웹킷은 이것을 <br> 으로 간주한다. 코드는 다음과 같다.

if(t->isCloseTag(brTag) && m_document->inCompatMode()) {  
    reportError(MalformedBRError);
    t->beginTag = true;
}

오류는 내부적으로 처리하고 사용자에게는 표시하지 않는다.

어긋난 표

어긋난 표는 표 안에 또 다른 표가 th 또는 td 셀 내부에 있지 않은 것을 의미한다. 아래 예제와 같은 경우를 말한다.

<table>

    <table>

    <tr><td>inner table</td></tr>

    </table>

    <tr><td>outer table</td></tr>

</table>  

이런 경우 웹킷은 표의 중첩을 분해하여 형제 요소가 되도록 처리한다.

<table>

    <tr><td>outer table</td></tr>

</table>

<table>

    <tr><td>inner table</td></tr>

</table>  

코드는 다음과 같다.

if(m_inStrayTableContent && localName == tableTag)  
popBlock(tableTag);  

웹킷은 이런 오류를 처리하는데 스택을 사용한다. 안쪽의 표는 바깥쪽 표의 외부로 옮겨져서 형제 요소가 된다.

중첩된 폼 요소

폼 안에 또 다른 폼을 넣은 경우 안쪽의 폼은 무시된다. 코드는 다음과 같다.

if(!m_currentFormElement) {  
    m_currentFormElement = new HTMLFormElement(formTag, m_document);
}
태그 중첩이 너무 깊을 때

주석에는 이렇게 적혀 있다.

www.liceo.edu.mx 사이트는 약 1,500개 수준의 태그 중첩이 되어 있는 예제인데 모든 요소가 <b>로 되어 있다. 최대 20개의 중첩만 허용하고 나머지는 무시한다.

bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)  
{
 
    unsigned i = 0;
    for (HTMLStackElem* curr = m_blockStack; 
        i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;  
        curr = curr->next, i++) { }
    return i != cMaxRedundantTagDepth;
}
잘못 닫힌 html 또는 body 태그

주석에는 이렇게 적혀 있다.

깨진 html을 지원한다. 일부 바보 같은 페이지는 문서가 끝나기 전에 body를 닫아버리기 때문에 브라우저는 body 태그를 닫지 않는다. 대신 종료를 위해 end()를 호출한다.

if (t->tagName == htmlTag || t->tagName == bodyTag )  
   return;

웹킷 오류 처리 코드에 예제로 등장하는 것을 원치 않는다면 웹 제작자는 올바른 HTML 작성을 위해 신경 써야 할 것이다.

CSS 파싱

소개 글에서 설명했던 파싱의 개념을 기억하는가? HTML과는 다르게 CSS는 문맥 자유 문법이고 소개 글에서 설명했던 파서 유형을 이용하여 파싱이 가능하다. 실제로 CSS 명세는 CSS 어휘와 문법을 정의하고 있다.

몇 가지 예제를 보자. 어휘 문법은 각 토큰을 위한 정규 표현식으로 정의되어 있다.

omment   \/*[^]*+([^/][^]*+)\/ 
num        [0-9]+|[0-9]"."[0-9]+ 
nonascii    [\200-\377] 
nmstart    [_a-z]|{nonascii}|{escape} 
nmchar    [_a-z0-9-]|{nonascii}|{escape} 
name        {nmchar}+ 
ident        {nmstart}{nmchar}

"ident"는 클래스 이름처럼 식별자(identifier)를 줄인 것이다. "name"은 요소의 아이디("#"으로 참조하는) 이다.

구문 문법은 BNF로 설명되어 있다.

Ruleset  
   : selector [ ',' S* selector ]*
       '{' S* declaration [ ';' S* declaration ]* '}' S*
   ;
Selector  
   : simple_selector [ combinator selector | S+ [ combinator? selector ]? ]?
   ;
simple_selector  
   : element_name [ HASH | class | attrib | pseudo ]*
   | [ HASH | class | attrib | pseudo ]+
   ;
Class  
   : '.' IDENT
   ;
element_name  
   : IDENT | '*'
   ;
Attrib  
   : '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S*
       [ IDENT | STRING ] S* ] ']'
   ;
Pseudo  
   : ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ]
   ;

룰셋(ruleset)은 다음과 같은 구조를 나타낸다.

div.error, a.error {  
   color: red;
   font-weight: bold;
}

div.error와 a.error 는 선택자(selector)이다. 중괄호 안쪽에는 이 룰셋에 적용된 규칙이 포함되어 있다. 이 구조는 공식적으로 다음과 같이 정의되어 있다.

Ruleset  
   : selector [ ',' S* selector ]*
       '{' S* declaration [ ';' S* declaration ]* '}' S*
   ;

룰셋은 쉼표와 공백(S가 공백을 의미함)으로 구분된 하나 또는 여러 개의 선택자라는 것을 의미한다. 룰셋은 중괄호 내부에 하나 또는 세미 콜론으로 구분된 여러 개의 선언을 포함한다. "선언"과 "선택자"는 이어지는 BNF에 정의되어 있다.

웹킷 CSS 파서

웹킷은 CSS 문법 파일로부터 자동으로 파서를 생성하기 위해 플렉스와 바이슨 파서 생성기를 사용한다. 파서 소개에서 언급했던 것처럼 바이슨은 상향식 이동 감소 파서를 생성한다. 파이어폭스는 직접 작성한 하향식 파서를 사용한다. 두 경우 모두 각 CSS 파일은 스타일 시트 객체로 파싱되고 각 객체는 CSS 규칙을 포함한다. CSS 규칙 객체는 선택자와 선언 객체 그리고 CSS 문법과 일치하는 다른 객체를 포함한다.

 brouser12

그림 12 CSS 파싱

스크립트와 스타일 시트의 진행 순서

스크립트

웹은 파싱과 실행이 동시에 수행되는 동기화(synchronous) 모델이다. 제작자는 파서가 < script > 태그를 만나면 즉시 파싱하고 실행하기를 기대한다. 스크립트가 실행되는 동안 문서의 파싱은 중단된다. 스크립트가 외부에 있는 경우 우선 네트워크로부터 자원을 가져와야 하는데 이 또한 실시간으로 처리되고 자원을 받을 때까지 파싱은 중단된다. 이 모델은 수 년간 지속됐고 HTML4와 HTML5의 명세에도 정의되어 있다. 제작자는 스크립트를 "지연(defer)"으로 표시할 수 있는데 지연으로 표시하게 되면 문서 파싱은 중단되지 않고 문서 파싱이 완료된 이후에 스크립트가 실행된다. HTML5는 스크립트를 비동기(asynchronous)로 처리하는 속성을 추가했기 때문에 별도의 맥락에 의해 파싱되고 실행된다.

예측 파싱

웹킷과 파이어폭스는 예측 파싱과 같은 최적화를 지원한다. 스크립트를 실행하는 동안 다른 스레드는 네트워크로부터 다른 자원을 찾아 내려받고 문서의 나머지 부분을 파싱한다.  이런 방법은 자원을 병렬로 연결하여 받을 수 있고 전체적인 속도를 개선한다. 참고로 예측 파서는 DOM 트리를 수정하지 않고 메인 파서의 일로 넘긴다. 예측 파서는 외부 스크립트, 외부 스타일 시트와 외부 이미지와 같이 참조된 외부 자원을 파싱할 뿐이다.

스타일 시트

한편 스타일 시트는 다른 모델을 사용한다. 이론적으로 스타일 시트는 DOM 트리를 변경하지 않기 때문에 문서 파싱을 기다리거나 중단할 이유가 없다. 그러나 스크립트가 문서를 파싱하는 동안 스타일 정보를 요청하는 경우라면 문제가 된다. 스타일이 파싱되지 않은 상태라면 스크립트는 잘못된 결과를 내놓기 때문에 많은 문제를 야기한다. 이런 문제는 흔치 않은 것처럼 보이지만 매우 빈번하게 발생한다. 파이어폭스는 아직 로드 중이거나 파싱 중인 스타일 시트가 있는 경우 모든 스크립트의 실행을 중단한다. 한편 웹킷은 로드되지 않은 스타일 시트 가운데 문제가 될만한 속성이 있을 때에만 스크립트를 중단한다.

렌더 트리 구축

DOM 트리가 구축되는 동안 브라우저는 렌더 트리를 구축한다. 표시해야 할 순서와 문서의 시각적인 구성 요소로써 올바른 순서로 내용을 그려낼 수 있도록 하기 위한 목적이 있다.

파이어폭스는 이 구성 요소를 "형상(frames)" 이라고 부르고 웹킷은 "렌더러(renderer)" 또는 "렌더 객체(render object)"라는 용어를 사용한다.

렌더러는 자신과 자식 요소를 어떻게 배치하고 그려내야 하는지 알고 있다.

웹킷 렌더러의 기본 클래스인 RenderObject 클래스는 다음과 같이 정의되어 있다.

class RenderObject { virtual  
    void layout(); virtual
    void paint(PaintInfo); virtual
    void rect repaintRect();
    Node * node; //the DOM node
    RenderStyle * style; // the computed style
    RenderLayer * containgLayer; //the containing z-index layer
}

각 렌더러는 CSS2 명세에 따라 노드의 CSS 박스에 부합하는 사각형을 표시한다. 렌더러는 너비, 높이 그리고 위치와 같은 기하학적 정보를 포함한다. 

박스 유형은 노드와 관련된 "display" 스타일 속성의 영향을 받는다(스타일 계산 참고). 여기 보이는 웹킷 코드는 display 속성에 따라 DOM 노드에 어떤 유형의 렌더러를 만들어야 하는지 결정하는 코드이다.

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)  
{
    Document* doc = node->document();
    RenderArena* arena = doc->renderArena();
    …
    RenderObject* o = 0;
 
    switch (style->display()) {
        case NONE:
            break;
        case INLINE:
            o = new (arena) RenderInline(node);
            break;
        case BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case INLINE_BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        case LIST_ITEM:
            o = new (arena) RenderListItem(node);
            break;
        ...
    }
    return o;
}

요소 유형 또한 고려해야 하는데 예를 들면 폼 콘트롤과 표는 특별한 구조이다. 요소가 특별한 렌더러를 만들어야 한다면 웹킷은 creatRenderer 메서드를 무시하고 비기하학 정보를 포함하는 스타일 객체를 표시한다.

DOM 트리와 렌더 트리의 관계

렌더러는 DOM 요소에 부합하지만 1:1로 대응하는 관계는 아니다. 예를 들어 "head" 요소와 같은 비시각적 DOM 요소는 렌더 트리에 추가되지 않는다. 또한 display 속성에 "none" 값이 할당된 요소는 트리에 나타나지 않는다(visibility 속성에 "hidden" 값이 할당된 요소는 트리에 나타난다).

여러 개의 시각 객체와 대응하는 DOM 요소도 있는데 이것들은 보통 하나의 사각형으로는 묘사할 수 없는 복잡한 구조다. 예를 들면 "select" 요소는 '표시 영역, 드롭다운 목록, 버튼' 표시를 위한 3개의 렌더러가 있다. 또한 한 줄에 충분히 표시할 수 없는 문자가 여러 줄로 바뀔 때 새 줄은 별도의 렌더러로 추가된다. 여러 렌더러와 대응하는 또 다른 예는 깨진 HTML이다. CSS 명세에 의하면 인라인 박스는 블록 박스만 포함하거나 인라인 박스만을 포함해야 하는데 인라인과 블록 박스가 섞인 경우 인라인 박스를 감싸기 위한 익명의 블록 렌더러가 생성된다.

어떤 렌더 객체는 DOM 노드에 대응하지만 트리의 동일한 위치에 있지 않다. float 처리된 요소 또는 position 속성 값이 absolute로 처리된 요소는 흐름에서 벗어나 트리의 다른 곳에 배치된 상태로 형상이 그려진다. 대신 자리 표시자가 원래 있어야 할 곳에 배치된다.

brouser13

그림 13 렌더 트리와 DOM 트리 대응(3.1). "뷰포트"는 최초의 블록이다. 웹킷에서는 "RenderView" 객체가 이 역할을 한다.

트리를 구축하는 과정

파이어폭스에서 프레젠테이션은 DOM 업데이트를 위한 리스너로 등록된다. 프레젠테이션은 형상 만들기를 FrameConstructor에 위임하고 FrameConstructor는 스타일(스타일 계산 참고)을 결정하고 형상을 만든다.

웹킷에서는 스타일을 결정하고 렌더러를 만드는 과정을 "어태치먼트(attachment)" 라고 부른다. 모든 DOM 노드에는 "attach" 메서드가 있다. 어태치먼트는 동기적인데 DOM 트리에 노드를 추가하면 새 노드의 "attach" 메서드를 호출한다.

html 태그와 body 태그를 처리함으로써 렌더 트리 루트를 구성한다. 루트 렌더 객체는 CSS 명세에서 포함 블록(다른 모든 블록을 포함하는 최상위 블록)이라고 부르는 그것과 일치한다. 파이어폭스는 이것을 ViewPortFrame이라 부르고 웹킷은 RenderView라고 부른다. 이것이 문서가 가리키는 렌더 객체다. 트리의 나머지 부분은 DOM 노드를 추가함으로써 구축된다.

CSS처리 모델 명세를 참고.

스타일 계산

렌더 트리를 구축하려면 각 렌더 객체의 시각적 속성에 대한 계산이 필요한데 이것은 각 요소의 스타일 속성을 계산함으로써 처리된다.

스타일은 인라인 스타일 요소와 HTML의 시각적 속성(예를 들면 bgcolor 같은 HTML 속성)과 같은 다양한 형태의 스타일 시트를 포함하는데 HTML의 시각적 속성들은 대응하는 CSS 스타일 속성으로 변환된다.

최초의 스타일 시트는 브라우저가 제공하는 기본 스타일 시트인데 페이지 제작자 또는 사용자도 이를 제공할 수 있다. 브라우저는 사용자가 선호하는 스타일을 정의할 수 있도록 지원하는데 파이어폭스의 경우 "파이어폭스 프로필" 폴더에 있는 스타일 시트를 변경함으로써 사용자 선호 스타일을 정의할 수 있다.

스타일을 계산하는 일에는 다음과 같은 몇 가지 어려움이 따른다.

  1. 스타일 데이터는 구성이 매우 광범위한데 수 많은 스타일 속성들을 수용하면서 메모리 문제를 야기할 수 있다.
  2. 최적화되어 있지 않다면 각 요소에 할당된 규칙을 찾는 것은 성능 문제를 야기할 수 있다. 각 요소에 할당된 규칙 목록을 전체 규칙으로부터 찾아내는 것은 과중한 일이다. 맞는 규칙을 찾는 과정은 얼핏 보기에는 약속된 방식으로 순탄하게 시작하는 것 같지만 실상 쓸모가 없거나 다른 길을 찾아야만 하는 복잡한 구조가 될 수 있다. 
    예를 들어 이런 복합 선택자가 있다.

    div div div div { … }

    이 선택자는 3번째 자손<div>에 규칙을 적용한다는 뜻이다. 규칙을 적용할 <div> 요소를 확인하려면 트리로부터 임의의 줄기를 선택하고 탐색하는 과정에서 규칙에 맞지 않는 줄기를 선택했다면 또 다른 줄기를 선택해야 한다.

  3. 규칙을 적용하는 것은 계층 구조를 파악해야 하는 꽤나 복잡한 다단계 규칙을 수반한다.

브라우저가 이 문제를 어떻게 처리하는지 살펴보자.

스타일 정보 공유

웹킷 노드는 스타일 객체(RenderStyle)를 참조하는데 이 객체는 일정 조건 아래 공유할 수 있다. 노드가 형제이거나 또는 사촌일 때 공유하며 다음과 같은 조건일 때 공유할 수 있다.

  1. 동일한 마우스 반응 상태를 가진 요소여야 한다. 예를 들어 한 요소가 :hover 상태가 될 수 없는데 다른 요소는 :hover가 될 수 있다면 동일한 마우스 상태가 아니다.
  2. 아이디가 없는 요소.
  3. 태그 이름이 일치해야 한다.
  4. 클래스 속성이 일치해야 한다.
  5. 지정된 속성이 일치해야 한다.
  6. 링크(link) 상태가 일치해야 한다.
  7. 초점(focus) 상태가 일치해야 한다.
  8. 문서 전체에서 속성 선택자의 영향을 받는 요소가 없어야 한다. 여기서 영향이라 함은 속성 선택자를 사용한 경우를 말한다(속성 선택자 예 input[type=text]{...})
  9. 요소에 인라인 스타일 속성이 없어야 한다(인라인 스타일 예 <p style="...">...</p>).
  10. 문서 전체에서 형제 선택자를 사용하지 않아야 한다. 웹 코어는 형제 선택자를 만나면 전역 스위치를 열고 전체 문서의 스타일 공유를 중단한다. 형제 선택자는 + 선택자와 :first-child 그리고 :last-child를 포함한다.

파이어폭스 규칙 트리

파이어폭스는 스타일 계산을 쉽게 처리하기 위해 규칙 트리와 스타일 문맥 트리라고 하는 두 개의 트리를 더 가지고 있다. 웹킷도 스타일 객체를 가지고 있지만 스타일 문맥 트리처럼 저장되지 않고 오직 DOM 노드로 관련 스타일을 처리한다.

brouser14

그림 14 파이어폭스 스타일 문맥 트리(2.2)

스타일 문맥에는 최종 값이 저장되어 있다. 값은 올바른 순서 안에서 부합하는 규칙을 적용하고 논리로부터 구체적인 값으로 변환함으로써 계산된다. 예를 들어 논리적인 값이 화면의 백분율(%) 이라면 이 값은 계산에 의해 절대적인 단위(px)로 변환된다. 이런 규칙 트리 아이디어는 정말 현명하다. 노드 사이에서 이 값을 공유함으로써 그것들을 다시 계산하는 일을 방지하기 때문이다.

부합하는 모든 규칙은 트리에 저장하는데 경로의 하위 노드가 높은 우선순위를 갖는다. 규칙 저장은 느리게 처리된다. 트리는 처음부터 모든 노드를 계산하지 않지만 노드 스타일이 계산될 필요가 있을 때 계산된 경로를 트리에 추가한다.

트리 경로를 어휘 목록 속에 있는 단어라고 생각하고 이미 규칙 트리를 계산했다고 가정해 보자.

brouser15

내용 트리에서 또 다른 요소에 부합하는 규칙이 필요하다고 가정하고 부합하는 규칙이 순서에 따라 B - E - I 라고 치자. 브라우저는 이미 A - B - E - I - L 경로를 계산했기 때문에 트리 안에 이 경로가 있고 할 일이 줄었다. 

트리가 작업량을 줄이는 방법을 살펴보자.

구조체로 분리

스타일 문맥은 구조체(structs)로 나뉘는데 선 또는 색상과 같은 종류의 스타일 정보를 포함한다. 구조체의 속성들은 상속되거나 또는 상속되지 않는다. 속성들은 요소에 따라 정해져 있지 않은 한 부모로부터 상속된다. 상속되지 않는 속성들은 "재설정(reset)" 속성이라 부르는데 상속을 받지 않는 것으로 정해져 있다면 기본 값을 사용한다.

트리는 최종으로 계산된 값을 포함하여 전체 구조체를 저장하는 방법으로 도움을 준다. 하위 노드에 구조체를 위한 속성 선언이 없다면 저장된 상위 노드의 구조체 속성을 그대로 받아서 사용하는 것이다.

규칙 트리를 사용하여 스타일 문맥을 계산

어떤 요소의 스타일 문맥을 계산할 때 가장 먼저 규칙 트리의 경로를 계산하거나 또는 이미 존재하는 경로를 사용한다. 그 다음 새로운 스타일 문맥으로 채우기 위해 경로 안에서 규칙을 적용한다. 가장 높은 우선순위(보통 가장 구체적인 선택자)를 가진 경로의 하위 노드에서 시작하여 구조체가 가득 찰 때까지 트리의 상단으로 거슬러 올라간다. 규칙 노드 안에서 구조체를 위한 특별한 선언이 없다면 상당한 최적화를 할 수 있다. 선언이 가득 채워질 때까지 노드 트리의 상위로 찾아 올라가서 간단하게 적용하면 최상의 최적화가 되고 모든 구조체는 공유된다. 이것은 최종 값과 메모리 계산을 절약한다.

선언이 완전하지 않으면 구조체가 채워질 때까지 트리의 상단으로 거슬러 올라간다.

구조체에서 어떤 선언도 발견할 수 없는 경우 구조체는 "상속(inherit)" 타입인데 문맥 트리에서 부모 구조체를 향하면서 성공적으로 구조체를 공유한다. 재설정 구조체라면 기본 값들이 사용될 것이다.

가장 구체적인 노드에 값을 추가하면 실제 값으로 변환하기 위해 약간의 추가적인 계산을 할 필요가 있는데 트리 노드에서 결과를 저장하기 때문에 자식에게도 사용할 수 있다.

같은 트리 노드를 가리키는 형제 요소가 있는 경우 전체 스타일 문맥이 이들 사이에서 공유된다.

이런 HTML이 있다고 가정해 보자.

<div class="err" id="div1">  
    <p>
    this is a <span class="big"> big error </span>
    this is also a <span class="big"> very big error</span> error
    </p>
</div>  
<div class="err" id="div2">another error</div>  

그리고 다음과 같은 규칙이 있다.

  1. div { margin:5px; color:black }
  2. .err { color:red }
  3. .big { margin-top:3px }
  4. div span { margin-bottom:4px }
  5. #div1 { color:blue }
  6. #div2 { color:green }

좀 단순하게 하기 위해 색상과 여백 이렇게 두 개의 구조체를 채울 필요가 있다고 치자. 색상 구조체는 오직 색상 값만을 포함하고 여백 구조체는 네 개의 면에 대한 값을 포함한다.

결과적으로 규칙 트리는 아래처럼 보일 것이다. 노드는 노드 이름과 노드가 가리키는 규칙의 번호로 표시되어 있다.

brouser16

그림 15 규칙 트리

문맥 트리는 아래처럼 보일 것이다. 노드는 노드 이름과 노드가 가리키는 규칙 노드로 표시되어 있다.

brouser17

그림 16 문맥 트리

HTML을 파싱하여 두 번째 <div> 태그인 <div class="err" id="div2">에 이르렀다고 가정하자. 이 노드에 필요한 스타일 문맥을 생성하고 스타일 구조체를 채워야 한다.

두 번째 <div> 규칙에 맞는 것을 찾으면 1, 2, 6이 되는데 이것은 요소가 사용할 수 있는 트리 경로(규칙 트리의 B:1, C:2)가 이미 존재한다는 것을 의미하고 규칙 6(규칙 트리에서 노드 F:6)에 이르는 또 다른 노드를 문맥 트리에 추가하면 된다. 스타일 문맥을 생성하고 문맥 트리에 추가하면 새로운 스타일 문맥이 규칙 트리의 F:6 노드를 가리킨다.

이제는 스타일 구조체를 채워야 하는데 여백 구조체를 채우는 것으로부터 시작한다. 마지막 규칙 노드(F:6)가 여백 구조체를 포함하지 않기 때문에 이전 노드에 저장된 구조체를 찾을 때까지 위로 거슬러 올라가서 계산된 값을 사용한다. 여백 규칙이 선언된 최상위 노드의 구조체를 규칙 노드 B:1 에서 찾았다.

색상 구조체 정의에는 저장된 구조체를 사용할 수 없다. 색상은 이미 하나의 속성 값을 가지고 있기 때문에 다른 값을 채우기 위해 규칙 트리 상단으로 거슬러 올라갈 필요가 없다. 최종 값을 계산하고 계산된 값(문자열에서 RGB 등으로 변환된)을 이 노드에 저장할 것이다.

두 번째 <span> 요소는 보다 수월하게 진행된다. 맞는 규칙을 찾다 보면 이전 span과 같이 규칙 트리의 G:3를 가리킨다는 결론에 이르는데 동일한 노드를 가리키는 형제가 있기 때문에 전체 스타일 문맥을 공유하고 이전 span의 문맥을 취하면 된다.

부모로부터 상속된 규칙을 포함하고 있는 구조체의 저장은 문맥 트리에서 처리된다. 색상 속성은 실제로 상속된다. 그러나 파이어폭스는 재설정으로 처리해서 규칙 트리에 저장한다.

예를 들어 문단 요소에 글꼴을 위한 규칙을 추가한다면.

p { font-family:Verdana; font size:10px; font-weight:bold }  

문맥 트리에서 div의 자식인 p 요소는 그 부모의 동일 글꼴 구조체를 공유할 수 있다. p 요소에 지정된 규칙이 없는 경우라도 마찬가지다.

규칙 트리가 없는 웹킷은 선언이 일치하는 규칙이 4번 탐색된다. 우선 중요하지 않은 상위 속성(display와 같은 속성이 의존하기 때문)이 적용되고, 그 다음 중요한 상위 속성이 적용된다. 그리고 나서 중요하지 않은 일반 속성이 적용되고 마지막으로 중요한 일반 속성이 적용된다. 이것은 여러 번 나타나는 속성들이 정확한 다단계 순서에 따라 결정된다는 것을 의미하고 가장 마지막 값이 적용된다.

요약하면 스타일 객체는 전체 또는 일부를 공유함으로써 1번과 3번 문제를 해결한다. 파이어폭스 규칙 트리는 올바른 순서에 따라 속성을 적용하는 것을 돕는다.

쉬운 선택을 위한 규칙 다루기

스타일 규칙을 위한 몇 가지 소스가 있다.

CSS 규칙을 외부 스타일 시트에서 선언하거나 style 요소에서 선언

p {color:blue}  

인라인 스타일 속성

<p style="color:blue"></p>  

HTML의 시각적 속성(이것들은 CSS 규칙으로 변환됨)

<p bgcolor="blue"></p>  

마지막 두 가지 스타일은 자신이 스타일 속성을 가지고 있거나 HTML 속성을 이용하여 연결할 수 있기 때문에 요소에 쉽게 연결된다.

위에서 언급한 문제 2번에 따라 CSS 규칙을 연결하는 것은 까다로울 수 있는데 이 문제를 해결하려면 쉽게 접근할 수 있도록 규칙을 교묘하게 처리해야 한다.

스타일 시트를 파싱한 후 규칙은 선택자에 따라 여러 해시맵 중 하나에 추가된다. 아이디, 클래스 이름, 태그 이름을 사용한 맵이 있고 이런 분류에 맞지 않는 것을 위한 일반적인 맵이 있다. 선택자가 아이디인 경우 규칙은 아이디 맵에 추가되고 선택자가 클래스인 경우 규칙은 클래스 맵에 추가된다.

이런 처리 작업을 통해 규칙을 찾는 일은 훨씬 쉬워진다. 맵에서 특정 요소와 관련 있는 규칙을 추출할 수 있기 때문에 모든 선언을 찾아 볼 필요가 없다. 이러한 최적화는 찾아야 할 규칙의 95% 이상을 제거하기 때문에 규칙을 찾는 동안 모든 선언을 고려할 필요가 없다.

다음 스타일 규칙 예제를 살펴보자.

p.error {color:red}  
#messageDiv {height:50px}
div {margin:5px}  

첫 번째 규칙은 클래스 맵에 추가된다. 두 번째는 아이디 맵에 추가되고 세 번째는 태그 맵에 추가된다. 

위 스타일과 관련된 HTML 코드는 다음과 같다.

<p class="error">an error occurred </p>  
<div id=" messageDiv">this is a message</div>  

우선 p 요소의 규칙을 찾아보자. 클래스 맵은 발견된 "p.error"를 위한 규칙 하부의 "error" 키를 찾았다. div 요소는 아이디 맵(키는 아이디)과 태그 맵에 관련 규칙이 있다. 그러므로 이제 남은 작업은 키를 사용하여 추출한 규칙 중에 실제로 일치하는 규칙을 찾는 것이다.

예를 들어 div에 해당하는 다음과 같은 또 다른 규칙이 있다고 가정하자.

table div {margin:5px}  

이 예제는 여전히 태그 맵에서 규칙을 추출할 것이다. 가장 우측에 있는 선택자가 키이기 때문이다. 그러나 앞서 작성한 div 요소와는 일치하지 않는다. 상위에 table이 없기 때문이다.

웹킷과 파이어폭스 모두 이런 방식으로 처리하고 있다.

다단계 순서에 따라 규칙 적용하기

스타일 객체는 모든 CSS 속성을 포함하고 있는데 어떤 규칙과도 일치하지 않는 일부 속성은 부모 요소의 스타일 객체로부터 상속 받는다. 그 외 다른 속성들은 기본 값으로 설정된다.

문제는 하나 이상의 속성이 정의될 때 시작되고 다단계 순서가 이 문제를 해결하게 된다.

스타일 시트 다단계 순서

스타일 속성 선언은 여러 스타일 시트에서 나타날 수 있고 하나의 스타일 시트 안에서도 여러 번 나타날 수 있는데 이것은 규칙을 적용하는 순서가 매우 중요하다는 것을 의미한다. 이것을 "다단계(cascade)" 순서라고 한다. CSS2 명세에 따르면 다단계 순서는 다음과 같다(우선 순위가 낮은 것에서 높은 순서임).

  1. 브라우저 선언 (browser declarations)
  2. 사용자 일반 선언 (user normal declarations)
  3. 저작자 일반 선언 (author normal declarations)
  4. 저작자 중요 선언 (author important declarations)
  5. 사용자 중요 선언 (user important declarations)

브라우저 선언의 중요도가 가장 낮으며 사용자가 저작자의 선언을 덮어 쓸 수 있는 것은 선언이 중요하다고 표시한 경우뿐이다. 같은 순서 안에서 동일한 속성 선언은 특정성(specificity)에 의해 정렬이 되고 이 순서는 곧 특정성이 된다. HTML 시각 속성은 CSS 속성 선언으로 변환되고 변환된 속성들은 저작자 일반 선언 규칙으로 간주된다.

특정성

선택자 특정성은 CSS명세에 다음과 같이 정의되어 있다.

  • 선택자 없이 'style' 속성이 선언된 것이면 1을 센다. 그렇지 않으면 0을 센다. (=a)
  • 선택자에 포함된 아이디 선택자 개수를 센다. (=b)
  • 선택자에 포함된 속성 선택자(클래스 선택자와 속성 선택자)와 가상 클래스 선택자의 숫자를 센다. (=c)
  • 선택자에 포함된 요소 선택자와 가상 요소 선택자의 숫자를 센다. (=d)

네 개의 연결된 숫자 a-b-c-d (큰 진법의 숫자)를 연결하면 특정성의 값이 된다.

사용할 진법은 분류 중에 가장 높은 숫자에 의해서 정의된다. 예를 들어 a=14이면 16진수를 사용할 수 있다. 흔치는 않겠지만 a=17과 같은 경우라면 17진법이 필요할 것이다. 17진법을 사용해야 하는 경우는 html body div div p … (선택자에 17개의 태그를 사용하는 경우로 흔치 않음)와 같이 선택자를 사용하는 경우에 발생할 수 있다.

다음과 같은 몇 가지 예제를 참고하기 바란다.

*{} /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li{} /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */  
li:first-line{} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */  
ul li{} /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */  
ul ol+li{} /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */  
h1+*[rel=up]{} /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */  
ul ol li.red{} /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */  
li.red.level{} /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */  
#x34y{} /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="" /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */  
규칙 정렬

맞는 규칙을 찾으면 다단계 규칙에 따라 정렬된다. 웹킷은 목록이 적으면 버블 정렬을 사용하고 목록이 많을 때는 병합 정렬을 사용한다. 웹킷은 규칙에 ">" 연산자를 덮어쓰는 방식으로 정렬을 실행한다.

static bool operator >(CSSRuleData& r1, CSSRuleData& r2)  
{
    int spec1 = r1.selector()->specificity();
    int spec2 = r2.selector()->specificity();
    return (spec1 == spec2) : r1.position() > r2.position() : spec1 > spec2;
}

점진적 처리

웹킷은 @import를 포함한 최상위 수준의 스타일 시트가 로드되었는지 표시하기 위해 플래그를 사용한다. DOM 노드와 시각정보를 연결하는 과정(attaching)에서 스타일이 완전히 로드되지 않았다면 문서에 자리 표시자를 사용하고 스타일 시트가 로드됐을 때 다시 계산한다.

배치

렌더러가 생성되어 트리에 추가될 때 크기와 위치 정보는 없는데 이런 값을 계산하는 것을 배치 또는 리플로라고 부른다.

HTML은 흐름 기반의 배치 모델을 사용하는데 이것은 보통 단일 경로를 통해 크기와 위치 정보를 계산할 수 있다는 것을 의미한다. 일반적으로 "흐름 속"에서 나중에 등장하는 요소는 앞서 등장한 요소의 위치와 크기에 영향을 미치지 않기 때문에 배치는 왼쪽에서 오른쪽으로 또는 위에서 아래로 흐른다. 단, 표는 크기와 위치를 계산하기 위해 하나 이상의 경로를 필요로 하기 때문에 예외가 된다 (3.5).

좌표계는 기준점으로부터 상대적으로 위치를 결정하는데 좌단(X축)과 상단(Y축) 좌표를 사용한다.

배치는 반복되며 HTML 문서의 <html> 요소에 해당하는 최상위 렌더러에서 시작한다. 배치는 프레임 계층의 일부 또는 전부를 통해 반복되고 각 렌더러에 필요한 크기와 위치 정보를 계산한다.

최상위 렌더러의 위치는 0,0 이고 브라우저 창의 보이는 영역에 해당하는 뷰포트 만큼의 면적을 갖는다.

모든 렌더러는 "배치" 또는 "리플로" 메서드를 갖는데 각 렌더러는 배치해야 할 자식의 배치 메소드를 불러온다.

더티 비트 체제

소소한 변경 때문에 전체를 다시 배치하지 않기 위해 브라우저는 "더티 비트" 체제를 사용한다. 렌더러는 다시 배치할 필요가 있는 변경 요소 또는 추가된 것과 그 자식을 "더티"라고 표시한다.

"더티"와 "자식이 더티" 이렇게 두 가지 플래그가 있다. 자식이 더터하다는 것은 본인은 괜찮지만 자식 가운데 적어도 하나를 다시 배치할 필요가 있다는 의미다.

전역 배치와 점증 배치

배치는 렌더러 트리 전체에서 일어날 수 있는데 이것을 "전역" 배치라 하고 다음과 같은 경우에 발생한다.

  1. 글꼴 크기 변경과 같이 모든 렌더러에 영향을 주는 전역 스타일 변경.
  2. 화면 크기 변경에 의한 결과.

배치는 더티 렌더러가 배치되는 경우에만 점증되는데 추가적인 배치가 필요하기 때문에 약간의 손실이 발생할 수 있다.

점증 배치는 렌더러가 더티일 때 비동기적으로 일어난다. 예를 들면 네트워크로부터 추가 내용을 받아서 DOM 트리에 더해진 다음 새로운 렌더러가 렌더 트리에 붙을 때이다.

brouser18

그림 17 점증 배치 - 오직 더티 렌더러와 그 자식만 배치된다(3.6).

비동기 배치와 동기 배치

점증 배치는 비동기로 실행된다. 파이어폭스는 점증 배치를 위해 "리플로 명령"을 쌓아 놓고 스케줄러는 이 명령을 한꺼번에 실행한다. 웹킷도 점증 배치를 실행하는 타이머가 있는데 트리를 탐색하여 "더티" 렌더러를 배치한다.

"offsetHeight" 같은 스타일 정보를 요청하는 스크립트는 동기적으로 점증 배치를 실행한다.

전역 배치는 보통 동기적으로 실행된다.

때때로 배치는 스크롤 위치 변화와 같은 일부 속성들 때문에 초기 배치 이후 콜백으로 실행된다.

최적화

배치가 "크기 변경" 또는 렌더러 위치 변화 때문에 실행되는 경우 렌더러의 크기는 다시 계산하지 않고 캐시로부터 가져온다.

어떤 경우는 하위 트리만 수정이 되고 최상위로부터 배치가 시작되지 않는 경우도 있다. 이런 경우는 입력 필드에 텍스트를 입력하는 경우와 같이 변화 범위가 한정적이어서 주변에 영향을 미치지 않을 때 발생한다. 만약 입력 필드 바깥쪽에 텍스트가 입력되는 경우라면 배치는 최상단으로부터 시작될 것이다.

배치 과정

배치는 보통 다음과 같은 형태로 진행된다.

  1. 부모 렌더러가 자신의 너비를 결정.
  2. 부모가 자식을 검토.

    1. 자식 렌더러를 배치(자식의 x와 y를 설정)
    2. (부모와 자식이 더티하거나 전역 배치 상태이거나 또는 다른 이유로) 필요하다면 자식 배치를 호출하여 자식의 높이를 계산한다.
  3. 부모는 자식의 누적된 높이와 여백, 패딩을 사용하여 자신의 높이를 설정한다. 이 값은 부모 렌더러의 부모가 사용하게 된다.
  4. 더티 비트 플래그를 제거한다.

파이어폭스는 "상태" 객체(nsHTMLReflowState)를 배치("리플로"를 의미)를 위한 매개 변수로 사용하는데 상태는 부모의 너비를 포함한다.

파이어폭스 배치의 결과는 "매트릭스" 객체(nsHTMLReflowMatrics)인데 높이가 계산된 렌더러를 포함한다.

너비 계산

렌더러의 너비는 포함하는 블록의 너비, 그리고 렌더러의 너비와 여백, 테두리를 이용하여 계산된다. 

예를 들어 다음은 div 요소의 너비를 보자.

<div style="width:30%"></div>  

웹킷은 다음(RenderBox 클래스의 calcWidth 메서드)과 같이 계산할 것이다.

  • 컨테이너의 너비는 컨테이너 availableWidth와 0 사이의 최대값이다. 이 경우 availableWidth는 다음과 같이 계산된 contentWidth이다.

    clientWidth() - paddingLeft() - paddingRight()

    clientWidth와 clientHeight는 객체의 테두리와 스크롤바를 제외한 내부 영역을 의미한다.

  • 요소의 너비는 "width" 스타일 속성의 값이다. 이 컨테이너 너비의 백분률 값은 절대 값으로 변환될 것이다.
  • 좌우측 테두리와 패딩 값이 추가된다.

여기까지 "미리 획득한 너비"의 계산이었다. 이제는 최소 너비와 최대 너비를 계산해야 한다.

미리 획득한 너비가 최대 너비보다 크면 최대 너비가 사용된다. 미리 획득한 너비가 최소 너비(깨지지 않는 가장 작은 단위)보다 작으면 최소 너비가 사용된다. 

배치할 필요가 있지만 너비가 고정된 경우 값은 캐시에 저장된다.

줄 바꿈

렌더러가 배치되는 동안 줄을 바꿀 필요가 있을 때 배치는 중단되고 줄 바꿀 필요가 있음을 부모에게 전달한다. 부모는 추가 렌더러를 생성하고 배치를 호출한다.

그리기

그리기 단계에서는 화면에 내용을 표시하기 위한 렌더 트리가 탐색되고 렌더러의 "paint" 메서드가 호출된다. 그리기는 UI 기반의 구성 요소를 사용한다.

전역과 점증 

그리기는 배치와 마찬가지로 전역 또는 점증 방식으로 수행된다. 점증 그리기에서 일부 렌더러는 전체 트리에 영향을 주지 않는 방식으로 변경된다. 변경된 렌더러는 화면 위의 사각형을 무효화 하는데 OS는 이것을 "더티 영역"으로 보고 "paint" 이벤트를 발생시킨다. OS는 몇 개의 영역을 하나로 합치는 방법으로 효과적으로 처리한다. 크롬은 렌더러가 별도의 처리 과정이기 때문에 조금 더 복잡하다. 크롬은 OS의 동작을 어느 정도 모방한다. 프레젠테이션은 이런 이벤트에 귀 기울기고 렌더 최상위로 메시지를 전달한다. 그러면 트리는 적절한 렌더러에 이를 때까지 탐색되고 스스로(보통 자식과 함께) 다시 그려진다.

그리기 순서

CSS 2는 그리기 과정의 순서를정의했다. 이것은 실제로 요소가 stacking contexts에 쌓이는 순서다. 스택은 뒤에서 앞으로 그려지기 때문에 이 순서는 그리기에 영향을 미친다. 블록 렌더러가 쌓이는 순서는 다음과 같다.

  1. 배경 색
  2. 배경 이미지
  3. 테두리
  4. 자식
  5. 아웃라인

파이어폭스 표시 목록

파이어폭스는 렌더 트리를 검토하고 그려진 사각형을 위한 표시 목록을 구성한다. 목록은 올바른 그리기 순서(배경, 테두리, 기타……)에 따라 사각형을 위한 적절한 렌더러를 포함한다. 이런 방법으로 트리는 여러 번 리페인팅을 실행하는 대신 한 번만 탐색하면서 배경 색, 배경 이미지, 테두리 그리고 나머지 순으로 그려낸다.

파이어폭스는 다른 불투명 요소 뒤에 완전히 가려진 요소는 추가하지 않는 방법으로 최적화를 진행한다.

웹킷 사각형 저장소

리페인팅 전에 웹킷은 기존의 사각형을 비트맵으로 저장하여 새로운 사각형과 비교하고 차이가 있는 부분만 다시 그린다.

동적 변경

브라우저는 변경에 대해 가능한 한 최소한의 동작으로 반응하려고 노력한다. 그렇기 때문에 요소의 색깔이 바뀌면 해당 요소의 리페인팅만 발생한다. 요소의 위치가 바뀌면 요소와 자식 그리고 형제의 리페인팅과 재배치가 발생한다. DOM 노드를 추가하면 노드의 리페인팅과 재 배치가 발생한다. "html" 요소의 글꼴 크기를 변경하는 것과 같은 큰 변경은 캐시를 무효화하고 트리 전체의 배치와 리페인팅이 발생한다.

렌더링 엔진의 스레드

렌더링 엔진은 통신을 제외한 거의 모든 경우에 단일 스레드로 동작한다. 파이어폭스와 사파리의 경우 렌더링 엔진의 스레드는 브라우저의 주요한 스레드에 해당한다. 크롬에서는 이것이 탭 프로세스의 주요 스레드이다.

통신은 몇 개의 병렬 스레드에 의해 진행될 수 있는데 병렬 연결의 수는 보통 2개에서 6개로 제한된다(예를 들면 파이어폭스 3은 6개를 사용).

이벤트 순환

브라우저의 주요 스레드는 이벤트 순환으로 처리 과정을 유지하기 위해 무한 순환된다. 배치와 그리기 같은 이벤트를 위해 대기하고 이벤트를 처리한다. 아래는 주요 이벤트 순환을 위한 파이어폭스 코드이다.

while (!mExiting)  
NS_ProcessNextEvent(thread);  

CSS2 시각 모델

캔버스

CSS명세는 캔버스를 "서식 구조가 표현되는 공간" 이라고 설명한다. 브라우저가 내용을 그리는 공간인 것이다. 캔버스 공간 각각의 면적은 무한하지만 브라우저는 뷰포트의 크기를 기초로 초기 너비를 결정한다.

CSS명세에 따르면 캔버스는 기본적으로 투명하기 때문에 다른 캔버스와 겹치는 경우 비쳐 보이고, 투명하지 않을 경우에는 브라우저에서 정의한 색이 지정된다.

CSS 박스 모델

CSS 박스 모델은 문서 트리에 있는 요소를 위해 생성되고 시각적 서식 모델에 따라 배치된 사각형 박스를 설명한다.

각 박스는 콘텐츠 영역(문자, 이미지 등)과 선택적인 패딩과 테두리, 여백이 있다.

brouser19

그림 18 CSS2 박스 모델

각 노드는 이런 상자를 0에서 n개 생성한다.

모든 요소는 만들어질 박스의 유형을 결정하는 "display" 속성을 갖는데 이 속성의 유형은 다음과 같다.

  • block  - 블록 상자를 만든다.
  • inline - 하나 또는 그 이상의 인라인 상자를 만든다.
  • none - 박스를 만들지 않는다.

기본 값은 인라인이지만 브라우저의 스타일 시트는 다른 기본 값을 설정한다. 예를 들면 "div" 요소의 display 속성에 대한 기본 값은 block 이다.

브라우저의 기본 스타일 시트 예제는 [www][45][.][45][w][45][3.][45][org][45][/][45][TR][45][/][45][CSS][45][2/][45][sample][45][.][45][html][45]에서 찾을 수 있다.

위치 결정 방법

위치를 결정하는 방법은 다음과 같은 세 가지다.

  1. Normal - 객체는 문서 안의 자리에 따라 위치가 결정된다. 이것은 렌더 트리에서 객체의 자리가 DOM 트리의 자리와 같고 박스 유형과 면적에 따라 배치됨을 의미한다.
  2. Float - 객체는 우선 일반적인 흐름에 따라 배치된 다음 왼쪽이나 오른쪽으로 흘러 이동한다.
  3. Absolute - 객체는 DOM 트리 자리와는 다른 렌더 트리에 놓인다.

위치는 "position" 속성과 "float" 속성에 의해 결정된다.

  • static과 relative로 설정하면 일반적인 흐름에 따라 위치가 결정된다.
  • absolute와 fixed로 설정하면 절대적인 위치가 된다.

position 속성을 정의하지 않으면 static이 기본 값이 되며 일반적인 흐름에 따라 위치가 결정된다. static 아닌 다른 속성 값(relatice, absolute, fixed)을 사용하면 top, bottom, left, right 속성으로 위치를 결정할 수 있다.

박스가 배치되는 방법은 다음과 같은 방법으로 결정된다.

  • 박스 유형(display, inline ...)
  • 박스 크기(width, height ...)
  • 위치 결정 방법(position, float)
  • 추가적인 정보 - 이미지 크기와 화면 크기 등

박스 유형

블록 박스: 브라우저 창에서 사각형 블록을 형성한다.

brouser20

그림 19 블록 박스

인라인 박스: 블록이 되지 않고 블록 내부에 포함된다.

brouser21

그림 20 인라인 박스

블록은 다른 블록 아래 수직으로 배치되고 인라인은 수평으로 배치된다.

brouser22

그림 21 블록과 인라인 배치

인라인 박스는 라인 또는 "라인 박스" 안쪽에 놓인다. 라인은 적어도 가장 큰 박스만큼 크지만 "baseline" 정렬일 때 더 커질 수 있다. 이것은 요소의 하단이 다른 상자의 하단이 아닌 곳에 배치된 경우를 의미한다. 포함하는 너비가 충분하지 않으면 인라인은 몇 줄의 라인으로 배치되는데 이것은 보통 문단 안에서 발생한다.

brouser23

그림 22 라인

위치 잡기

상대적인 위치

상대적인 위치 잡기는 일반적인 흐름에 따라 위치를 결정한 다음 필요한 만큼 이동한다.

brouser24

그림 23 상대적인 위치 잡기

플로트

플로트 박스는 라인의 왼쪽 또는 오른쪽으로 이동한다. 흥미로운 점은 다른 박스가 이 주변을 흐른다는 것이다.

HTML을 다음과 같이 작성하면.

<p>  
<img src="http://helloworld.naver.com/image.gif" alt="image.gif" width="100" height="100" style="float:right">  
Lorem ipsum dolor sit amet, consectetuer...  
</p>  

아래와 같이 보일 것이다.

brouser25

그림 24 플로트

절대적인(absolute) 위치와 고정된(fixed) 위치

절대와 고정 배치는 일반적인 흐름과 무관하게 결정되고, 일반적인 흐름에 관여하지 않으며, 면적은 부모에 따라 상대적이다. 고정인 경우 뷰포트로부터 위치를 결정한다.

brouser26

그림 25 고정된 위치 잡기

참고 - 고정된 박스는 문서가 스크롤되어도 따라 움직이지 않는다.

층 표현

이것은 CSS의 z-index 속성에 의해 명시된다. 층은 박스의 3차원 표현이고 "z 축"을 따라 위치를 정한다.

박스는 (stacking contexts라고 부르는) 스택으로 구분된다. 각 스택에서 뒤쪽 요소가 먼저 그려지고 앞쪽 요소는 사용자에게 가까운 쪽으로 나중에 그려진다. 가장 앞쪽에 위치한 요소는 겹치는 이전 요소를 가린다.

스택은 z-index 속성에 따라 순서를 결정한다. z-index 속성이 있는 박스는 지역 스택(local stack)을 형성한다. 뷰포트는 바깥쪽의 스택(outer stack)이다.

다음 예제를 보자.

<style type="text/css">  
div {  
    position: absolute;
    left: 2in;
    top: 2in;
}
</style>  
<p>  
    <div style="z-index:3;background-color:red;width:1in;height:1in"></div>
    <div style="z-index:1;background-color:green;width:2in;height:2in"></div>
</p>  

이 코드는 다음과 같이 보일 것이다.

brouser27

그림 26 고정 위치 잡기

붉은색 박스가 초록색 박스보다 마크업에서 먼저 나오기 때문에 일반적인 흐름이라면 먼저 그려져야 하지만 z-index 속성이 높기 때문에 더 앞쪽에 표시된다.

참고 자료

브라우저 아키텍처

  1. Grosskurth, Alan. AReferenceArchitectureforWebBrowsers (pdf)
  2. Gupta, Vineet. HowBrowsersWork - Part 1 - Architecture

파싱

  1. Aho, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (aka the "Dragon book"), Addison-Wesley, 1986
  2. Rick Jelliffe. TheBoldandtheBeautifultwonewdraftsforHTML 5.

파이어폭스

  1. L. David Baron, FasterHTMLandCSSLayoutEngineInternalsforWebDevelopers.
  2. L. David Baron, FasterHTMLandCSSLayoutEngineInternalsforWebDevelopers(Googletechtalkvideo)
  3. L. David Baron, Mozilla'sLayoutEngine
  4. L. David Baron, MozillaStyleSystemDocumentation
  5. Chris Waterson, NotesonHTMLReflow
  6. Chris Waterson, GeckoOverview
  7. Alexander Larsson, ThelifeofanHTMLHTTPrequest

웹킷

  1. David Hyatt, ImplementingCSS(part 1)
  2. David Hyatt, AnOverviewofWebCore
  3. David Hyatt, WebCoreRendering
  4. David Hyatt, TheFOUCProblem

W3C 명세

  1. HTML 4.01 Specification
  2. W3CHTMLSpecification
  3. CascadingStyleSheetsLevel 2 Revision 1 (CSS 2.1) Specification

브라우저 빌드 지침

  1. Firefox. https://developer.mozilla.org/en/Build_Documentation
  2. Webkit. http://webkit.org/building/build.html

번역 Ajax UI랩 장정환, 웹표준개발2팀 남덕현, 오픈퍼블리싱팀 정찬명

테크니컬 에디팅 웹플랫폼개발랩 송기선, 기술문서팀 박춘권

라이선스 특별한 고지가 없는 한 이 페이지의 내용은 크리에이티브커먼즈속성 3.0 라이선스 아래 있습니다. 코드 샘플은 아파치 2.0 라이선스 아래 있습니다.

이미지 출처 이 글에 첨부된 이미지의 출처는 특별한 언급이 없는 한 "HowBrowsersWork:Behindthescenesofmodernwebbrowsers"입니다.

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

[펌] git 파일 대소문자 변경하기

2015.09.22 17:25 Development/GIT



문제:

대부분의 프로젝트는 작업은 맥에서 하지만 운영은 CentOS에서 하고 있다.
작업할 때 가끔 실수로 파일명의 대소문자가 틀릴 때가 있는데,
맥에서는 대소문자를 구분하지 않기 때문에 오류가 나지 않아서 배포 후에야 대소문자를 구분하는 CentOS 환경에서 발견하곤 한다.

맥에선 대소문자를 변경해 커밋해도 같은 파일로 인식하기 때문에,
늘 다른 이름으로 변경했다가 커밋하고, 다시 대소문자를 변경한 후에 커밋하는 방법으로 해결하고 있었다.

번거롭다.
더 간단한 방법은 없을까?


해결책:

`git mv`에 `--force` 옵션을 사용하면 한 번에 처리할 수 있다!

예) `git mv --force myfile MyFile`

http://stackoverflow.com/questions/10523849/git-changing-capitalization-of-filenames

저작자 표시
신고

'Development > GIT' 카테고리의 다른 글

[펌] git 파일 대소문자 변경하기  (0) 2015.09.22
GIT 참고링크  (0) 2015.06.24
GIT 명령어  (0) 2015.06.24
현재 0 개의 댓글이 있습니다.
Comment

Blog

FormData Object 확인

2015.09.21 19:34 Development/JavaScript


HTML5 API 인 FormData 객체를 사용하여 데이터를 할당하는 도중,

삽질된통 했다.


나중에 How to FormData Object Inspect 라고 구글링 하면 여러 결과를 확인할 수 있지만 백문이불여일타다.

직접 쳐보는게 제일 좋다.

간단히 내가 겪은 상황은 아주 단순 심플하다.


var formData = new FormData();
formData.append('id', 'JKUN.NET');
console.log(formData);


위와 같은 상황이라면 요소검사의 떡하니 폼데이터 객체에 id 속성이 할당된줄만 알았다.

그래서 찍어보니... 없다... 아 돌아가시는 줄 알았다.


여기서 제가 찾은 방법은 XHR - AJAX 를 이용하여 네트워크에서 확인하는 방법말고는 특별히 없었습니다.

더 심플하고 좋으신 방법 있으신분들 조언 부탁드립니다.^^;;


일단은 내가 확인한 방법은 다음과 같다.


var formData = new FormData();
formData.append('id', 'JKUN.NET');

var xhr = new XMLHttpRequest();
xhr.open('post', 'sample', true);
xhr.send(formData);

실행결과


요 따위 삽질을 혹시나 겪으신 분이 있을수도 있어서 별거아닌 포스팅 합니다.


저작자 표시
신고
현재 1 개의 댓글이 있습니다.

힘없는염소 2015.11.13 17:45 신고

크롬 콘솔로 안보이데..삽질...ㄷㄷㄷㄷㄷ


Comment

Blog

GIT 참고링크

2015.06.24 00:15 Development/GIT



저작자 표시
신고

'Development > GIT' 카테고리의 다른 글

[펌] git 파일 대소문자 변경하기  (0) 2015.09.22
GIT 참고링크  (0) 2015.06.24
GIT 명령어  (0) 2015.06.24
현재 0 개의 댓글이 있습니다.
Comment

Blog

GIT 명령어

2015.06.24 00:05 Development/GIT


출처 : http://tech.sungamyang.com/?p=59

Nodeschool.io를 통해 필수적인 Git 사용법을 배우고 있다. 간단하지만 필수적인 명령어를 정리하면 다음과 같다.

Git 버전 확인

  • git –version

사용자 이름과 이메일 설정

  • git config –global user.name “<사용자이름>”
  • git config –global user.email “<사용자이메일>”

Git 저장소(repository) 생성 (Git 인스턴스 라고도 부름)

  • mkdir <폴더이름>
    cd <폴더이름>
    git init

Git 상태 확인

  • git status

파일의 변경사항 확인

  • git diff

업데이트한 파일을 commit하기 위해 추가

  • git add <파일이름>

변경된 모든파일을 commit하기위해 추가

  • git add . 또는 git add -A

변경사항을 짧은 메시지와 함께 commit(저장) 하기

  • git commit -m “<commit 메시지>”

로컬 저장소를 Remote(Github.com에 있는 온라인 저장소)와 연결

  • git remote add <Remote 이름> <Github 주소>

Remote 연결 보기

  • git remote -v

변경된 내용 local로 가져오기

  • git pull <remote 이름> <branch 이름>

변경된 내용 remote에 반영하기

  • git push <Remote 이름> <Branch 이름>

저장소 복제하기

  • git clone <gIthub 주소>

원본 저장소 연결하기

  • git remote add <Remote 이름> <github 주소>

브랜치 생성한 후 해당 브랜치로 작업공간 바꾸기

  • git checkout -b <branch 이름>

새로운 브랜치 생성하기

  • git checkout -b <branch 이름>

특정 브랜치로 작업공간 변경하기

  • git checkout <branch 이름>

브랜치 목록 출력하기

  • git branch

현재 작업중인 브랜치 이름 변경하기

  • git branch -m <새로운 branch 이름>

Pull in changes from a remote branch

  • git pull <remote 이름> <remote branch 이름>


저작자 표시
신고

'Development > GIT' 카테고리의 다른 글

[펌] git 파일 대소문자 변경하기  (0) 2015.09.22
GIT 참고링크  (0) 2015.06.24
GIT 명령어  (0) 2015.06.24
현재 0 개의 댓글이 있습니다.
Comment

Blog

조엘 온 소프트웨어

2015.06.16 14:02 Development




1부 비트와 바이트: 프로그래밍 실전

1장. 언어 선택하기
2장. 기본으로 돌아가기
3장. 조엘 테스트: 더 나은 코드를 위한 12단계
4장. 모든 개발자가 꼭 알아야 하는 유니코드와 문자 집합에 대한 고찰
5장. 손쉬운 기능 명세서 작성법
1강- 명세서 작업이 귀찮습니까?
6장. 손쉬운 기능 명세 방법
2강- 명세가 뭡니까?
7장. 손쉬운 기능 명세 방법
3강- 하지만, 어떻게?
8장. 손쉬운 기능 명세 방법
4강- 팁
9장. 손쉬운 소프트웨어 일정 관리법
10장. 일일 빌드는 당신의 친구입니다.
11장. 고리타분한 버그 수정
12장. 다섯 가지 세상
13장. 종이 프로토타이핑
14장. 화성인 아키텍트를 조심하세요.
15장. 쏘면서 움직여라.
16장. 장인정신
17장. 컴퓨터 과학 분야에서 떠도는 세가지 미신
18장. 더불어 살기
19장. 자동으로 충돌 보고서를 수집하세요.

2부 개발자 다루기

20장. 인터뷰를 위한 게릴라 가이드
21장. 성과급은 오히려 해가 된다.
22장. 테스터를 두지 않는 (잘못된) 5대 이유
23장. 개발자는 멀티태스킹 기계가 아닙니다.
24장. 당신이 결코 하지 말아야 하는 일, 1부
25장. 밝혀진 빙산의 비밀
26장. 허술한 추상화의 법칙
27장. 프로그래밍 세계에서 파머스톤 경
28장. 측정

3부 별로 잡다하지 않은 주제에 대한 잡다한 생각

29장. 릭 채프맨이 아둔함을 찾습니다.
30장. 이 나라에서는 개가 무슨 일을 하죠?
31장. 말단이면서도 해내기
32장. 두 가지 이야기
33장. 빅 맥 대 제이미는 요리사
34장. 세상에 쉬운 일은 없습니다.
35장. NIH 신드롬을 옹호하며
36장. 전략 메모 I: 벤 앤 제리 대 아마존
37장. 전략 메모 II: 닭이 먼저냐, 달걀이 먼저냐
38장. 전략 메모 III: 돌아가게 해주세요!
39장. 전략 메모 IV: 블로트웨어와 80/20 미신
40장. 전략 메모 V: 오픈 소스 경제학
41장. 머피의 법칙이 난무했던 한 주
42장. 마이크로소프트 사가 API 전쟁에 진 이유

4부 .NET에 대한 쓴소리 

43장. 마이크로소프트가 난관에 부딪히다.
44장. 우리의 .NET 전략
45장. 저기, 링커 좀 주시면 안될까요?

5부 조엘에게 물어보기, 가장 재미있었던 질문

부록. 조엘에게 물어보기, 가장 재미있었던 질문

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

JMeter - 웹사이트 성능 테스트 (스트레스 테스트)

2015.05.27 16:48 Development


http://codeigniter-kr.org/lecture/view/289/page/1

일단은 봤고 한번 테스트해보고 다시 적용허자;;

저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

IE 10 이상으로 셀렉트박스 아이콘 숨기기

2015.05.08 17:32 Development/CSS


셀렉트 박스를 배경이미지로 넣어 커스텀으로 사용하려 했는데,

인터넷 익스플로러에서는 적용이 되지를 않는다. 구글링 해서 찾을 결과 10이상으로는 적용이 되는것 같다.

내가 테스트 해본 결과로는 11 이상이었다.



select::-ms-expand { display:none; } 
저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

replaceArray - 배열간 문자열 탐색변경

2015.04.27 17:26 Development/JavaScript


출처 : http://stackoverflow.com/questions/5069464/replace-multiple-strings-at-once

 String.prototype.replaceArray = function (find, replace) {
     var replaceString = this;
     for (var i = 0; i < find.length; i++) {
         // global replacement
         var pos = replaceString.indexOf(find[i]);
         while (pos > -1) {
             replaceString = replaceString.replace(find[i], replace[i]);
             pos = replaceString.indexOf(find[i]);
         }
     }
     return replaceString;
 };
     var textT = "Hello world,,,,, hello people.....";
     var find = [".",","];
     var replace = ['2', '5'];
     textT = textT.replaceArray(find, replace);


저작자 표시
신고
현재 0 개의 댓글이 있습니다.
Comment

Blog

$.when()

2015.04.13 14:43 Development/JavaScript | jQuery


http://api.jquery.com/jQuery.when/


jQuery 에서 특정액션을 취하고 성공과 실패여부에 따라 기능을 수행해야 하는 경우가 있다.

이 경우에는 when() 메소드를 이용하면 보다 간편하고 명시적인 코드로 처리할 수 있다.

쿼리에서 CASE WHEN ... THEN 절과 비슷한 느낌이 든다.


사용은 다음과 같다.


$.when( alert('밥 먹고난 다음에 ') ).done(function() {
alert('커피한잔 허자');
});

그리고 추가적인 절차가 필요할때도 다음과 같다.

function work1() { /* action */ }
function work2() { /* action */ }
$.when( work1, work2 ).done(function() {
alert('End!');
});

그리고 done() 과 then() 의 차이가 있다.

처리후 콜백이 "성공" 과 ("성공","실패") 로 나누어 진다고 생각하면 된다.

function work() { /* action */ }
function trueWork() { /* action */ }
function falseWork() { /* action */ }
$.when ( work ).then( trueWork, falseWork );


저작자 표시
신고

'Development > JavaScript | jQuery' 카테고리의 다른 글

jquery attribute (속성 존재여부)  (0) 2016.01.21
$.when()  (0) 2015.04.13
노드 다루기  (0) 2014.12.29
jQuery Form Reset  (0) 2014.05.13
window.resize 크로스 브라우징  (0) 2013.11.15
현재 0 개의 댓글이 있습니다.
Comment