[CSS] 레이아웃을 마스터해보자 (position, flexbox, grid)

·2024년 3월 4일

Study Note ✍🏻

목록 보기
10/60

CSS 중에 핵심은 레이아웃

페이지 내 UI를 제작하면서 제일 어려웠던 건 역시 레이아웃이었다. HTML 태그는 block / inline 두 가지로 배치가 되기 때문에 레이아웃을 대충 알고 제작한다면, UI에 시간이 매우 많이 들어가게 될 것이다.
필자 또한 레이아웃을 일부만 공부한 채 개발에 들어갔기 때문에 개발 초기에 제작한 UI와 후기에 제작한 UI가 많이 다르다. 그렇기 때문에 UI 제작 전에 CSS를 확실히 공부하고 들어가는 게 매우 중요하다는 걸 깨닳았다. 특히 그 중에 레이아웃(Position, Flexbox, Grid)는 꼭 잘 알아두어야 한다.

이번 프로젝트에선

매우 아쉬운 점이 있다면 CSS에 대한 지식이 부족한 채로 개발에 들어갔기에 Flexbox와 Grid를 활용하지 못하고 작업을 했다는 것이다. 그렇기 때문에 반응형을 개발할 수 없었다. 이 부분이 프로젝트의 가장 큰 아쉬움인 것 같다. 다음 프로젝트에선 꼭 Flexbox와 Grid를 활용하여 인터랙티브한 웹을 만들고자 오늘 제대로 CSS 레이아웃에 대해 정리하고 들어가고자 한다.

들어가기 전

해당 자료는 구글링을 통해 추가 덧붙임을 하였으나, 기본적으로는 코드잇 강의를 듣고 작성한 것임. position, flexbox와 grid를 한 게시글로 업로드하여 다소 스크롤 압박이 있을 수 있음.

position

레이아웃을 배치하거나, 객체를 위치시킬때 사용하는 css 속성.
position 속성은 상속되지 않으며, 위(top), 아래(bottom), 왼쪽(left), 오른쪽(right)의 위치를 같이 설정 할 수 있다.

static, relative, absolute, fixed, sticky

static

position 속성의 기본 값. (원래 있어야 할 위치에 그대로 배치됨)
설정 전과 후의 값이 같음.

relative

원래 위치를 기준으로 상대적(relative)으로 배치.
marign을 사용하게 되면 다음 요소들에게도 영향을 미치지만, relative를 사용하면 현재 요소에만 영향을 미치게 됨.
top, right, bottom, left 프로퍼티가 있어야 원래의 위치에서 이동.

absoulte

가장 가까운 위치 지정 부모를 기준으로 절대적으로 배치.
가장 가까운 위치 지정 부모 : 가장 가까운 부모 태그 중 position 속성이 static이 아닌 태그
일반적으로 absolute를 쓸 경우, 기준이 될 부모에게 position: relative; 를 부여.
기존의 배치에 벗어나서 배치됨. 즉, 원래 배치에서 제외됨.

<div class="orange">content</div> // html

.orange { // css
	background-color: #114232;
    position: absolute;
}

크기를 지정해주지 않을 경우, 안에 있는 내용만큼의 크기를 가짐.
위 코드에서 orange에 크기가 지정되지 않았으므로, content만큼의 크기를 가지게 됨.

.red {
	background-color: #D04848;
    width: 500px;
    height: 500px;
    position: relative;
}

.orange {
	background-color: #114232;
    position: absolute;
    inset: 0;
}

orange의 부모 태그는 red라고 할 때, "inset: 0;"을 사용하면 모든 방향에 대해 위치를 0으로 하므로, red를 덮어씌울 수 있다. (top, bottom, left, right를 모두 0으로 한 것과 같음)

fixed

브라우저 화면의 상대 위치에 배치.
화면이 바뀌거나 스크롤이 되어도 고정된 위치를 설정 할 수 있고, 상위 요소에 영향을 받지 않음.
기존의 배치에 벗어나서 배치됨. 즉, 원래 배치에서 제외됨.
크기를 지정해주지 않을 경우, 안에 있는 내용만큼의 크기를 가짐.

sticky

브라우저 화면을 스크롤링할 때 효과가 나타남. 즉, 스크롤이 되어 요소가 화면에 나오면 고정시킴.
원래 위치에서 제외되지 않고, 공간을 차지 함.
static처럼 배치된 후, fixed처럼 작동하게 됨. (반대 상황도 가능)
부모 요소 밖으로는 벗어나지 않음.

.red {
	background-color: #D04848;
    width: 100%;
    height: 60px;
    position: sticky;
    top: 0; // 브라우저 상단에 위치했을 때 fixed 시킴.
}

z-index

position 속성을 이용한 요소들의 수직 위치를 결정. 기본적으로는 코드에서 밑의 줄에 위치할수록 화면에 앞쪽에 보이게 됨.
값은 정수이며, 숫자가 클수록 앞쪽에 보이고, 숫자가 작을수록 뒤쪽에 보임.
z-index 속성을 지정하지 않을 경우 0으로 취급.

<div class="x"><div>
<div class="y"><div>
<div class="z"><div>

위 코드의 경우, 나중에 입력한 순으로 앞쪽에 보이기 때문에, z가 위쪽에 보이게 될 것이다.

div.x {
	background-color: #5F5D9C;
    top: 20px;
    left: 10px;
    z-index: 10;
}
div.y {
	background-color: #6196A6;
    top: 50px;
    left: 20px;
}
div.z {
	background-color: #A4CE95;
    top: 10px;
    left: 60px;
    z-index: -1;
}

다음과 같은 경우 z-index의 수가 큰 순서대로 x->y->z 순으로 앞쪽에 위치하게 될 것이다.

쌓임 맥락(Stacking Context)

높이(Z축)의 개념을 활용하여 요소가 겹겹이 쌓였다는 관점의 해석

<div class="red">
  <div class="green"></div>
</div>
<div class="blue"></div>
.red {
  background-color: #e46e80;
  position: absolute;
  width: 100px;
  height: 100px;
  top: 100px;
  left: 100px;
  z-index: 1;
}

.green {
  background-color: #32b9c1;
  position: absolute;
  width: 50px;
  height: 50px;
  top: 25px;
  left: 25px;
  z-index: 3;
}

.blue {
  background-color: #5195ee;
  position: absolute;
  width: 100px;
  height: 100px;
  top: 150px;
  left: 150px;
  z-index: 2;
}

다음과 같은 코드가 있을 때, z-index에 의해 green이 제일 앞쪽에 위치할 것이라 생각하겠지만, 실제로는 blue가 앞쪽에 보이게 된다.
쌓임 맥락에 의해 red의 모든 자손 태그들이 red와 같이 z-index:1로 처리되기 때문이다.

flexbox

1차원으로 요소를 배치하는 방식. (가로/세로 방향)

배치 방향 -> flex-direction
정렬 -> jusify-content, align-items
요소 넘침 -> flex-wrap
간격 -> gap
크기 -> flex-grow, flex-shrink, flex-basis

relative, sticky는 요소의 원래 자리를 차지하기 때문에 flexbox의 영향을 받음.

배치 방향 (flex-direction)

row, column을 통해 가로/세로 방향으로 배치할 수 있다. (역방향도 가능)

정렬 (jusify-content, align-items)


Main Axis(기본 축) - 요소가 배치되는 방향
Cross Axis(교차 축) - 기본 축의 수직 방향

jusify-content

flex-start : 기본 축에서 맨 앞에 정렬 (기본)
flex-end : 기본 축에서 맨 끝에 정렬
space-around : 기본 축의 공간을 나눠 띄엄띄엄 배치 양쪽의 모두 같은 공간이 생김
space-between : 기본 축 방향으로 양 끝을 늘어뜨려 배치
space-evenly : 빈 공간의 크기가 모두 동일하도록 배치
center : 가운데 정렬

align-items

flex-start : 교차 축에서 앞쪽에 정렬 (기본)
flex-end : 교차 축에서 뒤쪽에 정렬
center : 가운데 정렬
stretch : 교차 축에 늘려서 배치
baseline : 교차 축의 자식 요소들의 텍스트 기준선에 정렬

요소 넘침 (flex-wrap)

요소들을 한 줄에 담을 여유 공간이 없을 때 줄바꿈을 결정.

nowrap : 줄바꿈을 하지 않음 (기본)
wrap : 줄바꿈
wrap-reverse : 역순으로 줄바꿈

간격 (gap)

요소들 사이 간격 (가로/세로 모두 적용)
margin을 사용하면 새로운 요소가 추가될 때마다 margin을 추가해주는 등 불편하기 때문에, gap을 사용.

.container {
	//다른 요소 생략
    display: flex;
    flex-wrap: wrap;
    flex-direction: column;
    gap: 30px 60px;
}

gap에 숫자를 1개 쓸 경우 가로/세로에 모두 적용되고, 2개 쓸 경우 세로 가로 순으로 적용.
중요한 건, flex-direction의 방향과 상관없이 항상 세로 가로 순으로 적용된다.
주로 grid에서 사용됨.

유연한 요소 (flex-grow, flex-shrink, flex-basis)

유연한 박스 기본 영역 (flex-basis)

width나 height를 참고해 시작크기를 결정.
flexbox에서 크기를 결정할 때는 width/height보다 flex-basis를 사용하는 게 좋음.

flex-grow: 1;
flex-shrink: 0;
width: 100px;
flex-grow: 1;
flex-shrink: 0;
flex-basis: 100px;

두 코드는 같다.

유연하게 늘리기 (flex-grow)

숫자에 비례해 요소 크기가 늘어남.
보통 빈 공간을 채우고 싶을 때 flex-grow: 1을 사용.

유연하게 줄이기 (flex-shrink)

숫자에 비례해 요소 크기가 줄어듦.
보통 요소의 크기를 고정하고 싶을 때 flex-shrink: 0을 사용.

flex

flex-grow, flex-shrink, flex-basis를 한 번에 쓸 수 있음.

flex-grow: 1;
flex-shrink: 0;
flex-basis: 100px;
flex: 1 0 100px;

위의 두 코드는 같다.

grid

2차원으로 요소를 배치하는 방식.

격자 나누기 : grid-template-rows, gird-template-columns
간격 : gap
크기 미리 정하기 : gird-auto-rows, gird-auto-columns
원하는 위치에 여러 칸 걸쳐 배치 : gird-row, gird-column, span
이름으로 배치 : grid-area, grid-template-areas

격자 나누기 (grid-template-rows, gird-template-columns)

grid 크기들을 지정

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template-columns: 100px 300px 100px;
    gird-template-rows: 200px 200px 100px;
}

아래와 같이 하나로 작성할 수 있다.

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 200px 200px 100px / 100px 300px 100px;
}

flexbox처럼 크기를 유연하게 하기 위해 fr 단위를 사용할 수 있다.
아래 코드는 모든 크기를 1:1:1로 지정해주었다.

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 1fr 1fr 1fr / 1fr 1fr 1fr;
}

minmax 함수를 사용해서 크기의 최소최대 값을 지정해줄 수도 있다.
아래 코드는 row의 첫번째의 크기를 200px~300px까지 지정해주었다.

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 1fr 1fr 1fr / minmax(200px, 300px) 1fr 1fr;
}

minmax에는 fr을 사용할 수 있으나 최댓값에만 가능하다.

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 1fr 1fr 1fr / minmax(200px, 1fr) 1fr 1fr;
}

repeact 함수를 사용해 반복되는 크기를 한 번에 지정해줄 수 있다.
아래 두 코드는 같은 코드이다.

.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 1fr 1fr 1fr / 1fr 1fr 1fr;
}
.container {
	// 일부 생략
    width: 500px;
    height: 500px;
    display: grid;
    gird-template : 1fr 1fr 1fr / repeact(3, 1fr);
}

크기 미리 정해두기 (gird-auto-rows, gird-auto-columns)

grid-template만을 사용하면 row/column의 개수가 정해진 게 아니라면, CSS가 계속 고쳐져야 함.
row 또는 column만 정의하고 나머지는 알아서 배치되도록 함.

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-auto-rows: 1fr;
}

위 코드에서 row는 자동적으로 1fr의 크기를 가지게 된다.

원하는 위치에 요소 배치 (gird-row, gird-column, span)

아래 이미지와 같은 grid가 있다고 가정해보자.

아래 이미지는 3x3 grid의 grid line을 의미한다.

. blue {
  // 생략
  grid-row : 2;
  grid-column : 1;
}

blue는 2번 row line과 1번 column line이 교차하는 지점인 (0, 1)에 위치하게 된다.

. blue {
  // 생략
  grid-row : 2 / 4;
  grid-column : 1;
}

blue는 2번 row line부터 4번 row line까지 걸쳐서 배치된다.
즉, (0,1)과 (0,2)에 위치하게 된다. (각각에 위치하는 게 아닌 두 영역을 합친 영역에 위치)

. blue {
  // 생략
  grid-row : 2 / 4;
  grid-column : 1 / -2;
}

blue는 1번 column line부터 -2번 column line(3번 column line)까지 배치된다.
즉, (0,1), (0,2), (1,1), (1,2)에 위치하게 된다. (각각에 위치하는 게 아닌 네 영역을 합친 영역에 위치)

. blue {
  // 생략
  grid-row : 2 / span 2;
  grid-column : 1 / span 2;
}

이전 코드와 현재 코드는 같은 코드이다. 즉, span 2는 해당 방향으로 2칸을 차지함을 의미한다.

이름으로 배치 (grid-area, grid-template-areas)

item 요소에 이름을 지정한 뒤 배치.

.red {
  background-color: #e46e80;
  grid-area : r;
}

.green {
  background-color: #32b9c1;
  grid-area : g;
}

.blue {
  background-color: #5195ee;
  grid-area : b;
}

.container {
	// 일부 생략
  display: grid;
  grid-template:
    repeat(2, 1fr) /
    repeat(2, 1fr);
  grid-template-areas:
  	"r g"
    "r b";
}

위 코드는 red, green, blue라는 div를 각각 r,g,b로 이름을 지정해준 뒤, grid의 배치해준 코드이다.
아래와 같이 배치가 되는 것을 알 수 있다.

배치에 따옴표(.)를 이용해주면 해당 부분은 비워둔 채 배치가 가능하다.

.container {
	// 일부 생략
  display: grid;
  grid-template:
    repeat(2, 1fr) /
    repeat(2, 1fr);
  grid-template-areas:
  	". g"
    "r b";
}

참고 자료

[CSS]position 속성 - relative, absolute, fixed에 대해 알아보자
[CSS] position
CSS / z-index / 요소의 수직 위치 정하는 속성
쌓임맥락(stacking context)
[CSS] flexbox (display: flex)
samanthaming
이번에야말로 CSS Flex를 익혀보자

profile
Frontend🍓

0개의 댓글