Saltar al contenido principal

🎪 스코프 함수

📖 스코프 함수란?

스코프 함수는 객체의 컨텍스트 내에서 코드 블록을 실행하는 함수입니다. 객체를 더 간결하게 다룰 수 있습니다.

💡 5가지 스코프 함수

let    - 객체를 it으로 받아 결과 반환
apply - 객체를 this로 받아 객체 자체 반환 (초기화)
run - 객체를 this로 받아 결과 반환
also - 객체를 it으로 받아 객체 자체 반환 (부가 작업)
with - 객체를 this로 받아 결과 반환 (확장 함수 아님)

🔧 let

기본 사용

fun main() {
val name = "홍길동"

val result = name.let {
println("이름: $it")
it.length // 반환값
}

println("길이: $result") // 길이: 3
}

null 체크

fun main() {
val name: String? = "홍길동"

// null이 아닐 때만 실행
name?.let {
println("이름: $it")
println("길이: ${it.length}")
}
}

지역 변수 대신

fun main() {
// ❌ 지역 변수 사용
val numbers = listOf(1, 2, 3, 4, 5)
val result = numbers.filter { it > 2 }
println(result)

// ✅ let 사용 (더 간결)
listOf(1, 2, 3, 4, 5)
.filter { it > 2 }
.let { println(it) }
}

🎨 apply

객체 초기화

data class Person(var name: String = "", var age: Int = 0)

fun main() {
val person = Person().apply {
name = "홍길동" // this.name
age = 25 // this.age
}

println(person) // Person(name=홍길동, age=25)
}

빌더 패턴

class StringBuilder {
private val sb = StringBuilder()

fun append(text: String) = apply {
sb.append(text)
}

fun appendLine(text: String) = apply {
sb.appendLine(text)
}

override fun toString() = sb.toString()
}

fun main() {
val text = StringBuilder()
.append("안녕하세요")
.appendLine("!")
.append("반갑습니다")
.toString()

println(text)
}

🚀 run

객체 초기화 + 결과 반환

data class Person(var name: String = "", var age: Int = 0)

fun main() {
val greeting = Person().run {
name = "홍길동"
age = 25
"안녕하세요, ${name}님! (${age}세)" // 반환값
}

println(greeting) // 안녕하세요, 홍길동님! (25세)
}

코드 블록 실행

fun main() {
val result = run {
val a = 10
val b = 20
a + b // 반환값
}

println(result) // 30
}

🎯 also

부가 작업 (로깅 등)

data class Person(val name: String, val age: Int)

fun main() {
val person = Person("홍길동", 25)
.also { println("생성됨: $it") }
.also { println("확인 완료") }

println(person)
}

체이닝 중간 로그

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
.filter { it % 2 == 0 }
.also { println("짝수: $it") } // 중간 확인
.map { it * it }
.also { println("제곱: $it") } // 중간 확인

println("최종: $numbers")
}

🌟 with

여러 메서드 호출

fun main() {
val numbers = mutableListOf(1, 2, 3)

with(numbers) {
add(4)
add(5)
remove(1)
println("크기: $size")
}

println(numbers) // [2, 3, 4, 5]
}

결과 반환

data class Person(val name: String, val age: Int)

fun main() {
val person = Person("홍길동", 25)

val info = with(person) {
"이름: $name, 나이: $age"
}

println(info) // 이름: 홍길동, 나이: 25
}

🎯 실전 예제

API 요청 처리

data class User(val id: Int, val name: String, val email: String?)

fun main() {
val user = User(1, "홍길동", null)

// let으로 null 체크
user.email?.let { email ->
println("이메일 전송: $email")
} ?: println("이메일 없음")
}

객체 설정

class Config {
var host: String = ""
var port: Int = 0
var timeout: Int = 0

fun connect() {
println("연결: $host:$port (timeout: ${timeout}ms)")
}
}

fun main() {
Config().apply {
host = "localhost"
port = 8080
timeout = 3000
}.connect()
}

파일 처리 시뮬레이션

class File(val path: String) {
fun read(): String = "파일 내용"
fun close() = println("파일 닫힘")
}

fun main() {
val content = File("test.txt").run {
val data = read()
close()
data // 반환
}

println(content)
}

📊 비교표

참조 방식

fun main() {
val name = "Kotlin"

// it으로 참조
name.let { println(it) }
name.also { println(it) }

// this로 참조 (생략 가능)
name.apply { println(this) }
name.run { println(this) }
with(name) { println(this) }
}

반환값

fun main() {
val name = "Kotlin"

// 결과 반환
val length1 = name.let { it.length } // 6
val length2 = name.run { length } // 6
val length3 = with(name) { length } // 6

// 객체 자체 반환
val name1 = name.apply { } // "Kotlin"
val name2 = name.also { } // "Kotlin"
}

🤔 자주 묻는 질문

Q1. 어떤 걸 써야 하나요?

A: 상황에 따라 선택!

// 객체 초기화 → apply
val person = Person().apply {
name = "홍길동"
age = 25
}

// null 체크 → let
name?.let {
println(it)
}

// 결과 계산 → run
val result = person.run {
"$name ($age세)"
}

// 부가 작업 → also
list.also { println("크기: ${it.size}") }

// 여러 메서드 호출 → with
with(list) {
add(1)
add(2)
}

Q2. it vs this의 차이는?

A: 가독성 차이!

fun main() {
val name = "Kotlin"

// it - 명시적
name.let { text ->
println(text.length)
}

// this - 생략 가능
name.run {
println(length) // this 생략
}
}

Q3. 중첩해서 쓸 수 있나요?

A: 가능하지만 가독성 주의!

fun main() {
val person = Person("홍길동", 25)

person.let { p ->
p.name.let { name ->
println(name)
}
}

// 더 나은 방법
println(person.name)
}

🎬 마치며

스코프 함수로 코드를 더 깔끔하게!

핵심 정리:
✅ let - null 체크, 결과 반환 (it)
✅ apply - 객체 초기화 (this, 객체 반환)
✅ run - 결과 계산 (this, 결과 반환)
✅ also - 부가 작업 (it, 객체 반환)
✅ with - 여러 메서드 호출 (this, 결과 반환)

다음 단계: 클래스에서 객체지향 프로그래밍을 시작하세요!