CSS의 구성 요소 (Selectors, Property, value)와 우선순위 총 정리
CSS 학습의 중요성
CSS를 학습하는 데 있어서 가장 중요한 것은 직접 타이핑 해보는 것이다.
퍼블리셔가 아니라 프론트엔드 과정을 희망하고 학습을 시작했다면, 이것 말고도 배워야 할 게 산더미이고 그 중에서 자바스크립트와 알고리즘, API 통신의 벽을 만나는 순간 사실 화면을 그리는 CSS의 중요도는 떨어질 수밖에 없다.
뒤로 갈수록 학습량이 현저히 줄어들기 때문에 처음에 HTML과 CSS를 배우는 단계에서 명확히 이해하고 넘어가는 것이 좋다. 본인도 이미 부트캠프 전과정을 수료했고 눈에 띄는 결과물도 만들어냈지만 결국 다시 처음부터 학습하는 이유는 기본기가 부족하다고 느껴졌기 때문이다.
이후에 bootstrap, NextUI, TailwindCSS 등 다양한 라이브러리를 만나게 되면서 실제로 내가 하나하나 구성하는 것보다 이미 만들어진 컴포넌트를 갖다 쓰는 게 더 빠르고 효율적인 순간을 만나게 된다. 이 때에는 CSS를 자세히 들여다 볼 시간적 여력이 부족하므로 지금 복붙하지 말고 꼭 직접 타이핑해보는 것을 추천하고 싶다.
CSS의 구성요소
CSS는 HTML과 함께 사용하며, 위 예제는 <div> 태그 자체를 스타일링 한 내용이다.
CSS를 보면, 자바스크립트를 모른다면 현재는 잘 모르겠지만 객체 형태로 이루어져있음을 알 수 있다.
위에서 div, 즉 어떠한 것을 지정하고 선택한 것을 선택자(Selectors)라고 부른다. 위 CSS는 div 태그 자체를 선택한 것이다.
그리고 font-size, color, background-color 등은 속성(Property)이라고 부른다. 그리고 가장 오른쪽에 실제 수치를 나타내는 것을 값(Value)라고 부른다.
그리고 color: white; 처럼 특정 속성에 값을 부여하는 행위를 선언(Declaration)이라 말한다. 선언이 끝날 때는 세미콜론(;)으로 선언 간의 구분을 한다. 자바스크립트에서 보통 객체는 콤마(,)로 구분하지만 CSS는 세미콜론이라는 것이 차이이고 초심자는 이마저도 헷갈릴 수 있으니 꼭 손으로 쳐보자.
선택자의 종류
선택자에는 여러 종류가 있다. 위의 예제에서는 <div> 태그 자체를 선택했지만 다양한 방법이 있다.
기본 선택자(Basic Selectors)
- 요소 선택자(Element Selector) : h1, div 등의 태그를 직접적으로 선택하는 것.
- 클래스 선택자(Class Selector) : .class-name 형태로 특정 클래스를 선택하는 것.
- 여기서 클래스란, <div class="main-area"> 와 같이 div 태그 자체를 선택하는 것이 아니라 이 클래스를 명시해준 곳에만 CSS 스타일링을 하기 위해서 지정하는 것이다.
- 위 처럼 클래스를 지정하면 CSS에서는 .main-area {} 와 같이 선택자를 지정할 수 있다.
- 클래스는 태그 요소가 다르더라도 여러 군데에 같은 클래스를 지정할 수 있다. 뒤에 나올 아이디 선택자는 단 하나의 고유한 요소에만 사용 가능하다.
- 아이디 선택자(ID Selector) : <div id="main-area">와 같이 id를 지정하는 것을 말한다. 이 값은 단 하나만 사용할 수 있다.
- 위처럼 특정 요소에 아이디를 부여하면 CSS에서는 #main-area {} 와 같이 선택자를 지정할 수 있다.
- 전체 선택자(Universal Selector) : * {} 기호로 선택자를 지정하며 HTML의 모든 요소에 같은 스타일을 지정한다는 의미가 된다.
- 후에 다루겠지만 보통 스타일링을 초기화, 즉 리셋할 때 주로 사용한다.
- * { margin: 0; padding: 0; } 과 같은 형태로 문서 전체의 패딩 값과 마진 값을 없앨 때 사용한다.
- 브라우저마다 각 요소마다 갖고 있는 여백이나 마진, 기본 폰트 등이 다르기 때문에 개발자의 의도대로 모두가 같은 화면을 보는 게 아닐 수도 있어서 프로젝트 단위의 서비스를 만들 때는 항상 이 리셋 스타일시트를 사용한다.
결합 선택자(Combinators)
- 자식 선택자(Child Combinator) : > 기호를 사용하며 부모의 직접적인 자식만 선택.
- 하위 선택자(Descendant Combinator) : 공백을 사용하며 부모의 모든 자식을 선택.
- 인접 형제 선택자(Adjacent Sibiling Combinator) : + 기호를 사용하며 동일한 부모 요소를 가진 인접 형제 요소를 선택.
- 일반 형제 선택자(General Sibiling Combinator) : ~ 기호를 사용하며 동일한 부모 요소를 가진 모든 형제 요소를 선택.
아주 복잡한 개념이 나왔다. 차근 차근 설명하겠다.
HTML과 같은 마크업 언어와 프로그래밍 언어에는 부모(Parents)와 자식(Childeren)이라는 개념이 있다. 코드를 보면 이해가 빠르다.
<div class="parent"> <!-- 부모 요소 -->
<p> 나는 자식 </p> <!-- 첫번째 자식 -->
<div> <!-- 두번째 자식 -->
<p> 나는 손주 </p> <!-- 두번째 자식의 자식, div라는 부모에게는 손주 -->
</div>
</div>
자 HTML을 보면 깊이(depth)에 따라서 부모오 자식이 결정되는 것을 볼 수 있다.
손주라는 개념은 없고 부모-자식만 있지만, 위 처럼 보면 이해가 빠르다.
자식 선택자 (>)
위 사진에서 자식선택자를 사용해서 부모인 .parent의 > p를 선택하니, 부모의 직접적인 자식인 첫번째 자식에게만 빨간색 텍스트 컬러가 적용된 것을 볼 수 있다.
<p> 태그가 두번째 자식의 자식, 즉 손주에게도 있지만 부모의 직접적인 자식이 아니기 때문에 적용되지 않은 것이다.
부모인 <div> 태그에 class를 지정한 이유는, div > p 의 형태로 쓰면 첫번째로 등장하는 div, 즉 부모의 자식인 첫번째 p태그 "나는 자식"도 해당되고, 두번째 자식도 <div> 이기 때문에 이 두번째 자식의 자식인 p태그에도 적용되기 때문에 클래스로 구분지은 것이다. 클래스를 없애고 div > p로 사용하게 되면 두 텍스트가 모두 빨갛게 변한다.
하위 선택자 (공백)
.parent p 처럼 > 기호 하나만 없애고 공백으로 두게 되면, 부모 요소의 자식, 손자, 중손자까지 모두 선택하게 된다. 따라서 모든 p태그가 빨갛게 변한 것을 볼 수 있다.
그러면 그냥 p 태그로 쓰면 되지 않느냐고 생각할 수 있지만, 그렇게 되면 문서 전체의 p 태그에 스타일이 적용되는 것이기에 특정 부모의 아래에만 스타일을 적용하고 싶을 땐 이런 결합 선택자를 사용한다.
인접 형제 선택자 (+)
위 사진처럼 인접 형제 선택자를 사용하면 바로 뒤의 요소에만 스타일을 적용한다는 의미이다. 따라서 h2 태그의 바로 인접한 형제인 p 태그에만 스타일이 적용된 것을 볼 수 있다.
그런데 나는 여기서 하나 궁금증이 생겼다. 이렇게 하나만 직접적으로 선택하는 거면 그냥 두번째 자식에게 id 선택자를 부여하면 될 것 같은데, 왜 h2+p 처럼 복잡하게 사용할까?
- 동적으로 생성되는 HTML 문서에 사용
- 위 사진의 예시는 개발자가 직접 의도하고 HTML 마크업을 작성한 것이지만, 지금 보고 있는 티스토리 같은 블로그에서는 글쓰기 에디터로 글을 작성하게 되는데, 글 작성자는 HTML 태그로 글을 작성한 것이 아니라 에디터에서 시각화되어 완성된 글 모습을 보며 작성하는 것이다.
하지만 이는 결국 우리 눈에 보이지만 않을 뿐 HTML 언어로 작성되어 있는 것이다. - 따라서 처음에는 빈 문서로 되어 있다가 사용자가 글을 쓰는 순간 <p> 태그가 생성되기에 이렇게 동적으로 만들어지는 문서에는 id나 class를 부여하기 어렵다. (안 되는 것은 아니지만, 자바스크립트로 또 동적으로 id를 부여해야 하기 때문에 자바스크립트에 자꾸 스타일링과 관련된 코드가 길어지면 가독성이 떨어지고 유지보수성이 나빠질 수 있다. 스타일과 관련된 것은 웬만하면 CSS에서 해결하는 게 제일 베스트.)
이럴 때 CSS 스타일링을 인접 형제 선택자로 한 번만 지정해놓으면 동적으로 계속 생성되더라도 스타일링을 의도대로 할 수 있다.
- 위 사진의 예시는 개발자가 직접 의도하고 HTML 마크업을 작성한 것이지만, 지금 보고 있는 티스토리 같은 블로그에서는 글쓰기 에디터로 글을 작성하게 되는데, 글 작성자는 HTML 태그로 글을 작성한 것이 아니라 에디터에서 시각화되어 완성된 글 모습을 보며 작성하는 것이다.
일반 형제 선택자(~)
일반 형제 선택자는 ~ 기호를 사용하고 바로 뒤에 따르는 모든 형제들을 선택한다.
속성 선택자(Attribute Selectors) (선택자[속성="값"])
HTML 요소 자체를 선택하는 것이 아닌, 특정 속성을 갖고 있는 요소만 선택한다.
요소[속성="값'] {} 형태로 선택자를 만들면 된다.
a 태그가 여러번 사용되겠지만 href라는 프로퍼티의 값이 구글... 인 것만 스타일링을 지정한다는 뜻이다.
속성 선택자의 다른 예시는 아래와 같은 것들도 많이 사용된다.
input[type="text"] { border: 1px solid blue; } /* 입력창 중에서 type이 text인 것만 */
img[alt] { border: 2px solid green; } /* alt 속성이 있는 이미지만 */
가상 클래스 선택자(Pseudo-Class Selectors)
특정한 '상태'에 있는 요소만 선택하는 것이다.
여러가지로 응용할 수 있는데, 아래에서 두 가지 예시를 들겠다.
가상 클래스 선택자 (:hover)
원래 버튼은 보통 그레이 색상의 배경이지만, 캡처에서는 마우스 포인트가 보이지 않아서 그렇지 마우스를 올린 상태다. 버튼에 마우스를 올리면, 즉 hover 하면 CSS 스타일을 적용시키라는 의미이다.
가상 클래스 선택자 (:first-child)
li 태그에 적용할 건데, li 태그 전부에 적용하는 것은 아니고 첫번째 자식인 것만 적용한다는 의미이다.
만약 2번째 자식 요소를 선택하고 싶다면 second-child가 아니라, 두번째 부터는 nth-child(2) 이런 식으로 숫자를 직접적으로 기입해서 사용한다.
가상 클래스 선택자 (:nth-child(n))
만약 첫번째와 두번째, 이런 식으로 지정하고 싶다면 nth-child를 콤마로 구분해서 여러 번 사용하면 된다.
가상 클래스 선택자 (:nth-child 중첩 사용 - 그룹 선택자(,))
만약 <li> 태그의 반복이 아니라 전부 다른 태그라면 인접 형테 선택자로 첫번째 형제 + 두번째 형제의 형태로 사용해도 되겠지만, 위 사진에서는 모두 <li> 태그이므로 구분이 불가능하여 nth-child(n) 가상 클래스 선택자의 중첩 사용이 더 쉽다.
여기에는 재미있는 규칙을 넣을 수도 있다. (n)에는 특정 숫자가 아니라 짝수 자식 요소들만 선택하는 (even), 홀수 (odd)도 지정 가능하고 n의 배수도 지정 가능하다.
가상 클래스 선택자 (:nth-child(even))
가상 클래스 선택자 (:nth-child(odd))
가상 클래스 선택자 (:nth-child(3n)
가상 클래스 선택자 (:nth-child(-n+3))
그런데 첫번째 자식 요소부터 n번째 자식요소까지 전부 선택하고 싶다면?
-n+1(첫번째에서 첫번째까지, 즉 1개), -n+2(첫번째에서 두번째까지, 즉 2개), ... 이런 식으로 증가
이렇게 작성하면 된다.
원리는 간단하다. n이라는 의미는 0에서 숫자를 계속해서 하나씩 대입해서 계산해본다는 의미이다.
즉 위 사진에서 3n이라고 작성한 것은 3x0=0, 3x1=3, 3x2=6, ...이 나오므로, 0은 없는 것이니 패스하고 3번째, 6번째를 선택해서 3의 배수의 자식 요소들만 선택한 것이 되는 것이고,
-n+3로 예를 들면, -0+3=3번째, -1+3=2번째, -2+3=1번째, -3+3=0, ... 이 되기 때문에 0은 마찬가지로 없는 것이니 패스하면 3번째~1번째가 된다. 즉 1~3번째까지 한 번에 선택한다는 의미가 된다.
가상 클래스 선택자 (:last-child)
이 외에도 만약 가장 마지막 요소만 지정하고 싶다면 last:child를 사용하면 된다.
정리하자면 자식 요소들 중 특정 요소만 선택하는 방법에는 아래와 같은 가상 클래스 선택자가 존재한다.
- first-child
- nth-child(n)
- nth-child(odd)
- nth-child(even)
- nth-child(3n)
- last-child
가상 클래스 선택자 (:focus)
입력창에 포커스가 잡히면 CSS 속성을 적용시킨다는 의미이다.
가상 요소 선택자(Pseudo-Element Selectors) (::)
위 가상 클래스 선택자는 어떠한 "상태"에 있는 클래스만 선택하는 선택자였다. 예를 들어 마우스를 호버한 상태, 자식 요소들 중 1~3번째에 있는 상태 등이 그 예시이다.
그러나 가상 요소 선택자는 요소의 특정 부분을 선택하는 방법이다. 기호로는 ::를 사용하는데, 이것도 역시 예제를 봐야 이해가 빠르겠다.
가상 요소 선택자 (::first-letter)
p 태그(요소)에서 첫번째 글자에만 스타일링을 지정하는 방법이다.
가상 요소 선택자 (::before)
선택한 요소의 가장 앞에 content라는 프로퍼티를 넣어서 특정 값을 삽입하는 것을 의미한다.
가상 요소 선택자 (::after)
물론 before가 있으니 after로 문장 가장 뒤에 특정 값을 삽입할 수도 있다.
그룹 선택자(Grouping Selectors) (,)
이건 위에서 한 번 나온 내용인데, 선택자를 콤마로 구분해서 여러 개를 사용할 수도 있다.
CSS 선택자의 우선순위
여기서 의문이 하나 생긴다.
CSS 선택자가 동시에 여러 개 적용되는 경우에는 어떤 것부터 적용이 되는가?
CSS의 선택자의 우선순위는 CSS 선택자 특이성(Specificity)에 의해 결정된다.
우선순위는 아래와 같으며 점수로 치환할 수 있다. 점수가 필요한 이유는 이런 경우가 있기 때문이다. 클래스와 가상 클래스는 모두 클래스인데 어떤 걸 우선해야 하는가? 일반적인 마크업 언어나 프로그래밍 언어는 위에서 아래로 해석하기에 먼저 작성된 것을 우선하지만 CSS는 조금 더 구체적으로 지정된 선택자에게 더 높은 점수를 주고 우선순위를 준다.
하지만! 이 점수는 실제로 CSS에서 공식적으로 사용하는 것이 아니라 우선순위의 계산을 쉽게 하도록 사용자들이 임의로 구분한 것이니 참고만 하면 된다. 보다 더 정확한 원리는 따로 있지만 초심자 단계에서는 복잡하기만 하니 아래처럼 점수로 치환해서 이해하는 것도 도움이 된다.
- !important
- 속성의 값 뒤에 바로 붙이는 것으로, 아래 우선순위를 전부 무시하고 1순위로 만들어 버린다.
- 인라인 스타일 (1,000점)
- style="color: green"
- ID 선택자 (100점)
- #id
- 클래스 선택자 (10점)
- .class
- 속성 선택자 : [href="google..."]
- 가상 클래스 : :first-child
- 요소(태그) 선택자 (1점)
- div, p 등...
- 가상 요소 : ::before, ::after
- 전체 선택자
- *
인라인 스타일링은 이번 포스트에서 다루지 않았는데, 쉽게 이야기해서 CSS로 별도로 빼서 스타일링 하는 것이 아니라 HTML 태그의 속성으로 바로 스타일링을 하는 것을 의미한다. 크게 어려운 개념이 아니니 예제를 보면 되겠다.
위 사진을 보면 li 태그 선택자에 !important가 붙었다. 원래 우선순위대로라면 태그 선택자가 가장 낮은 우선순위라 color red는 적용되지 않아야 하지만 이 특성 때문에 첫번째 두부사기도 빨간색으로 표기가 되었다.
만약 !important가 없다면?
HTML 문서를 보면 style="color: green"이라고 인라인 스타일링이 직접적으로 적용되어 있다. 따라서 이것이 1순위라 첫번째 요소인 두부사기는 녹색으로 스타일링 되었다.
만약 인라인 스타일링이 없다면?
인라인 스타일링을 지웠더니 다음 우선순위인 #id 선택자의 color: grey가 적용되었다.
만약 ID 선택자가 없다면?
클래스, 가상 클래스 둘 다 클래스인데 결과적으로는 가상 클래스가 이겼다.
위 점수표에서 클래스는 10점이고, 요소(태그)는 1점이었다. 그런데 가상 클래스 선택자를 보면 li라는 태그가 1점으로 있고, :first-child라는 가상 클래스는 클래스에 포함되어 10점을 차지하기 때문에, 단순히 클래스 하나만 있어서 10점을 차지하는 클래스 선택자보다 가상 클래스 선택자가 더 높은 우선순위를 부여받은 것이다.
위에서도 말했듯 점수는 단순히 계산을 편리하게 하기 위해서 만든 것이고, 실제 구체적인 원리는 더 구체적이고 자세하게 선택한 선택자에게 더 높은 우선순위를 준다는 것이다.
만약 같은 점수가 있다면? 위에서 아래로 작성 순서로 적용하는 것이 아니라 CSS는 가장 나중에 적용된 스타일을 적용한다. 매우 중요한 포인트이다.
CSS 우선순위 선정의 기본 원리
위에서는 이해를 돕기 위해 점수표를 사용했고 실제로 저렇게 설명하는 접근법이 많다. 하지만 본인은 공식적인 원리를 탐구하는 것을 더 좋아한다. 원리를 이해하는 데는 오래걸리겠지만 일단 이해하고 나면 얼마든지 응용이 가능하기 때문이다.
더 구체적인 것을 더 우선
인라인 스타일링은 적용하려는 HTML 요소 자체에 직접 들어가기 때문에 가장 구체적이다.
그 다음으로 #id 선택자와 .class 선택자 중에서는, ID 선택자는 문서에서 단 하나만 존재할 수 있기 때문에 여러 요소에 적용시킬 수 있는 class보다 더 구체적이다.
그리고 class와 태그(요소) 중에서는 역시, p 라고 선택자를 사용하면 모든 p 태그를 선택하기 때문에 class를 넣을지 말지 결정할 수 있는 class 선택자보다는 덜 구체적이다. 따라서 class 선택자가 더 구체적이기 때문에 우선된다.
더 많은 조건이 있는 것을 더 우선
ul li은 하위 선택자로 ul 요소의 자식 li을 모두 선택하라는 의미이고, ul > li은 ul 요소의 직접적인 li 자식들만 선택하라는 의미이다. 즉 손자나 중손자는 포함하지 않는다. 하지만 공백으로 구분하는 하위 선택자는 손자와 중손자도 포함한다. 따라서 이 경우에는 자식 선택자인 > 선택자가 훨씬 더 구체적이고 조건이 붙어 있기 때문에 더 우선되어 위 사진에서도 blue 색상이 적용된 것을 알 수 있다.
같은 우선순위에 있다면 나중에 작성된 것을 더 우선
위에서도 설명했었다.
!important는 모든 우선순위를 무시하고 최우선
원래대로라면 가장 마지막에 작성된 blue 색상이 적용되어야 하지만 !important로 이를 무시하고 가장 먼저 적용시켰다.
하지만 이 속성은 CSS의 우선순위를 무시하는 것이기 때문에 코드 작성에는 편리할 수 있지만 남발하는 경우 오히려 가독성과 유지보수성을 해칠 수 있을 것이라고 예상된다.
'Programing > CSS' 카테고리의 다른 글
CSS란? (0) | 2025.03.04 |
---|---|
Tailwind :: hover 애니메이션 : 위로 살짝 올라가기 (0) | 2024.06.30 |
Tailwind :: 마우스 호버 시 살짝 커지는 애니메이션 (0) | 2024.06.30 |
React Tailwind :: 모바일 고려 Layout 컴포넌트 (0) | 2024.06.29 |
호버 시 자연스럽게 커지는 애니메이션 (0) | 2024.06.26 |
댓글