์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฏ ์Šค์œ„ํ”„ํŠธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์™„์ „์ •๋ณต

๐ŸŽฏ ์Šค์œ„ํ”„ํŠธ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์™„์ „์ •๋ณต

๋ถˆ๋ณ€์„ฑ๊ณผ ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ๋งŒ๋“œ๋Š” ์•ˆ์ „ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ์˜ ์„ธ๊ณ„ โœจ

์•ˆ๋…•! ์˜ค๋Š˜์€ ์Šค์œ„ํ”„ํŠธ์—์„œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๋ถˆ๋ณ€์„ฑ๊ณผ ์ˆœ์ˆ˜ํ•จ์ˆ˜ ํŒจํ„ด์ด ์™œ ์ค‘์š”ํ•œ์ง€ ํ•จ๊ป˜ ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ๐Ÿš€

ํ˜น์‹œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค๊ฐ€ "์–ด? ์ด ๋ณ€์ˆ˜ ๊ฐ’์ด ์™œ ๋ฐ”๋€Œ์—ˆ์ง€?" ํ•˜๋ฉด์„œ ๋””๋ฒ„๊น…์— ๋ช‡ ์‹œ๊ฐ„์„ ๋‚ ๋ ค๋ณธ ๊ฒฝํ—˜ ์žˆ์–ด? ์•„๋‹ˆ๋ฉด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”๋ฐ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€์ž‘์šฉ(side effect)์ด ๋ฐœ์ƒํ•ด์„œ ๋จธ๋ฆฌ๋ฅผ ์ฅ์–ด๋œฏ์€ ์ ์€? ๊ทธ๋ ‡๋‹ค๋ฉด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๋„ˆ์—๊ฒŒ ๋”ฑ ๋งž๋Š” ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ์–ด!

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋‹จ์ˆœํžˆ ํŠธ๋ Œ๋””ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์ด ์•„๋‹ˆ๋ผ, ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” ์‹ค์šฉ์ ์ธ ์ ‘๊ทผ ๋ฐฉ์‹์ด์•ผ. ํŠนํžˆ ์Šค์œ„ํ”„ํŠธ๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์ง€์›ํ•˜๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์–ธ์–ด ์ฐจ์›์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ์–ด์„œ, ์ด๋ฅผ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ๊น”๋”ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์ข‹์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€. ๐Ÿ˜Š

๋ช…๋ นํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ƒํƒœ ๋ณ€๊ฒฝ ๋ถ€์ž‘์šฉ ๋ฐœ์ƒ ๋ถˆ๋ณ€์„ฑ ์œ ์ง€ ์ˆœ์ˆ˜ํ•จ์ˆ˜ ๐ŸŽฏ Swift์˜ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์•ˆ์ „ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ ์ž‘์„ฑ

๐Ÿค” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๋ญ๊ธธ๋ž˜?

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ(Functional Programming, FP)์€ ํ”„๋กœ๊ทธ๋žจ์„ ์ˆ˜ํ•™์  ํ•จ์ˆ˜์˜ ์กฐํ•ฉ์œผ๋กœ ๋ฐ”๋ผ๋ณด๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์ด์•ผ. ์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ์€ "์ˆœ์ˆ˜ํ•จ์ˆ˜"์™€ "๋ถˆ๋ณ€์„ฑ"์ด๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐœ๋…์ด์ง€.

๐Ÿ“Œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ํ•ต์‹ฌ ์›์น™

1. ์ˆœ์ˆ˜ํ•จ์ˆ˜ (Pure Functions)
๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•ด ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜

2. ๋ถˆ๋ณ€์„ฑ (Immutability)
ํ•œ ๋ฒˆ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ , ํ•„์š”ํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑ

3. ์ผ๊ธ‰ ํ•จ์ˆ˜ (First-Class Functions)
ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜์— ํ• ๋‹นํ•˜๊ณ , ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๊ณ , ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

4. ๊ณ ์ฐจ ํ•จ์ˆ˜ (Higher-Order Functions)
ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜

์Šค์œ„ํ”„ํŠธ๋Š” ์ด ๋ชจ๋“  ์›์น™์„ ์ง€์›ํ•˜๋Š” ๋ฉ€ํ‹ฐ ํŒจ๋Ÿฌ๋‹ค์ž„ ์–ธ์–ด์•ผ. ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๋„ ํ•  ์ˆ˜ ์žˆ๊ณ , ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ๋„ ํ•  ์ˆ˜ ์žˆ์ง€. ์ด๊ฒŒ ์Šค์œ„ํ”„ํŠธ์˜ ํฐ ์žฅ์ ์ด์•ผ! ๐ŸŽ‰

๐Ÿ’ก ์™œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?

โ€ข ๋ฒ„๊ทธ ๊ฐ์†Œ: ์ƒํƒœ ๋ณ€๊ฒฝ์ด ์—†์œผ๋‹ˆ ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฒ„๊ทธ๊ฐ€ ์ค„์–ด๋“ค์–ด
โ€ข ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ: ์ˆœ์ˆ˜ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์ •๋ง ์‰ฌ์›Œ
โ€ข ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ: ์ƒํƒœ ๊ณต์œ ๊ฐ€ ์—†์–ด์„œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์ด ์•ˆ์ „ํ•ด
โ€ข ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ: ์ž‘์€ ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•ด์„œ ๋ณต์žกํ•œ ๋กœ์ง์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด
โ€ข ๊ฐ€๋…์„ฑ: ์„ ์–ธ์  ์ฝ”๋“œ ์Šคํƒ€์ผ๋กœ ์˜๋„๊ฐ€ ๋ช…ํ™•ํ•ด์ ธ

๐Ÿ” ์ˆœ์ˆ˜ํ•จ์ˆ˜๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

์ˆœ์ˆ˜ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” ๊ฐœ๋…์ด์•ผ. ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งํ•˜๋ฉด, ์™ธ๋ถ€ ์„ธ๊ณ„์™€ ๋‹จ์ ˆ๋œ ๋…๋ฆฝ์ ์ธ ํ•จ์ˆ˜๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ.

์ˆœ์ˆ˜ํ•จ์ˆ˜์˜ ๋‘ ๊ฐ€์ง€ ์กฐ๊ฑด:

1. ์ฐธ์กฐ ํˆฌ๋ช…์„ฑ (Referential Transparency)
๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•ด ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ด. ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ๋Œ€์ฒดํ•ด๋„ ํ”„๋กœ๊ทธ๋žจ์˜ ๋™์ž‘์ด ๋ณ€ํ•˜์ง€ ์•Š์•„์•ผ ํ•˜์ง€.

2. ๋ถ€์ž‘์šฉ ์—†์Œ (No Side Effects)
ํ•จ์ˆ˜ ์™ธ๋ถ€์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์•„์•ผ ํ•ด. ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ฑฐ๋‚˜, ํŒŒ์ผ์„ ์“ฐ๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํ•˜๊ฑฐ๋‚˜, ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๋Š” ๋“ฑ์˜ ํ–‰์œ„๊ฐ€ ์—†์–ด์•ผ ํ•ด.

โœ… ์ˆœ์ˆ˜ํ•จ์ˆ˜ ์˜ˆ์ œ

// ์ˆœ์ˆ˜ํ•จ์ˆ˜ - ๊ฐ™์€ ์ž…๋ ฅ์— ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

// ์ˆœ์ˆ˜ํ•จ์ˆ˜ - ์™ธ๋ถ€ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š์Œ
func multiply(_ numbers: [Int], by factor: Int) -> [Int] {
    return numbers.map { $0 * factor }
}

// ์ˆœ์ˆ˜ํ•จ์ˆ˜ - ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์‚ฌ์šฉ
func updateUser(name: String, in user: User) -> User {
    return User(name: name, age: user.age, email: user.email)
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
let result1 = add(3, 5)  // ํ•ญ์ƒ 8
let result2 = add(3, 5)  // ํ•ญ์ƒ 8
print(result1 == result2)  // true

let numbers = [1, 2, 3, 4, 5]
let doubled = multiply(numbers, by: 2)
print(numbers)   // [1, 2, 3, 4, 5] - ์›๋ณธ ์œ ์ง€
print(doubled)   // [2, 4, 6, 8, 10] - ์ƒˆ๋กœ์šด ๋ฐฐ์—ด

โŒ ์ˆœ์ˆ˜ํ•˜์ง€ ์•Š์€ ํ•จ์ˆ˜ ์˜ˆ์ œ

// ๋น„์ˆœ์ˆ˜ํ•จ์ˆ˜ - ์ „์—ญ ์ƒํƒœ์— ์˜์กด
var globalCounter = 0

func incrementCounter() -> Int {
    globalCounter += 1  // ์™ธ๋ถ€ ์ƒํƒœ ๋ณ€๊ฒฝ!
    return globalCounter
}

// ๋น„์ˆœ์ˆ˜ํ•จ์ˆ˜ - ์™ธ๋ถ€ ์ƒํƒœ ๋ณ€๊ฒฝ
var users: [User] = []

func addUser(_ user: User) {
    users.append(user)  // ์™ธ๋ถ€ ๋ฐฐ์—ด ๋ณ€๊ฒฝ!
}

// ๋น„์ˆœ์ˆ˜ํ•จ์ˆ˜ - ๋žœ๋ค ๊ฐ’ ๋ฐ˜ํ™˜
func getRandomNumber() -> Int {
    return Int.random(in: 1...100)  // ๋งค๋ฒˆ ๋‹ค๋ฅธ ๊ฐ’!
}

// ๋น„์ˆœ์ˆ˜ํ•จ์ˆ˜ - ํ˜„์žฌ ์‹œ๊ฐ„ ์˜์กด
func getCurrentTimestamp() -> Date {
    return Date()  // ํ˜ธ์ถœ ์‹œ์ ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ’!
}

// ๋ฌธ์ œ์  ํ™•์ธ
let count1 = incrementCounter()  // 1
let count2 = incrementCounter()  // 2 - ๊ฐ™์€ ์ž…๋ ฅ(์—†์Œ)์ธ๋ฐ ๋‹ค๋ฅธ ์ถœ๋ ฅ!
print(count1 == count2)  // false - ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅ!

๐Ÿ’š ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๊ธฐ

๋น„์ˆœ์ˆ˜ํ•จ์ˆ˜๋ฅผ ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋Š” ํ•ต์‹ฌ์€ ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ณ , ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด์•ผ!

// ์ˆœ์ˆ˜ํ•จ์ˆ˜๋กœ ๋ณ€ํ™˜
func increment(_ counter: Int) -> Int {
    return counter + 1
}

func addUser(_ user: User, to users: [User]) -> [User] {
    var newUsers = users
    newUsers.append(user)
    return newUsers
}

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ• - ๋ฐฐ์—ด ๋ณต์‚ฌ ์—†์ด
func addUser(_ user: User, to users: [User]) -> [User] {
    return users + [user]
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
var counter = 0
counter = increment(counter)  // 1
counter = increment(counter)  // 2

var userList: [User] = []
userList = addUser(newUser, to: userList)
// ์›๋ณธ userList๋Š” ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ , ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์ด ๋ฐ˜ํ™˜๋จ

์ˆœ์ˆ˜ํ•จ์ˆ˜์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฝ๋‹ค๋Š” ๊ฑฐ์•ผ. ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ์‹ ๊ฒฝ ์“ธ ํ•„์š” ์—†์ด, ์ž…๋ ฅ๊ณผ ์ถœ๋ ฅ๋งŒ ํ™•์ธํ•˜๋ฉด ๋˜๊ฑฐ๋“ ! ๐ŸŽฏ

๐Ÿ”’ ๋ถˆ๋ณ€์„ฑ(Immutability)์˜ ํž˜

๋ถˆ๋ณ€์„ฑ์€ ํ•œ ๋ฒˆ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋Š” ์ ˆ๋Œ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์›์น™์ด์•ผ. ์ฒ˜์Œ์—๋Š” ๋น„ํšจ์œจ์ ์œผ๋กœ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์—„์ฒญ๋‚œ ์ด์ ์„ ๊ฐ€์ ธ๋‹ค์ค˜.

๐ŸŽ ๋ถˆ๋ณ€์„ฑ์˜ ์žฅ์ 

1. ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ
๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์œผ๋‹ˆ ์ฝ”๋“œ์˜ ๋™์ž‘์„ ์˜ˆ์ธกํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ. "์ด ๋ณ€์ˆ˜๊ฐ€ ์–ด๋””์„œ ๋ฐ”๋€Œ์—ˆ์ง€?"๋ผ๋Š” ๊ณ ๋ฏผ์„ ํ•  ํ•„์š”๊ฐ€ ์—†์–ด!

2. ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ
์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด๋„ ์•ˆ์ „ํ•ด. ๋ฐ์ดํ„ฐ ๊ฒฝ์Ÿ(data race)์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์—†๊ฑฐ๋“ .

3. ๋””๋ฒ„๊น… ์šฉ์ด์„ฑ
๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ ์ด๋ ฅ์„ ์ถ”์ ํ•˜๊ธฐ ์‰ฌ์›Œ. ๊ฐ ์‹œ์ ์˜ ์ƒํƒœ๊ฐ€ ๋ณด์กด๋˜๋‹ˆ๊นŒ.

4. ์บ์‹ฑ ์ตœ์ ํ™”
๊ฐ™์€ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•  ์ˆ˜ ์žˆ์–ด. ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์œผ๋‹ˆ ์บ์‹œ ๋ฌดํšจํ™”๋ฅผ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์ง€.

๐Ÿ“ ์Šค์œ„ํ”„ํŠธ์—์„œ ๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ํ•˜๊ธฐ

์Šค์œ„ํ”„ํŠธ๋Š” let๊ณผ var ํ‚ค์›Œ๋“œ๋กœ ๋ถˆ๋ณ€์„ฑ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด. ๊ฐ€๋Šฅํ•œ ํ•œ let์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„!

// ๋ถˆ๋ณ€ ๋ณ€์ˆ˜ ์„ ์–ธ
let immutableNumber = 42
// immutableNumber = 43  // ์ปดํŒŒ์ผ ์—๋Ÿฌ!

let immutableArray = [1, 2, 3, 4, 5]
// immutableArray.append(6)  // ์ปดํŒŒ์ผ ์—๋Ÿฌ!

// ๊ฐ€๋ณ€ ๋ณ€์ˆ˜
var mutableNumber = 42
mutableNumber = 43  // OK

var mutableArray = [1, 2, 3, 4, 5]
mutableArray.append(6)  // OK

// ๊ตฌ์กฐ์ฒด๋ฅผ ํ™œ์šฉํ•œ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ
struct User {
    let id: Int
    let name: String
    let email: String
    let age: Int
}

// ๋ถˆ๋ณ€ ์ธ์Šคํ„ด์Šค
let user = User(id: 1, name: "๊น€์ฒ ์ˆ˜", email: "kim@example.com", age: 25)
// user.name = "์ด์˜ํฌ"  // ์ปดํŒŒ์ผ ์—๋Ÿฌ!

// ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ์ƒˆ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
let updatedUser = User(
    id: user.id,
    name: "์ด์˜ํฌ",
    email: user.email,
    age: user.age
)

print(user.name)         // "๊น€์ฒ ์ˆ˜" - ์›๋ณธ ์œ ์ง€
print(updatedUser.name)  // "์ด์˜ํฌ" - ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค

๐Ÿ› ๏ธ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์—…๋ฐ์ดํŠธ ํŒจํ„ด

๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋Œ€์‹  ์ƒˆ๋กœ์šด ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•˜๋Š” ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด.

// 1. ๋‹จ์ˆœ ๋ณต์‚ฌ ํŒจํ„ด
struct Point {
    let x: Double
    let y: Double
    
    func moved(dx: Double, dy: Double) -> Point {
        return Point(x: x + dx, y: y + dy)
    }
}

let point1 = Point(x: 10, y: 20)
let point2 = point1.moved(dx: 5, dy: 3)

print(point1)  // Point(x: 10, y: 20) - ์›๋ณธ ์œ ์ง€
print(point2)  // Point(x: 15, y: 23) - ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค

// 2. ๋นŒ๋” ํŒจํ„ด ํ™œ์šฉ
struct UserProfile {
    let name: String
    let age: Int
    let email: String
    let phoneNumber: String?
    let address: String?
    
    func with(name: String? = nil,
              age: Int? = nil,
              email: String? = nil,
              phoneNumber: String? = nil,
              address: String? = nil) -> UserProfile {
        return UserProfile(
            name: name ?? self.name,
            age: age ?? self.age,
            email: email ?? self.email,
            phoneNumber: phoneNumber ?? self.phoneNumber,
            address: address ?? self.address
        )
    }
}

let profile = UserProfile(
    name: "๊น€์ฒ ์ˆ˜",
    age: 30,
    email: "kim@example.com",
    phoneNumber: nil,
    address: nil
)

// ์ผ๋ถ€ ํ•„๋“œ๋งŒ ์—…๋ฐ์ดํŠธ
let updatedProfile = profile.with(age: 31, phoneNumber: "010-1234-5678")

print(profile.age)          // 30 - ์›๋ณธ ์œ ์ง€
print(updatedProfile.age)   // 31 - ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค

// 3. ์ปฌ๋ ‰์…˜ ์—…๋ฐ์ดํŠธ ํŒจํ„ด
let numbers = [1, 2, 3, 4, 5]

// ์š”์†Œ ์ถ”๊ฐ€ (์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜)
let withSix = numbers + [6]

// ์š”์†Œ ์ œ๊ฑฐ (์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜)
let withoutFirst = Array(numbers.dropFirst())

// ์š”์†Œ ๋ณ€๊ฒฝ (์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜)
let doubled = numbers.map { $0 * 2 }

print(numbers)       // [1, 2, 3, 4, 5] - ์›๋ณธ ์œ ์ง€
print(withSix)       // [1, 2, 3, 4, 5, 6]
print(withoutFirst)  // [2, 3, 4, 5]
print(doubled)       // [2, 4, 6, 8, 10]

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ: ์ฐธ์กฐ ํƒ€์ž…๊ณผ ๋ถˆ๋ณ€์„ฑ

ํด๋ž˜์Šค(์ฐธ์กฐ ํƒ€์ž…)๋Š” let์œผ๋กœ ์„ ์–ธํ•ด๋„ ๋‚ด๋ถ€ ํ”„๋กœํผํ‹ฐ๊ฐ€ var์ด๋ฉด ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ด. ์ง„์ •ํ•œ ๋ถˆ๋ณ€์„ฑ์„ ์›ํ•œ๋‹ค๋ฉด ๊ตฌ์กฐ์ฒด(๊ฐ’ ํƒ€์ž…)๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํด๋ž˜์Šค์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๋ฅผ let์œผ๋กœ ์„ ์–ธํ•ด์•ผ ํ•ด!

// ํด๋ž˜์Šค - ์ฐธ์กฐ ํƒ€์ž…
class MutableUser {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let user1 = MutableUser(name: "๊น€์ฒ ์ˆ˜", age: 25)
user1.name = "์ด์˜ํฌ"  // let์ธ๋ฐ๋„ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ! (์ฐธ์กฐ๋Š” ๋ถˆ๋ณ€, ๋‚ด์šฉ์€ ๊ฐ€๋ณ€)

// ๊ตฌ์กฐ์ฒด - ๊ฐ’ ํƒ€์ž…
struct ImmutableUser {
    let name: String
    let age: Int
}

let user2 = ImmutableUser(name: "๊น€์ฒ ์ˆ˜", age: 25)
// user2.name = "์ด์˜ํฌ"  // ์ปดํŒŒ์ผ ์—๋Ÿฌ! ์ง„์ •ํ•œ ๋ถˆ๋ณ€์„ฑ

์Šค์œ„ํ”„ํŠธ์—์„œ๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ ๊ตฌ์กฐ์ฒด์™€ let์„ ํ™œ์šฉํ•ด์„œ ๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ํ›จ์”ฌ ์•ˆ์ „ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ด์ ธ! ๐Ÿ˜Š

๊ฐ€๋ณ€ ์ƒํƒœ (Mutable) ๐Ÿ˜ฐ ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅ ๋””๋ฒ„๊น… ์–ด๋ ค์›€ ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ ๋ฌธ์ œ ๋ถˆ๋ณ€ ์ƒํƒœ (Immutable) ๐Ÿ˜Š ์˜ˆ์ธก ๊ฐ€๋Šฅ ๋””๋ฒ„๊น… ์‰ฌ์›€ ์Šค๋ ˆ๋“œ ์•ˆ์ „ ๋ฆฌํŒฉํ† ๋ง

๐ŸŽจ ์Šค์œ„ํ”„ํŠธ์˜ ํ•จ์ˆ˜ํ˜• ๋„๊ตฌ๋“ค

์Šค์œ„ํ”„ํŠธ๋Š” ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋“ค์„ ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•ด. ์ด ๋„๊ตฌ๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์ •๋ง ๊ฐ„๊ฒฐํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฌ์›Œ์ ธ! โœจ

1๏ธโƒฃ map - ๋ณ€ํ™˜์˜ ๋งˆ๋ฒ•์‚ฌ

map์€ ์ปฌ๋ ‰์…˜์˜ ๊ฐ ์š”์†Œ๋ฅผ ๋ณ€ํ™˜ํ•ด์„œ ์ƒˆ๋กœ์šด ์ปฌ๋ ‰์…˜์„ ๋งŒ๋“ค์–ด์ค˜. ์›๋ณธ์€ ์ „ํ˜€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ง์ด์•ผ!

// ๊ธฐ๋ณธ map ์‚ฌ์šฉ
let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }
print(squared)  // [1, 4, 9, 16, 25]

// ํƒ€์ž… ๋ณ€ํ™˜
let strings = numbers.map { String($0) }
print(strings)  // ["1", "2", "3", "4", "5"]

// ๋ณต์žกํ•œ ๊ฐ์ฒด ๋ณ€ํ™˜
struct Product {
    let name: String
    let price: Double
}

let products = [
    Product(name: "์‚ฌ๊ณผ", price: 1000),
    Product(name: "๋ฐ”๋‚˜๋‚˜", price: 1500),
    Product(name: "์˜ค๋ Œ์ง€", price: 2000)
]

// ๊ฐ€๊ฒฉ๋งŒ ์ถ”์ถœ
let prices = products.map { $0.price }
print(prices)  // [1000.0, 1500.0, 2000.0]

// ํ• ์ธ๊ฐ€ ๊ณ„์‚ฐ
let discountedPrices = products.map { $0.price * 0.9 }
print(discountedPrices)  // [900.0, 1350.0, 1800.0]

// ์ƒˆ๋กœ์šด ๊ตฌ์กฐ์ฒด ์ƒ์„ฑ
struct DiscountedProduct {
    let name: String
    let originalPrice: Double
    let discountedPrice: Double
}

let discountedProducts = products.map { product in
    DiscountedProduct(
        name: product.name,
        originalPrice: product.price,
        discountedPrice: product.price * 0.9
    )
}

// ๋ช…๋ นํ˜• ์Šคํƒ€์ผ๊ณผ ๋น„๊ต
var squaredImperative: [Int] = []
for number in numbers {
    squaredImperative.append(number * number)
}
// vs
let squaredFunctional = numbers.map { $0 * $0 }
// ํ•จ์ˆ˜ํ˜•์ด ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๊ณ  ์˜๋„๊ฐ€ ๋ช…ํ™•ํ•ด!

2๏ธโƒฃ filter - ์„ ๋ณ„์˜ ๋‹ฌ์ธ

filter๋Š” ์กฐ๊ฑด์— ๋งž๋Š” ์š”์†Œ๋งŒ ๊ณจ๋ผ๋‚ด์„œ ์ƒˆ๋กœ์šด ์ปฌ๋ ‰์…˜์„ ๋งŒ๋“ค์–ด์ค˜.

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// ์ง์ˆ˜๋งŒ ํ•„ํ„ฐ๋ง
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers)  // [2, 4, 6, 8, 10]

// 5๋ณด๋‹ค ํฐ ์ˆ˜
let greaterThanFive = numbers.filter { $0 > 5 }
print(greaterThanFive)  // [6, 7, 8, 9, 10]

// ๋ณต์žกํ•œ ์กฐ๊ฑด
struct User {
    let name: String
    let age: Int
    let isActive: Bool
}

let users = [
    User(name: "๊น€์ฒ ์ˆ˜", age: 25, isActive: true),
    User(name: "์ด์˜ํฌ", age: 17, isActive: true),
    User(name: "๋ฐ•๋ฏผ์ˆ˜", age: 30, isActive: false),
    User(name: "์ •์ˆ˜์ง„", age: 22, isActive: true)
]

// ์„ฑ์ธ์ด๋ฉด์„œ ํ™œ์„ฑ ์‚ฌ์šฉ์ž๋งŒ
let activeAdults = users.filter { $0.age >= 18 && $0.isActive }
print(activeAdults.map { $0.name })  // ["๊น€์ฒ ์ˆ˜", "์ •์ˆ˜์ง„"]

// ๋ช…๋ นํ˜• ์Šคํƒ€์ผ๊ณผ ๋น„๊ต
var evenNumbersImperative: [Int] = []
for number in numbers {
    if number % 2 == 0 {
        evenNumbersImperative.append(number)
    }
}
// vs
let evenNumbersFunctional = numbers.filter { $0 % 2 == 0 }
// ์—ญ์‹œ ํ•จ์ˆ˜ํ˜•์ด ๋” ๊ฐ„๊ฒฐ!

3๏ธโƒฃ reduce - ์ง‘๊ณ„์˜ ์ „๋ฌธ๊ฐ€

reduce๋Š” ์ปฌ๋ ‰์…˜์˜ ๋ชจ๋“  ์š”์†Œ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ํ•ฉ์ณ์ค˜. ํ•ฉ๊ณ„, ๊ณฑ์…ˆ, ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

let numbers = [1, 2, 3, 4, 5]

// ํ•ฉ๊ณ„ ๊ณ„์‚ฐ
let sum = numbers.reduce(0) { $0 + $1 }
print(sum)  // 15

// ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ
let sum2 = numbers.reduce(0, +)
print(sum2)  // 15

// ๊ณฑ์…ˆ
let product = numbers.reduce(1, *)
print(product)  // 120

// ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ
let words = ["Hello", "Swift", "World"]
let sentence = words.reduce("") { $0 + " " + $1 }
print(sentence)  // " Hello Swift World"

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•
let sentence2 = words.reduce("") { result, word in
    result.isEmpty ? word : result + " " + word
}
print(sentence2)  // "Hello Swift World"

// ๋ณต์žกํ•œ ์ง‘๊ณ„
struct Transaction {
    let amount: Double
    let type: String  // "income" or "expense"
}

let transactions = [
    Transaction(amount: 1000, type: "income"),
    Transaction(amount: 500, type: "expense"),
    Transaction(amount: 2000, type: "income"),
    Transaction(amount: 300, type: "expense")
]

// ์ˆœ์ˆ˜์ต ๊ณ„์‚ฐ
let netIncome = transactions.reduce(0.0) { result, transaction in
    transaction.type == "income" ? result + transaction.amount : result - transaction.amount
}
print(netIncome)  // 2200.0

// ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๊ทธ๋ฃนํ™”
let grouped = transactions.reduce(into: [String: Double]()) { result, transaction in
    result[transaction.type, default: 0] += transaction.amount
}
print(grouped)  // ["income": 3000.0, "expense": 800.0]

4๏ธโƒฃ compactMap - nil ์ œ๊ฑฐ์˜ ๊ณ ์ˆ˜

compactMap์€ map๊ณผ ๋น„์Šทํ•˜์ง€๋งŒ, nil ๊ฐ’์„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•ด์ค˜. ์˜ต์…”๋„์„ ๋‹ค๋ฃฐ ๋•Œ ์ •๋ง ์œ ์šฉํ•ด!

let strings = ["1", "2", "three", "4", "five", "6"]

// ๋ฌธ์ž์—ด์„ Int๋กœ ๋ณ€ํ™˜ (์‹คํŒจํ•˜๋ฉด nil)
let numbers = strings.compactMap { Int($0) }
print(numbers)  // [1, 2, 4, 6] - nil์ด ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋จ!

// map์„ ์‚ฌ์šฉํ•˜๋ฉด?
let numbersWithNil = strings.map { Int($0) }
print(numbersWithNil)  // [Optional(1), Optional(2), nil, Optional(4), nil, Optional(6)]

// ์˜ต์…”๋„ ์ฒด์ด๋‹๊ณผ ํ•จ๊ป˜
struct Person {
    let name: String
    let email: String?
}

let people = [
    Person(name: "๊น€์ฒ ์ˆ˜", email: "kim@example.com"),
    Person(name: "์ด์˜ํฌ", email: nil),
    Person(name: "๋ฐ•๋ฏผ์ˆ˜", email: "park@example.com"),
    Person(name: "์ •์ˆ˜์ง„", email: nil)
]

// ์ด๋ฉ”์ผ์ด ์žˆ๋Š” ์‚ฌ๋žŒ๋งŒ
let emails = people.compactMap { $0.email }
print(emails)  // ["kim@example.com", "park@example.com"]

// ๋ณต์žกํ•œ ๋ณ€ํ™˜
let validEmails = people.compactMap { person -> String? in
    guard let email = person.email,
          email.contains("@") else {
        return nil
    }
    return email.lowercased()
}
print(validEmails)  // ["kim@example.com", "park@example.com"]

5๏ธโƒฃ flatMap - ์ค‘์ฒฉ ์ œ๊ฑฐ์˜ ๋‹ฌ์ธ

flatMap์€ ์ค‘์ฒฉ๋œ ์ปฌ๋ ‰์…˜์„ ํ‰ํ‰ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค˜. 2์ฐจ์› ๋ฐฐ์—ด์„ 1์ฐจ์›์œผ๋กœ ๋ฐ”๊ฟ€ ๋•Œ ์œ ์šฉํ•ด!

// 2์ฐจ์› ๋ฐฐ์—ด ํ‰ํƒ„ํ™”
let nestedNumbers = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
let flattened = nestedNumbers.flatMap { $0 }
print(flattened)  // [1, 2, 3, 4, 5, 6, 7, 8, 9]

// ๋ฌธ์ž์—ด ๋ฐฐ์—ด์„ ๋ฌธ์ž ๋ฐฐ์—ด๋กœ
let words = ["Hello", "Swift"]
let characters = words.flatMap { $0 }
print(characters)  // ["H", "e", "l", "l", "o", "S", "w", "i", "f", "t"]

// ๋ณต์žกํ•œ ์˜ˆ์ œ
struct Department {
    let name: String
    let employees: [String]
}

let departments = [
    Department(name: "๊ฐœ๋ฐœํŒ€", employees: ["๊น€์ฒ ์ˆ˜", "์ด์˜ํฌ"]),
    Department(name: "๋””์ž์ธํŒ€", employees: ["๋ฐ•๋ฏผ์ˆ˜"]),
    Department(name: "๋งˆ์ผ€ํŒ…ํŒ€", employees: ["์ •์ˆ˜์ง„", "์ตœ์˜์ˆ˜", "ํ•œ์ง€๋ฏผ"])
]

// ๋ชจ๋“  ์ง์› ์ด๋ฆ„ ์ถ”์ถœ
let allEmployees = departments.flatMap { $0.employees }
print(allEmployees)  // ["๊น€์ฒ ์ˆ˜", "์ด์˜ํฌ", "๋ฐ•๋ฏผ์ˆ˜", "์ •์ˆ˜์ง„", "์ตœ์˜์ˆ˜", "ํ•œ์ง€๋ฏผ"]

// ์˜ต์…”๋„๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉ (Swift 4.1 ์ดํ›„ compactMap ๊ถŒ์žฅ)
let optionalNumbers: [Int?] = [1, 2, nil, 4, nil, 6]
let validNumbers = optionalNumbers.compactMap { $0 }
print(validNumbers)  // [1, 2, 4, 6]

๐Ÿ’š ํ•จ์ˆ˜ ์ฒด์ด๋‹์˜ ๋งˆ๋ฒ•

์ด ํ•จ์ˆ˜๋“ค์„ ์—ฐ๊ฒฐํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•ด์ ธ! ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ ์ค„๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด.

struct Product {
    let name: String
    let price: Double
    let category: String
    let inStock: Bool
}

let products = [
    Product(name: "๋งฅ๋ถ", price: 2000000, category: "์ „์ž์ œํ’ˆ", inStock: true),
    Product(name: "์•„์ดํŒจ๋“œ", price: 800000, category: "์ „์ž์ œํ’ˆ", inStock: false),
    Product(name: "์ฑ…์ƒ", price: 150000, category: "๊ฐ€๊ตฌ", inStock: true),
    Product(name: "์˜์ž", price: 200000, category: "๊ฐ€๊ตฌ", inStock: true),
    Product(name: "์•„์ดํฐ", price: 1200000, category: "์ „์ž์ œํ’ˆ", inStock: true)
]

// ์žฌ๊ณ ๊ฐ€ ์žˆ๋Š” ์ „์ž์ œํ’ˆ์˜ ์ด ๊ฐ€๊ฒฉ
let totalElectronicsPrice = products
    .filter { $0.category == "์ „์ž์ œํ’ˆ" && $0.inStock }  // ์ „์ž์ œํ’ˆ + ์žฌ๊ณ  ์žˆ์Œ
    .map { $0.price }                                      // ๊ฐ€๊ฒฉ๋งŒ ์ถ”์ถœ
    .reduce(0, +)                                          // ํ•ฉ๊ณ„

print(totalElectronicsPrice)  // 3200000

// ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ํ‰๊ท  ๊ฐ€๊ฒฉ
let averagePriceByCategory = Dictionary(grouping: products, by: { $0.category })
    .mapValues { products in
        products.map { $0.price }.reduce(0, +) / Double(products.count)
    }

print(averagePriceByCategory)
// ["์ „์ž์ œํ’ˆ": 1333333.33, "๊ฐ€๊ตฌ": 175000.0]

// ์žฌ๊ณ  ์žˆ๋Š” ์ƒํ’ˆ ์ด๋ฆ„๋“ค์„ ์‰ผํ‘œ๋กœ ์—ฐ๊ฒฐ
let availableProducts = products
    .filter { $0.inStock }
    .map { $0.name }
    .joined(separator: ", ")

print(availableProducts)  // "๋งฅ๋ถ, ์ฑ…์ƒ, ์˜์ž, ์•„์ดํฐ"

์ด๋ ‡๊ฒŒ ํ•จ์ˆ˜๋“ค์„ ์ฒด์ด๋‹ํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ์ •๋ง ์ฝ๊ธฐ ์‰ฌ์›Œ์ ธ. ๋งˆ์น˜ ์˜์–ด ๋ฌธ์žฅ์ฒ˜๋Ÿผ "ํ•„ํ„ฐ๋งํ•˜๊ณ , ๋ณ€ํ™˜ํ•˜๊ณ , ํ•ฉ์ณ๋ผ"๋Š” ์‹์œผ๋กœ ์ฝํžˆ๊ฑฐ๋“ ! ๐Ÿ“–

๐ŸŽฏ ๊ณ ์ฐจ ํ•จ์ˆ˜(Higher-Order Functions)

๊ณ ์ฐจ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์•ผ. ์Šค์œ„ํ”„ํŠธ์—์„œ ํ•จ์ˆ˜๋Š” ์ผ๊ธ‰ ๊ฐ์ฒด(First-Class Citizen)์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒŒ ๊ฐ€๋Šฅํ•ด!

๐Ÿ”ง ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ธฐ

// ๊ธฐ๋ณธ ์˜ˆ์ œ
func applyOperation(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

// ์‚ฌ์šฉ
let sum = applyOperation(5, 3, operation: { $0 + $1 })
print(sum)  // 8

let product = applyOperation(5, 3, operation: { $0 * $1 })
print(product)  // 15

// ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ
let sum2 = applyOperation(5, 3, operation: +)
let product2 = applyOperation(5, 3, operation: *)

// ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ
func processArray(_ array: [Int], with processor: (Int) -> Int) -> [Int] {
    return array.map(processor)
}

let numbers = [1, 2, 3, 4, 5]
let doubled = processArray(numbers) { $0 * 2 }
let squared = processArray(numbers) { $0 * $0 }

print(doubled)  // [2, 4, 6, 8, 10]
print(squared)  // [1, 4, 9, 16, 25]

// ์กฐ๊ฑด๋ถ€ ์ฒ˜๋ฆฌ
func filterAndTransform<t, u="">(_ array: [T], 
                               filter: (T) -> Bool,
                               transform: (T) -> U) -> [U] {
    return array.filter(filter).map(transform)
}

let evenSquares = filterAndTransform(numbers,
                                      filter: { $0 % 2 == 0 },
                                      transform: { $0 * $0 })
print(evenSquares)  // [4, 16]</t,>

๐ŸŽ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ

// ๊ธฐ๋ณธ ์˜ˆ์ œ
func makeMultiplier(factor: Int) -> (Int) -> Int {
    return { number in
        return number * factor
    }
}

let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)

print(double(5))  // 10
print(triple(5))  // 15

// ๋” ๋ณต์žกํ•œ ์˜ˆ์ œ
func makeValidator(minLength: Int, maxLength: Int) -> (String) -> Bool {
    return { text in
        return text.count >= minLength && text.count <= maxLength
    }
}

let isValidUsername = makeValidator(minLength: 3, maxLength: 20)
let isValidPassword = makeValidator(minLength: 8, maxLength: 50)

print(isValidUsername("kim"))      // true
print(isValidUsername("ab"))       // false
print(isValidPassword("12345"))    // false
print(isValidPassword("12345678")) // true

// ํด๋กœ์ € ์บก์ฒ˜๋ฅผ ํ™œ์šฉํ•œ ์นด์šดํ„ฐ
func makeCounter() -> () -> Int {
    var count = 0
    return {
        count += 1
        return count
    }
}

let counter1 = makeCounter()
let counter2 = makeCounter()

print(counter1())  // 1
print(counter1())  // 2
print(counter1())  // 3
print(counter2())  // 1 - ๋…๋ฆฝ์ ์ธ ์นด์šดํ„ฐ
print(counter2())  // 2

// ํ•จ์ˆ˜ ์กฐํ•ฉ
func compose<a, b,="" c="">(_ f: @escaping (B) -> C, _ g: @escaping (A) -> B) -> (A) -> C {
    return { x in
        f(g(x))
    }
}

let addOne: (Int) -> Int = { $0 + 1 }
let multiplyByTwo: (Int) -> Int = { $0 * 2 }

let addOneThenMultiply = compose(multiplyByTwo, addOne)
print(addOneThenMultiply(5))  // (5 + 1) * 2 = 12</a,>

๐ŸŽจ ์ปค๋ง(Currying)

์ปค๋ง์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜์˜ ์ธ์ž๋ฅผ ๋ฐ›๋Š” ํ•จ์ˆ˜๋“ค์˜ ์ฒด์ธ์œผ๋กœ ๋ฐ”๊พธ๋Š” ๊ธฐ๋ฒ•์ด์•ผ.

// ์ผ๋ฐ˜ ํ•จ์ˆ˜
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

// ์ปค๋ง๋œ ํ•จ์ˆ˜
func curriedAdd(_ a: Int) -> (Int) -> Int {
    return { b in
        return a + b
    }
}

// ์‚ฌ์šฉ
let add5 = curriedAdd(5)
print(add5(3))   // 8
print(add5(10))  // 15

// ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ
func makeGreeting(_ greeting: String) -> (String) -> String {
    return { name in
        return "\(greeting), \(name)!"
    }
}

let sayHello = makeGreeting("์•ˆ๋…•ํ•˜์„ธ์š”")
let sayGoodbye = makeGreeting("์•ˆ๋…•ํžˆ ๊ฐ€์„ธ์š”")

print(sayHello("๊น€์ฒ ์ˆ˜"))      // "์•ˆ๋…•ํ•˜์„ธ์š”, ๊น€์ฒ ์ˆ˜!"
print(sayGoodbye("์ด์˜ํฌ"))    // "์•ˆ๋…•ํžˆ ๊ฐ€์„ธ์š”, ์ด์˜ํฌ!"

// ๋” ๋ณต์žกํ•œ ์ปค๋ง
func multiply(_ a: Int) -> (Int) -> (Int) -> Int {
    return { b in
        return { c in
            return a * b * c
        }
    }
}

let result = multiply(2)(3)(4)
print(result)  // 24

// ๋ถ€๋ถ„ ์ ์šฉ
let multiplyBy2 = multiply(2)
let multiplyBy2And3 = multiplyBy2(3)
print(multiplyBy2And3(4))  // 24

๐Ÿ—๏ธ ์‹ค์ „ ํŒจํ„ด: ํ•จ์ˆ˜ํ˜• ์•„ํ‚คํ…์ฒ˜

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ๋“ค์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž! ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™์„ ๋”ฐ๋ฅด๋ฉด ์ฝ”๋“œ๊ฐ€ ์ •๋ง ๊น”๋”ํ•ด์ ธ. ๐ŸŽจ

๐Ÿ“ฆ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์„ค๊ณ„

// ๋‚˜์œ ์˜ˆ: ๊ฐ€๋ณ€ ์ƒํƒœ
class MutableShoppingCart {
    var items: [Product] = []
    var totalPrice: Double = 0
    
    func addItem(_ product: Product) {
        items.append(product)
        totalPrice += product.price
    }
    
    func removeItem(at index: Int) {
        let product = items.remove(at: index)
        totalPrice -= product.price
    }
}

// ์ข‹์€ ์˜ˆ: ๋ถˆ๋ณ€ ์ƒํƒœ
struct Product {
    let id: String
    let name: String
    let price: Double
}

struct ShoppingCart {
    let items: [Product]
    
    var totalPrice: Double {
        items.reduce(0) { $0 + $1.price }
    }
    
    var itemCount: Int {
        items.count
    }
    
    func adding(_ product: Product) -> ShoppingCart {
        return ShoppingCart(items: items + [product])
    }
    
    func removing(at index: Int) -> ShoppingCart {
        var newItems = items
        newItems.remove(at: index)
        return ShoppingCart(items: newItems)
    }
    
    func removingProduct(withId id: String) -> ShoppingCart {
        let newItems = items.filter { $0.id != id }
        return ShoppingCart(items: newItems)
    }
    
    func applyingDiscount(_ percentage: Double) -> ShoppingCart {
        let discountedItems = items.map { product in
            Product(id: product.id,
                   name: product.name,
                   price: product.price * (1 - percentage / 100))
        }
        return ShoppingCart(items: discountedItems)
    }
}

// ์‚ฌ์šฉ
let emptyCart = ShoppingCart(items: [])
let product1 = Product(id: "1", name: "๋งฅ๋ถ", price: 2000000)
let product2 = Product(id: "2", name: "์•„์ดํŒจ๋“œ", price: 800000)

let cart1 = emptyCart.adding(product1)
let cart2 = cart1.adding(product2)
let discountedCart = cart2.applyingDiscount(10)

print(cart1.totalPrice)        // 2000000
print(cart2.totalPrice)        // 2800000
print(discountedCart.totalPrice) // 2520000

// ์›๋ณธ์€ ๋ณ€ํ•˜์ง€ ์•Š์Œ!
print(emptyCart.itemCount)  // 0
print(cart1.itemCount)      // 1
print(cart2.itemCount)      // 2

๐Ÿ”„ ์ƒํƒœ ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ

// ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ
struct User {
    let id: Int
    let name: String
    let email: String
    let age: Int
    let isActive: Bool
}

struct UserViewModel {
    let displayName: String
    let email: String
    let statusText: String
}

// ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋“ค๋กœ ๊ตฌ์„ฑ๋œ ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ
func filterActiveAdults(_ users: [User]) -> [User] {
    return users.filter { $0.isActive && $0.age >= 18 }
}

func sortByName(_ users: [User]) -> [User] {
    return users.sorted { $0.name < $1.name }
}

func toViewModels(_ users: [User]) -> [UserViewModel] {
    return users.map { user in
        UserViewModel(
            displayName: user.name,
            email: user.email,
            statusText: user.isActive ? "ํ™œ์„ฑ" : "๋น„ํ™œ์„ฑ"
        )
    }
}

// ํŒŒ์ดํ”„๋ผ์ธ ์กฐํ•ฉ
func processUsers(_ users: [User]) -> [UserViewModel] {
    return toViewModels(sortByName(filterActiveAdults(users)))
}

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•: ์ปค์Šคํ…€ ์—ฐ์‚ฐ์ž
infix operator |> : AdditionPrecedence

func |> <t, u="">(value: T, function: (T) -> U) -> U {
    return function(value)
}

// ์‚ฌ์šฉ
let users = [
    User(id: 1, name: "๊น€์ฒ ์ˆ˜", email: "kim@example.com", age: 25, isActive: true),
    User(id: 2, name: "์ด์˜ํฌ", email: "lee@example.com", age: 17, isActive: true),
    User(id: 3, name: "๋ฐ•๋ฏผ์ˆ˜", email: "park@example.com", age: 30, isActive: false),
    User(id: 4, name: "์ •์ˆ˜์ง„", email: "jung@example.com", age: 22, isActive: true)
]

let viewModels = users
    |> filterActiveAdults
    |> sortByName
    |> toViewModels

print(viewModels.map { $0.displayName })  // ["๊น€์ฒ ์ˆ˜", "์ •์ˆ˜์ง„"]</t,>

๐ŸŽญ Result ํƒ€์ž…์œผ๋กœ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

// ์ˆœ์ˆ˜ ํ•จ์ˆ˜ํ˜• ์—๋Ÿฌ ์ฒ˜๋ฆฌ
enum ValidationError: Error {
    case emptyName
    case invalidEmail
    case ageTooYoung
    case ageTooOld
}

struct UserInput {
    let name: String
    let email: String
    let age: Int
}

// ๊ฐ ๊ฒ€์ฆ์„ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ
func validateName(_ name: String) -> Result<string, validationerror=""> {
    guard !name.isEmpty else {
        return .failure(.emptyName)
    }
    return .success(name)
}

func validateEmail(_ email: String) -> Result<string, validationerror=""> {
    guard email.contains("@") && email.contains(".") else {
        return .failure(.invalidEmail)
    }
    return .success(email)
}

func validateAge(_ age: Int) -> Result<int, validationerror=""> {
    guard age >= 18 else {
        return .failure(.ageTooYoung)
    }
    guard age <= 120 else {
        return .failure(.ageTooOld)
    }
    return .success(age)
}

// ๊ฒ€์ฆ ์กฐํ•ฉ
func validateUser(_ input: UserInput) -> Result<user, validationerror=""> {
    let nameResult = validateName(input.name)
    let emailResult = validateEmail(input.email)
    let ageResult = validateAge(input.age)
    
    // ๋ชจ๋“  ๊ฒ€์ฆ์ด ์„ฑ๊ณตํ•ด์•ผ User ์ƒ์„ฑ
    switch (nameResult, emailResult, ageResult) {
    case (.success(let name), .success(let email), .success(let age)):
        return .success(User(id: 0, name: name, email: email, age: age, isActive: true))
    case (.failure(let error), _, _),
         (_, .failure(let error), _),
         (_, _, .failure(let error)):
        return .failure(error)
    }
}

// ์‚ฌ์šฉ
let input1 = UserInput(name: "๊น€์ฒ ์ˆ˜", email: "kim@example.com", age: 25)
let result1 = validateUser(input1)

switch result1 {
case .success(let user):
    print("์‚ฌ์šฉ์ž ์ƒ์„ฑ ์„ฑ๊ณต: \(user.name)")
case .failure(let error):
    print("๊ฒ€์ฆ ์‹คํŒจ: \(error)")
}

let input2 = UserInput(name: "", email: "invalid", age: 15)
let result2 = validateUser(input2)

switch result2 {
case .success(let user):
    print("์‚ฌ์šฉ์ž ์ƒ์„ฑ ์„ฑ๊ณต: \(user.name)")
case .failure(let error):
    print("๊ฒ€์ฆ ์‹คํŒจ: \(error)")  // "๊ฒ€์ฆ ์‹คํŒจ: emptyName"
}</user,></int,></string,></string,>

๐Ÿ’š ํ•จ์ˆ˜ ์กฐํ•ฉ ํŒจํ„ด

์ž‘์€ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•ด์„œ ๋ณต์žกํ•œ ๋กœ์ง์„ ๋งŒ๋“ค๋ฉด, ๊ฐ ๋ถ€๋ถ„์„ ๋…๋ฆฝ์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿงช ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ๊ฐ€์žฅ ํฐ ์žฅ์  ์ค‘ ํ•˜๋‚˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์ •๋ง ์‰ฝ๋‹ค๋Š” ๊ฑฐ์•ผ! ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” ์™ธ๋ถ€ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฐ„๋‹จํ•ด์ ธ. ๐ŸŽฏ

import XCTest

// ํ…Œ์ŠคํŠธํ•  ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋“ค
func calculateDiscount(price: Double, percentage: Double) -> Double {
    return price * (1 - percentage / 100)
}

func isValidEmail(_ email: String) -> Bool {
    return email.contains("@") && email.contains(".")
}

func filterAdults(_ users: [User]) -> [User] {
    return users.filter { $0.age >= 18 }
}

// ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
class FunctionalProgrammingTests: XCTestCase {
    
    // ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์ •๋ง ์‰ฌ์›Œ!
    func testCalculateDiscount() {
        // Given
        let price = 10000.0
        let percentage = 10.0
        
        // When
        let result = calculateDiscount(price: price, percentage: percentage)
        
        // Then
        XCTAssertEqual(result, 9000.0)
    }
    
    func testIsValidEmail() {
        // ์—ฌ๋Ÿฌ ์ผ€์ด์Šค๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ
        XCTAssertTrue(isValidEmail("test@example.com"))
        XCTAssertFalse(isValidEmail("invalid"))
        XCTAssertFalse(isValidEmail("@example.com"))
        XCTAssertFalse(isValidEmail("test@"))
    }
    
    func testFilterAdults() {
        // Given
        let users = [
            User(id: 1, name: "๊น€์ฒ ์ˆ˜", email: "kim@example.com", age: 25, isActive: true),
            User(id: 2, name: "์ด์˜ํฌ", email: "lee@example.com", age: 17, isActive: true),
            User(id: 3, name: "๋ฐ•๋ฏผ์ˆ˜", email: "park@example.com", age: 30, isActive: true)
        ]
        
        // When
        let adults = filterAdults(users)
        
        // Then
        XCTAssertEqual(adults.count, 2)
        XCTAssertTrue(adults.allSatisfy { $0.age >= 18 })
    }
    
    // ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ํ…Œ์ŠคํŠธ
    func testShoppingCartImmutability() {
        // Given
        let product = Product(id: "1", name: "๋งฅ๋ถ", price: 2000000)
        let emptyCart = ShoppingCart(items: [])
        
        // When
        let cart1 = emptyCart.adding(product)
        let cart2 = cart1.adding(product)
        
        // Then
        XCTAssertEqual(emptyCart.itemCount, 0)  // ์›๋ณธ ์œ ์ง€
        XCTAssertEqual(cart1.itemCount, 1)
        XCTAssertEqual(cart2.itemCount, 2)
        XCTAssertEqual(cart2.totalPrice, 4000000)
    }
}

// ํ”„๋กœํผํ‹ฐ ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ (Property-Based Testing)
func testMapPreservesLength() {
    let numbers = [1, 2, 3, 4, 5]
    let doubled = numbers.map { $0 * 2 }
    
    // map์€ ๊ธธ์ด๋ฅผ ๋ณด์กดํ•ด์•ผ ํ•จ
    XCTAssertEqual(numbers.count, doubled.count)
}

func testFilterReducesOrMaintainsLength() {
    let numbers = [1, 2, 3, 4, 5]
    let evens = numbers.filter { $0 % 2 == 0 }
    
    // filter๋Š” ๊ธธ์ด๋ฅผ ์ค„์ด๊ฑฐ๋‚˜ ์œ ์ง€ํ•ด์•ผ ํ•จ
    XCTAssertLessThanOrEqual(evens.count, numbers.count)
}

๐ŸŽฏ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ์˜ ํŠน์ง•

โ€ข ์˜ˆ์ธก ๊ฐ€๋Šฅ: ๊ฐ™์€ ์ž…๋ ฅ์— ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ
โ€ข ๋…๋ฆฝ์ : ์™ธ๋ถ€ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š์Œ
โ€ข ๋ถ€์ž‘์šฉ ์—†์Œ: ์ „์—ญ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Œ
โ€ข ์ž‘์€ ๋‹จ์œ„: ๊ฐ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ ๊ฐ€์ง

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์›์น™ ์ˆœ์ˆ˜ํ•จ์ˆ˜ Pure Functions ๋ถˆ๋ณ€์„ฑ Immutability ๊ณ ์ฐจํ•จ์ˆ˜ Higher-Order ์žฅ์  โœ“ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ โœ“ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ โœ“ ๋ฒ„๊ทธ ๊ฐ์†Œ โœ“ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ์•ˆ์ „ โœ“ ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ โœ“ ์œ ์ง€๋ณด์ˆ˜ ํŽธ์˜ โœ“ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ ํ•ต์‹ฌ ๋„๊ตฌ โ€ข map - ๋ณ€ํ™˜ โ€ข filter - ํ•„ํ„ฐ๋ง โ€ข reduce - ์ง‘๊ณ„ โ€ข compactMap - nil ์ œ๊ฑฐ โ€ข flatMap - ํ‰ํƒ„ํ™” โ€ข sorted - ์ •๋ ฌ โ€ข forEach - ๋ฐ˜๋ณต ์‹ค์ „ ํŒจํ„ด โ†’ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ โ†’ ํ•จ์ˆ˜ ์ฒด์ด๋‹ โ†’ ํ•จ์ˆ˜ ์กฐํ•ฉ โ†’ ์ปค๋ง โ†’ Result ํƒ€์ž… โ†’ ํŒŒ์ดํ”„๋ผ์ธ โ†’ ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ๋ถ„๋ฆฌ

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๋น„ํšจ์œจ์ ์ด๋ผ๋Š” ์˜คํ•ด๊ฐ€ ์žˆ๋Š”๋ฐ, ์‚ฌ์‹ค ์ œ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ๋„ ์ข‹์•„! ๋ช‡ ๊ฐ€์ง€ ์ตœ์ ํ™” ํŒ์„ ์•Œ์•„๋ณด์ž. ๐Ÿš€

1๏ธโƒฃ Lazy ํ‰๊ฐ€ ํ™œ์šฉ

// ์ผ๋ฐ˜ ๋ฐฐ์—ด ์—ฐ์‚ฐ - ์ฆ‰์‹œ ํ‰๊ฐ€
let numbers = Array(1...1000000)

let result1 = numbers
    .map { $0 * 2 }        // ๋ฐฑ๋งŒ ๊ฐœ ์ฒ˜๋ฆฌ
    .filter { $0 > 100 }   // ๋ฐฑ๋งŒ ๊ฐœ ์ฒ˜๋ฆฌ
    .prefix(10)            // 10๊ฐœ๋งŒ ํ•„์š”ํ•œ๋ฐ...

// Lazy ํ‰๊ฐ€ - ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์ฒ˜๋ฆฌ
let result2 = numbers.lazy
    .map { $0 * 2 }        // ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์ฒ˜๋ฆฌ
    .filter { $0 > 100 }   // ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ์ฒ˜๋ฆฌ
    .prefix(10)            // 10๊ฐœ๋งŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ค‘๋‹จ!

// ์„ฑ๋Šฅ ๋น„๊ต
import Foundation

func measureTime(_ closure: () -> Void) {
    let start = Date()
    closure()
    let end = Date()
    print("์‹คํ–‰ ์‹œ๊ฐ„: \(end.timeIntervalSince(start))์ดˆ")
}

measureTime {
    let _ = numbers
        .map { $0 * 2 }
        .filter { $0 > 100 }
        .prefix(10)
}
// ์‹คํ–‰ ์‹œ๊ฐ„: ์•ฝ 0.5์ดˆ

measureTime {
    let _ = Array(numbers.lazy
        .map { $0 * 2 }
        .filter { $0 > 100 }
        .prefix(10))
}
// ์‹คํ–‰ ์‹œ๊ฐ„: ์•ฝ 0.001์ดˆ - 500๋ฐฐ ๋น ๋ฆ„!

// Lazy์˜ ์ฃผ์˜์‚ฌํ•ญ
let lazyResult = numbers.lazy.map { $0 * 2 }
// ์•„์ง ๊ณ„์‚ฐ๋˜์ง€ ์•Š์Œ!

let array = Array(lazyResult)
// ์ด์ œ ๊ณ„์‚ฐ๋จ!

2๏ธโƒฃ ๋ถˆํ•„์š”ํ•œ ๋ณต์‚ฌ ํ”ผํ•˜๊ธฐ

// ๋น„ํšจ์œจ์  - ๋งค๋ฒˆ ์ƒˆ ๋ฐฐ์—ด ์ƒ์„ฑ
func inefficientAppend(_ array: [Int], _ element: Int) -> [Int] {
    var newArray = array  // ๋ณต์‚ฌ!
    newArray.append(element)
    return newArray
}

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ• - ๋ฐฐ์—ด ์—ฐ๊ฒฐ ์—ฐ์‚ฐ์ž
func efficientAppend(_ array: [Int], _ element: Int) -> [Int] {
    return array + [element]  // ์ตœ์ ํ™”๋จ
}

// ์—ฌ๋Ÿฌ ์š”์†Œ ์ถ”๊ฐ€ํ•  ๋•Œ
let numbers = [1, 2, 3]

// ๋น„ํšจ์œจ์ 
var result1 = numbers
result1 = efficientAppend(result1, 4)
result1 = efficientAppend(result1, 5)
result1 = efficientAppend(result1, 6)

// ํšจ์œจ์ 
let result2 = numbers + [4, 5, 6]

// reduce(into:) ์‚ฌ์šฉ - ๊ฐ€๋ณ€ ๋ˆ„์‚ฐ๊ธฐ
let sum = numbers.reduce(into: 0) { result, number in
    result += number  // ๋ณต์‚ฌ ์—†์ด ๋ˆ„์‚ฐ๊ธฐ ๋ณ€๊ฒฝ
}

// ๋”•์…”๋„ˆ๋ฆฌ ๊ทธ๋ฃนํ™” ์ตœ์ ํ™”
struct Product {
    let category: String
    let name: String
}

let products = [
    Product(category: "์ „์ž์ œํ’ˆ", name: "๋งฅ๋ถ"),
    Product(category: "์ „์ž์ œํ’ˆ", name: "์•„์ดํŒจ๋“œ"),
    Product(category: "๊ฐ€๊ตฌ", name: "์ฑ…์ƒ")
]

// ๋น„ํšจ์œจ์ 
let grouped1 = products.reduce([String: [Product]]()) { result, product in
    var newResult = result  // ๋งค๋ฒˆ ๋ณต์‚ฌ!
    newResult[product.category, default: []].append(product)
    return newResult
}

// ํšจ์œจ์  - reduce(into:)
let grouped2 = products.reduce(into: [String: [Product]]()) { result, product in
    result[product.category, default: []].append(product)  // ๋ณต์‚ฌ ์—†์Œ!
}

3๏ธโƒฃ ๋ฉ”๋ชจ์ด์ œ์ด์…˜(Memoization)

// ๋น„ํšจ์œจ์ ์ธ ์žฌ๊ท€ ํ•จ์ˆ˜
func fibonacci(_ n: Int) -> Int {
    if n <= 1 { return n }
    return fibonacci(n - 1) + fibonacci(n - 2)
}

// fibonacci(40) ํ˜ธ์ถœ ์‹œ ์•ฝ 2์ดˆ ์†Œ์š”...

// ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์ ์šฉ
class Memoizer<input: hashable,="" output=""> {
    private var cache: [Input: Output] = [:]
    private let function: (Input) -> Output
    
    init(_ function: @escaping (Input) -> Output) {
        self.function = function
    }
    
    func callAsFunction(_ input: Input) -> Output {
        if let cached = cache[input] {
            return cached
        }
        let result = function(input)
        cache[input] = result
        return result
    }
}

// ์‚ฌ์šฉ
let memoizedFib = Memoizer<int, int=""> { n in
    if n <= 1 { return n }
    return memoizedFib(n - 1) + memoizedFib(n - 2)
}

// fibonacci(40) ํ˜ธ์ถœ ์‹œ ์•ฝ 0.001์ดˆ - 2000๋ฐฐ ๋น ๋ฆ„!

// ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ
struct ExpensiveCalculation {
    static let memoized = Memoizer<string, string=""> { input in
        // ๋ณต์žกํ•œ ๊ณ„์‚ฐ...
        Thread.sleep(forTimeInterval: 1)  // 1์ดˆ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…
        return input.uppercased()
    }
}

// ์ฒซ ํ˜ธ์ถœ: 1์ดˆ
let result1 = ExpensiveCalculation.memoized("hello")

// ๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ: ์ฆ‰์‹œ ๋ฐ˜ํ™˜!
let result2 = ExpensiveCalculation.memoized("hello")</string,></int,></input:>

โš ๏ธ ์ตœ์ ํ™” ์ฃผ์˜์‚ฌํ•ญ

โ€ข ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ์‹ค์ œ๋กœ ์žˆ์„ ๋•Œ๋งŒ ์ตœ์ ํ™”ํ•ด
โ€ข ํ”„๋กœํŒŒ์ผ๋ง์œผ๋กœ ๋ณ‘๋ชฉ ์ง€์ ์„ ๋จผ์ € ์ฐพ์•„
โ€ข ๊ฐ€๋…์„ฑ์„ ํฌ์ƒํ•˜๋ฉด์„œ๊นŒ์ง€ ์ตœ์ ํ™”ํ•˜์ง€ ๋งˆ
โ€ข ๋ฉ”๋ชจ์ด์ œ์ด์…˜์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋‹ˆ ์ฃผ์˜ํ•ด

๐ŸŽ“ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์˜ˆ์ œ

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด์ž! ๊ฐ„๋‹จํ•œ ํ•  ์ผ ๊ด€๋ฆฌ ์•ฑ์„ ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๋กœ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ. ๐Ÿ“ฑ

// 1. ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ
struct Todo {
    let id: UUID
    let title: String
    let description: String
    let isCompleted: Bool
    let createdAt: Date
    let priority: Priority
    
    enum Priority: Int, Comparable {
        case low = 0
        case medium = 1
        case high = 2
        
        static func < (lhs: Priority, rhs: Priority) -> Bool {
            return lhs.rawValue < rhs.rawValue
        }
    }
}

// 2. ์ƒํƒœ ๊ด€๋ฆฌ
struct TodoState {
    let todos: [Todo]
    let filter: TodoFilter
    
    enum TodoFilter {
        case all
        case active
        case completed
    }
    
    // ํ•„ํ„ฐ๋ง๋œ ํ•  ์ผ ๋ชฉ๋ก
    var filteredTodos: [Todo] {
        switch filter {
        case .all:
            return todos
        case .active:
            return todos.filter { !$0.isCompleted }
        case .completed:
            return todos.filter { $0.isCompleted }
        }
    }
    
    // ํ†ต๊ณ„
    var statistics: Statistics {
        Statistics(
            total: todos.count,
            completed: todos.filter { $0.isCompleted }.count,
            active: todos.filter { !$0.isCompleted }.count
        )
    }
    
    struct Statistics {
        let total: Int
        let completed: Int
        let active: Int
        
        var completionRate: Double {
            guard total > 0 else { return 0 }
            return Double(completed) / Double(total) * 100
        }
    }
}

// 3. ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ƒํƒœ ๋ณ€๊ฒฝ
extension TodoState {
    func adding(_ todo: Todo) -> TodoState {
        return TodoState(todos: todos + [todo], filter: filter)
    }
    
    func removing(id: UUID) -> TodoState {
        let newTodos = todos.filter { $0.id != id }
        return TodoState(todos: newTodos, filter: filter)
    }
    
    func toggling(id: UUID) -> TodoState {
        let newTodos = todos.map { todo in
            guard todo.id == id else { return todo }
            return Todo(
                id: todo.id,
                title: todo.title,
                description: todo.description,
                isCompleted: !todo.isCompleted,
                createdAt: todo.createdAt,
                priority: todo.priority
            )
        }
        return TodoState(todos: newTodos, filter: filter)
    }
    
    func updating(id: UUID, title: String, description: String, priority: Todo.Priority) -> TodoState {
        let newTodos = todos.map { todo in
            guard todo.id == id else { return todo }
            return Todo(
                id: todo.id,
                title: title,
                description: description,
                isCompleted: todo.isCompleted,
                createdAt: todo.createdAt,
                priority: priority
            )
        }
        return TodoState(todos: newTodos, filter: filter)
    }
    
    func changingFilter(to newFilter: TodoFilter) -> TodoState {
        return TodoState(todos: todos, filter: newFilter)
    }
    
    func sortedByPriority() -> TodoState {
        let sortedTodos = todos.sorted { $0.priority > $1.priority }
        return TodoState(todos: sortedTodos, filter: filter)
    }
    
    func sortedByDate() -> TodoState {
        let sortedTodos = todos.sorted { $0.createdAt > $1.createdAt }
        return TodoState(todos: sortedTodos, filter: filter)
    }
}

// 4. ์‚ฌ์šฉ ์˜ˆ์ œ
var state = TodoState(todos: [], filter: .all)

// ํ•  ์ผ ์ถ”๊ฐ€
let todo1 = Todo(
    id: UUID(),
    title: "์Šค์œ„ํ”„ํŠธ ๊ณต๋ถ€ํ•˜๊ธฐ",
    description: "ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋งˆ์Šคํ„ฐ",
    isCompleted: false,
    createdAt: Date(),
    priority: .high
)

state = state.adding(todo1)

let todo2 = Todo(
    id: UUID(),
    title: "์žฅ๋ณด๊ธฐ",
    description: "์šฐ์œ , ๋นต, ๊ณ„๋ž€",
    isCompleted: false,
    createdAt: Date(),
    priority: .medium
)

state = state.adding(todo2)

// ํ•  ์ผ ์™„๋ฃŒ
state = state.toggling(id: todo1.id)

// ํ•„ํ„ฐ ๋ณ€๊ฒฝ
state = state.changingFilter(to: .active)

// ํ†ต๊ณ„ ํ™•์ธ
let stats = state.statistics
print("์ „์ฒด: \(stats.total), ์™„๋ฃŒ: \(stats.completed), ์ง„ํ–‰์ค‘: \(stats.active)")
print("์™„๋ฃŒ์œจ: \(stats.completionRate)%")

// ์šฐ์„ ์ˆœ์œ„๋กœ ์ •๋ ฌ
state = state.sortedByPriority()

// 5. ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ - ๊ฒ€์ƒ‰
extension TodoState {
    func searching(query: String) -> TodoState {
        guard !query.isEmpty else { return self }
        
        let searchResults = todos.filter { todo in
            todo.title.localizedCaseInsensitiveContains(query) ||
            todo.description.localizedCaseInsensitiveContains(query)
        }
        
        return TodoState(todos: searchResults, filter: filter)
    }
}

// 6. ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ - ์ผ๊ด„ ์ž‘์—…
extension TodoState {
    func completingAll() -> TodoState {
        let completedTodos = todos.map { todo in
            Todo(
                id: todo.id,
                title: todo.title,
                description: todo.description,
                isCompleted: true,
                createdAt: todo.createdAt,
                priority: todo.priority
            )
        }
        return TodoState(todos: completedTodos, filter: filter)
    }
    
    func removingCompleted() -> TodoState {
        let activeTodos = todos.filter { !$0.isCompleted }
        return TodoState(todos: activeTodos, filter: filter)
    }
}

์ด๋ ‡๊ฒŒ ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ๋กœ ์ž‘์„ฑํ•˜๋ฉด ์ƒํƒœ ๋ณ€๊ฒฝ์ด ๋ช…ํ™•ํ•˜๊ณ  ์˜ˆ์ธก ๊ฐ€๋Šฅํ•ด์ ธ. ๊ฐ ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋‹ˆ๊นŒ ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ(undo)๋„ ์‰ฝ๊ณ , ํ…Œ์ŠคํŠธํ•˜๊ธฐ๋„ ํŽธํ•ด! ๐Ÿ˜Š

๐ŸŒŸ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ vs ๊ฐ์ฒด์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

ํ•จ์ˆ˜ํ˜•๊ณผ ๊ฐ์ฒด์ง€ํ–ฅ, ๋ญ๊ฐ€ ๋” ์ข‹์„๊นŒ? ์‚ฌ์‹ค ์ •๋‹ต์€ ์—†์–ด. ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ ์ ˆํžˆ ์„ž์–ด ์“ฐ๋Š” ๊ฒŒ ์ตœ์„ ์ด์•ผ! ๐ŸŽจ

๐ŸŽฏ ์–ธ์ œ ํ•จ์ˆ˜ํ˜•์„ ์‚ฌ์šฉํ• ๊นŒ?

โœ… ํ•จ์ˆ˜ํ˜•์ด ์ข‹์€ ๊ฒฝ์šฐ:
โ€ข ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ํŒŒ์ดํ”„๋ผ์ธ
โ€ข ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ
โ€ข ํ…Œ์ŠคํŠธ๊ฐ€ ์ค‘์š”ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
โ€ข ์ƒํƒœ ๊ด€๋ฆฌ (Redux ์Šคํƒ€์ผ)

โœ… ๊ฐ์ฒด์ง€ํ–ฅ์ด ์ข‹์€ ๊ฒฝ์šฐ:
โ€ข UI ์ปดํฌ๋„ŒํŠธ
โ€ข ๋ณต์žกํ•œ ์ƒํƒœ๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด
โ€ข ์ƒ์†์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
โ€ข ํ”„๋ ˆ์ž„์›Œํฌ ํ†ตํ•ฉ (UIKit, SwiftUI)

// ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์ ‘๊ทผ๋ฒ• - ๋‘˜ ๋‹ค ํ™œ์šฉ!

// 1. ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ (ํ•จ์ˆ˜ํ˜•)
struct User {
    let id: Int
    let name: String
    let email: String
}

// 2. ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง (ํ•จ์ˆ˜ํ˜•)
func validateUser(_ user: User) -> Result<user, validationerror=""> {
    // ๊ฒ€์ฆ ๋กœ์ง...
    return .success(user)
}

func transformToViewModel(_ user: User) -> UserViewModel {
    return UserViewModel(displayName: user.name, email: user.email)
}

// 3. ํด๋ž˜์Šค๋กœ ์ƒํƒœ ๊ด€๋ฆฌ (๊ฐ์ฒด์ง€ํ–ฅ)
class UserManager {
    private var users: [User] = []
    
    // ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ์˜ ๋ฉ”์„œ๋“œ
    func addUser(_ user: User) -> Result<void, error=""> {
        return validateUser(user).map { validUser in
            self.users.append(validUser)
        }
    }
    
    func getUsers(matching predicate: (User) -> Bool) -> [User] {
        return users.filter(predicate)
    }
    
    func transformUsers<t>(_ transform: (User) -> T) -> [T] {
        return users.map(transform)
    }
}

// 4. SwiftUI ๋ทฐ (์„ ์–ธ์  + ํ•จ์ˆ˜ํ˜•)
struct UserListView: View {
    let users: [User]
    
    var body: some View {
        List(users, id: \.id) { user in
            UserRow(user: user)
        }
    }
}

// ์ตœ๊ณ ์˜ ์กฐํ•ฉ: ๊ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ์žฅ์ ์„ ํ™œ์šฉ!</t></void,></user,>

๐Ÿ’ก ์‹ค๋ฌด ํŒ๊ณผ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์‹ค๋ฌด์— ์ ์šฉํ•  ๋•Œ ์•Œ์•„๋‘๋ฉด ์ข‹์€ ํŒ๋“ค์„ ์ •๋ฆฌํ•ด๋ดค์–ด! ๐ŸŽฏ

1. ์ ์ง„์ ์œผ๋กœ ๋„์ž…ํ•˜๊ธฐ

์ฒ˜์Œ๋ถ€ํ„ฐ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ํ•จ์ˆ˜ํ˜•์œผ๋กœ ์ž‘์„ฑํ•˜๋ ค๊ณ  ํ•˜์ง€ ๋งˆ. ๋จผ์ € ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜ ๋กœ์ง๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ํ™•๋Œ€ํ•ด๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ข‹์•„.

2. ๊ฐ€๋…์„ฑ ์šฐ์„ 

ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ์ด ํ•ญ์ƒ ๋” ์งง์€ ๊ฑด ์•„๋‹ˆ์•ผ. ๋•Œ๋กœ๋Š” ๋ช…๋ นํ˜• ์Šคํƒ€์ผ์ด ๋” ์ฝ๊ธฐ ์‰ฌ์šธ ์ˆ˜ ์žˆ์–ด. ์ฝ”๋“œ์˜ ์˜๋„๊ฐ€ ๋ช…ํ™•ํ•œ ๊ฒŒ ๊ฐ€์žฅ ์ค‘์š”ํ•ด!

// ๊ณผ๋„ํ•˜๊ฒŒ ํ•จ์ˆ˜ํ˜• - ์ฝ๊ธฐ ์–ด๋ ค์›€
let result = users
    .filter { $0.age >= 18 }
    .map { ($0.name, $0.email) }
    .reduce(into: [String: String]()) { $0[$1.0] = $1.1 }

// ๋” ๋‚˜์€ ๋ฐฉ๋ฒ• - ์˜๋„๊ฐ€ ๋ช…ํ™•
let adultUsers = users.filter { $0.age >= 18 }
let emailMap = Dictionary(
    uniqueKeysWithValues: adultUsers.map { ($0.name, $0.email) }
)

3. ์ ์ ˆํ•œ ์ถ”์ƒํ™” ์ˆ˜์ค€

๋„ˆ๋ฌด ๋งŽ์€ ์ถ”์ƒํ™”๋Š” ์˜คํžˆ๋ ค ์ฝ”๋“œ๋ฅผ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์–ด. ์‹ค์šฉ์ ์ธ ์ˆ˜์ค€์—์„œ ๋ฉˆ์ถ”๋Š” ๊ฒŒ ์ค‘์š”ํ•ด.

4. ๋ฌธ์„œํ™”์™€ ํƒ€์ž… ๋ช…์‹œ

ํ•จ์ˆ˜ํ˜• ์ฝ”๋“œ๋Š” ๋•Œ๋กœ ํƒ€์ž… ์ถ”๋ก ์ด ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์–ด. ์ค‘์š”ํ•œ ํ•จ์ˆ˜์—๋Š” ํƒ€์ž…์„ ๋ช…์‹œํ•˜๊ณ  ๋ฌธ์„œ๋ฅผ ์ž‘์„ฑํ•˜์ž.

/// ์‚ฌ์šฉ์ž ๋ชฉ๋ก์„ ํ•„ํ„ฐ๋งํ•˜๊ณ  ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
/// - Parameters:
///   - users: ํ•„ํ„ฐ๋งํ•  ์‚ฌ์šฉ์ž ๋ฐฐ์—ด
///   - predicate: ํ•„ํ„ฐ๋ง ์กฐ๊ฑด
///   - transform: ๋ณ€ํ™˜ ํ•จ์ˆ˜
/// - Returns: ๋ณ€ํ™˜๋œ ๊ฒฐ๊ณผ ๋ฐฐ์—ด
func processUsers<t>(
    _ users: [User],
    filter predicate: (User) -> Bool,
    transform: (User) -> T
) -> [T] {
    return users
        .filter(predicate)
        .map(transform)
}</t>

5. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ช…ํ™•ํžˆ

ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ์—์„œ๋„ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” ์ค‘์š”ํ•ด. Result ํƒ€์ž…์ด๋‚˜ throws๋ฅผ ์ ์ ˆํžˆ ํ™œ์šฉํ•˜์ž.

6. ํŒ€๊ณผ ์†Œํ†ตํ•˜๊ธฐ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ํŒ€์›์ด ์žˆ๋‹ค๋ฉด, ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋‚˜ ํŽ˜์–ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ†ตํ•ด ํ•จ๊ป˜ ๋ฐฐ์›Œ๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ข‹์•„. ํ˜ผ์ž๋งŒ ์•„๋Š” ์ฝ”๋“œ๋Š” ์ข‹์€ ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ์•ผ!

๐Ÿš€ ๋” ๋‚˜์•„๊ฐ€๊ธฐ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ์ด๋Ÿฐ ๊ฒƒ๋“ค์„ ์•Œ์•„๋ณด๋ฉด ์ข‹์•„! ๐Ÿ“š

๐ŸŽ“ ์ถ”๊ฐ€ ํ•™์Šต ์ฃผ์ œ

1. ๋ชจ๋‚˜๋“œ(Monad)
Optional, Result, Array ๋“ฑ์ด ๋ชจ๋‘ ๋ชจ๋‚˜๋“œ์•ผ. ๋ชจ๋‚˜๋“œ ํŒจํ„ด์„ ์ดํ•ดํ•˜๋ฉด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๋” ๋ช…ํ™•ํ•ด์ ธ.

2. ํ•จ์ˆ˜ ์กฐํ•ฉ(Function Composition)
์ž‘์€ ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•ด์„œ ํฐ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ๋ฒ•. ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๊ทน๋Œ€ํ™”๋ผ.

3. ๋ Œ์ฆˆ(Lens)
๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•. ์ค‘์ฒฉ๋œ ๊ตฌ์กฐ์ฒด๋ฅผ ์šฐ์•„ํ•˜๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์–ด.

4. ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ
Combine์ด๋‚˜ RxSwift ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํ•จ์ˆ˜ํ˜• ๋ฆฌ์•กํ‹ฐ๋ธŒ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ฐฐ์›Œ๋ณด์ž.

5. ํƒ€์ž… ์ฃผ๋„ ๊ฐœ๋ฐœ
ํƒ€์ž… ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•ด์„œ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๋ฒ„๊ทธ๋ฅผ ์žก๋Š” ๊ธฐ๋ฒ•.

๐Ÿ’š ํ•™์Šต ๋ฆฌ์†Œ์Šค

โ€ข Apple์˜ Swift ๊ณต์‹ ๋ฌธ์„œ
โ€ข "Functional Swift" ์ฑ…
โ€ข Point-Free ๋น„๋””์˜ค ์‹œ๋ฆฌ์ฆˆ
โ€ข Swift by Sundell ๋ธ”๋กœ๊ทธ
โ€ข ๊ทธ๋ฆฌ๊ณ  ์žฌ๋Šฅ๋„ท์—์„œ ์Šค์œ„ํ”„ํŠธ ์ „๋ฌธ๊ฐ€๋“ค์˜ ์ง€์‹์„ ๊ณต์œ ๋ฐ›์„ ์ˆ˜๋„ ์žˆ์–ด!

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—๋Š” ์Šค์œ„ํ”„ํŠธ์™€ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•œ ๋‹ค์–‘ํ•œ ์ง€์‹๊ณผ ๊ฒฝํ—˜์„ ๊ฐ€์ง„ ๊ฐœ๋ฐœ์ž๋“ค์ด ๋งŽ์•„. ๊ถ๊ธˆํ•œ ๊ฒŒ ์žˆ๋‹ค๋ฉด ์ง€์‹์ธ์˜ ์ˆฒ์„ ๋‘˜๋Ÿฌ๋ณด๊ฑฐ๋‚˜, ์ง์ ‘ ์งˆ๋ฌธํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ๐ŸŒณ

Swift ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ˆœ์ˆ˜ํ•จ์ˆ˜ ๋ถˆ๋ณ€์„ฑ ๊ณ ์ฐจํ•จ์ˆ˜ ํ•จ์ˆ˜์กฐํ•ฉ โœจ

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ์Šค์œ„ํ”„ํŠธ์˜ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๋ถˆ๋ณ€์„ฑ, ์ˆœ์ˆ˜ํ•จ์ˆ˜ ํŒจํ„ด์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐Ÿ˜Š

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ์ฒ˜์Œ์—๋Š” ๋‚ฏ์„ค๊ณ  ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์กฐ๊ธˆ์”ฉ ์—ฐ์Šตํ•˜๋‹ค ๋ณด๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ผ๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋  ๊ฑฐ์•ผ. ์ฝ”๋“œ๊ฐ€ ๋” ์•ˆ์ „ํ•ด์ง€๊ณ , ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์›Œ์ง€๊ณ , ๋ฒ„๊ทธ๊ฐ€ ์ค„์–ด๋“œ๋Š” ๊ฑธ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ.

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ

์ˆœ์ˆ˜ํ•จ์ˆ˜: ๊ฐ™์€ ์ž…๋ ฅ์— ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ, ๋ถ€์ž‘์šฉ ์—†์Œ

๋ถˆ๋ณ€์„ฑ: ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ ์ƒ์„ฑ

๊ณ ์ฐจํ•จ์ˆ˜: ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜

ํ•จ์ˆ˜ ์กฐํ•ฉ: ์ž‘์€ ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•ด์„œ ๋ณต์žกํ•œ ๋กœ์ง ๊ตฌํ˜„

์‹ค์šฉ์„ฑ: ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ•จ์ˆ˜ํ˜•๊ณผ ๊ฐ์ฒด์ง€ํ–ฅ์„ ์ ์ ˆํžˆ ์„ž์–ด ์‚ฌ์šฉ

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฑด ์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด๋Š” ๊ฒƒ์ด์•ผ. ์ด๋ก ๋งŒ ์•Œ์•„์„œ๋Š” ์†Œ์šฉ์—†์–ด. ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ํ•จ์ˆ˜ํ˜• ์Šคํƒ€์ผ์„ ์ ์šฉํ•ด๋ณด์ž. ์ฒ˜์Œ์—๋Š” ์–ด์ƒ‰ํ•˜๊ฒ ์ง€๋งŒ, ์ ์  ์ž์—ฐ์Šค๋Ÿฌ์›Œ์งˆ ๊ฑฐ์•ผ! ๐Ÿ’ช

๊ทธ๋ฆฌ๊ณ  ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ์†Œํ†ตํ•˜๋ฉด์„œ ๋ฐฐ์šฐ๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฒŒ ๊ฐ€์žฅ ๋น ๋ฅธ ๊ธธ์ด๊ฑฐ๋“ ! ๐ŸŒฑ

๐Ÿ’š ๋‹ค์Œ ๋‹จ๊ณ„

1. ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๊ธฐ
2. map, filter, reduce๋ฅผ ์˜์‹์ ์œผ๋กœ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ
3. ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ๋ชจ๋ธ ์„ค๊ณ„ํ•ด๋ณด๊ธฐ
4. ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๋ถ„๋ฆฌํ•ด๋ณด๊ธฐ
5. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•ด๋ณด๊ธฐ

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋‹จ์ˆœํžˆ ์ฝ”๋”ฉ ์Šคํƒ€์ผ์ด ์•„๋‹ˆ๋ผ, ๋” ๋‚˜์€ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์‚ฌ๊ณ ๋ฐฉ์‹์ด์•ผ. ์ด ์‚ฌ๊ณ ๋ฐฉ์‹์„ ์ตํžˆ๋ฉด ์–ด๋–ค ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋“  ๋” ์ข‹์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋  ๊ฑฐ์•ผ.

์ž, ์ด์ œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Ÿฌ ๊ฐ€๋ณผ๊นŒ? ํ™”์ดํŒ…! ๐Ÿš€โœจ

๐ŸŽ‰ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์„ธ๊ณ„์— ์˜ค์‹  ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค! ๐ŸŽ‰

๋” ์•ˆ์ „ํ•˜๊ณ , ๋” ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ณ , ๋” ์•„๋ฆ„๋‹ค์šด ์ฝ”๋“œ๋ฅผ ํ–ฅํ•ด! ๐Ÿ’ซ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0