Frontend 에 익숙하지않은 많은 개발자들이 과제 이상의 어느정도 규모있는 Frontend 를 작업하게 되면 HTML 과 CSS 의 구조에 대해 놀라는 경향이 있다.

 

HTML 과 CSS 는 생각보다 정교한 구조를 이루고 있고, 초보 수준 이상의 Frontend 실력을 갖기 위해서는 필수적으로 구조에 대해 이해하고 어떻게 브라우저가 웹(Web)을 스타일링하는 지 알아야 한다.

 

이해해야할 잘 갖춰진 Frontend 의 특징 중 하나로, "HTML 문서의 모든 프로퍼티(properties) 들은 상속가능하다" 는 점이 있다.

 

계층적으로 구조화된 DOM 모델에서 한 Element 는 Parent Element 의 속성을 지니며, 모든 element 들의 root parent 는 <html> 이 된다.

HTML 태그의 DOM 모델들 간 상속은 다른 포스팅에서 다루도록 하고, 본 포스팅에서는 CSS 의 상속에 대해 다루기로 한다.

 

복잡한 종류의 UI 를 구조할 때, 비슷한 CSS 스타일링을 중복해서 사용하게 되는 경우가 많이 있다.

가령, 웹페이지 전체의 UI 는 어느정도 일관성이 있어야 하기 때문에, 검색을 위한 검색바(Bar)와 검색 추천을 위한 표시바(Bar) 가 있는데 이 둘의 스타일을 비슷하게 가져가되 하이라이트 컬러 정도만 바꾸고자 한다고 해보자.

 

이 때, 세세하게 작업된 모든 디자인 스타일을 복사해서 사용하는 것은 비효율적인 일이다.

 

이럴 때에는 다음과 같이 CSS 상속을 이용해 쉽게 해결할 수 있다.

 

위의 코드에서 bar-focus 라는 CSS 스타일은 bar 스타일을 차용하되 background-color 에 특징을 입힌다.

위의 마크업은 다음과 같은 페이지를 나타내게 된다.

 

색배합이 끔찍하지만, 스타일을 재활용할 수 있다는 점정도는 쉽게 알 수 있다.

 

쉽지않은가? 이런식으로 스타일의 재활용을 통해 노가다로 여겨질 수 있는 Frontend 의 디자인 코드를 확연하게 정리할 수 있다.

 

하지만 CSS 의 상속과 관련되어 알아야할 성질들은 조금 더 있다.

 

먼저 브라우저가 상속을 처리하는 순서이다.

브라우저는 CSS 상속을 Inline > Internal > External 순서로 처리한다.

 

여기서 Inline, Internal, External 은 CSS를 정의하는 방법에 대한 분류를 말하며, 다음과 같다.

 - Inline : 해당 태그 안에 style=”” 속성을 통해 정의

 - Internal : 해당 파일 안에 <css> </css> 태그를 작성하고 해당 Scope 내에 정의

 - External : 별도의 파일로 CSS 를 분리하고 해당 파일을 HTML 에서 import

 

즉, CSS를 적용할 때에 위의 순서를 유의해야 해당 스타일이 반영될 수 있다.

 

또 당신이 프론트엔드 개발자라면 주의해야할 사항으로 태그의 속성 관리 부분이 있다.

 

만약, CSS 가 적용된 태그의 속성이 "float" 을 가지고 있는 경우 해당 DOM 은 상속관계 및 레이아웃에 영향을 받지않는 붕-뜬 상태가 된다. 

해당 요소에 상속성을 적용시키기 위해서는 overflow 속성의 적용이 필요하다.

 




웹 페이지를 구성하는 DOM Element 노드들은 트리 모양의 계층 구조를 갖고 있으며 DOM Element 와 연결된 Javascript 내부 동작 역시 이 구조에 대한 로직이 고려되어 있다.


이벤트 버블링과 캡쳐링은 Javascript 에서 주로 사용되는 Event Delegation Pattern 을 이해하는 기본적인 개념이다.


- 이벤트 버블링(Event Bubbling) 


웹페이지 내의 한 엘리먼트에서 이벤트가 감지되었을 때, 해당 엘리먼트의 계층구조를 따라 올라가면서 Root Element 까지 이벤트가 전달되는 것을 Event Bubbling 이라 한다.


이해를 돕기 위해 다음 코드를 확인해보자.


<div class="col-sm-1" onclick="alert('c')">
<div onclick="alert('b')">
<div onclick="alert('a')">
HELLO
</div>
</div>
</div>


위의 코드를 웹 페이지에서 확인해보면, HELLO 를 감싸고 있는 DOM 엘리먼트를 클릭하면 a - b - c 순서대로 3개의 Alert 창을 확인해볼 수 있다.


이처럼 이벤트 버블링(Bubbling)이란, 이벤트가 발생하는 가장 안쪽부터 바깥쪽, 하위 노드에서 상위노드로 이벤트가 전파(Propagation)되는 과정인 것이다.


하위 DOM Element 부터 전파되는 이벤트는 최종적으로 Document 객체까지 전달되며, 상위 Element 에서 이벤트 핸들러를 재정의해서 전파를 제어할 수 있다.


가령, 다음과 같은 코드에서 이벤트는 두번째 div 이상으로 전파되지 않는다.



<div class="col-sm-1" onclick="alert('c')">
<div onclick="event.stopPropagation()">
<div onclick="alert('a')">
HELLO
</div>
</div>
</div>


전파되지 않는 이유는 이벤트가 발생한 target Element 의 상위 Element 에서 onclick 메서드를 재구현하는데, event.stopPropagation() 함수를 이용해서 전파를 중단했기 때문이다.


이렇게 전파를 제어하는 것을 Stop Bubbling 이라고 한다.



- 이벤트 캡쳐링(Event Capturing)


이벤트 캡쳐링은 이벤트 버블링과 반대방향으로 이벤트가 전달된다.


다음 코드를 통해 이해해보자.


<div id="parent">
<div id="child">
HELLO
</div>
</div>
<script>
var parent = document.querySelector('#parent');
var child = document.querySelector('#child');
parent.addEventListener('click', function(){
alert("Parent clicked");
},true);
child.addEventListener('click', function(){
alert("Child clicked");
});
</script>


똑같이 계층 구조로 Element 들이 배치되어 있지만 이번에는 Parent 노드부터 이벤트가 전달된다.


이처럼 이벤트 캡쳐링(Event Capturing)이란, 상위노드부터 하위노드로 이벤트가 전달되는 형태를 말한다.


이 경우에 이벤트는 window 객체부터 차례로 아래 노드로 전파되며 addEventListener 의 3번째 인자로 capture 플래그를 두어 탐색 방향을 맞출 수 있다.


이벤트 캡쳐링의 경우 버블링보다는 덜 사용된다.



이벤트 버블링(Event Bubbling) 과 이벤트 캡쳐링(Event Capturing) 의 개념은 간단하지만, 이해하고 있지 않으면 은근히 프론트엔드 개발 시 삽질을 할 수 있는 부분이다.


잘 이해해두어 복잡한 UI 이벤트 처리 시에 헷갈리는 일이 없도록 하자.




 AngularJS에서의 핵심 개념인 $scope란 양방향 데이터 바인딩의 핵심이자, 뷰와 컨트롤러를 연결하는 개념이다. 

$scope 는 HTML과 Javascript 를 연결해주는 구조로, 이는 단순한 Javascript 객체에 불과하지만, AngularJS 엔진에 의해 이 객체는 연결된 DOM 요소에서 Data와 Function이 사용되는 공간이라고 할 수 있다.

$scope 는 Javascript Object 이며 view 와 controller 사이를 연결할 수 있도록 몇가지 attribute 들을 포함하고 있다.


모든 AngularJS 로 제작된 Application 은 $rootScope 를 갖고 있으며 이는 ng-app 태그가 포함된 HTML 문서 최상단에 위치하는 $scope 가 된다.

따라서 다른 모든 $scope 들은 글로벌하게 정의되는 $rootScope를 상속하는 계층적인 구조를 이루고 있다. 즉, $rootScope 는 많은 child scope 들을 갖고 있다.

$rootScope 만이 AngularJS App 전체에서 유일한 Global Scope 로 처리가 되기 때문에 다른 AngularJS 의 Feature 들, 가령 Service 나 Factory 등이 $rootScope 를 이용하는 경우가 종종 있다.




위의 그림과 같이 하나의 HTML 에서 여러개의 ng-controller 를 정의할 경우 계층적으로 $scope 를 갖게 되고 이는 자연스럽게 계층 구조를 갖게 된다.


$scope는 뷰와 컨트롤러를 이어주는 다리이면서 연결된 DOM에서 양방향 데이터 바인딩을 처리해준다. 또한 이벤트를 처리할 수도 있다.

이 때, AngularJS 의 각 Controller는 하나의 컨트롤러에 하나의 $scope만을 갖게 된다.

그렇기 때문에 만약 독립된 $scope간 참조가 필요하다면 컨트롤러 간 데이터를 공유해야할 경우에는 Service를 이용한다.


AngularJS 의 실 구현은 대부분 ng-controller 로 명시되는 Controller 에서 수행되는 경우가 많기 때문에, 현재 다루고 있는 $scope 를 이해하고 있는건 매우 중요하다.


다음은 Scope 의 라이프사이클을 설명한다.


$scope 의 LifeCycle


1. 브라우저가 Javascript 를 로딩할 때, AngularJS 의 $injector 를 통해 정의된 Application(ng-app) 에 scope 를 할당한다.


2. 링킹 과정에서 $scope 하위에 $watch 오브젝트들이 등록된다.


3. 연결된 model 의 변화가 감지되면 $scope.$apply() 메서드가 AngularJS 내부에서 호출된다. 작업은 $http, $timeout, $interval 모듈을 이용해서 비동기적으로 이루어진다.


4. 변화가 감지되면 AngularJs 는 $digest cycle 을 rootScope 에서 수행한다. 변경 내용은 모든 child Scope 로 전파되며 당연히 각 $scope 에 정의된 $watch 가 이벤트를 전달받는다.


5. $scope 혹은 child Scope 가 더이상 필요가 없어지면 scope.$destroy 가 이루어지며 root Scope 에 의한 전파가 중단된다. 중단된 $scope 는 향후 가비지 컬렉팅된다.



참조 : https://docs.angularjs.org/guide/scope




최근에 AngularJS 를 이용해서 작업을 하던 도중 알게된 작은 버그이다.

(참고로 버전은 AngularJS 1 버전이고, 크롬에서 발생한 버그이다.)


문제가 된 HTML 은 다음과 같다.



<button class="button btn-danger" ng-click="deleteElement($index)">
<span class="glyphicon glyphicon-remove" aria-hidden="true">Delete</span></button>


위의 코드는 단순히 버튼을 클릭하면 해당 Element 에 대한 정보를 이용해서 ng-click 이벤트를 발생시키는 HTML 코드이다.


이 코드는 아주 잘 작동하지만, HTML 에서 엔터키 입력 시, 아주 사소한 버그를 발견할 수 있었다.


위의 코드에서 같은 scope 로 묶인 Text Box 에서 엔터를 입력하자마자 맨 위 레코드가 삭제되는 버그를 발견하였다.


정확히는 Enter Key 를 입력받아 ng-click 이벤트가 작동한 것이다. 다만, 명시적으로 ng-click 이 호출되지 않은 탓에 Parameter인 $index 는 전달되지 않은듯 하다.


문제의 원인은 <button> 태그에 있었는데, 기본적으로 button 태그의 type 은 "submit" 형이기 때문에, 엔터키 입력에 반응할 수 있다.

(이는 브라우저 별로도 다소 다른 듯 하다.)


문제 해결을 위해서는 다음과 같이 명시적으로 타입을 지정해주면 간단히 해결된다.



<button type="button" class="button btn-danger" ng-click="deleteElement($index)">
<span class="glyphicon glyphicon-remove" aria-hidden="true">Delete</span></button>


혹은 어느정도 규모의 AngularJS 프로젝트를 운용중이라면 directive 로 묶어서 처리하는 것도 좋은 방법이다.



(참고자료 : http://www.jomendez.com/2015/02/16/prevent-ng-click-action-hitting-enter/)



 Web Storage 는 HTML5 에서 새로 생겨난 신기술로, Cookie 를 대체할 목적으로 만들어진 Browser Storage 이다.


HTML5 이전에 Application data는 쿠키에 저장되었고 모든 Server 에 대한 Request 에 첨부되었다.

이에 반에 Web Storage 는 보다 안전하고 많은 데이터를 저장할 수 있으면서 웹 사이트 퍼포먼스 저하에 영향을 미치지 않는다.


4kb 까지 저장할 수 있는 쿠키와 다르게 5mb 까지 저장할 수 있으며 저장된 정보는 매요청마다 서버로 전달되지 않는다. 


또한 암호화가 가능하며, 쿠키에 비해 접근 및 내용확인이 어렵다.(보안 상 안전하다.)


실제로 오래 유지될 필요가 있을지는 의문이지만... 만료일자가 지정되어있는 쿠키와 다르게 반영구적 보존이 가능하다는 점도 장점이다.



HTML5 Web Storage 는 Local Storage & Session Storage 로 나뉘며 간단한 Key : Value 를 저장할 수 있는 점은 같다.


 - Local Storage : 로컬에 Origin 별로 지속되는 스토리지. Domain 마다 별도로 Local Storage 가 생성된다. Javascript 에서 Window 전역 객체의 localStorage 컬렉션으로 접근이 가능하다.


 - Session Storage : 현재 세션 동안만 유지되는 스토리지. Window 전역 객체의 sessionStorage 컬렉션을 통해 접근이 가능하다. Browser Context 내에서만 데이터가 유지되므로 브라우저 종료 시 데이터도 삭제된다.



HTML5 의 Web Storage 는 기존의 쿠키를 대체할 목적이 아닌 언어 레벨에서 제공해주는 훌륭한 Browser Storage 옵션에 가깝다. 

실제로 쿠키처럼 매 요청시 서버로 전송되지 않기 때문에 필요한 요청에 대해 명시적으로 Request 를 날려야할 필요가 있다.





웹은 HTTP 의 특징 상 TCP와는 다르게 Stateless 프로토콜을 사용하기 때문에 HTTP 를 사용하면서 연결 관리를 위해 여러가지 도구를 사용한다.


그 중 하나가 Cookie 이며, 쿠키는 다양한 방법으로 웹서버의 동작에 도움을 준다.


먼저 쿠키에 대한 흔한 오해로, 웹사이트가 하드디스크에 저장하는 개인에 대한 정보이며 웹사이트에 접근시 사이트는 쿠키의 정보를 이용하며 새로운 정보를 저장할 수 있다는건 잘못된 정보이다.


Cookie 는 프로그램이 아니며, 개인에 대한 정보를 저장하지 못한다. 


Cookie 는 웹서버가 유저의 하드디스크에 저장하는 텍스트 조각이며 Binary 형태의 데이터가 될 수 없다.

일반적으로 쿠키는 Name-Value 쌍으로 이루어진 웹사이트 접근에 대한 정보이다.


웹서버마다 쿠키를 활용하는 방식은 다양하며, 단순히 접속에 대한 정보나 ID 만을 저장하는 데 그치지 않고, 세션 혹은 접속 시간 등 다양한 정보를 기록하기도 한다.


쿠키는 그 자체만으로 어떤 역할도 할 수 없으며, 단지 웹서버로 전송될 수 있는 정보를 가질 뿐이다.


쿠키는 HTTP 프로토콜 상의 제약에 따라 4kb 까지 저장할 수 있으며 하나의 도메인은 브라우저마다 다룰 수 있는 쿠키 갯수의 상한을 갖는다.

즉, 쿠키는 도메인별로 브라우저가 Storage 에 관리하는 형태의 정보로, 브라우저가 쿠키의 보안을 관리한다.


이렇듯 브라우저가 도메인별로 쿠키를 관리하기 때문에 서로 다른 브라우저는 서로 다른 쿠키를 갖는다. 즉, 브라우저별로 쿠키는 공유되지 않는 내부 저장 데이터이다.


쿠키는 서버와 유저의 Local storage 간의 약속이므로, 같은 Name 으로 Value 를 기록(Write) 하고 읽어오는 것(Read)이 중요하다. 

대부분 쿠키는 웹브라우저의 내부 저장소에서 Javascript 를 통해 서버에서 핸들링 할 수 있도록 처리된다.


"여기서 클라이언트가 서버에게 쿠키를 보낸다는 점이 중요한데, 대부분의 경우 특별한 제약 등을 정의하지 않으면 Cookie 는 서버로 향하는 모든 요청과 함께 전송된다."


주로 쿠키를 사용하는 목적은 다음과 같다.


- Authentication : 세션 관리.


- User Tracking : 유저 방문 추적


- Personalization : 테마, 언어 등 커스텀화된 설정에 대한 목적



웹사이트를 통해 개발한다면 브라우저의 개발자도구를 이용해 쿠키에 대한 정보를 언제든 접근 가능하다.




대부분 쿠키나 세션의 경우 브라우저에서는 Cookie API 를, 서버 측에서는 Web framework 들이 처리할 수 있는 로직을 내장하고 있으나, 

관련 작업을 할 경우 위와 같이 체크하면서 작업하면 유용하다.


Mozilla 공식 홈페이지는 쿠키의 생성 및 사용 로직이 단순하기 때문에 정해진 용도 이상의 사용, 특히 개인정보의 저장 등을 위해 사용하는 것이 위험하다고 경고하고 있다.



DOM은 W3C 표준으로 정의되어있는 문서의 항목을 객체화하기 위한 방법으로, 다음과 같은 3가지 파트로 나뉘어 있다.


 - Core DOM : 대부분의 Document Type 에서의 Object 형태를 말한다.

 - XML DOM : XML 문서 타입에서의 객체형태를 말한다.

 - HTML DOM : 포스팅에서 다루고자 하는 내용이며, HTML 문서 내에서 객체들이 해석되기 위한 형태를 말한다.


즉, HTML 에서 DOM이라함은 W3C의 HTML DOM 표준을 말하는 것이다.


DOM 오브젝트란 Document Object Model 의 약어로 웹브라우저가 HTML 을 해석하는 방식이자, Document 및 내부 구성요소를 객체화한 형태의 집합이라 할 수 있다.


HTML 의 각 요소들은 HTML Tag 및 텍스트들로 구성되어 있고, 이 각각의 항목을 노드(Node) 라 하고, 각각의 항목들은 Tree 의 계층형 구조를 이루고 있다.


HTML DOM 은 프로그래밍적 인터페이스를 허용하게 되어있으며 그에 따라 인터프리터가 Object 형태로 관리하게 되어있다.

각 Element 들은 Property 나 methods, events 를 가질 수 있게 설계되어 있다.


HTML DOM 은 HTML 의 Element 들을 나타내는 방식이자 Web 에서 문서가 동작하기 위한 표준으로 이해하면 된다.


<참고 : https://www.w3schools.com/js/js_htmldom.asp>




먼저 <div> 태그와 <span> 태그의 차이점을 알아보자.


사용하는데 있어서 차이를 알아내긴 어렵지만 이는 생각보다 간단한데 단순히 div 는 block element 이고, span 은 inline element 라는 점이다.


좀더 설명하자면, div는 Document 의 부분들을 감쌀 수 있도록 설계되어 있지만, span 은 small portion(text, image 등)에 해당하는 작은 부분만 감싸게끔 설계되어 있다.


여기서 Block Level Element 와 Inline Level Element 라는 다소 생소한 분류가 등장한다.


* Block Level Element 와 Inline Level Element 의 차이점


 : Block Level Element 들은 <body> 요소 안에서만 표현되도록 정의되어 있다. 또한 Block Level element 들은 

 감싸고 있는 표현 전후를 분리시켜주는(개행) 특징을 가진다. 즉, 이 영역은 앞 뒤 영역과 구분된 독자적인 영역이 된다.

 또한 Block Level Element 들은 Inline Level Element 들을 포함할 수 있다.


 : Inline Element 는 Tag 가 시작된 라인에서 바로 시작하며 이전과 이후를 구분(개행)하지 않는다. HTML5 이전의 일반적인 Inline Element 는 Block Element 를 포함하지 못한다.



 HTML5가 되면서, 몇몇 Block Element 들이 Inline Element 안에 포함이 가능하게 되면서, 차이점이 조금 불분명해졌다.


그럼에도 Span 은 Phrasing content 만을 안에 포함시킬 수 있고, div 는 flow content 를 포함시킬 수 있다는 차이점이 있다.

여기서 flow content 와 phrasing content 는 부가적인 분류 카테고리이다. 

flow content 는 마크업으로써 표현되는 컨텐츠 자체를 의미하며, phrasing content 는 해당 요소가 강조 하거나 꾸며주는 역할을 하는 도구로써의 Element 일 경우를 말한다.


HTML이 모던해짐에 따라 element 들은 복수의 역할을 수행할 수 있도록 진화하고 있으나, 그건 HTML 초기 디자인이 의도한 바가 아니다.

가령 <p> 태그는 paragraph 를 위해 만들어진 태그이며, <li> 는 list item 을 위해 만들어진 태그이다.

HTML 은 모던해짐과 동시에 혼용될 수 있는 역할에 따른 사용을 방지하기 위해 의미없는 차이임에도 태그를 나누는 경우들이 있다.




+ Recent posts