Backend/🩡 Go

[Go] Ellipsis 의 ν™œμš©κ³Ό μ£Όμ˜μ‚¬ν•­

Hugehoo 2025. 1. 22. 01:35

Go μ—μ„œ ... 은 Ellipsis (μ€„μž„ν‘œ) μ—°μ‚°μžλΌκ³  λΆ€λ₯Έλ‹€. JS μ—μ„œλ„ 이런 문법이 있던 걸둜 κΈ°μ–΅ν•œλ‹€. 

Ellipsis μ—°μ‚°μžμ˜ μ£Όμš” μš©λ„λŠ” 두가지 인데 1) κ°€λ³€μΈμž λ§€κ°œλ³€μˆ˜, 2) 슬라이슀 ν™•μž₯ μ •λ„λ‘œ μ •μ˜ν•  수 μžˆλ‹€.

 

1. κ°€λ³€μΈμž (Variadic Parameters)

κ°€λ³€ μΈμžλŠ” ν•¨μˆ˜κ°€ μž„μ˜μ˜ 개수의 인자λ₯Ό 받을 수 있게 ν•˜λŠ” κΈ°λŠ₯이닀. 예λ₯Ό λ“€μ–΄ int ν˜• 인자λ₯Ό νŒŒλΌλ―Έν„°λ‘œ λ„˜κ²¨μ•Ό ν•  λ•Œ ν•œκ°œλ₯Ό λ„˜κΈ°κ±°λ‚˜, κ·Έ μ΄μƒμ˜ 수λ₯Ό λ„˜κ²¨μ•Ό ν•˜λŠ” 경우λ₯Ό μƒκ°ν•΄λ³΄μž. 인자의 μˆ˜κ°€ λ³€ν•˜λŠ” 경우, 보톡 배열을 인자둜 λ°›μ•„μ•Ό ν•œλ‹€κ³  생각할 수 μžˆμ§€λ§Œ ellipsis λ₯Ό μ‚¬μš©ν•˜λ©΄ νŒŒλΌλ―Έν„°μ˜ νƒ€μž…μ„ ꡳ이 λ°°μ—΄λ‘œ λ³€κ²½ν•  ν•„μš”κ°€ μ—†λ‹€.

 

μ•„λž˜ μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄μž. sum() ν•¨μˆ˜λŠ” 인자둜 int ν˜• ellipsis λ₯Ό 받을 수 μžˆλ‹€.

// ν•¨μˆ˜ μ •μ˜ μ‹œ
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

// ν•¨μˆ˜ 호좜
sum(1, 2, 3)
sum(1, 2, 3, 4, 5)

 

 

μœ„ μ˜ˆμ‹œλ₯Ό 보고 띠용 ν•  수 μžˆλ‹€. κ·Έλƒ₯ 배열을 λ„˜κΈ°λ©΄ λ˜λŠ” 것 μ•„λ‹Œκ°€? ꡳ이 ellipsis λ₯Ό μΈμžμ— μ„ μ–Έν•΄μ•Όν•˜λ‚˜? 그런 생각이 λ“€μ—ˆλ‹€λ©΄ μ•„λž˜ μ½”λ“œλ₯Ό 보자. 차이가 λŠκ»΄μ§€μ‹€λž‘κ°€ λͺ¨λ₯΄κ² λ‹€. κ°€λ³€ 인자λ₯Ό μ‚¬μš©ν•œ sum() ν•¨μˆ˜λŠ” ꡳ이 배열을 μ‚¬μš©ν•˜μ§€ μ•Šκ³  직접 ν•¨μˆ˜ 호좜이 κ°€λŠ₯ν•˜μ§€λ§Œ, λ°°μ—΄ 슬라이슀λ₯Ό 인자둜 λ°›λŠ” sumArray() λŠ” 슬라이슀λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μƒμ„±ν•΄μ€˜μ•Ό ν•œλ‹€.

// κ°€λ³€ 인자 μ‚¬μš©
func sum(numbers ...int) int {
    ...
}

// 직접 호좜이 κ°„νŽΈν•˜λ‹€
sum(1, 2, 3)

// λ°°μ—΄/슬라이슀 μ‚¬μš©
func sumArray(numbers []int) int {
    ...
}

// 호좜 μ‹œ 슬라이슀λ₯Ό λͺ…μ‹œμ μœΌλ‘œ 생성해야 ν•œλ‹€
sumArray([]int{1, 2, 3})

 

또 λ‹€λ₯Έ μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄μž. λ‹€μŒ μ˜ˆμ‹œλŠ” 문자λ₯Ό concatenate ν•˜λŠ” ν•¨μˆ˜λ‘œ, elemetns μΈμžλŠ” κ°€λ³€μ μœΌλ‘œ string νƒ€μž…μ˜ λ³€μˆ˜λ₯Ό 넣을 수 μžˆλ‹€. concatenate ν•˜κ³  싢은 string 이 λͺ‡κ°œμ΄λ“  상관없이 인자λ₯Ό μΆ”κ°€ν•  수 μžˆλ‹€.

// λ¬Έμžμ—΄ κ²°ν•© ν•¨μˆ˜
func concatenate(delimeter string, elements ...string) string {
    return strings.Join(elements, delimeter)
}

result := concatenate("-", "Go", "is", "awesome")
fmt.Println(result) // 좜λ ₯: Go-is-awesome

concatenate("-", "Go", "is", "awesome", "μ­‰μ­‰", "μΆ”κ°€", "κ°€λŠ₯")

 

2. 슬라이슀 ν™•μž₯

λ‹€μŒμ€ 슬라이슀 ν™•μž₯ μ‹œμ—λ„ ellipsis λ₯Ό μœ μš©ν•˜κ²Œ ν™œμš©ν•  수 μžˆλ‹€. 

이전 1번 μ˜ˆμ‹œμ—μ„œλŠ” type μ•žμ— ... 이 λΆ™μ—ˆμ§€λ§Œ, 슬라이슀λ₯Ό ν™•μž₯ν•˜λŠ”(μ‰½κ²Œ 말해 배열을 λ²—κ²¨λ²„λ¦¬λŠ”) κ²½μš°μ—” ... λ₯Ό λ³€μˆ˜ 뒀에 뢙이면 끝이닀.

nums := []int{1, 2, 3}
otherNums := []int{4, 5, 6}

// 슬라이슀λ₯Ό κ°œλ³„ μš”μ†Œλ‘œ ν™•μž₯ν•˜μ—¬ 전달
allNums := append(nums, otherNums...)


// ellipsisλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ μ•„λž˜μ²˜λŸΌ 각 인자λ₯Ό μˆœνšŒν•΄μ•Ό ν•œλ‹€.
allNums := nums
for _, num := range otherNums {
    allNums = append(allNums, num)
}
// ellipsis λ₯Ό μ‚¬μš©ν•œ 것 보단 가독성이 μ’‹μ§€μ•Šλ‹€.

 

3. μ‹€μ œ μ˜ˆμ‹œ

이번 글을 μ“°κ²Œ 된 이유λ₯Ό μ½”λ“œλ‘œ μ„€λͺ…해보렀 ν•œλ‹€. 

κΈ°μ‘΄μ—λŠ” ν•˜λ‚˜μ˜ redis μ—”λ“œν¬μΈνŠΈλ₯Ό μ‚¬μš©ν•˜λ˜ μ‹œμŠ€ν…œμ—μ„œ, replica μ—”λ“œν¬μΈνŠΈλ₯Ό μΆ”κ°€ν•˜κ²Œ λ˜λ©΄μ„œ 총 2개의 endpoint λ₯Ό μ“°κ²Œ 됐닀. Redis 섀정값을 μ΄ˆκΈ°ν™” ν•  λ•Œ 이 endpoint 값을 μ£Όμž… μ‹œ ellipsis λ₯Ό μ‚¬μš©ν•  수 μžˆμ—ˆλ‹€. κΈ°μ‘΄ μ½”λ“œμ—μ„œλŠ” ν•˜λ‚˜μ˜ endpoint 만 λ°›μ§€λ§Œ, κ°œμ„ λœ μ½”λ“œμ—μ„œλŠ” string 을 ellipsis 둜 λ°›μ•„ 2개 μ΄μƒμ˜ endpoint 도 인자둜 받을 수 있게 됐닀. 

// κΈ°μ‘΄
func mustConnectRedis(endpoint string) redis.UniversalClient {
	...
    redisClient := redis.NewUniversalClient(&redis.UniversalOptions{
		Addrs:        endpoint,
		ReadOnly:     true,
	})
}
mustConnectRedis("127.0.0.1:6379")



// 슬라이슀 ν™•μž₯ν•˜λ„λ‘ κ°œμ„ 
func mustConnectRedis(endpoint ...string) redis.UniversalClient {
	...
    redisClient := redis.NewUniversalClient(&redis.UniversalOptions{
		Addrs:        endpoint,
		ReadOnly:     true,
	})
}
mustConnectRedis("127.0.0.1:6379", "127.0.0.1:6380")

 

 

μ£Όμ˜μ‚¬ν•­κ³Ό 팁

1. κ°€λ³€μΈμžλŠ” 항상 ν•¨μˆ˜ λ§ˆμ§€λ§‰ λ§€κ°œλ³€μˆ˜μ—¬μ•Ό ν•œλ‹€.

// μ˜¬λ°”λ₯Έ μ‚¬μš©
func correct(prefix string, numbers ...int)

// 잘λͺ»λœ μ‚¬μš©
func incorrect(numbers ...int, prefix string) // 컴파일 μ—λŸ¬

 

2. νƒ€μž… μ•ˆμ •μ„± μœ μ§€

func betterFunction(numbers ...int) // interface{} λŒ€μ‹  ꡬ체적인 νƒ€μž… μ‚¬μš© ꢌμž₯
func avoidThis(args ...interface{}) // κ°€λŠ₯ν•˜λ©΄ 피할것

 

3. μž‘μ€ 데이터 μ…‹μ—μ„œ μ‚¬μš© ꢌμž₯

가독성 μΈ‘λ©΄μ—μ„œ 큰 μž₯점을 λ³΄μ΄λŠ” ellipsis μ§€λ§Œ μ„±λŠ₯ 츑면도 κ³ λ €ν•΄μ•Ό ν•œλ‹€. 

ellipsis λ₯Ό μ‚¬μš©ν•˜λ©΄ λ‚΄λΆ€μ μœΌλ‘œ μŠ¬λΌμ΄μŠ€κ°€ μƒμ„±λ˜λ―€λ‘œ 맀우 큰 데이터 셋을 λ‹€λ£° λ•ŒλŠ” 직접 슬라이슀λ₯Ό μ „λ‹¬ν•˜λŠ” 것이 더 효율적이기 λ•Œλ¬Έμ΄λ‹€. μ•„λž˜ μ½”λ“œλ₯Ό 보며 μžμ„Ένžˆ μ•Œμ•„λ³΄μž. 

func sum(numbers ...int) int {
    // numbersλŠ” λ‚΄λΆ€μ μœΌλ‘œ 슬라이슀둜 λ³€ν™˜λ¨
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

// 호좜 μ‹œ
sum(1, 2, 3)

 

sum() 이 싀행될 λ•Œ λ‚΄λΆ€μ μœΌλ‘œ μΌμ–΄λ‚˜λŠ” 일을 μ •λ¦¬ν•˜λ©΄,

1. μƒˆλ‘œμš΄ μŠ¬λΌμ΄μŠ€κ°€ 생성됨

2. μΈμžλ“€ (1,2,3)이 슬라이슀둜 볡사됨

3. ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ 슬라이슀λ₯Ό μ‚¬μš©ν•¨.

 

μœ„ μ˜ˆμ‹œμ²˜λŸΌ 적은 수의 μΈμžκ°€ 슬라이슀둜 λ³΅μ‚¬λ˜μ–΄λ„ μ„±λŠ₯ μ°¨μ΄λŠ” 거의 λ°œμƒν•˜μ§€ μ•Šμ„ 거라 μƒκ°ν•œλ‹€. ν•˜μ§€λ§Œ λ§Œμ•½ 크기가 (μ˜€λ²„ν•΄μ„œ) 100만인 슬라이슀λ₯Ό μ΄ˆκΈ°ν™”ν•˜μ—¬ ellipsis 둜 λ„˜κΈ΄λ‹€κ³  κ°€μ •ν•΄λ³΄μž.

μΌ€μ΄μŠ€ 1 처럼 슬라이슀λ₯Ό ν™•μž₯ν•˜λ©΄ sumVariadic() λ‚΄λΆ€μ—μ„œ 크기가 100만인 μŠ¬λΌμ΄μŠ€κ°€ μƒˆλ‘œ μƒμ„±λ˜μ–΄ λ³΅μ‚¬λ˜λ©° μ΄λŸ¬ν•œ μž‘μ—…μ΄ 반볡되면 μ‹¬κ°ν•œ λ©”λͺ¨λ¦¬ 문제λ₯Ό μ΄ˆλž˜ν•  수 μžˆλ‹€.

λ•Œλ¬Έμ— 데이터 셋이 λ„ˆλ¬΄ 컀질 것 같을 땐 ellipsis λ₯Ό μ‚¬μš©ν•˜κΈ° 보닀 μΌ€μ΄μŠ€ 2 처럼 κΈ°μ‘΄ 슬라이슀λ₯Ό 직접 μ‚¬μš©ν•˜λŠ” 것이 λ©”λͺ¨λ¦¬ μΈ‘λ©΄μ—μ„œ 효율적이라 μƒκ°ν•œλ‹€.  

numbers := make([]int, 1000000)

// μΌ€μ΄μŠ€ 1: 좔가적인 슬라이슀 생성 및 볡사 λ°œμƒ
sumVariadic(numbers...)  

// μΌ€μ΄μŠ€ 2: κΈ°μ‘΄ 슬라이슀 직접 μ‚¬μš© (μΆ”κ°€ λ©”λͺ¨λ¦¬ ν• λ‹Ή μ—†μŒ)
sumSlice(numbers)

  

μ •λ¦¬ν•˜λ©΄

ellipsis λ₯Ό μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” μ½”λ“œ μœ μ—°μ„±κ³Ό μž¬μ‚¬μš©μ„±μ„ ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€. ν•¨μˆ˜ 호좜 μ‹œ 더 κΉ”λ”ν•˜κ³  직관적인 문법을 μ‚¬μš©ν•˜μ—¬ 가독성을 높일 수 있고, λ§€κ°œλ³€μˆ˜ 개수의 μœ μ—°μ„±λ„ λ†’μ΄λŠ” μž₯점을 노릴 수 μžˆλ‹€. 단 슬라이슀λ₯Ό ν™•μž₯ν•˜λ©΄ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ ν•΄λ‹Ή 슬라이슀 크기만큼 μƒˆλ‘œμš΄ 슬라이슀λ₯Ό μƒμ„±ν•˜κ³  λ³΅μ‚¬ν•˜λŠ” μž‘μ—…μ΄ λ°œμƒν•  수 μžˆλ‹€. λ•Œλ¬Έμ— 크기가 큰 데이터λ₯Ό μ‚¬μš©ν•˜λŠ” 경우라면 λ©”λͺ¨λ¦¬ 츑면도 κ³ λ €ν•˜μ—¬ ellipsis λ₯Ό 적절히 μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•œλ‹€.