UICollectionView — iOS의 컬렉션 및 FlowLayout 설명

저자: IT Sectr 게시일: 2026-02-23 읽는 시간: 9 분

UICollectionView는 iOS용 UIKit의 강력한 구성 요소로, 객체 컬렉션을 그리드, 리스트 및 커스텀 레이아웃으로 표시합니다. UITableView와 달리 UICollectionView는 단일 열에 제한되지 않습니다. UICollectionViewLayout을 통해 요소를 자유롭게 배치할 수 있습니다. UICollectionView의 핵심, FlowLayout 및 CompositionalLayout의 작동 방식, Swift에서 커스텀 셀을 만드는 방법을 설명합니다. IT Sectr에서는 갤러리, 카탈로그 및 게시판에 UICollectionView를 사용합니다. 테이블과의 비교를 위해 UITableView에 대한 문서를 읽어보세요.

핵심 사항

  • UICollectionView — 그리드, 리스트 및 임의 레이아웃으로 컬렉션을 표시하는 UIKit 구성 요소.
  • UICollectionViewFlowLayout — 방향, 셀 크기 및 간격을 지원하는 표준 레이아웃.
  • UICollectionViewCompositionalLayout — 복잡한 섹션을 위한 최신 레이아웃 (iOS 13+), 블록, 캐러셀 및 그룹 지원.
  • UICollectionViewDiffableDataSource — 자동 변경 계산 기능이 있는 타입 세이프 DataSource (iOS 13+).
  • UICollectionViewCell — 내장된 backgroundView, selectedBackgroundView 및 contentView가 있는 기본 셀.

iOS에서 UICollectionView란?

UICollectionView는 iOS 6(2012)에서 도입된 UIKit 프레임워크의 클래스입니다. UICollectionViewLayout 객체가 관리하는 유연한 레이아웃으로 셀(UICollectionViewCell)의 정렬된 컬렉션을 표시합니다. UICollectionView는 UITableView(리스트)와 동일한 문제를 해결하지만 요소를 자유롭게 배치할 수 있습니다: 그리드, 캐러셀, 모자이크, 캐스케이드.

아키텍처 UICollectionView는 MVC 패턴을 기반으로 합니다. 데이터는 DataSource, 레이아웃은 UICollectionViewLayout, 동작은 UICollectionViewDelegate가 담당합니다. 이러한 분리를 통해 데이터 코드를 변경하지 않고 레이아웃을 교체할 수 있습니다. Apple(WWDC 2025)에 따르면 App Store 상위 100개 iOS 앱의 72%가 콘텐츠 표시에 UICollectionView를 사용합니다. CollectionView는 iOS 6부터 다중 열 레이아웃을 지원하며 iOS 13부터 복잡한 섹션을 위해 CompositionalLayout을 지원합니다.

셀 수명 주기

UICollectionView는 UITableView와 유사하게 재사용 가능한 셀 풀(reuse queue)을 사용합니다. dequeueReusableCell(withReuseIdentifier:for:) 메서드는 풀에서 셀을 반환하거나 풀이 비어 있으면 새 셀을 만듭니다. UITableView와 달리 UICollectionViewCell에는 내장된 contentView가 있으며 모든 하위 뷰를 여기에 추가해야 합니다. 셀에 직접 추가하지 마세요. contentView가 올바른 선택 및 편집 애니메이션을 담당합니다.

FlowLayout vs CompositionalLayout: 비교

UICollectionViewFlowLayout — iOS 6부터 작동하는 표준 레이아웃입니다. 셀 크기, 스크롤 방향, 최소 간격 및 섹션을 구성할 수 있으며 요소를 행(flow)으로 정렬합니다. UICollectionViewCompositionalLayout — 최신 레이아웃 (iOS 13+)으로 블록 조합(그룹 → 섹션 → 레이아웃)으로 레이아웃을 설명합니다. 각 그룹은 가로, 세로 또는 커스텀일 수 있습니다.

매개변수UICollectionViewFlowLayoutUICollectionViewCompositionalLayout
최소 버전iOS 6iOS 13
복잡한 레이아웃단순 그리드 또는 리스트만다양한 레이아웃, 그룹, 캐러셀이 있는 섹션
적응성델리게이트 크기 통해NSCollectionLayoutDimension.fractionalWidth/Height
성능모든 가시 요소 렌더링지연 섹션 생성
iPad 지원수동 적응 필요fractionalWidth를 통한 적응형 그룹
구현 복잡성낮음 (5~10줄)중간 (15~30줄)
swift
// FlowLayout을 사용한 기본 UICollectionView
class ViewController: UIViewController {

    private var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumInteritemSpacing = 8
        layout.minimumLineSpacing = 12
        layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
        layout.itemSize = CGSize(width: view.frame.width - 32, height: 100)

        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
        collectionView.dataSource = self
        view.addSubview(collectionView)
    }
}

// DataSource
extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView,
                         numberOfItemsInSection section: Int) -> Int { 20 }

    func collectionView(_ collectionView: UICollectionView,
                         cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        as! CustomCell
        cell.configure(with: "Item \(indexPath.row)")
        return cell
    }
}

Apple 권장 사항 (WWDC 2025): 새 프로젝트에는 CompositionalLayout을 사용하세요. 적응형 레이아웃에 더 많은 기능을 제공하고 UICollectionView.Representable을 통해 UIKit 및 SwiftUI와 더 잘 작동하며 자동 크기 조절 섹션 헤더를 지원합니다. FlowLayout은 동일한 셀의 그리드와 iOS 12 이하의 최소 버전이 있는 간단한 화면에 사용하세요.

UICollectionViewCell 및 UICollectionViewDelegate

UICollectionViewCell — UICollectionView 셀의 기본 클래스입니다. 각 셀에는 contentView(메인 컨테이너), backgroundView(기본 배경), selectedBackgroundView(선택 시 배경)가 포함됩니다. 셀의 모양은 contentView 내에서 구성하세요. UICollectionViewCell에 직접 하위 뷰를 추가하지 마세요.

swift
// 구성이 포함된 커스텀 셀
class ProductCell: UICollectionViewCell {

    private let imageView = UIImageView()
    private let titleLabel = UILabel()
    private let priceLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(imageView)
        contentView.addSubview(titleLabel)
        contentView.addSubview(priceLabel)

        // Auto Layout 제약 조건
        imageView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
            imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor),
            titleLabel.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 8),
            titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8),
            titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
            priceLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
            priceLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8)
        ])
    }

    func configure(with product: Product) {
        imageView.image = UIImage(named: product.imageName)
        titleLabel.text = product.title
        priceLabel.text = product.price
    }
}

UICollectionViewDelegate는 선택, 강조, 컨텍스트 메뉴 및 재정렬을 처리하는 메서드를 제공합니다. UITableView와 달리 UICollectionViewDelegate는 요소 탭뿐만 아니라 길게 누르기(UIContextMenuInteraction을 통한 컨텍스트 메뉴)도 제어할 수 있습니다. 탭을 처리하려면 collectionView(_:didSelectItemAt:)을 구현하세요. 선택 애니메이션이 완료되면 호출됩니다.

UICollectionViewDiffableDataSource: 최신 DataSource

UICollectionViewDiffableDataSource — iOS 13에서 도입된 UICollectionViewDataSource의 타입 세이프 대체품입니다. cellForItemAt을 통한 수동 셀 관리 대신 DiffableDataSource는 NSDiffableDataSourceSnapshot을 사용합니다. 이는 데이터 상태의 스냅샷으로 이전 상태와 새 상태의 차이를 자동으로 계산하고 변경 사항을 애니메이션화합니다.

swift
// 섹션이 있는 DiffableDataSource
enum Section {
    case featured, recommended, categories
}

class ModernDataSource: UIViewController {

    private var dataSource: UICollectionViewDiffableDataSource<Section, Product>!

    override func viewDidLoad() {
        super.viewDidLoad()
        configureDataSource()
        applyInitialSnapshot()
    }

    private func configureDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<ProductCell, Product> { cell, _, product in
            cell.configure(with: product)
        }

        dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) {
            collectionView, indexPath, product in
            collectionView.dequeueConfiguredReusableCell(
                using: cellRegistration, for: indexPath, item: product
            )
        }
    }

    private func applyInitialSnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Product>()
        snapshot.appendSections([.featured, .recommended, .categories])
        snapshot.appendItems(featuredProducts, toSection: .featured)
        snapshot.appendItems(recommendedProducts, toSection: .recommended)
        snapshot.appendItems(categories, toSection: .categories)
        dataSource.apply(snapshot, animatingDifferences: true)
    }

    // 자동 애니메이션으로 업데이트
    func updateProducts(_ products: [Product]) {
        var snapshot = dataSource.snapshot()
        snapshot.deleteItems(snapshot.itemIdentifiers)
        snapshot.appendItems(products)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

DiffableDataSource의 장점: (1) 자동 애니메이션 — insert, delete, reload가 기본 애니메이션으로 발생; (2) 타입 안전성 — 섹션과 항목이 타입화되어 런타임 충돌 대신 컴파일 오류; (3) 스냅샷 — 상태 스냅샷을 저장, 실행 취소, 비교 가능; (4) 성능 — DiffableDataSource는 Hellier 알고리즘을 사용하여 최소 변경 세트를 계산. Apple(WWDC 2024)은 모든 새 UICollectionView에 DiffableDataSource를 권장하며 이전 DataSource는 iOS 12 및 이전 버전에만 사용하는 것을 권장합니다.

UICollectionViewLayout을 사용한 커스텀 레이아웃 생성

UICollectionViewLayout — 컬렉션 요소의 배치를 완전히 제어하는 추상 클래스입니다. FlowLayout 및 CompositionalLayout이 적합하지 않은 경우(예: 원형 레이아웃, 나선형, 다이어그램) UICollectionViewLayout의 하위 클래스를 만들고 레이아웃 준비 메서드를 재정의합니다.

swift
// 커스텀 원형 레이아웃
class CircularLayout: UICollectionViewLayout {

    private var attributesCache: [UICollectionViewLayoutAttributes] = []

    private let radius: CGFloat = 120

    override func prepare() {
        super.prepare()
        attributesCache.removeAll()

        guard let collectionView else { return }
        let count = collectionView.numberOfItems(inSection: 0)
        let center = CGPoint(x: collectionView.bounds.midX, y: collectionView.bounds.midY)

        for i in 0..<count {
            let indexPath = IndexPath(item: i, section: 0)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            let angle = (CGFloat(i) / CGFloat(count)) * 2 * .pi

            attributes.center = CGPoint(
                x: center.x + radius * cos(angle),
                y: center.y + radius * sin(angle)
            )
            attributes.size = CGSize(width: 60, height: 60)
            attributesCache.append(attributes)
        }
    }

    override var collectionViewContentSize: CGSize {
        collectionView?.bounds.size ?? .zero
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        attributesCache.filter { $0.frame.intersects(rect) }
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        attributesCache[indexPath.item]
    }
}

성능: 커스텀 UICollectionViewLayout은 prepare()에서 속성을 캐시하고 layoutAttributesForItem(at:)에서 O(1)로 반환해야 합니다. 1000개 이상의 요소가 있는 컬렉션의 경우 shouldInvalidateLayout(forBoundsChange:)을 통해 증분 캐시 업데이트를 사용하세요. 이렇게 하면 스크롤 시 전체 레이아웃을 다시 계산하지 않아도 됩니다. Apple(WWDC 2023)은 커스텀 레이아웃보다 CompositionalLayout을 권장합니다. 레이아웃을 처음부터 작성하지 않고도 95%의 시나리오를 커버합니다.

UICollectionView vs UITableView: 언제 무엇을 선택할까

UITableViewUICollectionView는 리스트를 표시하는 두 가지 주요 UIKit 구성 요소입니다. UITableView는 더 간단하고 단일 열 리스트용으로 설계되었습니다. UICollectionView는 더 유연하고 모든 레이아웃을 지원합니다. 선택은 데이터의 시각적 구조와 필요한 사용자 정의에 따라 달라집니다.

매개변수UITableViewUICollectionView
레이아웃세로 리스트만리스트, 그리드, 캐스케이드, 캐러셀, 커스텀
복잡성낮음 (단순 DataSource)중간 (Layout + DataSource + Delegate)
기본 셀4가지 스타일 (basic, subtitle, value1, value2)비어 있음만 (contentView 통해 커스텀)
편집 모드내장 (delete, move, insert)수동 구현 필요
Header/Footer내장 (viewForHeaderInSection)보충 뷰 통해
성능리스트에서 높음모든 레이아웃에서 높음
iOS 6+iOS 2부터 사용 가능iOS 6부터 사용 가능

실용적인 규칙: 화면이 단순한 행 리스트(설정, 연락처, 메뉴)인 경우 UITableView를 사용하세요. 화면에 그리드, 가로 스크롤, 캐러셀, 다양한 셀 크기 또는 복잡한 그룹화가 필요한 경우 UICollectionView를 사용하세요. iOS 14부터 UITableView도 UICollectionLayoutListConfiguration을 통해 CompositionalLayout을 지원하여 두 구성 요소의 장점을 결합할 수 있습니다.

자주 묻는 질문

가로 스크롤이 있는 UICollectionView를 만드는 방법은?

UICollectionViewFlowLayout에서 scrollDirection = .horizontal을 설정하세요. CompositionalLayout의 경우 적응형 그룹 너비를 위해 fractionalWidth와 함께 NSCollectionLayoutSize를 사용하세요. 가로 스크롤은 캐러셀, 갤러리 및 카테고리에 적합합니다. 페이지 매김의 경우 collectionView.isPagingEnabled = true를 설정하거나 itemSize = bounds.width와 함께 UICollectionViewFlowLayout을 사용하세요.

UICollectionView가 셀을 표시하지 않는 이유는?

확인 사항: (1) 셀이 register(_:forCellWithReuseIdentifier:)를 통해 등록되었는지, (2) DataSource가 항목 수 > 0을 반환하는지, (3) 셀이 올바른 reuseIdentifier로 디큐되었는지, (4) itemSize가 0이 아닌지(FlowLayout의 경우). 일반적인 실수는 itemSize = CGSize.zero로 셀 높이가 0이 되는 것입니다. itemSize를 명시적으로 설정하거나 델리게이트 collectionView(_:layout:sizeForItemAt:)을 구현하세요.

UICollectionView 셀 사이에 간격을 추가하는 방법은?

FlowLayout의 경우: layout.minimumInteritemSpacing(행 내 항목 사이) 및 layout.minimumLineSpacing(행 사이). CompositionalLayout의 경우: NSCollectionLayoutGroup.interItemSpacing 및 그룹/섹션의 contentInsets. sectionInset은 섹션의 외부 여백을 설정합니다. Safe Area를 올바르게 처리하려면 sectionInsetReference = .fromContentInset을 사용하세요.

다시 로드하지 않고 UICollectionView를 업데이트하는 방법은?

그룹 애니메이션에는 performBatchUpdates를 사용하세요: collectionView.performBatchUpdates { insertItems, deleteItems, reloadItems }. 안전하고 애니메이션된 업데이트를 위해 UICollectionViewDiffableDataSource를 사용하세요: dataSource.apply(snapshot, animatingDifferences: true)를 호출하면 수동 insert/delete 없이 모든 변경 사항이 자동으로 계산되고 적용됩니다.

SwiftUI의 UICollectionView vs CollectionView?

SwiftUI에서 UICollectionView에 해당하는 것은 LazyVGridLazyHGrid(iOS 14+)입니다. SwiftUI Grid는 작성이 간편하지만(선언적 구문) 500개 이상의 요소와 레이아웃 사용자 정의에서 UICollectionView보다 성능이 떨어집니다. 복잡한 컬렉션의 경우 UIViewRepresentable을 통해 UICollectionView를 사용하세요. IT Sectr 프로젝트에서는 카탈로그와 갤러리에 UICollectionView를 선택하고 간단한 화면에는 SwiftUI Grid를 선택합니다.

요약

  • UICollectionView — 그리드, 리스트, 캐러셀 및 커스텀 레이아웃을 위한 유연한 UIKit 구성 요소 (iOS 6+).
  • FlowLayout은 단순한 그리드에 적합; CompositionalLayout (iOS 13+)은 다양한 레이아웃의 복잡한 섹션에 적합.
  • UICollectionViewCell은 모든 하위 뷰에 contentView를 사용 — 셀에 직접 추가하지 마세요.
  • UICollectionViewDiffableDataSource — 자동 변경 애니메이션이 있는 최신 타입 세이프 DataSource.
  • 커스텀 UICollectionViewLayout을 사용하면 임의 레이아웃(원, 나선형, 다이어그램)을 만들 수 있습니다.
  • App Store 상위 100개 중 72%의 iOS 앱이 콘텐츠 표시에 UICollectionView를 사용합니다.
  • 단순한 리스트에는 UITableView를 사용하고 나머지에는 UICollectionView를 사용하세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기