DYO 공부하는 블로그

Pinterest같은 Masonry 레이아웃 만들기 본문

React

Pinterest같은 Masonry 레이아웃 만들기

DYODa 2025. 12. 2. 16:06

이 레이아웃을 적용한 것으로 가장 유명한 pinterest

 

Masonry란?

네이버 영어사전

 

Masonry는 사전적으로는 벽돌 쌓기입니다. 아이템들을 높이가 다른 벽돌을 쌓는 것처럼 쌓아주는 레이아웃을 의미합니다.

 

flex, grid레이아웃은 줄 단위로 정렬하기 때문에 높낮이가 다른 아이템을 정렬할 때, 아래 공간이 남는 문제가 발생하게 됩니다. 이러한 문제를 해결하기 위해 Masonry 레이아웃이 등장했습니다.

grid기반 높낮이가 다른 아이템

 

 

비율을 계산해 이미지를 잘리지 않도록 고정 width에 고정하는 것은 어렵지 않지만, 결국 문제는 "배치를 어떻게 할 것인가." 입니다.

// ...

  const aspectRatio = imageWidth / imageHeight;
  const height = width / aspectRatio;
  
  return (
    <div
      onClick={onClick}
      style={{ width: width, height: height }}
      className={tw(
        `bg-gray-100 rounded-md relative cursor-pointer group`,
        className
      )}
    >
    
    )
// ...

구현 아이디어와 함께 배치 방법을 살펴보겠습니다.

 

구현 아이디어

- flex 레이아웃에 flex-cols를 적용해 각 줄별 배치를 자연스럽게 구현하는 방법

출처: WIT 블로그

이 경우는 각각의 줄이 독립된 상태로, 배치 방향에 따라 자연스럽게 직전 요소에 다음 아이템이 달라붙는다.가장 단순하게 구현할 수 있고, 순서를 보장하는 것도 어렵지 않으나, row 한 쪽에 아이템 높이가 높은 것들만 배치된다면 어색한 구조로 배치될 수 있다는 문제가 있습니다.

 

출처: WIT 블로그

위와 같이 아이템의 높이를 고려하지 않았을 경우, 한 열만 튀어나오는 문제가 발생할 수 있습니다. 이와 같은 문제를 해결하기 위해 각각의 열의 높이를 계산하고 배치함으로써, 평균적인 높이를 보장할 수 있습니다. 이 경우에는 순서를 보장하지 않습니다.

 

- 어떻게 구현할까

function arrangeMasonry(items, columnCount, gap = 16) {
  const columnHeights = new Array(columnCount).fill(0);
  const columns = new Array(columnCount).fill(null).map(() => []);
  
  items.forEach((item) => {
    // 가장 낮은 컬럼 찾기
    const shortestColumnIndex = columnHeights.indexOf(
      Math.min(...columnHeights)
    );
    
    // 해당 컬럼에 아이템 추가
    columns[shortestColumnIndex].push(item);
    
    // 컬럼 높이 업데이트
    columnHeights[shortestColumnIndex] += item.height + gap;
  });
  
  return columns;
}

아이템 배치를 연산할 때, 아이템 개수 기준이 아닌 높이 기준으로 연산합니다. 가장 높이가 낮은 열에 아이템을 배치함으로써 위와 같은 문제를 해결할 수 있습니다.

 

- grid의 masonry속성 적용해 구현하는 방법

놀랍게도 CSS grid 속성에 masonry 속성이 존재합니다. 

.grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  grid-template-rows: masonry;
}

 

그런데 실험적 기능이고, 브라우저 업데이트에 맞춰 적용되지 않고 있는 상태처럼 보입니다.

크롬 환경, 정상적으로 동작하지 않는 것처럼 보인다.

MDN 문서에서도 예시 코드가 정상적으로 작동하지 않는 모습을 확인할 수 있습니다.

 

호환성 정보

역시 예상대로, 호환성이 나쁜 실험적 기능이라는 것을 확인할 수 있습니다.

 

- absolute를 이용해 각각의 아이템의 배치를 지정하는 방법

각각의 아이템을 absolute로 지정해 realative 컨테이너에 배치하는 방법도 있습니다. 이 경우는 각각의 아이템의 top, left 속성을 지정해 배치를 결정합니다.

 

배치 흐름은 아래 그림과 같습니다.

컨테이너 배치 흐름

위와 같이 전체 컨테이너에서 row 컨테이너를 관리하지 않고, 각각의 아이템에 대해 배치해주는 방법입니다.

 

이 방법의 장점은

열로 나누어진 컨테이너를 관리하지 않아도 된다.

transform X, Y로 리플로우를 유발하지 않고 배치할 수 있다

 

특히 장점이라고 할 수 있는 것은 transform을 이용한 방법으로 구현할 수 있기 때문에 반응형 작업으로 아이템의 위치가 변경되는 작업을 하더라도 리플로우를 방지할 수 있을 것입니다.

 

Pinterest는 어떤 방법을 사용하고 있을까?

다시 처음으로 돌아가서, Pinterest와 같은 레이아웃을 구성하기로 했을 때, Pinterest가 어떻게 구현했는지 살펴보는 게 좋을 것입니다.

Pinterest의 Container

Pinterest의 아이템 컨테이너는 relative로 구성되어 있고, 하위의 아이템들은 flat하게 absolute 아이템들로 이루어진 것을 확인할 수 있습니다. 

Item의 속성

단일 Item의 스타일 속성을 봤을 때, left, top을 0으로 고정하고 기준점으로 잡은 뒤, translate X, Y 속성을 통해 리플로우를 방지하는 방식으로 구현되어 있는 것을 확인할 수 있습니다.

 

요약

1. Pinterest는 무한 스크롤 로드와 가상 스크롤을 사용하고 있다.

2. 아이템 컨테이너는 relative로 구성되어 있다.

3. 하위 아이템은 flat하게 absolute 아이템으로 구성되어 있다

4. 아이템의 속성은 left, top 0으로 고정, translate X, Y 속성 조정을 통해 리플로우를 방지하고 있다.

 

라이브러리 이용하기

- react-masonry-css

과거부터 많이 사용되던 라이브러리이고, 지속적으로 개선되고 있는 라이브러리인 react-masonry-css는 CSS기반으로 동작하고 번들 사이즈도 가벼워 성능 부하가 가장 적은 선택이고, CSS기반이므로 TypeScript 프로젝트에도 적용하는 데 문제가 없습니다. 그런데 배치를 Pinterest 형태로 원한다면 무한스크롤을 위해 열 높이를 맞춰 주는 것이 필요한데, react-masonry-css는 이를 지원하지 않는 것을 고려해야 합니다.

출처: WIT 블로그

 

NPM 통계

하지만 여전히 인기 많은 라이브러리이다.

 

- react-responsive-masonry

masonry 배치를 지원하면서 flex 기반으로 동작하며 반응형도 지원하는 라이브러리입니다. 비교적 최근까지 업데이트되는 것으로 보이며, 번들 사이즈는 70kb정도로 masonry 배치 라이브러리중에서는 무거운 편입니다. 반응형도 정말 잘 되는 편이고, 궁금하다면 데모 페이지가 있으니 직접 들어가서 확인해보면 좋을 것 같습니다. npm 데모 이미지에서는 반응형으로 아이템 개수를 선택할 수 있는 것으로 보이는데, 실제 데모에서는 크기만 줄어듭니다.

 

데모 페이지

 

react-responsive-masonry 2.3.0 Demo

 

cedricdelpoux.github.io

 

TypeScript 지원도 비교적 최근인 8개월 전 업데이트로 추가된 것으로 보입니다.

타입스크립트도 지원하는 것으로 보인다.

 

적용할 프로젝트의 속성에 따라 적용할 라이브러리를 선택하면 좋을 것 같습니다.

 

참고 자료

[MDN] https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Grid_layout/Masonry_layout

 

Masonry layout - CSS | MDN

To create the most common masonry layout, your columns will be the grid axis and the rows the masonry axis, defined with grid-template-columns and grid-template-rows. The child elements of this container will now lay out item by item along the rows, as the

developer.mozilla.org

[WIT 블로그] https://wit.nts-corp.com/2022/10/26/6595

 

Masonry 레이아웃 구현하기 | WIT블로그

Masonry layout is a layout method where one axis uses a typical strict grid layout, most often columns, and the other a masonry layout.On the masonry axis, rather than sticking to a strict grid with gaps being left after shorter items, the items in the fol

wit.nts-corp.com