setproduct 함수
setproduct 함수
setproduct 함수는 데카르트 곱(Cartesian product)을 계산해서 주어진 모든 집합의 요소들로 만들 수 있는 가능한 모든 조합을 찾아주는 함수예요.
출처: 문서
본문
setproduct(sets...)
이 함수는 여러 집합의 구성원들로 만들 수 있는 모든 조합을 빠짐없이 구할 때 특히 유용해요. 예를 들어 "애플리케이션별 × 환경별" 리소스를 만들 때가 대표적이에요.
> setproduct(["development", "staging", "production"], ["app1", "app2"])
[
[
"development",
"app1",
],
[
"development",
"app2",
],
[
"staging",
"app1",
],
[
"staging",
"app2",
],
[
"production",
"app1",
],
[
"production",
"app2",
],
]
이 함수에는 반드시 최소 두 개 이상의 인자를 전달해야 해요.
주로 집합(set)을 위해 정의되지만, 이 함수는 리스트에도 동작해요. 주어진 인자가 모두 리스트라면 결과도 리스트이며, 주어진 리스트들의 순서를 보존해요. 그렇지 않으면 결과는 집합이에요. 어느 쪽이든 결과의 요소 타입은 주어진 각 인자에 순서대로 대응하는 값들의 리스트예요.
예제
이 함수의 흔한 사용법 예시는 위에 있어요. 손으로 작성할 때는 덜 흔하지만 재사용 가능한 모듈 상황에서는 나타날 수 있는 다른 경우들도 있어요.
인자 중 하나라도 비어 있으면 결과도 항상 비어요. 어떤 수에 0을 곱하면 0이 되는 것과 비슷하죠.
> setproduct(["development", "staging", "production"], [])
마찬가지로 모든 인자가 요소를 하나만 갖고 있다면 결과도 요소가 하나뿐이며, 각 인자의 첫 번째 요소가 돼요.
> setproduct(["a"], ["b"])
[
[
"a",
"b",
],
]
각 인자는 모든 요소가 일관된 타입이어야 해요. 그렇지 않으면 테라폼이 가장 일반적인 타입으로 변환을 시도하거나, 그런 변환이 불가능하면 오류를 만들어요. 예를 들어 문자열과 숫자를 섞으면 결과 요소들이 모두 일관된 타입이 되도록 숫자가 문자열로 변환돼요.
> setproduct(["staging", "production"], ["a", 2])
[
[
"staging",
"a",
],
[
"staging",
"2",
],
[
"production",
"a",
],
[
"production",
"2",
],
]
for_each를 위한 조합 찾기
리소스 for_each와 dynamic 블록 언어 기능은 모두 반복마다 요소 하나씩을 갖는 컬렉션 값이 필요해요.
때로는 입력 데이터가 for_each 인자에 직접 사용할 수 없는 별개의 값들로 제공되는 경우가 있어요. 그럴 때 여러 다른 컬렉션들의 요소로 가능한 모든 고유 조합을 찾고 싶다면 setproduct가 유용한 도우미 함수가 돼요.
예를 들어 다음처럼 변수를 선언하는 모듈을 생각해 봐요.
variable "networks" {
type = map(object({
base_cidr_block = string
}))
}
variable "subnets" {
type = map(object({
number = number
}))
}
목표가 정의된 각 네트워크마다 정의된 각 서브넷을 만드는 것이라면, 최상위 네트워크는 var.networks를 직접 사용해 만들 수 있어요. 이미 결과 인스턴스가 맵 요소와 일대일로 대응되는 형태이기 때문이에요.
resource "aws_vpc" "example" {
for_each = var.networks
cidr_block = each.value.base_cidr_block
}
그러나 서브넷 전부를 단일 resource 블록으로 선언하려면, 먼저 각 요소가 네트워크와 서브넷의 모든 조합을 나타내는 컬렉션을 만들어야 해요. 그래야 각 요소가 하나의 서브넷을 나타내게 돼요.
locals {
# setproduct works with sets and lists, but the variables are both maps
# so convert them first.
networks = [
for key, network in var.networks : {
key = key
cidr_block = network.cidr_block
}
]
subnets = [
for key, subnet in var.subnets : {
key = key
number = subnet.number
}
]
network_subnets = [
# in pair, element zero is a network and element one is a subnet,
# in all unique combinations.
for pair in setproduct(local.networks, local.subnets) : {
network_key = pair[0].key
subnet_key = pair[1].key
network_id = aws_vpc.example[pair[0].key].id
# The cidr_block is derived from the corresponding network. Refer to the
# cidrsubnet function for more information on how this calculation works.
cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number)
}
]
}
resource "aws_subnet" "example" {
# local.network_subnets is a list, so project it into a map
# where each key is unique. 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
}
위 예제의 network_subnets 리스트는 입력 변수의 네트워크 요소와 서브넷 요소의 각 조합마다 서브넷 인스턴스를 하나씩 만들어요. 예를 들어 다음 입력에 대해:
networks = {
a = {
base_cidr_block = "10.1.0.0/16"
}
b = {
base_cidr_block = "10.2.0.0/16"
}
}
subnets = {
a = {
number = 1
}
b = {
number = 2
}
c = {
number = 3
}
}
network_subnets 출력은 다음과 비슷하게 나와요.
[
{
"cidr_block" = "10.1.16.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "a"
},
{
"cidr_block" = "10.1.32.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "b"
},
{
"cidr_block" = "10.1.48.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "c"
},
{
"cidr_block" = "10.2.16.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "a"
},
{
"cidr_block" = "10.2.32.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "b"
},
{
"cidr_block" = "10.2.48.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "c"
},
]
더 알아보기 (Learn more)
contains— 주어진 리스트/집합에 특정 요소 값이 포함되는지 검사해요.flatten— 계층 데이터를 단일 리스트로 평탄화해요. 두 객체 타입 간의 관계가 명시적으로 정의된 상황에 유용해요.setintersection— 여러 집합의 교집합을 계산해요.setsubtract— 두 집합의 상대 여집합을 계산해요.setunion— 여러 집합의 합집합을 계산해요.