flatten 함수

flatten 함수

flatten은 리스트를 받아서, 그 안에 들어 있는 리스트 요소들을 그 내용물로 펼쳐서 평평한(1차원) 시퀀스로 바꿔줘요. 중첩 구조를 한 단계로 정리하고 싶을 때 쓰는 함수죠.

출처: Packer 공식 문서

본문

flatten takes a list and replaces any elements that are lists with a flattened sequence of the list contents.

예시 (Examples)

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

중첩된 리스트 안에 또 바로 중첩된 리스트가 들어 있어도, 이 역시 재귀적으로 평평하게 만들어져요:

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

다만 맵(map) 안에 담긴 것처럼 간접적으로 중첩된 리스트는 펼쳐지지 않아요.

for_each를 위한 중첩 구조 평탄화 (Flattening nested structures for for_each)

리소스 for_each와 dynamic 블록 언어 기능은 반복 횟수만큼 요소를 가진 컬렉션 값이 필요해요.

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

예를 들어 아래처럼 변수를 선언하는 폴더를 생각해 볼게요:

variable "networks" {
  type = map(object({
    cidr_block = string
    subnets = map(object({
      cidr_block = string
    })
  })
}

이런 구조는 최상위 네트워크와 그 서브넷처럼 자연스럽게 트리를 이루는 객체들을 모델링하는 합리적인 방법이에요. 최상위 네트워크의 반복은 이 변수를 그대로 쓸 수 있는데, 이미 결과 인스턴스가 맵 요소와 일대일로 대응되는 형태거든요:

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 = {
    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
}

이렇게 하면 서브넷 객체 하나마다 서브넷 인스턴스 하나가 생기면서도, 서브넷과 이를 담은 네트워크 사이의 연결 관계는 그대로 유지돼요.

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