코드스피츠 CSS Rendering 5회차 정리 (STEP 46, 47)
Semantic Web & CSS Query (microdata, dataset 활용)
STEP 46, 47
- 작성자: Wol-dan (@pul8219)
- 스터디 주제: FrontEnd 면접 스터디 https://gitlab.com/siots-study/topics/-/wikis/%EC%8B%AC%ED%99%941
- 공부 범위: STEP 46 코드스피츠76 CSS Rendering - 5회차
1/2
, STEP 47 코드스피츠76 CSS Rendering - 5회차2/2
- 기한: 07/17(토) ~ 07/20(화) (STEP 45), 07/24(토) ~ 07/27(화) (STEP 47)
- 📋 스터디 문서 목록 바로가기
코드스피츠76 CSS Rendering - 5회차 강의를 보고 정리한 내용입니다.
✍️ Semantic Web
Semantic Web에서 CSS를 어떻게 쿼리처럼 쓸 수 있을까?
- HTML 문서는 Semantic하게 구성되어있다.
- Semantic Web = 의미론적 웹 = HTML 태그를 하나의 문서로서
의미
를 갖도록 태깅하자는 철학 - 웹 표준에서 권장하는 방식:
HTML은 의미론적으로 작성
,화면에 보이는 모양은 CSS로 조정
- Semantic Web을 사용하는 이유: 검색엔진이 HTML을 읽어들였을 때 마치 데이터베이스처럼 문서로부터 유의미한 것들을 추출해 다양한 검색 결과를 나타내기 위해서(기계 친화적, 크롤러 친화적인 웹을 위함)
- 오늘 배우는 것의 핵심: 이러한 Semantic Web에서 CSS를 어떻게 사용하는 것이 좋을까에 대한 고민
-
DOM -> 의미를 갖도록 태깅
- 의미상 무엇인가(Semantic)를 기준으로 태그를 만들 것
- 엘리먼트가 화면에 어떻게 보일지, 위치가 어딘지에 상관없이 태깅하기(요소가 태그상으로는 제일 먼저 작성됐지만 화면에서는 맨 아래 있을 수도 있음)
- 줄을 나누기 위해 div, p태그를 함부로 쓰거나, 장식을 위해 img 태그를 넣는 등의 행위를 허용하지 않아야 한다.
- Semantic Web을 잘 사용하면 태그 수가 줄어드는 효과 O
- Ex) 시간을 표현 -> time 태그 / 제목 -> header 태그 / 글 하나하나는 -> article
- 의미상 무엇인가(Semantic)를 기준으로 태그를 만들 것
-
CSS Selector -> DOM의 구조에 밀접하게 선택
- 보통 CSS Selector는 Sematic과 무관하게 DOM 구조에 밀접하게 작성된다.
- Ex) a 태그 안에 p -> 종종 이런식으로 선택함
- CSS Selector로 Semantic Web을 만드려는 것은 잘못된 시도처럼 보인다. (뒤에서 자세히 설명할 것)
-
CSS 자체 -> 의미와 무관한 스타일
- 그래픽스 렌더링 엔진과 관련됨(그림을 어떻게 그릴지)
- Ex) background-color: yellow
CSS Selector
는 DOM
과 CSS(자체)
를 잇는 역할을 한다. 이를 잘 작성하면 그 둘이 유기적으로 연결되고 유지보수가 쉬워진다.
보통은 문서의 모양(DOM 구조)에 따라 CSS Selector
를 작성하는데, 이때 중간에 엘리먼트를 넣는 등의 수정이 일어나면 CSS Selector가 모두 깨지는 일이 발생할 수 있다. 결국 CSS Selector로 인해 DOM을 건드리지 못하는 상황까지 생겨버린다.(CSS Selector를 잘 설계해야하는 이유)
-
문제점
- 1️⃣ DOM을 스타일에 맞춰 제작하는 것 -> Semantic Web에서 DOM은 의미를 갖도록 태깅해야한다.
- 2️⃣ (1번의 문제로 인해) 태그의 변화가 스타일을 깨먹음
- => 결국 HTML, CSS를 유지보수하기 어려워진다.
- 해결방법
- Semantic Selector: CSS Selector도 Semantic하게 짜보자
- DOM의 구조에서 벗어나자: Selector에 오직 class만 써도 반은 해결된다.(
p > a > ...
이런건 유지보수가 힘듦) - ➕ 오늘 배우는 내용들을 숙지할 것
CSS Attribute Selector
CSS Attribute Selector
는 해당 엘리먼트의 속성(attribute)을 기반으로 엘리먼트를 선택할 수 있게 해준다.- 대괄호(
[]
)로 시작한다.
사용 가능한 여러 문법 맛보기
[attr]
- 속성이 존재함
- Ex)
input[type]
input 태그 중에 type이 존재하는 엘리먼트들 모두를 선택함[attr=val]
- 값(val)과 일치[attr~=val]
- 공백으로 구분된 단어로 포함되면 일치
- Ex) HTML에 속성을 줄 때 공백(Space)로 여러개를 줄 수 있음을 기억하자. 그렇게 준 속성안에 일치하는 값이 있는지 찾는다.
[attr:=val]
- 일치하거나 뒤에 -가 붙을 때[attr*=val]
- 값이 포함될 때[attr^=val]
- 값으로 시작될 때 # 정규식에서 온 표현(^
=시작,$
=끝)[attr$=val]
- 값으로 끝날 때
출처: https://drafts.csswg.org/selectors-3/#attribute-selectors
Query Style Selector
CSS Query는 SQL Query와 굉장히 비슷하게 작성한다.
SQL Query: select
*
fromtable
wherefield conditions..
CSS Query:
base selector
[field conditions
][..
][..
]..
base selector
:table
과 같은 역할. 얻고자하는 DOM 그룹을 의미field conditions
: 조건 (나열해서 작성한다)
내가 원하는 DOM의 그룹에서 원하는 엘리먼트들만 고르는 작업이다.
Query Style Selector 예제
// 📁scss 파일
#join {
[name="userid"] {
border: 1px solid #f00;
}
[name="pw"] {
border: 1px solid #0f0;
}
[name="nick"] {
border: 1px solid #00f;
}
}
<!-- 📁html 파일 -->
<form id="join">
<input type="text" name="userid" />
<input type="password" name="pw" />
<input type="text" name="nick" />
</form>
- 기존 방법: DOM 구조와 관련된 방법들 ex) 선택하고싶은 태그에 id나 class를 모두 부여하거나 혹은 form 태그 안에 몇번째 태그인지 식별하는 방식으로 셀렉터를 작성
- 제안 방법:
CSS Query Selector
사용- (위 html 코드 참고)
join
을 하나의table
로 보고 질의(query)를 하자 name
과 같이 DOM 구조나 디자인에 영향받지 않는 서버와 합의된 내용을 선택하도록 한다. 이렇게 데이터 구조에 근거를 둔 셀렉팅이 적절하다.
- (위 html 코드 참고)
💡 Sass에선 selector를 중첩하여 쓸 수 있다. CSS였다면 다음과 같이 작성해야한다. Sass를 사용하면 위 예제처럼 코드의 중복을 없애 셀렉터 수정시 실수를 방지할 수 있다.
#join [name="userid"] {
border: 1px solid #f00;
}
#join [name="pw"] {
border: 1px solid #0f0;
}
#join [name="nick"] {
border: 1px solid #00f;
}
결국, 태그에서 진짜 의미를 가진 것을 추출해 거기에 스타일을 주는 것이 핵심이다.
1️⃣ HTML5 Microdata
HTML Semantic 스펙인 microdata를 본격적으로 알아보자. 아래 속성들을 사용하자는 것이 microdata의 핵심이다.
itemscope
- 적용범위 설정- 어떤 div에 scope를 지정하면 그 안에만 적용이 된다.
itemtype
- 스키마 설정- 스코프에 type을 지정
itemid
- 특정 id 부여- 스코프 내부 여러 아이템들에 특정 id 부여
itemprop
- 속성명content
- 비가시적일 때 값을 설정value
- 가시적인 값이 원하는 값이 아닐 때itemref
- scope 계층구조 안에 없을
출처: https://www.w3.org/TR/microdata
Schema.org
Schema.org 사이트에서는 microdata 포맷으로 된 스키마(데이터 구조)를 공유하고 있다. 공식적인 표준은 아니지만 많은 사람들이 따르고 개선하고 있는 문서이다.
우리가 사용해볼 스키마들
- http://schema.org/BreadcrumbList
- http://schema.org/WebPage
- WebPage라는 형태(type)에 대해 이러이러한 종류의 속성(property)이 있다고 기술되어있다. 그 속성들에 올 수 있는 값들도 기술되어있다. 값이 그냥 Text인 경우도 있지만 또 다른 스키마인 경우도 있다.
- http://schema.org/CollegeOrUniversity
💡 우리가 임의로 스키마를 정의해도 되지만, 이 경우 정의 후에 웹에 url로 스키마 포맷을 공유해야한다.
microdata 예제
- 일단 먼저 html 틀을 작성해보자
<!-- 화면에 보이는 디자인 문서에 가까움 -->
<body>
<h1>CodeSpitz76 - 5</h1>
<nav>Home</nav>
<p>코드스피츠76 5회차 수업은 css를 쿼리화하여 사용하는 방법을 다룹니다.</p>
<ul>
<li>
<h2>HTML5 MicroData</h2>
<p>마이크로데이터에 대한 개념과 예제</p>
</li>
<li>
<h2>HTML5 DataSet</h2>
<p>데이터셋에 대한 개념과 예제</p>
</li>
</ul>
<footer>
<div>MIT</div>
<div>Bsidesoft co.</div>
</footer>
</body>
- microdata 적용을 위해 위의 html 파일 아래와 같이 수정
<!-- 위 HTML에 microdata 적용 -->
<!-- microdata를 적용하기 전보다 문서화, 데이터베이스화 됨 -->
<body itemscope itemtype="http://schema.org/WebPage">
<h1 itemprop="name">CodeSpitz76 - 5</h1>
<nav itemprop="breadcrumb">Home</nav>
<p itemprop="description">
코드스피츠76 5회차 수업은 css를 쿼리화하여 사용하는 방법을 다룹니다.
</p>
<ul>
<li
itemprop="mainEntity"
itemscope
itemtype="http://schema.org/CollegeOrUniversity"
>
<h2 itemprop="name">HTML5 MicroData</h2>
<p itemprop="description">마이크로데이터에 대한 개념과 예제</p>
</li>
<li
itemprop="mainEntity"
itemscope
itemtype="http://schema.org/CollegeOrUniversity"
>
<h2 itemprop="name">HTML5 DataSet</h2>
<p itemprop="description">데이터셋에 대한 개념과 예제</p>
</li>
</ul>
<footer>
<div itemprop="license">MIT</div>
<div itemprop="publisher">Bsidesoft co.</div>
</footer>
</body>
-
코드 설명
- body에
itemscope
를 줌으로써 하나의 스코프로 지정 - 스코프를 지정하면서 type(
itemtype
)을 같이 준다. type은 uri로 준다.(고유하게) 이로써 type이 무엇인지 알 수 있다. - li에
itemprop
속성값으로 mainEntity(여러개 지정 가능)를 주고, 새로운 스코프를 지정했다. 그럼 li안이 또 다른 스코프가 된다. <h1 itemprop="name">CodeSpitz76 - 5</h1>
의 name은 WebPage 스코프의 name이지만, li 내부<h2 itemprop="name">HTML5 MicroData</h2>
의 name은 CollegeOrUniversity의 name으로 전자와 완전히 다른 name이다.- license, publisher는 WebPage 스코프에 소속된 속성이다.
- 이 문서는 Website(WebPage 타입)를 나타내는 문서이고 각각은 논문 아이템들(CollegeOrUniversity 타입)인 것이다.
- body에
이처럼 microdate포맷은 HTML의 구조와 일치하면서 문서에 의미(Semantic)를 부여할 수 있게 한다.
위 그림을 보면 스코프의 의미가 무엇인지 명확하게 이해할 수 있을 것이다.
위 예제를 기반으로 Semantic Query를 짜면 아래와 같다. 쿼리를 보면 DOM 구조가 아예 나오지 않는다.(DOM 구조에서 벗어남!)
microdata 정리
- microdata를 적용하면 유지보수가 용이해진다. 위에서 본 예제 코드에서 디자인이 개편되더라도 작성자를 고치고 싶다면 WebPage의 publisher로 가서 고치면 된다.
- 또한 예를 들어 다크 테마로 바꾸고 싶은 경우에도 셀렉터가 아닌 내부 코드만 고치면 테마를 쉽게 바꿀 수 있다.
- Semantic화가 잘 되어있는 문서는 JSON으로 번역하지 않고 HTML 채로 서버로 보내도 된다. 왜냐하면 microdata DOM parser도 사용할 수 있게 표준화돼있기 때문이다.
- 아까 봤던 코드에서 name으로 짰던 쿼리보다 microdata를 활용한 이 방법이 더 Semantic한 코드이다.
- DOM 의존성을 벗어나는 방법은 attribute selector를 쓰는게 아니라 이렇게 DOM 구조에 영향을 받지 않는 셀렉팅 방법을 쓰는 것이다.
2️⃣ HTML5 dataset
-
HTML5에서는 microdata 외에도 커스텀 속성을 사용할 수 있는 dataset가 있다.
-
W3C HTML5 Validator를 통과한다는 특징이 있다.
- 예를 들어 일본에 웹사이트 납품하는 경우를 생각해보자. 일본의 경우 HTML, CSS가 W3C Validator 기준을 통과해야 납품이 가능하다. 태그에 마음대로 커스텀 속성을 쓰면 납품할 수 없다. 하지만 그래도 태그에 커스텀 속성을 넣고 싶다면? 표준적인 커스텀 속성을 위한 속성 확장자인
dataset
을 쓰면 된다.
- 예를 들어 일본에 웹사이트 납품하는 경우를 생각해보자. 일본의 경우 HTML, CSS가 W3C Validator 기준을 통과해야 납품이 가능하다. 태그에 마음대로 커스텀 속성을 쓰면 납품할 수 없다. 하지만 그래도 태그에 커스텀 속성을 넣고 싶다면? 표준적인 커스텀 속성을 위한 속성 확장자인
-
dataset을 사용해도 micro-data 처럼 DOM구조, 변해야 할 요소와의 의존성을 낮추면서 Semantic하게 코드를 작성할 수 있다.
dataset 사용법
data-
뒤에 원하는 속성 이름을 붙인다.- 단어가 더 필요하면
-
로 잇는다. ex)<div data-memberId></div>
가 아니라<div data-member-id></div>
(html은 모두 소문자로 적용됨) - 자바스크립트에서는
element.dataset.속성명
으로 사용 가능 ex)console.log(div.dataset.memberId)
dataset 예제
<!-- 📁 index.html -->
<body data-type="http://schema.org/WebPage">
<h1 data-name>CodeSpitz76 - 5</h1>
<nav data-breadcrumb>Home</nav>
<p data-description>
코드스피츠76 5회차 수업은 css를 쿼리화하여 사용하는 방법을 다룹니다.
</p>
<ul>
<li data-mainEntity data-type="http://schema.org/CollegeOrUniversity">
<h2 data-name>HTML5 MicroData</h2>
<p data-description>마이크로데이터에 대한 개념과 예제</p>
</li>
<li data-mainEntity data-type="http://schema.org/CollegeOrUniversity">
<h2 data-name>HTML5 DataSet</h2>
<p data-description>데이터셋에 대한 개념과 예제</p>
</li>
</ul>
<footer>
<div data-license>MIT</div>
<div data-publisher>Bsidesoft co.</div>
</footer>
</body>
- 이렇게
data-name
만 쓴 부분처럼 값이 없는 선언 상태도 허용하는게 dataset의 장점이다. (=
로 데이터값을 주지 않아도 괜찮다는 것) - microdata를 쓸 때처럼 일일이 type을 알 필요가 없다.
/* 📁 style.scss */
[data-type$="WebPage"] {
color: #900;
[data-name] {
font-size: 20px;
padding: 0;
margin: 10px 0;
}
[data-breadcrumb] {
font-size: 11px;
margin: 0 10px;
color: #234a7f;
}
[data-description] {
font-size: 14px;
line-height: 20px;
color: #444;
}
ul {
list-style: none;
padding: 0;
}
[data-mainEntity] {
padding: 0 20px;
margin: 10px;
border: 1px solid #999;
border-radius: 20px;
}
[data-license] {
font-size: 11px;
color: #999;
}
[data-publisher] {
font-size: 12px;
font-weight: bold;
font-family: sans-serif;
}
}
[data-type$="CollegeOrUniversity"] {
[data-name] {
font-size: 15px;
}
[data-description] {
font-size: 12px;
line-height: 15px;
}
}
scss를 이용한 다차원 쿼리 예제
정적인 사이트의 구조를 포맷팅하는 다차원 쿼리
-
코드에서 구현하고자하는 것
- 계속 개근인 사람 -> 초록색 표시
- 계속 결석인 사람 -> 빨간색 표시
- 한번이라도 출석한 사람 -> 노란색 표시
-
코드 설명
- CSS module 3,4 는 본문 인식을 할 수 없다. (아래 코드에서 ‘출석’, ‘결석’ 인식 불가능하다는 말) -> 하지만 CSS module 5에서 기능이 추가될 예정이다.
- CSS에서
,(콤마)
: 여러개에 동시적용 - selector
+
: 옆에 있는 태그를 인식. nextSibling과 연결한다. (sibling은 형제자매란 뜻. previousSibling, nextSibling 등의 개념이 있음)
💡 1회차에서 언급했듯, CSS는 모듈별로 스펙이 존재한다. CSS3 이런건 없다! CSS는 공식 스펙은 2.1에서 멈췄고 그 이후론 CSS 모듈 스펙으로 바뀌어서 개별 모듈마다 버전이 따로 존재한다.
<!-- 📁 index.html -->
<h1 data-name>CodeSpitz76</h1>
<table>
<thead>
<th>번호</th>
<th>이름</th>
<th>1강</th>
<th>2강</th>
<th>3강</th>
<th>개근</th>
</thead>
<tr data-id="1">
<td>1</td>
<td data-name="이수박">이수박</td>
<td data-1="on">출석</td>
<td data-2="on">출석</td>
<td data-3="on">출석</td>
<td></td>
</tr>
<tr data-id="2">
<td>2</td>
<td data-name="김레몬">김레몬</td>
<td data-1="on">출석</td>
<td data-2="on">출석</td>
<td data-3="off">결석</td>
<td></td>
</tr>
<tr data-id="3">
<td>3</td>
<td data-name="박오렌">박오렌</td>
<td data-1="on">출석</td>
<td data-2="off">결석</td>
<td data-3="off">결석</td>
<td></td>
</tr>
<tr data-id="4">
<td>4</td>
<td data-name="서라임">서라임</td>
<td data-1="off">결석</td>
<td data-2="off">결석</td>
<td data-3="off">결석</td>
<td></td>
</tr>
</table>
// 📁 style.scss
[data-1="on"],
[data-2="on"],
[data-3="on"] {
color: green;
}
[data-1="off"],
[data-2="off"],
[data-3="off"] {
color: red;
}
/* 초록색을 표시하는 코드 */
// 출석,출석,출석인 경우 그 옆에 있는 td에 스타일을 주겠다는 것
[data-1="on"] + [data-2="on"] + [data-3="on"] + td {
background: green;
}
/* 빨간색을 표시하는 코드 */
[data-1="off"] + [data-2="off"] + [data-3="off"] + td {
background: red;
}
/*노란색 표시 로직 (scss 문법을 떠올려보기)*/
// 1) on/off/off, off/on/off와 같은 경우
// i와 j를 돌면서 i==j인 경우는 on, 아닌 경우는 off를 ret 빈 문자열인 ret 변수에 더해가면서 결과적으로 ret 문자열에 모든 경우의 수가 담김.
@for $i from 1 through 3 {
$ret: "";
@for $j from 1 through 3 {
@if $j == $i {
$ret: $ret + "[data-" + $j + '="on"]';
} @else {
$ret: $ret + "[data-" + $j + '="off"]';
}
$ret: $ret + "+";
}
$ret: $ret + "td";
#{$ret} {
background: yellow;
}
}
// 2) on/on/off 같은 경우
@for $i from 1 through 3 {
$ret: "";
@for $j from 1 through 3 {
@if $j == $i {
$ret: $ret + "[data-" + $j + '="off"]';
} @else {
$ret: $ret + "[data-" + $j + '="on"]';
}
$ret: $ret + "+";
}
$ret: $ret + "td";
#{$ret} {
background: yellow;
}
}
위 scss파일을 css파일로 컴파일해서 실행결과를 확인해보자. 변환된 css파일을 보면 일일이 css코드를 짜는 것보다 scss 문법을 사용하여 쿼리를 짜는 것이 훨씬 빠르고 좋은 것을 알 수 있다.
- 고민🤔
- 위 코드에서
2)on/on/off 같은 경우
를 직접 작성했는데 만약 가짓수가 3개가 아닌 4, 5개로 늘어나면 코드가 제대로 동작하지 않는다. 다르게 짜야할 것 같다.
- 위 코드에서
✍️ SCSS > CSS 변환 방법
1️⃣ scss 설치
# global로 설치
$ npm install -g node-sass
2️⃣ scss > css 변환
$ node-sass style.scss > style.css
# or
# scss 파일이 변경되는 것을 감지하여 자동으로 css 파일을 수정하는 명령
$ node-sass style.css --watch style.css
컴파일 도중 마주친 에러
Case 1) VSCode 터미널에서 scss 관련 명령어가 실행되지 않음
-
node-sass 혹은 scss 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다...
-> ‘node’ 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다. /환경변수 에러 방법으로 해결(제어판 > 시스템 및 보안 > 시스템 > 환경변수 > Path에 node.js 경로를 추가 후 재부팅, VSCode 재시작) -
더군다나 새로운 에러가 발생함
- VSCode 터미널 오류: 이 시스템에서 스크립트를 실행할 수 … 글을 참고하여 수정한 이후, 명령어가 실행은 되지만 변환된 CSS 파일이 이상한 문자와 함께 깨지는 문제가 생겼다.
-
scss 파일이 있는 파일 경로에서 명령프롬프트(cmd)로 명령어를 실행했을 때는 문제없이 잘 실행된다.(?)
마무리
- 생명력이 길고, 유지보수가 용이해지는 CSS Selector는 어떻게 작성해야할까에 대한 고민이 필요하다.
- Semantic 웹 기반으로 Selector를 작성하는 것은 그 많은 요령들 중 하나일 뿐이다.
- 강조했듯 HTML5에서 id, class를 의미론으로 사용하지 말고 디자인을 위해 비워두자. 변하지 않는 데이터에 대해서는 micro-data나 dataset을 사용하자.
Comment
References
팀원들 결과물
- @eyabc
- @khw970421