Masonry(Pinterest) Layout

장동혁·2020년 10월 29일
7

css

목록 보기
1/2

1. 개요

웹페이지에서 정보를 잘 표현해 주기 위해서 엘리먼트의 효율적인 배치는 매우 중요하다. 중요한 정보는 위에 덜 중요한 정보는 밑으로 배치를 하며 비슷한 중요도를 가지는 내용은 가로로 나란히 배치하기도 합니다. 이러한 배치를 하는 작업 또는 배치된 상태를 레이아웃이라고 부른다.

일반적으로 UI/UX 디자이너에 의해서 레이아웃이 정해지면 퍼블리셔나 웹 개발자가 해당 레이아웃을 코드로 구현하게 되는데 요즘 웹은 워낙 다양한 정보를 표현해 줘야 하기 때문에 상황에 맞는 적절한 레이아웃 선택은 UI/UX 디자인에 있어서 굉장히 중요하다. 여러 가지 레이아웃 중 flex, grid를 활용하여 masonry 레이아웃을 구현하는 방법에 대하 기록하려고 한다.

2. Masonry Layout?

masonry이라는 단어가 다소 생소하게 느껴질 수도 있는데 '석조', '돌쌓기'라는 뜻을 가지고 있다. 벽돌을 가지고 담을 만들때 각각의 돌의 크기가 일정하지 않을 때에는 그림의 처럼 쌓을 수 밖에 없다.

이와 마찬가지로 화면에 표시해야 할 엘리먼트들의 가로 또는 세로의 길이가 제각각이라면 css의 float 속성을 이용한 카드 레이아웃을 사용했을 때는 다음 그림과 같이 중간마다 빈 공간이 생기게 됩니다.

이처럼 가로길이는 고정이지만 세로길이가 각양각색일때 벽돌을 쌓듯이 빈 공간을 없애주는 masonry 레이아웃을 만드는 방법을 찾아보자.

3. display: flex 속성 이용

flex 속성값은 display 속성에서 사용할 수 있는 속성값으로 이름 그대로 신축성 있는 엘리먼트를 만들 수 있게 해준다.

html

<div class="wrap">
  <div class="item">1</div>
  <div class="item item--short">2</div>
  <div class="item item--long">3</div>
  <div class="item item--short">4</div>
  <div class="item item--long">5</div>
  <div class="item">6</div>
</div>

css

div{
  box-sizing: border-box;
}

.wrap {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 510px;
  background-color: #aaa;
  align-content: flex-start;
}

.wrap > .item {
  flex: 0 0 150px;
  width: 150px;
  color: #fff;
  background-color: #000;
  padding: 10px;
  margin: 10px;
}

.wrap > .item.item--short {
  flex-basis: 100px;
}

.wrap > .item.item--long {
  flex-basis: 200px;
}

flex의 성질을 활용해서 빈공간을 채우면서 자식 엘리먼트들을 배치했지만 순서가 가로부터 채워지는 게 아니고 세로부터 채워지게 되기 때문에 부모 엘리먼트의 세로길이를 고정해야한다. 세로길이가 고정이 되면 화면이 자식엘리먼트들로 전부 채워졌을 때 스크롤이 세로가 아닌 가로로 생기게 되는 문제가 생긴다.가로 길이가 고정이고 세로 길이가 가변적일 경우에는 flex가 적합하겠지만 반대일 경우에는 부적합 하다.

3. display: grid 속성 이용

div{
  box-sizing: border-box;
}

.wrap {
  display: grid;
  grid-template-columns: repeat(auto-fill, 170px);
  grid-auto-rows: 1px;
}

.wrap > .item {
  width: 150px;
  grid-row-end: span 150;
  background-color: #000;
  color: #fff;
  padding: 10px;
  margin: 10px;
    grid-row-end: span 200;
}

.wrap > .item.item--short {
  grid-row-end: span 100;
}

.wrap > .item.item--long {
  grid-row-end: span 300;
}

그리드 시스템을 사용하게 되면 격자 형태의 레이아웃 이외에도 다양한 형태의 레이아웃을 구현할 수 있다. 그리드 시스템의 속성 중grid-auto-rows를 사용해서 모든 자식 엘리먼트들의 높이를 지정하고 자식 엘리먼트에서 grid-row-end속성을 사용해서 높이를 개별적으로 조정 할 수 있다. 또한 자식 엘리먼트들은 바둑판 형태로 배치되는 것이 아닌 위 엘리먼트(바로 이전 엘리 먼트가 아닌 위치상 위에 위치한 엘리먼트) 바로 밑에 위치하게 되며 배치 순서 또한 왼쪽에서 오른쪽으로 순서대로 배치 되는 것이 아닌 가장 높게 배치될 수 있는 곳에 배치된다.

엘리먼트의 높이를 미리 지정되어 있는 경우라면 위 방법을 사용할 수 있겠지만 엘리먼트의 높이가 컨텐츠의 높이를 따르거나 이미지 처럼 원래의 높이를 가지고 있는 엘리먼트일 경우라면 css에서는 엘리먼트의 높이를 참조할 수 없기 때문에 불가능하다.

3. display: grid 속성 + JS

따라서 먼저 엘리먼트를 일단 배치 한 뒤 스크립트로 엘리먼트의 높이 값을 구한 뒤 css를 지정해 주는 방법을 사용해보자.

html

<div class="wrap">
  <div class="item">1Lorem, ipsum dolor.</div>
  <div class="item">2Lorem ipsum dolor sit, amet consectetur adipisicing elit. Possimus, natus.</div>
  <div class="item">3Lorem ipsum dolor sit, amet consectetur.</div>
  <div class="item">4Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum praesentium fugit quaerat excepturi, a rem?</div>
  <div class="item">5Lorem, ipsum dolor.</div>
  <div class="item">6Lorem ipsum dolor sit, amet consectetur adipisicing elit. Possimus, natus.</div>
  <div class="item">7Lorem ipsum dolor sit, amet consectetur.</div>
  <div class="item">8Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum</div>
</div>

css

div{
  box-sizing: border-box;
}

.wrap > .item {
  width: 150px;
  background-color: #000;
  color: #fff;
  padding: 10px;
  margin: 10px;
}

js

window.onload = () => {
  document.querySelectorAll(".item").forEach((item) => {
     item.style.gridRowEnd = `span ${item.clientHeight + 20}`;
  });
  const wrap = document.querySelector(".wrap");
  wrap.style.display = "grid";
  wrap.style.gridTemplateColumns = "repeat(auto-fill, 170px)";
  wrap.style.gridAutoRows = "1px";
}

마무리

이런 저런 방법으로 masonry 레이아웃을 구성해 봤는데 html + css의 조합만으로는 불가능하다는 것을 깨닳았다. 만약 jquery를 사용 중이라면 masonry 메서드로 좀더 쉽게 레이아웃을 구성할 수 있다. 그리고 masonry 레이아웃 구현에 사용되는 css가 공식 스펙으로 추가될 수 있다는 소식이 있기 때문에 기대된다.

profile
기록하는 습관

0개의 댓글