반응형
Swift에서 문자열 결합을 위해 joined(separator:) 사용하기
문자열을 다수 결합할 때 + 반복보다 joined(separator:) 가 빠르고 명확합니다. 내부적으로 버퍼를 효율적으로 관리해 불필요한 중간 문자열 생성을 줄이기 때문입니다.
1. 핵심 요약
- 여러 문자열을 하나로 합칠 때는 joined(separator:)가 가독성·성능 모두에서 유리합니다.
- reduce나 + 반복 결합은 반복적인 복사로 느려질 수 있습니다.
- Substring·String 혼합, 숫자 변환, 지연 평가(lazy)와도 잘 어울립니다.
2. 기본 사용
let words = ["Hello", "Swift", "World"]
let sentence = words.joined(separator: " ") // "Hello Swift World"
let noSep = words.joined() // "HelloSwiftWorld" (separator 기본값 "")
3. String/Substring 혼용
let text = " A, B, C "
let tokens = text.split(separator: ",") // [Substring]
let trimmed = tokens.map { $0.trimmingCharacters(in: .whitespaces) } // [String]
let result = trimmed.joined(separator: " | ") // "A | B | C"
split은 Substring을 내놓습니다. 바로 joined해도 되지만, 장기 보관할 값이면 String으로 변환을 권장합니다(원본 문자열의 저장 비용에서 분리).
4. 숫자·커스텀 타입 결합
문자열이 아닌 요소는 먼저 문자열로 변환하세요.
let nums = [10, 20, 30]
let csv = nums.map(String.init).joined(separator: ",") // "10,20,30"
고급 포맷팅(3자리 구분 등)은 NumberFormatter를 사용한 뒤 joined로 합칩니다.
import Foundation
let f = NumberFormatter()
f.numberStyle = .decimal // 1,234 형식
let out = [1234, 56789].compactMap { f.string(from: $0 as NSNumber) }
.joined(separator: /) // 1,234 / 56,789
5. 경로·URL 조합 팁
let components = ["Users", "me", "Documents", "file.txt"]
let path = components.joined(separator: "/") // "Users/me/Documents/file.txt"
실제 파일 경로/URL은 URL(fileURLWithPath:)나 URL.appendingPathComponent 사용을 권장(플랫폼별 구분자 문제 방지). joined는 단순 문자열 결합일 때만.
6. 라인 결합 / CSV 생성
let header = ["id","name","age"].joined(separator: ",")
let rows = [
["1","Alice","28"],
["2","Bob","31"]
].map { $0.joined(separator: ",") }
let csv = ([header] + rows).joined(separator: "
")
/*
id,name,age
1,Alice,28
2,Bob,31
*/
7. reduce/+ 대비 성능 차이
let words = Array(repeating: "swift", count: 50_000)
// 비권장: 누적 복사 비용 큼
let s1 = words.reduce("") { $0 + ($0.isEmpty ? "" : " ") + $1 }
// 권장: 내부 버퍼 최적화
let s2 = words.joined(separator: " ")
joined는 전체 길이를 예측·버퍼를 한번에 확보하는 경향이 있어 대용량에서 유리합니다. (실측은 Release + 최적화에서)
8. lazy와 결합해 중간 배열 없애기
let raw = ["1"," two ","3 "]
let normalized = raw.lazy
.map { $0.trimmingCharacters(in: .whitespaces) } // 지연 변환
.map(\.uppercased)
let out = normalized.joined(separator: ",") // materialize 시점에 결합
9. ListFormatter로 지역화된 목록 문자열
사용자에게 보이는 자연어 목록은 ListFormatter가 더 적합합니다.
import Foundation
let lf = ListFormatter()
lf.locale = Locale(identifier: ko_KR)
let pretty = lf.string(from: [사과, 배, 포도]) // 사과, 배 및 포도
UI 문맥에서는 joined(" / ") 같은 임의 구분자보다 지역화 규칙을 따르는 API를 고려하세요.
10. 줄바꿈·공백 처리 팁
let lines = [" line1 ", "", "line3 "]
let clean = lines
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
.joined(separator: "\n")
/*
line1
line3
*/
11. 긴 문자열 빌드가 잦다면
- 잦은 누적/삽입이 필요하면 TextOutputStream 또는 String.reserveCapacity(_:)로 직접 버퍼 관리
- 하지만 단순 결합이라면 joined가 대부분 최적
var s = ""
s.reserveCapacity(10_000)
for p in payloads { s += p } // 혹은 s.append(contentsOf: payloads)
12. 예외·엣지 케이스
- 빈 배열의 joined는 ""를 반환합니다.
- 요소에 이미 구분자가 포함되어 있다면, 이중 구분 방지 로직이 필요할 수 있습니다.
- 메모리 상한에 가까운 매우 큰 결합은 Data 스트리밍(파일 핸들, OutputStream)로 대체 고려.
13. 선택 가이드
| 상황 | 권장 방법 |
| 단순 다중 문자열 결합 | joined(separator:) |
| 숫자/커스텀 객체 결합 | map(String.init) 후 joined |
| 사용자 표시용 자연어 목록 | ListFormatter |
| 경로/URL | URL API 사용 |
| 대용량 파이프라인 | lazy + joined |
14. 간단 벤치마크 스케치
import Foundation
let items = (0..<200_000).map { "v\($0)" }
func measure(_ name: String, _ body: () -> Void) {
let t1 = CFAbsoluteTimeGetCurrent(); body(); let t2 = CFAbsoluteTimeGetCurrent()
print(name, ":", t2 - t1, "sec")
}
measure("joined") { _ = items.joined(separator: ",") }
measure("reduce +") {
_ = items.reduce("") { $0 + ($0.isEmpty ? "" : ",") + $1 }
}
15. 참조 링크
- Sequence.joined(separator:)
https://developer.apple.com/documentation/swift/sequence/joined(separator:) - Array.joined(separator:) (String 전용 시그니처)
https://developer.apple.com/documentation/swift/array/2892894-joined - ListFormatter (지역화된 목록)
https://developer.apple.com/documentation/foundation/listformatter - String.reserveCapacity(_:)
https://developer.apple.com/documentation/swift/string/1538753-reservecapacity
16. 결론
- 문자열 다중 결합 = joined(separator:) 가 기본값입니다.
- 숫자/포맷팅/지역화가 섞이면 변환(map) 또는 전용 API와 결합하세요.
- 대용량/스트리밍은 lazy·TextOutputStream·URL/OutputStream 등 상황 맞춤형으로 보완하면 됩니다.
반응형
'Dev Study > Swift' 카테고리의 다른 글
| Swift에서 ARC 최적화: weak vs unowned (0) | 2025.11.11 |
|---|---|
| Swift에서 @inlinable / @inline(__always) (0) | 2025.11.11 |
| Swift에서 구조체(Value Type) 기반 설계 (0) | 2025.11.11 |
| Swift에서 switch문의 pattern matching (0) | 2025.11.11 |
| Swift에서 guard let vs 중첩 if let (0) | 2025.11.11 |
| Swift에서 Set를 사용해서 포함(contains) 검사 (0) | 2025.11.11 |
| Swift에서 append(contentsOf:) vs 반복 append (0) | 2025.11.11 |
| Swift에서 reduce(into:) vs reduce (0) | 2025.11.11 |


