flatten 함수

flatten 함수

flatten은 리스트를 받아서, 그 요소 중 리스트인 것들을 해당 리스트 내용의 평평한(flattened) 순서로 바꿔주는 함수예요.

출처: 문서

본문

예제

> flatten([["a", "b"], [], ["c"]])
["a", "b", "c"]

중첩된 리스트 안에 또 직접 중첩된 리스트가 있다면, 그 역시 재귀적으로 평평하게 만들어져요.

> flatten([["a", "b"], [], ["c"]])
["a", "b", "c"]

맵 안처럼 간접적으로 중첩된 리스트는 평평하게 만들어지지 않아요.

for_each를 위한 중첩 구조 평평하게 만들기

리소스 for_eachdynamic 블록 언어 기능은 모두 반복마다 요소 하나씩을 담은 컬렉션 값이 필요해요.

때로는 입력 데이터 구조가 for_each 인자에 쓰기에 자연스럽지 않은 형태일 수 있는데, 이때 중첩 데이터 구조를 평평한 구조로 줄일 때 flatten이 유용한 도우미 함수가 돼요.

예를 들어, 다음과 같은 변수를 선언하는 모듈을 생각해 보세요.

variable "networks" {
  type = map(object({
    cidr_block = string
    subnets    = map(object({ cidr_block = string }))
  }))
  default = {
    "private" = {
      cidr_block = "10.1.0.0/16"
      subnets = {
        "db1" = {
          cidr_block = "10.1.0.0/24"
        }
        "db2" = {
          cidr_block = "10.1.1.0/24"
        }
      }
    },
    "public" = {
      cidr_block = "10.2.0.0/16"
      subnets = {
        "webserver" = {
          cidr_block = "10.2.1.0/24"
        }
        "email_server" = {
          cidr_block = "10.2.2.0/24"
        }
      }
    }
    "dmz" = {
      cidr_block = "10.3.0.0/16"
      subnets = {
        "firewall" = {
          cidr_block = "10.3.1.0/24"
        }
      }
    }
  }
}

위는 네트워크와 그 서브넷처럼 자연스럽게 트리를 이루는 객체를 모델링하는 합리적인 방법이에요. 최상위 네트워크의 반복은 결과 인스턴스가 맵 요소와 일대일로 일치하는 형태이므로 이 변수를 직접 사용할 수 있어요.

resource "aws_vpc" "example" {
  for_each = var.networks

  cidr_block = each.value.cidr_block
}

하지만 모든 서브넷을 단일 resource 블록으로 선언하려면, 우선 구조를 평평하게 만들어 각 최상위 요소가 서브넷 하나씩을 나타내는 컬렉션을 만들어야 해요.

locals {
  # flatten ensures that this local value is a flat list of objects, rather
  # than a list of lists of objects.
  network_subnets = flatten([
    for network_key, network in var.networks : [
      for subnet_key, subnet in network.subnets : {
        network_key = network_key
        subnet_key  = subnet_key
        network_id  = aws_vpc.example[network_key].id
        cidr_block  = subnet.cidr_block
      }
    ]
  ])
}

resource "aws_subnet" "example" {
  # local.network_subnets is a list, so we must now project it into a map
  # where each key is unique. We'll combine the network and subnet keys to
  # produce a single unique key per instance.
  for_each = tomap({
    for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet
  })

  vpc_id            = each.value.network_id
  availability_zone = each.value.subnet_key
  cidr_block        = each.value.cidr_block
}

위 결과는 서브넷과 그 서브넷이 속한 네트워크 사이의 연결 관계를 유지하면서, 서브넷 객체 하나당 서브넷 인스턴스 하나씩을 만들게 돼요.

더 알아보기 (Learn more)

  • setproduct — 여러 리스트나 집합 값의 모든 조합을 찾아주며, for_each 구문에 쓸 컬렉션을 준비할 때도 유용해요.