CodeGym /행동 /Kotlin SELF /Method and state: behaviour next to data, this, and basic...

Method and state: behaviour next to data, this, and basic validation

Kotlin SELF
레벨 30 , 레슨 2
사용 가능

1. Waehye behaviour-reul data 옆에 둬야 할까

Program-eul sseugi sijak-hamyeon, logic-eul modu “bakk-e” du-go sip-eojimnida: yeogi data-ga 있고, geu data-reul 가지고 mwonga 하는 function-deul-i tto 따로 있neun geotcheoreomyo. Geureohge haneun geot-eun jeong-sang-igo — cheoeum-eun geureohge haeya swipge chulbal-halsu isseumnida. Hajiman code-ga keojimyeon bulpyeon-han hyogwa-ga saenggimnida: data-leul 다루는 rule-i yeoreo gos-e-seo banbok-doego, byeongyeong-i “naega check-reul kkamppakhago bbeojin modeun gos chajgi” game-gwa gat-a jipsumnida.

Ye-reul 들어, uri-ga expense(지출)-ga 있다고 haebosipsida: title-gwa amount. Han beonman sseumyeon gwenchanhseumnida. Hajiman title-eul 바꾸고, amount-eul 바꾸고, “keun 지출-inga” check-hago, 출력 format-hamyeon — logic-i project jeonche-e 퍼져버립니다.

Cheoeum-eun daeryak ireon neukkim-imnida:

fun isLargeExpense(amount: Int): Boolean {
    return amount >= 1000
}

fun main() {
    val title = "Taxi"
    val amount = 1200

    println("$title: $amount")                 // Taxi: 1200
    println(isLargeExpense(amount))            // true
}

Dongjak-hanayo? Ne. Scale-up-doenayo? Mureup 위 diploma project-cheoreom — geureohge-doen-da-go haedo orae gaji-neun 못합니다.

Geureomyeon i ttae idea-ga 나옵니다: “keun-ji출 check”가 expense-ui uimi-jeog-in 일부분이라면, wae geugeol expense-ui method-ro 만들지 않을까요?

2. Method: seon-eon, 호출, geurigo property access

Class method: eotteohge seon-eonhago eotteohge bulleona

Method-neun bogtong function-ina, class an-e salgo object-eseo dot-euro 호출됩니다. Kotlin-eseoneun geo-i jayeonseureopge ilghyeo: expense.isLarge() neun jumuncheoreom-i anira han munjangcheoreom deullimnida.

Gajang gan-danhan geosbuteo: expense-reul 한 줄로 예쁘게 출력하는 method-reul chuga-haebopsida.

class Expense(val title: String, val amount: Int) {
    fun asLine(): String {
        return "$title: $amount"
    }
}

fun main() {
    val e = Expense("Coffee", 300)
    println(e.asLine()) // Coffee: 300
}

Duas 가지 pointeureul jom bwajuseyo. Cheotjjae, method-neun class-ui 중괄호 안에서 fun-euro seon-eon-d됩니다. Duljjae, 호출은 e.asLine() cheoreom: 왼쪽에 object, 오른쪽에 method. I 습관은 중요합니다. Ihu Kotlin OOP-ui geoui modeun geot-i “dot-euro” 진행됩니다.

Wae method-neun object-ui property-reul “muryoro” bonda

Cho-bogja-eseo method-ui gajang joeun jeom-eun, method-ga imi object-reul “jwiggo” 있다는 geot-imnida. Geuraeseo title gwa amount-reul 따로 전달할 필요가 없습니다 — 이미 object an-e 있으니까요. Magia-cheoreom boijiman, sil-eun dan순한 logic-imnida: method-neun 특정 object-e daehae 호출되므로, geu object-ui state-reul 다룹니다.

Du 가지 접근 — 바깥 function vs 안쪽 method — reul bigyo-haebopsida.

Bakk-e function:

class Expense(val title: String, val amount: Int)

fun asLine(e: Expense): String {
    return "${e.title}: ${e.amount}"
}

fun main() {
    val e = Expense("Lunch", 900)
    println(asLine(e)) // Lunch: 900
}

Gat-eun geot-ijiman “data 옆에” 둔 버전:

class Expense(val title: String, val amount: Int) {
    fun asLine(): String = "$title: $amount"
}

fun main() {
    val e = Expense("Lunch", 900)
    println(e.asLine()) // Lunch: 900
}

Duljjae beon-i daechero deoug swipge 읽힙니다: 호출에 잡음이 적고, “다른 object 전달” 실수-do deo 적습니다. Geurigo deo 중요한 geon — formatting rule-i 한 곳에 모여 있어, project jeonche-e 퍼지지 않는다는 geot-imnida.

3. Object state: var 와 “시간 속의 object”

State-neun object-ui var property-deul-ui hyeonjae 값-imnida. Object-e mutable field-ga 있damyeon, object-neun 시간에 따라 바뀔 수 있습니다: eonje-neun ireon 모습, 나중엔 다른 모습. I geot-eun counter, wallet, hyeonjae setting 등, “살아있고 바뀌는” geot-e yuyong합니다.

Jageun 예: 증가할 수 있는 counter-reul mandeureo bopsida.

class Counter(var value: Int = 0) {
    fun inc() {
        value += 1
    }
}

fun main() {
    val c = Counter()
    c.inc()
    c.inc()
    println(c.value) // 2
}

Geuri-go cheoeum-e jogeum cereom gakkeum meori-ga kkojineun 중요한 detail: val c = Counter() neun reference 재할당-eul geumji합니다 (tteukhi c = Counter() cheoreom). Hajiman object an-e var-ga 있다면, object-ui state reul 바꾸는 geot-eun 막지 않습니다.

즉, val eun object-reul “얼려두는” geosi 아니라, variable-eul “재할당 불가” 로 만드는 것입니다.

4. this: eonjega pilyo eopseogo eonjega sareulda

Kotlin-eseo this 는 “현재 object” 를 뜻합니다. Daebubun-eun Kotlin-i geugeol 안 써도 되게 해 줍니다: title-e 접근하면 compiler-ga geugeol this.title 로 이해합니다.

Hajiman gakkeum-eun ambiguity-reul 없애기 위해 this ga pilyo합니다 — teukhi method parameter-ga property-wa 같은 이름일 때요. “rename”, “amount update” 같은 작업에서 자주 마주칩니다.

Da-eum 예-eseo this ga uido-reul 명확히 해 줍니다:

class Person(var name: String) {
    fun rename(name: String) {
        this.name = name.trim()
    }
}

fun main() {
    val p = Person("Ann")
    p.rename("  Anna  ")
    println(p.name) // Anna
}

Silje rule-eun dan-sun합니다: name conflict-ga eop-seumyeon this reul 안 써도 됩니다. “Mweonga susanghan데?” 라는 neukkim-i 드는 순간, this ga “내가 바꾸는 건 object-ui state-da” reul 명시적으로 보여 줍니다.

5. Invariant and validation: object-reul 올바른 상태로 유지하기

Invariant-neun state correctness-reul wihan dan-sunhan rule-imnida. Il상-적 uimi-ro “eonjena cham-이어야 object-ga uimi-ga 있다” 입니다. Ye-reul 들어, expense-eseo amount-neun 음수면 안 되고, title-neun 빈 문자열이면 안 됩니다.

Invariant-neun 변화가 일어나는 곳, 즉 method-eseo 검사하는 것이 가장 좋습니다. Geureom “control point han gose” seumyeo, program-ui modeun gos-eseo check-reul 기억할 필요가 없어집니다.

Expense reul mutable-ha게 하고, minimal validation-gwa 함께 update method-reul 주어 bopsida:

class Expense(var title: String, var amount: Int) {

    fun rename(newTitle: String): Boolean {
        val t = newTitle.trim()
        if (t.isBlank()) return false
        title = t
        return true
    }

    fun changeAmount(newAmount: Int): Boolean {
        if (newAmount < 0) return false
        amount = newAmount
        return true
    }
}

Yeogi-seo method-neun Boolean-eul return-haneunde, i geos-eun ajju “hyeonsiljeog-in” contract-imnida: true — seonggong, false — silpae, geuraedo object-neun 망가지지 않습니다. I bangsig-eun error-ga “예상 가능한 일” il ttae jo-eumnida (사용자가 이상한 값을 입력, 흔한 일입니다).

Early return gwa require/check: dareun scenario

Gakkeum-eun error-ga normal-han situation-igo, gakkeum-eun 논리적 재앙입니다 (예: “wallet 안에 음수 balance-ga 있는데, uri-ga geureon 일-eun 없다고 약속했는데?”). Kotlin-eun du 가지 popular tool-eul 제공합니다: 부드러운 스타일(return false) gwa 강한 스타일(require/check) 입니다.

  • require(...) neun botong 입력 argument ui 올바름을 검사하고, 위반 시 IllegalArgumentException eul 던집니다.
  • check(...) neun botong state reul 검사하고, 위반 시 IllegalStateException eul 던집니다.

Wallet 예제로 접근을 비교해 봅시다.

부드러운 버전 (사용자가 실수할 수 있다고 기대):

class Wallet(var balance: Int) {

    fun withdraw(amount: Int): Boolean {
        if (amount <= 0) return false
        if (amount > balance) return false
        balance -= amount
        return true
    }
}

fun main() {
    val w = Wallet(100)
    println(w.withdraw(30))  // true
    println(w.balance)       // 70
    println(w.withdraw(200)) // false
}

강한 버전 (“여기엔 잘못된 데이터로 오면 안 된다” 고 가정):

class Wallet(var balance: Int) {

    fun withdraw(amount: Int) {
        require(amount > 0) { "Amount must be positive: $amount" }
        check(balance >= 0) { "Balance is broken: $balance" }

        require(amount <= balance) {
            "Not enough money. Balance=$balance, amount=$amount"
        }
        balance -= amount
    }
}

Chai-ineun syntax-ga 아니라 철학입니다.

  • false return-eun program-i 계속 진행하면서 사용자에게 “다시 입력” 을 요청할 수 있게 합니다.
  • require/check neun “이 상황에서 program-eun 계속되면 안 된다, contract 위반이다” 라는 signal 입니다.

6. Silje-ro 적용: method-ro Expense update하기

Ije jageun fragment-eseo core idea-reul bo-yul su 있습니다: 외부 code-neun expense 변경 rule-eul 전부 알아서는 안 됩니다. 외부 code-neun object-ege 행동을 요청하고, object-ga 무엇이 허용되는지 스스로 결정해야 합니다.

Ye-reul 들어 title update-neun 이렇게 보일 수 있습니다: expense.title = ... reul 직접 하지 않고, rename(...) eul 호출합니다.

class Expense(var title: String, var amount: Int) {
    fun rename(newTitle: String): Boolean {
        val t = newTitle.trim()
        if (t.isBlank()) return false
        title = t
        return true
    }
}

fun main() {
    val e = Expense("Coffee", 300)

    val ok = e.rename("   ")
    println(ok)       // false
    println(e.title)  // Coffee
}

Juyeo bo세요: error-ga 나도 object-neun 이전의 올바른 상태로 남아 있습니다. I geot-i invariant-ui jageun “seunli” 입니다: 나중에 어떻게 다룰지 모르는 이상한 object-reul 만들지 않게 막았습니다.

Amount-do update-ha고 싶다면, 외부 code-neun 여전히 rule-eul “모르고” — danji method-reul 호출합니다:

class Expense(var title: String, var amount: Int) {
    fun changeAmount(newAmount: Int): Boolean {
        if (newAmount < 0) return false
        amount = newAmount
        return true
    }
}

fun main() {
    val e = Expense("Taxi", 1200)

    e.changeAmount(1500)
    println(e.amount) // 1500
}

Jageun geot처럼 보ijiman, real project-eseoneun nerve-reul mani 아껴 줍니다: “어디서 음수 amount-reul 대입했지?” 를 code jeonche-eseo 찾지 않아도 됩니다. 바깥에서의 직접 대입을 거의 하지 않게 되니까요.

7. Typical mistakes

Mistake #1: class-neun 있는데 logic-eun 여전히 전부 바깥에 있다.
Cho-bogja-neun class Expense(...) reul 만들어 놓고, geu daeum-e-do formatExpense(e), isLargeExpense(e), renameExpense(e, ...), validateExpense(e) 같은 function-eul yeoreot dubang-ssik 계속 추가합니다. Geureom class-ga “field bag” cheoreom 되고, rule-eun project jeonche-e 퍼져버립니다. Haengdong-i 특정 object-e jayeonseureopge 속한다면 method-ro 만드는 게 좋습니다: rule-eul eoneu han gos-eseo라도 빼먹을 확률이 줄어듭니다.

Mistake #2: mutability(var) reul uimi eopsi “혹시 몰라서” 넣는다.
Modeun property-reul var 로 만들면, eotteon code-eseo든 object-reul eotteohge든 eonjedeun 바꿀 수 있습니다. Geuraedo eoneu nal gapjagi amount = -100 eul bogo “project-e eumchimhan magia-ga sumeodeul-eotna?” 라고 생각하게 됩니다. Cheoeum-eun val 로 시작하고, 변화가 scenario-eseo jeongmal pilyohan 경우에만 var reul 추가하는 게 좋습니다.

Mistake #3: method-reul 우회해서 state change-reul 직접 한다.
Validation-i 들어간 rename() reul 써 놨는데도, code 어딘가에 expense.title = readln() 이 남아 있으면, validation-eun uimi-ga 없어집니다. 변화의 일부는 “정상 입구” 로, 일부는 “뒷문” 으로 들어오니까요. Invariant-reul method-ro 보호하기로 했다면, field-reul 직접 바꾸기보다 method-reul 호출하려고 노력하세요.

Mistake #4: method-ga state-reul 바꾸는데, 성공 여부를 알리지 않는다.
Method-ga “실패할 수” 있는데(예: 새 title-ga 빈 문자열), Unit 을 반환하며 조용히 아무 일도 하지 않으면, 외부 code-neun 무슨 일이 일어났는지 모릅니다. Geuraeseo i상한 scenario-ga 생깁니다: 사용자가 빈 문자열을 입력했는데 program-eun 다 된 것처럼 진행하고, 사실 변경은 적용되지 않았습니다. 예상 가능한 오류에는 Boolean contract-ga 편리합니다: caller-ga message-reul 보여 주고 입력을 다시 받을 수 있습니다.

Mistake #5: 같은 이름 때문에 생기는 혼란 + this 부재.
rename(name: String) 같은 method-neun 살면서 거의 확실히 만나고, geu 중 한 번은 name = name.trim() 처럼 써 놓고 1분 동안 모니터를 바라보며 깨달음을 기다릴 것입니다. Ireon gos-eseo this 는 “쓸데없는 글자” 가 아니라, 의도를 분명히 하는 방법입니다: this.name = name.trim().

Mistake #6: require/check reul scenario-ga 계속돼야 하는 데서 쓴다.
User input-eul require(...) 로 검증하면, 오류 시 program-eun exception-euro “crash” 합니다 — require ga 원래 그렇게 동작하니까요. Contract 보호에는 좋지만, 사용자가 그저 실수했을 수도 있는 친절한 CLI-eseoneun 나쁩니다. 예상 가능한 오류에는 false/null 을 반환하고 “다시 입력” 을 요청하는 쪽이 낫고, require/check 는 “그러면 안 되는 상황” 에 남겨 두세요.

1
과제
Kotlin SELF, 레벨 30, 레슨 2
잠금
프로필 명함
프로필 명함
1
과제
Kotlin SELF, 레벨 30, 레슨 2
잠금
영웅의 걸음
영웅의 걸음
1
과제
Kotlin SELF, 레벨 30, 레슨 2
잠금
불필요한 문자가 없는 닉네임
불필요한 문자가 없는 닉네임
1
과제
Kotlin SELF, 레벨 30, 레슨 2
잠금
규칙 지갑
규칙 지갑
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION