✅ 함수형 프로그래밍과 불변성(immutability) 완벽 가이드
함수형 프로그래밍(Functional Programming, FP)은 수학적 함수의 개념을 기반으로 한 프로그래밍 패러다임입니다.
**불변성(immutability)**은 함수형 프로그래밍에서 가장 중요한 개념 중 하나이며,
코드의 예측 가능성, 안전성, 유지보수성을 향상시키는 역할을 합니다.
🔹 1. 함수형 프로그래밍이란?
함수형 프로그래밍(Functional Programming, FP)은 “순수 함수(Pure Function)“를 기반으로 하는 프로그래밍 방식입니다.
✅ 함수형 프로그래밍의 핵심 원칙
1. 순수 함수(Pure Function) 사용 → 같은 입력이 들어오면 항상 같은 출력 반환.
2. 불변성(Immutability) 유지 → 변수나 데이터 변경 없이 새 값을 생성.
3. 고차 함수(Higher-Order Function) 지원 → 함수를 인자로 전달하거나 반환 가능.
4. 선언형 프로그래밍(Declarative Programming) → “어떻게”가 아니라 “무엇”을 할지 표현.
5. 부수 효과(Side Effect) 최소화 → 함수 외부의 값을 변경하지 않음.
6. 일급 객체(First-Class Citizen)로 함수 지원 → 함수를 변수처럼 다룰 수 있음.
📌 대표적인 함수형 프로그래밍 언어: Haskell, Lisp, Scala, Kotlin, Java (Java 8+에서 함수형 스타일 지원)
🔹 2. 불변성이란?
**불변성(immutability)**이란 객체(변수, 데이터 등)의 상태가 한 번 생성되면 변경되지 않는 속성을 의미합니다.
✅ 불변성의 장점
1. 멀티스레드 환경에서 안전(Thread-Safe) → 동기화 문제 없음.
2. 예측 가능한 코드 작성 가능 → 상태 변화가 없으므로 디버깅이 쉬움.
3. 버그 방지 → 예상치 못한 값 변경이 발생하지 않음.
4. 함수형 프로그래밍과 잘 맞음 → map, filter, reduce 같은 연산에서 유용.
📌 불변성을 유지하는 방법:
• 변수 대신 val(Kotlin) / final(Java) 사용.
• 변경할 때는 새로운 객체를 생성 (copy(), map 함수 활용).
• 컬렉션도 불변으로 사용 (listOf(), setOf() 등).
🔹 3. 불변성과 가변성 비교
특징 | 가변(Mutable) 객체 | 불변(Immutable) 객체 |
상태 변경 가능 여부 | ✅ 가능 (값 변경 가능) | ❌ 불가능 (값 변경 불가) |
멀티스레드 환경 | ❌ 동기화 필요 | ✅ 안전(Thread-Safe) |
예측 가능성 | ❌ 예측 어려움 (side-effect 발생 가능) | ✅ 예측 가능 |
메모리 사용량 | ✅ 적음 (값을 변경함) | ❌ 많음 (새로운 객체 생성) |
대표적인 예제 | ArrayList, MutableList | List, data class |
🔹 4. 함수형 프로그래밍에서 불변성 활용하기
✅ (1) 변수 대신 val 사용 (Kotlin)
var mutableValue = 10 // ❌ 가변 변수 (값 변경 가능)
mutableValue = 20
val immutableValue = 10 // ✅ 불변 변수 (값 변경 불가)
// immutableValue = 20 // ❌ 컴파일 오류 발생
📌 val을 사용하면 변수를 재할당할 수 없음 → 불변성 유지 가능.
✅ (2) data class와 copy()를 활용한 불변성 유지
data class User(val name: String, val age: Int)
val user1 = User("Alice", 25)
val user2 = user1.copy(age = 26) // ✅ 새로운 객체 생성 (불변성 유지)
println(user1) // User(name=Alice, age=25)
println(user2) // User(name=Alice, age=26)
📌 객체를 직접 수정하는 대신 copy()를 사용하여 새로운 객체를 생성.
✅ (3) 불변 컬렉션 사용
val immutableList = listOf(1, 2, 3) // ✅ 불변 리스트 (수정 불가능)
val mutableList = mutableListOf(1, 2, 3) // ❌ 가변 리스트 (수정 가능)
mutableList.add(4) // ✅ 가능
// immutableList.add(4) // ❌ 불가능 (컴파일 오류 발생)
📌 Kotlin에서는 기본적으로 불변 컬렉션(listOf, setOf)을 사용해야 함.
✅ (4) map, filter 활용 (불변 컬렉션 유지)
val numbers = listOf(1, 2, 3, 4, 5)
// 원본 리스트를 변경하지 않고 새로운 리스트 반환
val doubled = numbers.map { it * 2 }
println(doubled) // [2, 4, 6, 8, 10]
📌 map 함수는 원본 데이터를 변경하지 않고 새로운 리스트를 생성하여 반환.
✅ (5) reduce를 활용한 순수 함수 구현
val numbers = listOf(1, 2, 3, 4, 5)
val sum = numbers.reduce { acc, num -> acc + num }
println(sum) // 15
📌 기존 데이터를 변경하지 않고, 새로운 값을 반환하는 함수형 스타일.
🔹 5. 불변성이 필요한 이유 (왜 불변성을 유지해야 할까?)
✅ (1) 멀티스레드 환경에서 동기화 문제 방지
• 가변 객체는 여러 스레드에서 동시에 접근하면 동기화 문제 발생 가능.
• 불변 객체는 동기화 없이 안전하게 공유 가능.
❌ 가변 객체 사용 시 문제
class Counter {
private int count = 0;
public void increment() {
count++; // ❌ 여러 스레드에서 동시에 접근하면 문제 발생 가능
}
}
✅ 불변 객체 사용 시 해결
data class Counter(val count: Int) {
fun increment(): Counter = copy(count = count + 1) // ✅ 새로운 객체 반환
}
📌 불변 객체를 사용하면 멀티스레드 환경에서도 안전하게 동작 가능.
✅ (2) 예측 가능성 증가
• 가변 객체는 어디서 값이 변경될지 예측하기 어려움.
• 불변 객체는 한 번 값이 설정되면 변하지 않으므로 예측 가능성이 높아짐.
✅ (3) 함수형 프로그래밍과 궁합이 잘 맞음
• 불변 객체를 사용하면 순수 함수(Pure Function)를 쉽게 구현할 수 있음.
• 원본 데이터를 변경하지 않고 새로운 값을 생성하는 방식 → 데이터 흐름이 명확해짐.
🔥 결론
1. 함수형 프로그래밍은 순수 함수, 불변성, 고차 함수를 활용하는 패러다임.
2. 불변성을 유지하면 멀티스레드 환경에서도 안전하고, 버그를 줄이며, 예측 가능성을 높일 수 있음.
3. Kotlin에서는 val, data class, copy(), map, reduce 등을 활용하여 불변성을 유지할 수 있음.
4. 불변 객체를 사용하면 동기화 문제 없이 멀티스레드 환경에서 안정적으로 사용할 수 있음.
5. Java는 기본적으로 가변 객체 중심이지만, Kotlin은 불변성을 기본으로 지향하여 유지보수가 쉬운 코드 작성을 가능하게 함.
📌 Kotlin에서 불변성을 적극적으로 활용하면, 안전하고 유지보수하기 쉬운 코드 작성이 가능! 🚀
'BE > Kotlin' 카테고리의 다른 글
[Kotlin] as? 와 null 형변환 (0) | 2025.03.27 |
---|---|
[Kotlin] 코루틴 (Coroutines) (1) | 2025.03.02 |
[Kotlin] JPA에서 필드에 final을 붙이지 않는 이유 (val, var) (0) | 2025.02.21 |
[Kotlin] 코틀린 문법 6 (open, 상속, 인터페이스) (0) | 2025.02.20 |
[Kotlin] 코틀린 문법 5 (lateinit, lazy, companion object) (0) | 2025.02.19 |