## CSS position 학습 :::info **2025.03.07** 수업내용 - stylesheet를 별도 파일로 선언하고 HTML 파일 내에서 임베딩할 수 있는 방법 재언급 - position 프로퍼티에 적용할 수 있는 값들을 배움 ::: # 수업 내용 - position `position`은 문서 상에 요소를 배치하는 방법을 지정하는 property로, 정말이지 이름 그대로 `위치를 지정하는 속성`이다. 대부분의 브라우저에서 안정적으로 동작한다. 처음 HTML과 CSS를 다룰 때부터 자연스럽게 별 생각 안 하고 사용하던 프로퍼티라 구체적으로 여러 속성을 살펴보지 않다가 이번 기회에 MDN 에서 배운 내용을 정리해 문서로 남긴다. ## 1. 일반적인 position값 | 값 | 설명 | 비고 | |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| | **static** | - 기본값<br>- 요소는 문서의 일반적인 흐름(normal flow)에 따라 배치됨<br>- `top`, `right`, `bottom`, `left` 등 offset 속성이 적용되지 않음 | 명시하지 않아도 기본값임 | | **relative** | - 요소의 일반 흐름에 따라 배치되지만 `top`, `right`, `bottom`, `left` 속성으로 원래 위치를 기준으로 상대적으로 이동 가능<br> - 다른 요소의 레이아웃에는 영향을 안 주고 원래 자리를 그대로 차지함|자식 요소의 absolute 배치 기준이 될 수 있음 | | **absolute** | - 요소가 일반 흐름에서 제거됨<br>- 가장 가까운 positioned ancestor(`static`이 아닌 부모 요소)를 기준으로 배치됨 <br> - 일반 문서 흐름에서 제거되어 다른 요소들이 이 요소의 공간을 무시하게 됨. 그래서 원래 위치하던 공간을 차지하지 않아 다른 요소들이 그 자리를 채움 | positioned ancestor가 없으면 뷰포트를 기준으로 함 <br> 자식 요소 입장에서 보면 니가 무어라하든 '나는 자유롭게 움직일 것이다' 는 늬앙스 | | **fixed** | - 요소가 일반 흐름에서 제거되므로 다른 요소들이 이 요소의 공간을 무시함 <br>- `뷰포트`를 기준으로 고정되어 스크롤해도 위치가 변경되지 않고 항상 같은 위치에 고정됨 | 설명항목에 기술한 특징 때문에 네비게이션바, 사이드메뉴, 맨 위로 버튼 등에서 자주 사용됨 | | **sticky** | - 기본적으로는 `relative`처럼 동작하다가 스크롤 위치에 따라 일정 지점부터 `fixed`처럼 뷰포트에 고정됨 | 스크롤 컨테이너 내에서 일정 위치에 도달하면 고정되는 특수한 동작에 사용됨. 이 특징 때문에 섹션 헤더, 목차 등 스크롤 시 따라오는 요소에 적합함.<br> 가끔 랜딩페이지에서 브라우저 특정 영역에 도달했을 때 보이던 UI들이 몇 개 떠올랐음 | ## 2. 글로벌(Global) 값 **FYI**: 글로벌 값은 모든 CSS 속성에 공통으로 사용할 수 있는 값임 | 값 | 설명 | 비고 | |-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **inherit** | 부모 요소의 계산된(computed) 값을 그대로 상속받음 | | | **initial** | 해당 속성의 기본값(예: `position`의 기본값은 **static**)으로 강제로 초기화함 | | | **unset** | 속성이 상속 가능하면 **inherit**처럼, 상속이 불가능하면 **initial**처럼 동작함<br> | position은 기본적으로 상속되지 않으므로 **static**과 동일한 효과 | | **revert** | CSS 캐스케이딩(cascade)에서 이전 스타일 규칙(예: 사용자 스타일 또는 브라우저 기본값)으로 되돌림<br>→ 단순한 부모 상속과는 개념이 다름 | 원래 적용되었어야 할 값을 복원하는 역할. initial 보다 약한 이니셜라이징 같았음. 이해를 포기함 | | **revert-layer**| CSS Cascade Layers 내에서 바로 아래 레이어의 스타일 값으로 되돌림<br>→ 비교적 새로운 기능으로, 아직 브라우저 지원이나 사용성이 제한적임 | `revert`와 유사하지만, cascade layer 개념 내에서 동작함<br>*revert-layer 문서 2번 읽었지만 이해를 포기함. 데코레이터를 아직 학습하지 않아서같다. revert와 함께 그냥 암기* | ## 3. 이해가 어려웠던 부분 - **static, relative, absolute, fixed, sticky** → **relative** 의 부분. 막연히 자식 요소 배치에 관여한다고 생각했는데, `자식의 absolute 포지셔닝 기준` 이 될 수 있다... 가 임밀한 표현이었음. - **inherit, initial, unset** → **unset**은 상속 가능한 경우는 상속, 그렇지 않으면 초기값을 적용하는 복합적인 동작임을 유념해야한다고 강조되는 경우가 많았다. - **revert, revert-layer** → 이해를 포기함. 히히!! MDN에 적힌 내용을 외우고 적용해보고 삽질을 하는 방법밖에는 달리 떠오르지 않았다. 이걸 나에게 이해시킬 수 있는 FE를 만난다면 밥을 사줘야할 것 같다. - 부모 요소의 `position` 값 상속이라고 이해하면 부정확하다. - **revert**: 부모 값이 아니라, CSS 캐스케이딩 규칙에 맞춰 사용자 스타일이나 브라우저 기본값 등 “원래” 적용되었어야 할 값으로 복원하는 거라고한다. MDN예제에 선언된 데코레이터들의 의미를 익히기 전까지는 계속 이해하지 못할 것 같다. - **revert-layer**: cascade layer 내에서 이전 레이어의 값을 복원하는 기능으로, 단순한 상속과는 개념이 다르다고 한다. - 그냥 넘어가자. <br> # 숙제하며 배운 것 확인하기 :::success **Goal** - absolute나 fixed를 사용하면 요소가 문서 기본 흐름에서 제거된다는 점을 직접 확인 - 부모 요소나 형제 요소들과의 레이아웃이 깨지거나 겹침이 발생하는 상황 만들어 확인해보기 - 좌표계를 통한 레이아웃 조정 - top, left, right, bottom을 이용해 직접 픽셀 단위로 요소를 배치해보기 - flow, flexbox 등과 달리 수동으로 조정하며 불편함 겪어보기 - 창 크기가 변하거나 내용이 늘어나도 위치가 고정되어 있어 레이아웃이 깨지는 상황 경험하기 - 반응형이나 유연한 레이아웃 설계에 비해 얼마나 비효율적인지를 체험 해본달까 ::: ## 1. 완성할 최종 모습 ![image](https://hackmd.io/_uploads/B1-GkAFiJg.png) - 강사님이 숙제하라고 저 이미지를 줬다. - flex를 사용하면 간단하게 구현되겠지만, 포지션 속성에 사용할 수 있는 값들을 배웠으니 일부러 굳이굳이 position을 이용해 구현하기. - 아이콘 깎기는 아직 어려우니까 폰트 어썸 아이콘 가져오기. ## 2. relative 컨테이너와 absolute 포지션을 가진 카드 생성 먼저 타이틀과 설명용 문단을 적당히 만들고, 핵심이 될 카드 컨테이너를 먼저 구성다. 컨테이너에 `position: relative;` 속성을 주고, 내부를 채울 자식요소인 카드에 `position: absolute;` 속성을 적용할 **예정**이다. 먼저 `absolute` 속성 적용 없이 어떻게 페인팅 되는지 보자. ![image](https://hackmd.io/_uploads/r1Zfwyqske.png) 카드가 컨테이너를 이탈하며 아랫방향으로 흐르는 모습을 확인할 수 있다. 맨 처음 예상은 카드가 왼쪽에서 오른쪽으로 줄줄이 늘어질 것으로 생각했다가 아랫방향으로 흐르는 모습을 보고 당황했다. 조금 더 생각해보니 내가 너비를 조정해놨다한들, `div`가 블록 엘리멘트라 한 줄을 전체 차지하므로 한 줄씩 쌓이는 게 맞다. `absolute`를 적용 전에는 카드가 아랫 방향으로 하나씩 생성되는 것이 정상적인 문서 내 흐름이다. 부모 요소인 컨테이너에 `relative`가 적용된 상태지만, `card` 클래스의 영향을 받는 자식 요소가 아직 아무런 포지션 속성을 가지고 있지 않으므로 `static` 상태이고 static 상태에서는 `top` 과 같은 오프셋 속성이 무시되므로 카드들은 아직 `top: 60px;`의 효과를 받지 않으며 정상적인 문서 흐름에 맞춰 디스플레이 되는 것이다. 정리하면 - `.card-container`는 높이가 300px로 고정되어 있고 - 3개의 `.card` 요소는 각각의 높이를 140px로 지정했으므로 총 높이는 420px인 상태에서 - 카드가 `position: static;` 상태이므로 카드들이 정상적으로 문서 흐름에 따라 쌓이게 되는데 - 다 쌓인 카들의 총 높이는 420px가 되어 컨테이너의 고정된 300px 높이를 초과하게 되고 - 카드들이 컨테이너 밖으로 이탈한 상태인 것이다. ![image](https://hackmd.io/_uploads/S1Luzl9s1l.png) *그와중에 친절한 개발자 도구.. 오프셋 설정이 무시당하는 내용도 잘 보여준다. 감사.. 압도적 감사.. 너 진짜 똑똑하다..* :::spoiler 위 상태인 코드 펼쳐보기 ``` html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>position 학습</title> </head> <style> /*기본 스타일 초기화*/ * { margin: 0; padding: 0; font-size: 16px; box-sizing: border-box; } .container { margin: 50px auto; width: 800px; } .card-container { height: 300px; position: relative; /* 내부 자식 요소를 absolute로 배치하기 위해 relative 지정 */ margin: 50px auto; padding: 20px; background-color: #f5f5f5; } .about-title { text-align: center; margin-bottom: 20px; font-size: 1.5rem; } .card { /*position: absolute; 확인을 위해 일단 여기를 주석처리 해두었다.*/ width: 200px; height: 140px; background-color: #333; color: #fff; border-radius: 8px; padding: 16px; box-sizing: border-box; text-align: center; top: 60px; } </style> <body> <div class="container"> <h1 class="about-title">About me</h1> <p> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Numquam repudiandae nemo nihil, similique quis hic! Numquam eligendi cupiditate temporibus in qui sed, hic debitis minus, eum laborum veritatis deserunt rem. </p> <div class="card-container"> <div class="card"> <h2>Front-end</h2> <p>HTML, CSS, JavaScript, TypeScript, React, Vue, Next.js</p> </div> <div class="card"> <h2>Mobile</h2> <p>Android, iOS, React Native, Flutter, Java, Swift, Kotlin</p> </div> <div class="card"> <h2>Back-end</h2> <p>Java, JavaScript, Go, Python, Node.js, GraphQL</p> </div> </div> </div> </body> </html> ``` ::: <br> 위 예제 코드에서 주석처리해둔 `position: absolute;`를 주석 해제하고 살펴보면 다음과 같은 상태가 된다. 이 상태를 보면 드디어 문서 흐름을 무시한다는 말이 어떤 뜻인지 알게된다. 내가 선언한 카드들이 블록인데도 문서 내에 한 줄씩 차지하며 배치되지 않고 `top` 오프셋으로 지정해둔 위치를 기준으로 모두 겹쳐있는 모습을 볼 수 있다. ![image](https://hackmd.io/_uploads/BkAxo15o1x.png) *TMI: 피그마에서 겹친 오브젝트들의 노출 순서를 조절 가능하듯, 겹쳐있는 카드들을 z-index 속성으로 레이어 노출 순서를 조절할 수 있다.* 이제 카드들을 제대로 배치해보자. 800 너비에 내부 콘텐츠 배치를 위해 조상 요소에 패딩을 20씩 주고 있으므로 남은 760px 내에 너비 200인 세 장의 카드가 배치돼야한다. 카드 사이에 간격도 적절히 주어야하니까... 계산하기 편하게 부모 컨테이너 내에 50px 만큼의 패딩을 흉내내고 카드간 간격은 30px로 설정해보았다. 벌써 ~~두뇌를 풀가동~~한 느낌이다. | 패딩인척 | 카드 1 | 간격 | 카드2 | 간격 | 카드3 | 패딩인척| | -------- | -------- | -------- | -------- | -------- | -------- |-------- | | 50px | 200px | 30px | 200px | 30px | 200px | 50px | 좋아 완벽한 계획임..! ```css= .card:nth-child(1) { left: 70px; /** 일단 첫 카드를 컨테이너 내에 수동 배치. * 조상 요소가 가진 패딩 20px에 패딩인척 하는 공간 50px을 더해서 * 왼쪽으로부터 70px 떨어진 곳에서 시작 */ } .card:nth-child(2) { left: 310px; /** 50px 의 패딩인 척 하는 공간 + 첫카드 면적 200px * + 첫 카드와의 간격 30을 더해 수동 배치 */ } .card:nth-child(3) { left: 530px; } ``` ![image](https://hackmd.io/_uploads/H1Icje5oJl.png) 완성 모습은 아직 멀었지만 positon의 여러 value를 이용해 불편하고 고통스러운 카드 배치와 정렬을 완성했다. 너무 당연하게도 이런 코드를 엔터프라이즈용 사이트에 쓸 수 없다는 것을 다시 깨닫고 flex 같은 레이아웃 속성이 얼마나 소중한지도 알게되며 Goal을 달성했다. 디스플레이 속성을 `flex`로 변경하고 마무리 코딩을 하자. ## 3. 완성하기 ![image](https://hackmd.io/_uploads/HJuLUG9jyg.png) 오..! 거의 똑같아! 깨알같이 마우스오버하면 살짝 올라오도록 애니메이션 효과도 주고, 백엔드 카드에서 이젠 좀 꼴보기 싫어진 자바도 지워주고.. (코쓱) ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>숙제는 귀찮다</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <style> /* 기본 스타일 초기화 */ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-font-smoothing: antialiased; font-size: 16px; } /* 컨테이너 레이아웃 */ .container { width: 40rem; margin: 3rem auto; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 1rem; } .about-title { font-size: 1.5rem; text-align: center; } .container p { font-size: 1rem; text-align: center; } /* 카드 레이아웃 */ .card-container { display: flex; align-items: center; justify-content: center; gap: 1em 0.5em; } .card { padding: 1.75rem 0.5rem; color: #fff; text-align: center; background-color: rgba(0, 0, 0, 0.9); border-radius: 8px; box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.2); transition: transform 0.3s ease, box-shadow 0.3s ease; /* 애니메이션 효과 주기 */ } .card:hover { transform: translateY(-5px); box-shadow: 0 0.8rem 1.5rem rgba(0, 0, 0, 0.3); } .card h2 { line-height: 2rem; } .card p { font-size: 0.7rem; } .card i { line-height: 3rem; font-size: 2.5rem; color: #6AE5F6; } </style> </head> <body> <div class="container"> <h1 class="about-title">About me</h1> <p> Lorem ipsum dolor sit, amet consectetur adipisicing elit. Numquam repudiandae nemo nihil, similique quis hic! Numquam eligendi cupiditate temporibus in qui sed, hic debitis minus, eum laborum veritatis deserunt rem. </p> <div class="card-container"> <div class="card"> <i class="fa-brands fa-html5"></i> <h2>Front-end</h2> <p>HTML, CSS, JavaScript, TypeScript, React, Vue, Next.js</p> </div> <div class="card"> <i class="fa-solid fa-mobile"></i> <h2>Mobile</h2> <p>Android, iOS, React Native, Flutter, Java, Swift, Kotlin</p> </div> <div class="card"> <i class="fa-solid fa-server"></i> <h2>Back-end</h2> <p>Kotlin, JavaScript, Go, Python, Node.js, GraphQL</p> </div> </div> </div> </body> </html> ``` <br> # 참조한 문서들 문서를 보다보면 연관된 정보가 많아 수많은 탭으로 도배된 브라우징 경험을 할 때가 많은데, 비교적 컨텍스트가 강한 내용을 들여쓰기로 정리해 링크를 분류했다. - *(한글) [MDN - CSS > positon](https://developer.mozilla.org/ko/docs/Web/CSS/position)* - *MDN 문서에서 실험적인 피쳐들을 구경할 수 있어서 흥미로웠다.* - *(영문) [stackoverflow - What is a positioned ancestor?](https://stackoverflow.com/questions/45784777/what-is-a-positioned-ancestor)* - *영문 MDN을 먼저 읽고 positioned ancestor 에 대해 검색했다.* - *(한글) [MDN - CSS display > 컨테이닝 블록의 모든 것 #컨테이닝 블록 식별 ](https://developer.mozilla.org/ko/docs/Web/CSS/CSS_display/Containing_block#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%8B%9D_%EB%B8%94%EB%A1%9D_%EC%8B%9D%EB%B3%84)* - *(한글) [MDN - CSS > CSS display> 대열과 탈대열](https://developer.mozilla.org/ko/docs/Web/CSS/CSS_display/In_flow_and_out_of_flow)* - *기계 번역으로 어색한 부분이 많아 [영문](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_display/In_flow_and_out_of_flow)으로 접근해서 다시 번역기 돌리는 게 나았다.* - *(영문) [MDN - CSS > CSS 값과 단위](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units#global_values)* - *(영문) [MDN - CSS > revert-layer](https://developer.mozilla.org/en-US/docs/Web/CSS/revert-layer) 두 번 봤지만 이해를 포기한 문제의 그 문서. 아 어쩌라긔...* - *(영문) [Fontawesome](https://fontawesome.com)* - *[cnd 목록](https://cdnjs.com/libraries/font-awesome) 아이콘 가져다 쓸라고 찾아봤다.* - *[Styling Icons on the Web > Styling Basics](https://docs.fontawesome.com/web/style/basics)* - *[Styling Icons on the Web > Sizing Icons](https://docs.fontawesome.com/web/style/size)* - *(영문) [Wikipedia - Lorem ipsum ㅋㅋㅋ](https://en.wikipedia.org/wiki/Lorem_ipsum)*