์ „์ฒด ๊ธ€ (165)

๋ฐ˜์‘ํ˜•
10
11

๋กœ๊ทธ์ธ์ด ๊ตฌํ˜„๋œ fragment์—์„œ ๊ฐ€์ž…๋˜์ง€์•Š์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์‹œ๋„๋ฅผ ํ•  ๋•Œ, ์นด์นด์˜ค ๋กœ๊ทธ์ธ์—์„œ ๋ฐ˜ํ™˜ ๋ฐ›์€ access token์„ ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€๋กœ ๋„˜๊ฒจ์•ผํ–ˆ๋Š”๋ฐ, ์ด๋•Œ ์„œ๋กœ ๋‹ค๋ฅธ Fragment๋กœ ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ๋‹ค viewmodel๋„ ๊ณต์œ ํ•˜๊ณ  ์žˆ์ง€์•Š์•„์„œ ๊ณจ์น˜๊ฐ€ ์•„ํŒ ๋‹ค. datastore๋ฅผ ์ด์šฉํ•ด access token์„ ์ €์žฅํ•˜๋Š” ๊ฑด ๋ฏธ๋ฆฌ ์งœ ๋‘์—ˆ์ง€๋งŒ, ๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋ž˜๊ทธ๋จผํŠธ๋ผ๋ฆฌ ๊ฐ’์„ ๋„˜๊ธฐ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ ๊ณ ๋ฏผํ•˜๋˜ ์ค‘, ํ”„๋กœ์ ํŠธ ํŒ€์žฅํ˜•์ด safe args๋ผ๋Š” ํ‚ค์›Œ๋“œ๋ฅผ ์•Œ๋ ค์ฃผ์…จ๋‹ค.

 

safe args๋Š” android jetpack navigation component์— ์†ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ค‘ ํ•˜๋‚˜์ด๊ณ , navigation์˜ ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„ ์†Œ์Šค์ฝ”๋“œ์˜ action์— ๊ฐ’์„ ๋‹ด์•„์„œ ๋„˜๊ธฐ๋Š” ์•„์ฃผ ํš๊ธฐ์ ์ธ ๊ธฐ๋Šฅ์„ ๊ฐ–๊ณ ์žˆ๋‹ค.

๋Œ€์ƒ ์‚ฌ์ด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ์ „๋‹ฌํ•  ๋•Œ ์œ ํ˜• ์•ˆ์ •์„ฑ์„ ์ œ๊ณตํ•˜๋Š” ๊ทธ๋ž˜ํ”„ ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค. ์ธ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์•ˆํ‹ฐํŒจํ„ด์œผ๋กœ ๊ฐ„์ฃผ๋ฉ๋‹ˆ๋‹ค. ๊ฐ ๋Œ€์ƒ์€ ํ•ญ๋ชฉ ID์™€ ๊ฐ™์ด ํ•„์š”ํ•œ ์ตœ์†Œ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ UI ๋ฐ์ดํ„ฐ ๋กœ๋“œ๋ฅผ ๋‹ด๋‹นํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ”„๋กœ์„ธ์Šค ์žฌ์ƒ์„ฑ์ด ๊ฐ„์†Œํ™”๋˜๊ณ  ์ž ์žฌ์  ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐฉ์ง€๋ฉ๋‹ˆ๋‹ค.

๋‚ด ๊ฒฝ์šฐ์—๋Š” string์œผ๋กœ ๋œ ๊ฐ’์„ ๋„˜๊ธฐ๋ฉด ๋˜๋Š” ์ƒํ™ฉ์ด๋ผ ์‚ฌ์šฉํ•˜๊ธฐ ์ ํ•ฉํ•œ ์กฐ๊ฑด์ด๋‹ค. ์ด์ œ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž

Safe Args ์‚ฌ์šฉ๋ฒ•

gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์ด๋‹ค ๋ณด๋‹ˆ, gradle์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๋ฐฉ๋ฒ•์ด๊ณ , ๊ทธ ๋•Œ๋Š” bundle์„ ์ด์šฉํ•ด ๊ฐ’์„ ๋„˜๊ฒจ์•ผํ•œ๋‹ค.

์ตœ์ƒ์œ„ gradle ํŒŒ์ผ(ํ”„๋กœ์ ํŠธ ์ˆ˜์ค€)์— classpath ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.5.3"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}
 

nav_version์€ ๊ณ„์† ์—…๋ฐ์ดํŠธ ๋  ๊ฒƒ์ด๋ฏ€๋กœ version catalog๋‚˜ ๋”ฐ๋กœ buildSrc ๋ชจ๋“ˆ์„ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์œ ์ง€ ๋ณด์ˆ˜์— ์ข‹๋‹ค.

์‚ฌ์šฉํ•  app / presentation ๋ชจ๋“ˆ์— ์•„๋ž˜ dependency๋ฅผ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

plugins {
    id("androidx.navigation.safeargs.kotlin")
}
 

AndroidX ํŒจํ‚ค์ง€์ด๊ธฐ ๋•Œ๋ฌธ์— gradle.propertiesํŒŒ์ผ์— android.useAndroidX=true๊ฐ€ ์žˆ์–ด์•ผํ•œ๋‹ค. ์ตœ๊ทผ ์ƒ์„ฑํ•œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์ด๋ฏธ ๋“ค์–ด๊ฐ€ ์žˆ์„ ๊ฒƒ์ด์ง€๋งŒ ์˜ˆ์ „์— ์ƒ์„ฑํ•œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์—†์„ ์ˆ˜๋„ ์žˆ๋‹ค.

 

์ด์ œ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋ฉด, ์†ก์‹ (๋ฐœ์‹ )๋Œ€์ƒ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋ฆ„ + Directions๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค. ์ด์ œ ๊ฐ’์„ ๋„˜๊ธธ ์ค€๋น„๋ฅผ ํ•˜๊ฒ ๋‹ค. ๋จผ์ € ๊ฐ’์„ ๋‹ด์•„์„œ ๋„˜๊ธธ action์— ์ธ์ˆ˜๋ฅผ ์„ค์ •ํ•œ๋‹ค.

<action
    android:id="@+id/action_loginFragment_to_signupFragment"
    app:destination="@id/signupFragment">
    <argument
        android:name="acToken"
        app:argType="string"/>
</action>
 

์ด action์€ ๋ฐœ์‹ ๋Œ€์ƒ์—์„œ ์‚ฌ์šฉํ•˜๋Š” action์ด๋‹ค. ์ˆ˜์‹ ๋Œ€์ƒ๋„ argument๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผํ•˜๋Š”๋ฐ, ์ˆ˜์‹ ๋Œ€์ƒ์€ fragment์—์„œ ๊ฐ’์„ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— argument์˜ ์œ„์น˜๊ฐ€ action์ด ์•„๋‹Œ fragment์ด๋‹ค.

<fragment
    android:id="@+id/signupFragment"
    android:name="com.shypolarbear.presentation.ui.join.JoinFragment"
    android:label="SignupFragment"
    tools:layout="@layout/fragment_signup">
    <argument
        android:name="acToken"
        app:argType="string"/>
</fragment>
 

์ด๊ฒŒ ์ˆ˜์‹ ๋Œ€์ƒ์˜ ์„ค์ •์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ”„๋ž˜๊ทธ๋จผํŠธ์ด๋ฆ„ + Args์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ, action์„ argument๊ฐ’์„ ๋„ฃ์–ด์„œ ์žฌ์ •์˜ํ•œ๋‹ค.

sendTokenToJoin = LoginFragmentDirections.actionLoginFragmentToSignupFragment(token.accessToken)
 

Directions์— ๋ฉ”์„œ๋“œ๋กœ action id๊ฐ€ ์žกํžˆ๊ณ , ์ธ์ˆ˜์— ์‚ฌ์šฉํ•  ๊ฐ’์„ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. ๋‚ด ์ฝ”๋“œ์—์„œ sendTokenToJoin์€ NavDirection Type์œผ๋กœ, ๋”ฐ๋กœ navigateํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๊ฑฐ๊ธฐ์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ดlateinit var ์„ ์–ธํ•ด๋’€๋‹ค.

์ˆ˜์‹ ์ชฝ์—์„œ๋Š” ๋” ๊ฐ„๋‹จํ•˜๋‹ค. ktx๊ณ„์—ด dependency๋ฅผ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด(์•„๋งˆ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉ์ค‘์ด๋ผ๋ฉด android-ktx๋ฅผ ์“ฐ๊ณ  ์žˆ์„ ํ™•๋ฅ ์ด ๋†’๋‹ค.) by navArgs()๋ฅผ ์ด์šฉํ•ด ๊ฐ’์„ ๋ฐ›์•„์˜ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

private val args: JoinFragmentArgs by navArgs()
val acToken = args.acToken
 

์ˆ˜์‹ ๋Œ€์ƒ์˜ ์ฝ”๋“œ๋‹ค. ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„ ui์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ ๋‚˜๋Š” ์ฝ”๋“œ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒŒ ํŽธํ•ด์„œ ์ด๋ ‡๊ฒŒ ์ง„ํ–‰ํ–ˆ๋‹ค.

Safe Args ์‚ฌ์šฉ์ด์ 

๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด ์œ ํ˜• ์•ˆ์ „์„ฑ์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ด๊ฒŒ ๋ฒˆ์—ญ์ด ์–ด์ƒ‰ํ•ด์„œ ๊ทธ๋Ÿฐ๊ฑด๋ฐ Type Safety๊ฐ€ ์›๋ฌธ์— ์ ํ˜€์žˆ๋Š” ๋‹จ์–ด๋‹ค.

safe args๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„ ์ธ์ž์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ™•์ธํ•˜๊ธฐ ๋•Œ๋ฌธ ์ปดํŒŒ์ผ ์‹œ์ ์— ์—๋Ÿฌ๋ฅผ ๋ฐœ๊ฒฌ ํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

(10/11 ์ถ”๊ฐ€) Safe Args๋ฅผ ์“ฐ๋ฉด์„œ DataClass๋ฅผ ๋„˜๊ธฐ๋Š” ๋ฒ•

์›์‹œํƒ€์ž…๋งŒ ๋„˜๊ธฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ DataClass๋กœ ๋„˜๊ธฐ๊ณ  ์‹ถ๋‹ค๋ฉด Parcelable๋กœ ์„ ์–ธํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค.

@Parcelize
data class StoreDataArgs(
    val storeId: Int,
    val storeName: String,
    val imageUrl: String
): Parcelable

Parcelable๋กœ ์„ ์–ธํ•ด์ค˜์„œ ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ๊ณผ์ •์—๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ data class๋ฅผ ์ž‘์„ฑํ•œ๋‹ค์Œ

<action
    android:id="@+id/action_navigation_map_to_navigation_detail"
    app:destination="@id/navigation_detail">
    <argument
        android:name="storeInfo"
        app:argType="com.suchelin.android.util.parcelable.StoreDataArgs"/>
</action>

argType์— data class ์œ„์น˜๋ฅผ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. ์ˆ˜๋™์œผ๋กœ ํ•˜๋ฉด ํ—ท๊ฐˆ๋ฆด์ˆ˜๋„ ์žˆ์œผ๋‹ˆ ์ด๋•Œ๋Š” ๋„๊ตฌ๋ฅผ ์“ฐ๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

Attributes์— add arguments๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ด๋ ‡๊ฒŒ custom parcelable๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ณ  IDE๊ฐ€ ์ฐพ์•„์ค€๋‹ค.

์ฐธ์กฐ:

https://developer.android.com/guide/navigation/navigation-pass-data?hl=ko

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•
COMMENT
 
10
10

DataStore๋Š” ์ฝ”๋ฃจํ‹ด๊ณผ flow๋ฅผ ์‚ฌ์šฉํ•˜๋Š” SharedPreferences์˜ ๊ฐœ์„ ํŒ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. SharedPreferences๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ณดํ†ต ์„ค์ •๊ฐ’๊ณผ ๊ฐ™์€ ๋‹จ์ˆœ ๋ฐ์ดํ„ฐ ์…‹์ธ key-value๋กœ ๋ฌถ์ด๋Š” ๊ฐ’๋“ค์„ ์ €์žฅํ•˜๋Š”๋ฐ, primitive type๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ๊ณผ, String์œผ๋กœ ๋งŒ๋“  JSON์„ ์ €์žฅํ•œ๋‹ค๊ณ  ์ณ๋„ ๋ถˆ์‹œ์— ๋ฐœ์ƒํ•˜๋Š” ๋Ÿฐํƒ€์ž„์—๋Ÿฌ๋ฅผ ํ”ผํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.

๋˜ํ•œ ์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ์ด XMLํŒŒ์ผ์ด๊ณ  ์‹ค์ œ ํŒŒ์ผ์ž‘์—…์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— UI ์“ฐ๋ ˆ๋“œ์—์„œ ๋Œ๋ ค๋ฒ„๋ฆฌ๋ฉด ANR์ด ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋Ÿฐํƒ€์ž„์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†ํ›„ํ•˜๋‹ค. ๋น„๋™๊ธฐ ๊ธฐ๋Šฅ๋„ ์œ ์—ฐํ•˜์ง€์•Š๋‹ค. DB์ˆ˜์ค€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋‹น์—ฐํžˆ Room์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

 

DataStore๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ ๊ณ„๊ธฐ๋Š” ์„œ๋ฒ„์—์„œ ๋ฐ›์•„์˜ค๋Š” access token์„ ์ €์žฅํ•ด๋†”์•ผ api ์š”์ฒญํ•  ๋•Œ ํ—ค๋”์— ํ† ํฐ์„ ๋„ฃ์–ด์„œ ์š”์ฒญํ•˜๋Š”๋ฐ, SharedPreferences๋Š” ํŒŒ์ผ์ด ๋‚จ์•„์žˆ์–ด ์•ˆ์ „ํ•˜์ง€ ์•Š์•„ DataStore๋ฅผ ์„ ํƒํ–ˆ๋‹ค.

 

์ด ๊ธ€์—์„œ SharedPreference -> DataStore ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ๋‹ค๋ฃจ์ง€ ์•Š๋Š”๋‹ค.

DataStore

DataStore๋Š” Preferences DataStore์™€ Proto DataStore๋กœ ๋‚˜๋‰œ๋‹ค.

์ „์ž๋Š” ๊ธฐ์กด์˜ SharedPreferences์™€ ๊ฑฐ์˜ ๋™์ผํ•œ ๊ธฐ๋Šฅ, ์‚ฌ์šฉ๋ฒ•์„ ๊ฐ–๊ณ , ํ›„์ž๋Š” ์ปค์Šคํ…€ ๋ฐ์ดํ„ฐํƒ€์ž…์„ Protocol Buffer๋ฅผ ํ†ตํ•ด ์ €์žฅํ•œ๋‹ค. ์ปค์Šคํ…€ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— Type-Safety๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ์Šคํ‚ค๋งˆ๋ฅผ ๋ฏธ๋ฆฌ ์ง€์ •ํ•ด์•ผ๋œ๋‹ค๋Š” ๊ท€์ฐฎ์Œ์ด ์žˆ๊ธดํ•˜๋‹ค.

์„ค์ •๋ฒ•๊ณผ ๊ฐ™์€ ๊ฑด ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ๋‹ค๋ฃจ์ง„ ์•Š๊ฒ ๋‹ค. ๋˜ํ•œ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Preferences DataStore๊ฐ€ ์‚ฌ์šฉ๋๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฑธ ๋‹ค๋ฃจ๊ฒ ๋‹ค.

Preferences DataStore

DataStore ํด๋ž˜์Šค์™€ Preferencesํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•œ๋‹ค. Preferencesํด๋ž˜์Šค๋Š” Mapํ˜•์‹์œผ๋กœ DataStore๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํƒ€์ž…์ด๋‹ค.

์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์ธ์Šคํ„ด์Šค ๋งŒ๋“ค๊ธฐ

val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "preferences name")
 

์ด ๊ณผ์ •์ด ์ œ์ผ ์ค‘์š”ํ•˜๋‹ค. ์ตœ์ƒ์œ„ ํ”„๋กœํผํ‹ฐ๋กœ data store์ธ์Šคํ„ด์Šค๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“œ๋Š” ์ž‘์—…์ธ๋ฐ, ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๋ฉ”์‹œ์ง€๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

You should either maintain your DataStore as a singleton or confirm that there is no two DataStore’s active on the same file (by confirming that the scope is cancelled)

๋™์ผํ•œ ์ด๋ฆ„์„ ๊ฐ–๋Š” datastore์ธ์Šคํ„ด์Šค๊ฐ€ ์ค‘๋ณต์œผ๋กœ ์ƒ์„ฑ๋˜์–ด ๋ฒ„๋ฆฐ ๋Œ€์ฐธ์‚ฌ๋‹ค.

์ฝ๊ณ  ์“ฐ๊ธฐ(๋ฉ€ํ‹ฐ๋ชจ๋“ˆ์—์„œ)

์“ธ ๋•Œ๋Š” ์ฝ”๋ฃจํ‹ด์—์„œ, ์ฝ์„ ๋•Œ๋Š” flow๋กœ ์ž‘๋™ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์ƒ˜ํ”Œ์ฝ”๋“œ ๋ง๊ณ  ์‹ค์ œ๋กœ ์ž‘์—…ํ–ˆ๋˜ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™€๋ดค๋‹ค.

๋ฉ€ํ‹ฐ๋ชจ๋“ˆ์„ ์‚ฌ์šฉ ์ค‘์ด๊ณ  data layer์— ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋‹ค. ์ „์ฒด ์ฝ”๋“œ ๋จผ์ € ์˜ฌ๋ ค๋†“๊ฒ ๋‹ค. ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ, ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ๋ถ€๋ถ„๋„ ์žˆ์ง€๋งŒ ํŽธ์˜๋ฅผ ์œ„ํ•ด์„œ Access Token๋ถ€๋ถ„๋งŒ ๊ฐ€์ ธ์™”๋‹ค.

@Singleton
val Context.tokenDataStore by preferencesDataStore("tokens")

class TokenRepoImpl @Inject constructor(
    private val api: TokenApi,
    @ApplicationContext private val context: Context
): TokenRepo {

    private object PreferenceKeys {
        val ACCESS_TOKEN = stringPreferencesKey("access_token")
    }

    override suspend fun getAccessToken(): String {
        val userAccessToken: Flow<String?> = context.tokenDataStore.data.map {
            it[ACCESS_TOKEN]
        }
        return userAccessToken.first().toString()
    }
    override suspend fun setAccessToken(accessToken: String) {
        context.tokenDataStore.edit {prefs ->
            prefs[ACCESS_TOKEN] = accessToken
        }
    }
}
 

์ตœ์ƒ์œ„ ํ”„๋กœํผํ‹ฐ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

@Singleton
val Context.tokenDataStore by preferencesDataStore("tokens")
 

์—ฌ๊ธฐ์„œ preferencesDataStore์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด๊ฐ€๋Š” String๊ฐ’์€ ์ผ์ข…์˜ ๋””๋ ‰ํ„ฐ๋ฆฌ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” tokens์ธ๋ฐ, ์•ž์œผ๋กœ tokenDataStore๋กœ ์ ‘๊ทผํ•˜๋Š” key-value ์Œ์€ ๋ชจ๋‘ tokens ๋ฐ‘์— ์ƒ์„ฑ๋œ๋‹ค.

private object PreferenceKeys {
        val ACCESS_TOKEN = stringPreferencesKey("access_token")
    }
 

key-value์Œ์œผ๋กœ ๋“ค์–ด๊ฐˆ ๊ฐ’์˜ Key๋ฅผ ๋‹ด๋Š” ๊ณณ์ด๋‹ค. ๊ทธ๋ƒฅ “access_token”์ด๋ผ๊ณ  ์ง์ ‘ ๊ฐ’์„ ๋„ฃ์–ด๋„ ๋˜์ง€๋งŒ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด object๋กœ ๋ฌถ์–ด์„œ ๋งŒ๋“ค์–ด ์คฌ๋‹ค.

์ง€์›๋˜๋Š” PreferencesKey ์œ ํ˜•์€ ์—ฌ๊ธฐ์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

override suspend fun setAccessToken(accessToken: String) {
        context.tokenDataStore.edit {prefs ->
            prefs[ACCESS_TOKEN] = accessToken
        }
    }
 

datastore์— ์“ฐ๋Š” ๋ถ€๋ถ„์ด๋‹ค. SharedPreferences์™€ ์œ ์‚ฌํ•œ ๋ชจ์Šต์ธ๋ฐ, edit ๋ฉ”์„œ๋“œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํŠธ๋žœ์žญ์…˜ ๋ฐฉ์‹์œผ๋กœ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค. ๋žŒ๋‹ค๋กœ ๋ฌถ์ธ ๋ถ€๋ถ„์€ ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ทจ๊ธ‰๋œ๋‹ค. suspend fun์œผ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ์ฝ”๋ฃจํ‹ด์—์„œ ํ˜ธ์ถœํ•ด์•ผํ•œ๋‹ค.

override suspend fun getAccessToken(): String {
        val userAccessToken: Flow<String?> = context.tokenDataStore.data.map {
            it[ACCESS_TOKEN]
        }
        return userAccessToken.first().toString()
    }
 

Flow๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ์ฝ๋Š” ์ฝ”๋“œ๋‹ค. first()๋Š” Flow์˜ ์ฒซ๋ฒˆ์งธ ๊ฐ’์„ collectํ•˜๋Š” ๋ฉ”์„œ๋“œ์ธ๋ฐ, ์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๊ฐ’์ด ํ•˜๋‚˜๊ธฐ ๋•Œ๋ฌธ์— first๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.

HTTPํ†ต์‹  ํ—ค๋”์— ํ† ํฐ ๊ฐ’์„ ๋„ฃ์„ ๋•Œ๋Š” ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ฐ’ ๋ณด์žฅ์ด ์•ˆ๋  ์ˆ˜ ์žˆ์–ด์„œ runBlocking์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

์ฐธ์กฐ:

https://developer.android.com/jetpack/androidx/releases/datastore?authuser=1&hl=ko#:~:text=kotlin%20%ED%8C%8C%EC%9D%BC%EC%9D%98%20%EC%B5%9C%EC%83%81%EC%9C%84%20%EC%88%98%EC%A4%80%EC%97%90%20%EB%B0%B0%EC%B9%98%ED%95%98%EB%A9%B4%20%EC%9D%B4%20%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4%EA%B0%80%20%ED%95%98%EB%82%98%EB%A7%8C%20%EC%83%9D%EA%B9%81%EB%8B%88%EB%8B%A4.

https://medium.com/androiddevelopers/introduction-to-jetpack-datastore-3dc8d74139e7

https://developer.android.com/topic/libraries/architecture/datastore?authuser=1&hl=ko

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•
COMMENT
 
10
10

๊ณ ์ฐจ ํ•จ์ˆ˜ ์ •์˜

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ๋žŒ๋‹ค๋‚˜ ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด ํ•จ์ˆ˜๋ฅผ ๊ฐ’์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ณ ์ฐจ ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. filter, map๋„ ๊ณ ์ฐจํ•จ์ˆ˜์— ์†ํ•œ๋‹ค.

ํ•จ์ˆ˜ ํƒ€์ž… - ๋žŒ๋‹ค๋กœ ์ธ์ž ๋ฐ›๊ธฐ

ํ•จ์ˆ˜ ํƒ€์ž…์„ ์ •์˜ํ•˜๋ ค๋ฉด ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์„ ๊ด„ํ˜ธ ์•ˆ์— ๋„ฃ๊ณ , ๊ทธ ๋’ค์— ํ™”์‚ดํ‘œ๋ฅผ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ, ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค. Unit ํƒ€์ž…์€ ์˜๋ฏธ ์žˆ๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์— ์“ฐ๋Š” ํŠน๋ณ„ํ•œ ํƒ€์ž…์ด๋‹ค. ๊ทธ๋ƒฅ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•œ๋‹ค๋ฉด ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ชฉ๋ก ๋’ค์— ์˜ค๋Š” Unit ๋ฐ˜ํ™˜ ํƒ€์ž… ์ง€์ •์„ ์ƒ๋žตํ•ด๋„ ๋˜์ง€๋งŒ, ํ•จ์ˆ˜ ํƒ€์ž…์„ ์„ ์–ธํ•  ๋•Œ๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ฐ˜๋“œ์‹œ ๋ช…์‹œํ•ด์•ผ ํ•˜๋ฏ€๋กœ Unit์„ ๋นผ๋จน์–ด์„œ๋Š” ์•ˆ ๋œ๋‹ค.

val action: () -> Unit = { println(42) }        
 

์ธ์ž๋กœ ๋ฐ›์€ ํ•จ์ˆ˜ ํ˜ธ์ถœ

์ธ์ž๋กœ ๋ฐ›์€ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ทธ๋ƒฅ ์ผ๋ฐ˜ ํ•จ์ˆ˜์™€ ๊ฐ™๋‹ค.

fun twoAndThree(operation: (op1: Int, Int) -> Int) {
	  val result = operation(2, 3)
    println("The result is $result")
}
 

๋ฐ˜ํ™˜ํƒ€์ž…์— ๋‹น์—ฐํžˆ Nullable ํ•˜๊ฒŒ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๊ณ  ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค. ์ด๋ฆ„์„ ๋ถ™์—ฌ์„œ ์“ฐ๋ฉด ๊ฐ€๋…์„ฑ์ด ๋” ์ข‹์•„์ง€๋Š” ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค.

๋””ํดํŠธ ๊ฐ’์„ ์ง€์ •ํ•œ ํ•จ์ˆ˜ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋‚˜ ๋„์ด ๋  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ

ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•จ์ˆ˜ ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•  ๋•Œ๋„ ๋””ํดํŠธ ๊ฐ’์„ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

fun <T> Collection<T>.joinToString(
        separator: String = ", ",
        prefix: String = "",
        postfix: String = "",
        transform: (T) -> String = { it.toString() } 
): String {
    ...
}
 

๋ณด๋‹ค์‹œํ”ผ ๋””ํดํŠธ ๊ฐ’์„ ๋žŒ๋‹ค๋กœ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

ํ•จ์ˆ˜ ํƒ€์ž…์€ invoke ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ผ๋Š” ๊ฒŒ ์ด์ œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ดํ•ด๋˜๋Š”๋ฐ, ๊ทธ๋ƒฅ ํ•จ์ˆ˜๋กœ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ invoke๋ฉ”์„œ๋“œ๋ฅผ safe call๋กœ ํ˜ธ์ถœํ•˜๋ฉด ๋” null safeํ•œ ์ฝ”๋“œ๊ฐ€ ๋œ๋‹ค. ํ˜ธ์ถœ ๋ถ€๋ถ„๋งŒ ์‚ดํŽด๋ณด์ž.

transform?.invoke(element) ?: element.toString()// ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•˜๋‚˜๋‹ค
 

์ด๋ ‡๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•จ์ˆ˜ํƒ€์ž…๋„ nullableํ•˜๊ฒŒ ๊ณ ์ณ์ค˜์•ผํ•œ๋‹ค.

transform: ((T) -> String)? = null
 

ํ•จ์ˆ˜๋ฅผ ํ•จ์ˆ˜์—์„œ ๋ฐ˜ํ™˜

ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ ค๋ฉด return ์‹์— ๋žŒ๋‹ค๋‚˜ ๋ฉค๋ฒ„ ์ฐธ์กฐ๋‚˜ ํ•จ์ˆ˜ ํƒ€์ž…์˜ ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๋Š” ์‹์„ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.

fun getPredicate(): (Person) -> Boolean {
        val startsWithPrefix = { p: Person ->
            p.firstName.startsWith(prefix) || p.lastName.startsWith(prefix)
        }
        if (!onlyWithPhoneNumber) {
            return startsWithPrefix
        }
        return { startsWithPrefix(it)
                    && it.phoneNumber != null }
    }
 

์ต์ˆ™ํ•˜์ง€์•Š์€ ๋ชจ์Šต์ด์ง€๋งŒ ๋ฐ˜ํ™˜๊ฐ’์ด ํ•จ์ˆ˜ํƒ€์ž…์ด๋‹ค. return์— ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๊ฒƒ๋„, ๋ณ€์ˆ˜๋กœ ์ •์˜๋œ ๋žŒ๋‹ค์™€ ๊ทธ๋ƒฅ ๋žŒ๋‹ค ์‹์ด๋‹ค.

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

๋žŒ๋‹ค๋ฅผ ํ™œ์šฉํ•œ ์ค‘๋ณต ์ œ๊ฑฐ

์‚ฌ์‹ค ์ด๊ฑด filter, map์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋ฏธ๋ฆฌ ์ฒด๊ฐํ–ˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์œผ๋กœ ์ฝ”๋”ฉํ…Œ์ŠคํŠธ๋ฅผ ๋ณธ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ, ์ž…๋ ฅ๊ฐ’์„ ๋ฐ›์•„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•œ์ค„๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

val input = br.readLine().split(" ").map{ it.toInt }.toIntArray()
 

ํ•จ์ˆ˜ํƒ€์ž…์ด ๋ฐ˜ํ™˜๋˜๋Š”๊ฑธ ๋ฐ›์•„์„œ split, map, toIntArray๋กœ ์ฐจ๋ก€๋กœ ๋ณ€ํ™˜๋˜๋Š” ๋ชจ์Šต์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•จ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋Š” ๊ฑด ๋””์ž์ธํŒจํ„ด ์ค‘ ์ „๋žตํŒจํ„ด์— ๋Œ€์‘ํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ „๋žต์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋žŒ๋‹ค๋ฅผ ๋„˜๊ธฐ๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ž๋ฐ” ๊ตฌ๋ฒ„์ „์ฒ˜๋Ÿผ ์ „๋žต๋ณ„๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.

์ธ๋ผ์ธ ํ•จ์ˆ˜: ๋žŒ๋‹ค์˜ ๋ถ€๊ฐ€ ๋น„์šฉ ์—†์• ๊ธฐ inline

inline ๋ณ€๊ฒฝ์ž๋ฅผ ์–ด๋–ค ํ•จ์ˆ˜์— ๋ถ™์ด๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๊ทธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ชจ๋“  ๋ฌธ์žฅ์„ ํ•จ์ˆ˜ ๋ณธ๋ฌธ์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ๋กœ ๋ฐ”๊ฟ”์น˜๊ธฐ ํ•ด์ค€๋‹ค. ์ฆ‰ ๋‚ด๊ฐ€ ๋ณผ ๋•Œ๋Š” ๋ณ„๋„์˜ ํ•จ์ˆ˜์ง€๋งŒ, ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณผ ๋•Œ๋Š” ๋ณธ๋ฌธ์— ๋“ค์–ด๊ฐ€ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ํ˜ธ์ถœ ๋น„์šฉ์„ ์—†์• ์ค€๋‹ค๋Š” ๋ง์ด๋‹ค

์ธ๋ผ์ด๋‹์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹

์–ด๋–ค ํ•จ์ˆ˜๋ฅผ inline์œผ๋กœ ์„ ์–ธํ•˜๋ฉด ๊ทธ ํ•จ์ˆ˜์˜ ๋ณธ๋ฌธ์ด ์ธ๋ผ์ธ๋œ๋‹ค. ๋‹ค๋ฅธ ๋ง๋กœ ํ•˜๋ฉด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ ๋Œ€์‹ ์— ํ•จ์ˆ˜ ๋ณธ๋ฌธ์„ ๋ฒˆ์—ญํ•œ ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋กœ ์ปดํŒŒ์ผํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

inline fun foo(): Person -> Boolean { }

 

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

์ธ๋ผ์ธ ํ•จ์ˆ˜์˜ ํ•œ๊ณ„์ 

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

Illegal usage of inline-parameter ๋ฉ”์‹œ์ง€๊ฐ€ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์— noinline ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์„œ ๋ฏธ๋ฆฌ ์ธ๋ผ์ด๋‹์„ ๊ธˆ์ง€์‹œ์ผœ์•ผํ•œ๋‹ค.

fillter์™€ map์€ ์ธ๋ผ์ธ ํ•จ์ˆ˜๋กœ ์ปฌ๋ ‰์…˜์—์„œ ์•„์ฃผ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š”๋ฐ, ๊ณผ์ •์—์„œ ์ค‘๊ฐ„๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปฌ๋ ‰์…˜์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ง€๋ฉด ๋ถ€๊ฐ€๋น„์šฉ๋„ ๊ฐ™์ด ์ปค์ง„๋‹ค. ์ด๋•Œ๋Š” asSequence๋ฅผ ๋ถ™์—ฌ ์‹œํ€€์Šค๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๋‹ค๋งŒ ๋žŒ๋‹ค๋ฅผ ์ €์žฅํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์‹œํ€€์Šค๋Š” ๋žŒ๋‹ค๋ฅผ ์ธ๋ผ์ธํ•ด์„œ ์‚ฌ์šฉํ•˜์ง€์•Š๋Š”๋‹ค.

ํ•จ์ˆ˜๋ฅผ ์ธ๋ผ์ธ์œผ๋กœ ์„ ์–ธํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

inline ํ‚ค์›Œ๋“œ์˜ ์ด์ ์„ ๋ฐฐ์šฐ๊ณ  ๋‚˜๋ฉด ์ฝ”๋“œ๋ฅผ ๋” ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ ์—ฌ๊ธฐ์ €๊ธฐ์—์„œ inline์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์–ด์งˆ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ์‚ฌ์‹ค ์ด๋Š” ์ข‹์€ ์ƒ๊ฐ์ด ์•„๋‹ˆ๋‹ค. inline ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋žŒ๋‹ค๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜๋งŒ ์„ฑ๋Šฅ์ด ์ข‹์•„์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค(์ „์ฒด๋กœ ๋ณด๋ฉด ์„ฑ๋Šฅ์ด ๋‚˜๋น ์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์•ผ๊ธฐ).

์ผ๋ฐ˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ์˜ ๊ฒฝ์šฐ JVM์€ ์ด๋ฏธ ๊ฐ•๋ ฅํ•˜๊ฒŒ ์ธ๋ผ์ด๋‹์„ ์ง€์›ํ•œ๋‹ค. JVM์€ ์ฝ”๋“œ ์‹คํ–‰์„ ๋ถ„์„ํ•ด์„œ ๊ฐ€์žฅ ์ด์ต์ด ๋˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ํ˜ธ์ถœ์„ ์ธ๋ผ์ด๋‹ํ•œ๋‹ค. ์ด๋Ÿฐ ๊ณผ์ •์€ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์‹ค์ œ ๊ธฐ๊ณ„์–ด ์ฝ”๋“œ๋กœ ๋ฒˆ์—ญํ•˜๋Š” ๊ณผ์ •(JIT)์—์„œ ์ผ์–ด๋‚œ๋‹ค. ์ด๋Ÿฐ JVM์˜ ์ตœ์ ํ™”๋ฅผ ํ™œ์šฉํ•œ๋‹ค๋ฉด ๋ฐ”์ดํŠธ์ฝ”๋“œ์—์„œ๋Š” ๊ฐ ํ•จ์ˆ˜ ๊ตฌํ˜„์ด ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ๋งŒ ์žˆ์œผ๋ฉด ๋˜๊ณ , ๊ทธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๋”ฐ๋กœ ํ•จ์ˆ˜ ์ฝ”๋“œ๋ฅผ ์ค‘๋ณตํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋ฐ˜๋ฉด ์ฝ”ํ‹€๋ฆฐ ์ธ๋ผ์ธ ํ•จ์ˆ˜๋Š” ๋ฐ”์ดํŠธ ์ฝ”๋“œ์—์„œ ๊ฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ง€์ ์„ ํ•จ์ˆ˜ ๋ณธ๋ฌธ์œผ๋กœ ๋Œ€์น˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ ์ค‘๋ณต์ด ์ƒ๊ธด๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉด ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๊ฐ€ ๋” ๊น”๋”ํ•ด์ง„๋‹ค.

inline ๋ณ€๊ฒฝ์ž๋ฅผ ํ•จ์ˆ˜์— ๋ถ™์ผ ๋•Œ๋Š” ์ฝ”๋“œ ํฌ๊ธฐ์— ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ ํ•œ๋‹ค. ์ธ๋ผ์ด๋‹ํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ํฐ ๊ฒฝ์šฐ ํ•จ์ˆ˜์˜ ๋ณธ๋ฌธ์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ๋ชจ๋“  ํ˜ธ์ถœ ์ง€์ ์— ๋ณต์‚ฌํ•ด ๋„ฃ๊ณ  ๋‚˜๋ฉด ๋ฐ”์ดํŠธ์ฝ”๋“œ๊ฐ€ ์ „์ฒด์ ์œผ๋กœ ์•„์ฃผ ์ปค์งˆ ์ˆ˜ ์žˆ๋‹ค.

์ž์› ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์ธ๋ผ์ธ๋œ ๋žŒ๋‹ค ์‚ฌ์šฉ

์ž๋ฐ” 7๋ถ€ํ„ฐ๋Š” ์ž์›์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํŠน๋ณ„ํ•œ ๊ตฌ๋ฌธ์ธ try-with-resource๋ฌธ์ด ์ƒ๊ฒผ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์œผ๋กœ๋Š” useํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š”๋ฐ closable ์ž์›์— ๋Œ€ํ•œ ํ™•์žฅํ•จ์ˆ˜๋ฉฐ, ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋žŒ๋‹ค๋‹ค. ์ž‘๋™๋ฐฉ์‹์€ ๋žŒ๋‹ค๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ์ž์›์„ ๋‹ซ๋Š”๋‹ค.

BufferedReader(FileReader(path)).use { br ->
        return br.readLine()
    }
 

๋žŒ๋‹ค๋ฅผ ์‹คํ–‰ํ•˜๊ณ , FileReader๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” BufferedReader๋ฅผ closeํ•œ๋‹ค.

๊ณ ์ฐจ ํ•จ์ˆ˜ ์•ˆ์—์„œ ํ๋ฆ„ ์ œ์–ด

๋žŒ๋‹ค ์•ˆ์˜ return๋ฌธ: ๋žŒ๋‹ค๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ๋ฐ˜ํ™˜

for์™€ forEach์˜ ์ฐจ์ด๋ฅผ ๋ณด๊ฒ ๋‹ค.

fun lookForAlice(people: List<Person>) {
    people.forEach {
        if (it.name == "Alice") {
            println("Found!")
            return
        }
    }
    println("Alice is not found")
}
 

๋žŒ๋‹ค ์•ˆ์—์„œ return์„ ์‚ฌ์šฉํ•˜๋ฉด ๋žŒ๋‹ค๋กœ๋ถ€ํ„ฐ๋งŒ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๊ทธ ๋žŒ๋‹ค๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰์„ ๋๋‚ด๊ณ  ๋ฐ˜ํ™˜๋œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ์ž์‹ ์„ ๋‘˜๋Ÿฌ์‹ธ๊ณ  ์žˆ๋Š” ๋ธ”๋ก๋ณด๋‹ค ๋” ๋ฐ”๊นฅ์— ์žˆ๋Š” ๋‹ค๋ฅธ ๋ธ”๋ก์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒํ•˜๋Š” ๋ฐฉ์‹์„ non-local return์ด๋ผ ๋ถ€๋ฅธ๋‹ค. ์˜ˆ์ œ์—์„œ forEach๋Š” ์ธ๋ผ์ธ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ๋žŒ๋‹ค ๋ณธ๋ฌธ๊ณผ ํ•จ๊ป˜ ์ธ๋ผ์ด๋‹๋œ๋‹ค. ๋”ฐ๋ผ์„œ return ์‹์ด ๋ฐ”๊นฅ์ชฝ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜์‹œํ‚ค๋„๋ก ์‰ฝ๊ฒŒ ์ปดํŒŒ์ผํ•  ์ˆ˜ ์žˆ๋‹ค.

๋žŒ๋‹ค๋กœ๋ถ€ํ„ฐ ๋ฐ˜ํ™˜: ๋ ˆ์ด๋ธ”(label)์„ ์‚ฌ์šฉํ•œ return

๋žŒ๋‹ค ์‹์—์„œ๋„ ๋กœ์ปฌ return์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋ณต๋ฌธ ํƒˆ์ถœ ํ•  ๋•Œ break์ง€์  ์ง€์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•œ๋ฐ. ๋กœ์ปฌ return๊ณผ ๋„Œ๋กœ์ปฌ return์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ๋ ˆ์ด๋ธ”(label)์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

fun lookForAlice(people: List<Person>) {
    people.forEach label@{
        if (it.name == "Alice") return@label
    }
    println("Alice might be somewhere")
}โ€‹

๋žŒ๋‹ค์— ๋ ˆ์ด๋ธ”์„ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  ๋žŒ๋‹ค๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š” ์ธ๋ผ์ธ ํ•จ์ˆ˜์˜ ์ด๋ฆ„์„ return ๋’ค์— ๋ ˆ์ด๋ธ”๋กœ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค. ๋žŒ๋‹ค ์‹์˜ ๋ ˆ์ด๋ธ”์„ ๋ช…์‹œํ•˜๋ฉด ๋žŒ๋‹ค์‹์— ๋ ˆ์ด๋ธ”์„ 2๊ฐœ ์ด์ƒ ๋„ฃ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜์ด๋ฆ„์„ ๋ ˆ์ด๋ธ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

people.forEach {
        if (it.name == "Alice") return@forEach
    }
 

๋ฌด๋ช… ํ•จ์ˆ˜: ๊ธฐ๋ณธ์ ์œผ๋กœ ๋กœ์ปฌ return

๋ฌด๋ช… ํ•จ์ˆ˜๋Š” ์ฝ”๋“œ ๋ธ”๋ก์„ ํ•จ์ˆ˜์— ๋„˜๊ธธ ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์ด๋‹ค. ๋žŒ๋‹ค ์•ˆ์—์„œ ์—ฌ๋Ÿฌ๊ฐœ์˜ return@label์ด ํ•„์š”ํ•˜๊ฒŒ ๋˜๋ฉด ๋ณด๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿด ๋•Œ๋Š” ๋ฌด๋ช…ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

fun (person) {
        if (person.name == "Alice") return
        println("${person.name} is not Alice")
    }
 

์ด๋ ‡๊ฒŒ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ์—†๋Š”๊ฒŒ ๋ฌด๋ช…ํ•จ์ˆ˜์ธ๋ฐ

fun lookForAlice(people: List<Person>) {
    people.forEach(fun (person) {
        if (person.name == "Alice") return
        println("${person.name} is not Alice")
    })
}
 

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

์š”์•ฝํ•˜์ž๋ฉด return์„ ์ดํ•ดํ• ๋•Œ๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด fun์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ๋ณด๋Š”๊ฒŒ ํŽธํ•˜๋‹ค.

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•

'๐Ÿ“–๐Ÿ–‹๏ธ > ์ฝ”ํ‹€๋ฆฐ ์ธ ์•ก์…˜๐Ÿ“–' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Kotlin in action - ๋ถ€๋ก E. ์ฝ”๋ฃจํ‹ด๊ณผ Async/Await  (0) 2023.10.13
Kotlin in action(7)  (0) 2023.10.10
Kotlin in action(6)  (0) 2023.10.10
Kotlin in action(5)  (0) 2023.10.10
Kotlin in action(4)  (0) 2023.10.10
COMMENT
 
10
10

Part2 ์ฒซ ์žฅ์ด๋‹ค. ์—ฐ์‚ฐ์ž ์˜ค๋ฒ„๋กœ๋”ฉ, ๋ฉ”์„œ๋“œ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜, ์œ„์ž„ ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ๋‚ด์šฉ์ด ๋‚˜์˜จ๋‹ค.

operator

์—ฐ์‚ฐ์ž ์˜ค๋ฒ„๋กœ๋”ฉ์ด๋‹ค.

data class Point(val x: Int, val y: Int)

operator fun Point.plus(other: Point): Point {
    return Point(x + other.x, y + other.y)
}
 

์‚ฌ์šฉํ•  ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์˜ ํ™•์žฅํ•จ์ˆ˜๋กœ ์—ฐ์‚ฐ์ž ์˜ค๋ฒ„๋กœ๋”ฉ์„ ๋งŒ๋“ค์—ˆ๋‹ค. + ์—ฐ์‚ฐ์€ plusํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ปดํŒŒ์ผ๋˜๊ธฐ ๋•Œ๋ฌธ์— operator๋ฅผ ์•ˆ๋ถ™์ธ๋‹ค๋ฉด ๋ฏธ๋ฆฌ ์˜ˆ์•ฝ๋œ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ธ plus๊ฐ€ ๊ฒน์นœ๋‹ค๊ณ  ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ฝ”ํ‹€๋ฆฐ์€ ๋ฏธ๋ฆฌ ์ง€์ •๋œ ์—ฐ์‚ฐ์ž๋งŒ ์˜ค๋ฒ„๋กœ๋”ฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, times(๊ณฑ์…ˆ), div(๋‚˜๋ˆ—์…ˆ), mod(๋‚˜๋จธ์ง€์—ฐ์‚ฐ), plus, minus ๊ฐ€ ์žˆ๋‹ค.

๋ฐ˜ํ™˜ ํƒ€์ž…์ด ์ˆ˜์‹ ๊ฐ์ฒด์™€ ๋‹ฌ๋ผ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

operator fun Char.times(count: Int): String {
    return toString().repeat(count)
}
 

ํ•œ๊ฐ€์ง€ ์œ ์˜ํ•  ๊ฑด ์ฝ”ํ‹€๋ฆฐ์€ ๊ตํ™˜๋ฒ•์น™์„ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— 1.5*p, p*1.5๋Š” ๋‹ค๋ฅธ ์—ฐ์‚ฐ์ž๋ฅผ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ •์˜๋œ ๋ณ€์ˆ˜๊ฐ€ var์ด๋ผ๋ฉด +=, -= ์™€ ๊ฐ™์€ ๋ณตํ•ฉ๋Œ€์ž…์—ฐ์‚ฐ์ž๋„ ์˜ค๋ฒ„๋กœ๋”ฉ์ด ๋œ๋‹ค. ๋‹ค๋งŒ ๋ฐ˜ํ™˜ํƒ€์ž…์ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ƒˆ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ์ฐธ์กฐ์‹œํ‚ค๋Š”๋ฐ, ์›๋ž˜ ๊ฐ’์˜ ๋‚ด๋ถ€๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Unit์œผ๋กœ ๋ฐ˜ํ™˜๊ฐ’์„ ์ฃผ๋Š” -Assign ์—ฐ์‚ฐ์žํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋กœ๋”ฉํ•˜๋ฉด๋œ๋‹ค.

๋ฌธ์ œ๋Š” plus์™€ plusAssign์„ ๋™์‹œ์— ์ •์˜ํ–ˆ๋‹ค๋ฉด, var ๋ณ€์ˆ˜์˜ ๊ฒฝ์šฐ ๋ชจํ˜ธ์„ฑ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฟœ๋Š”๋‹ค. ๋”ฐ๋ผ์„œ val๋กœ ์ง€์ •ํ•ด์„œ ์“ฐ๊ฑฐ๋‚˜, ๋™์‹œ์— ์ •์˜ํ•˜๋Š” ์ผ์„ ํ”ผํ•ด์•ผํ•œ๋‹ค.

 

๋‹จํ•ญ์—ฐ์‚ฐ๋„ ์˜ค๋ฒ„๋กœ๋”ฉ์ด ๋˜๋Š”๋ฐ, unaryPlus, unaryMinus, inc, dec, not ์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋‹จํ•ญ์ด๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ์—†๋‹ค.

operator fun Point.unaryMinus(): Point {
    return Point(-x, -y)
}
 

์ฆ๊ฐ์—ฐ์‚ฐ์ž์ธ inc, dec์„ ๋ณด๋ฉด

operator fun BigDecimal.inc() = this + BigDecimal.ONE
 

this๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์Œ์€ ๋น„๊ต ์—ฐ์‚ฐ์ž์ธ๋ฐ, equals๋‚˜ compareTo๋ฅผ ์˜ค๋ฒ„๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. equals๋Š” Any์— ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— override๋ฅผ ๋ถ™์—ฌ์„œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

class Point(val x: Int, val y: Int) {
    override fun equals(obj: Any?): Boolean {
        if (obj === this) return true
        if (obj !is Point) return false
        return obj.x == x && obj.y == y
    }
}
 

===๋ฅผ ์ด์šฉํ•ด ๋ฉ”๋ชจ๋ฆฌ ๊ฐ’์„ ๋น„๊ตํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์˜ค๋ฒ„๋ผ์ด๋”ฉ์ด ์•ˆ๋œ๋‹ค. ๋”ฐ๋ผ์„œ if (obj === this) return true ์ด๋ ‡๊ฒŒ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

compareTo๋Š” Comparable ์ธํ„ฐํŽ˜์ด์Šค์— ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ์‚ฌ์šฉํ•  ํด๋ž˜์Šค์—์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์•ผํ•œ๋‹ค.

class Person(
        val firstName: String, val lastName: String
) : Comparable<Person> {

    override fun compareTo(other: Person): Int {
        return compareValuesBy(this, other,
            Person::lastName, Person::firstName)
    }
}
 

Comparable<T> ์ด๊ธฐ ๋•Œ๋ฌธ์— ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค˜์•ผํ•œ๋‹ค.

Collection์—์„œ in์€ contains์— ๋Œ€์‘ํ•œ๋‹ค. ์ปฌ๋ ‰์…˜ ์•ˆ์˜ ์›์†Œ์ค‘ ํ•„์š”ํ•œ ๊ฐ’์˜ ์œ ๋ฌด๋ฅผ ์ฐพ์œผ๋ ค๋ฉด in์„ ์“ฐ๋ฉด ๋œ๋‹ค. for์—์„œ ์‚ฌ์šฉํ•˜๋Š” in์€ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ์–ป๋Š” ๊ฒƒ์ด๋ผ์„œ ์˜๋ฏธ๊ฐ€ ๋‹ค๋ฅด๋‹ค.

๋‹ซํžŒ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ• ๋•Œ๋งŒ๋“œ๋Š” ..๋Š” rangeTo์— ๋Œ€์‘ํ•œ๋‹ค. Comparable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์ƒํƒœ๋ผ๋ฉด rangeTo๋Š” ์ž๋™์œผ๋กœ ์ง€์›๋˜๋ฏ€๋กœ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ฑ…์„ ๋ณด๋‹ค๊ฐ€ ์ด๊ฑฐ๋Š” ์ข€ ํ—ท๊ฐˆ๋ฆด๋งŒํ•œ ๊ฒƒ ๊ฐ™์•„์„œ ๊ฐ€์ ธ์™”๋‹ค

0..n+1
0..(n+1)
 

๋‘ ๋ฒ”์œ„๋Š” ๊ฐ™๊ฒŒ ๋‚˜์˜จ๋‹ค. rangeTo์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ๊ธฐ ๋•Œ๋ฌธ์— ๊ด„ํ˜ธ๋ฅผ ์ณ์ค„ ํ•„์š”๋Š” ์—†์ง€๋งŒ ๊ฐ€๋…์„ฑ์„ ์œ„ํ•ด ๊ด„ํ˜ธ๋ฅผ ์“ฐ๊ฑฐ๋‚˜, ๋„์–ด์“ฐ๊ธฐ๋ฅผ ํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹๋‹ค.

destructuring๋„ ๋Œ€์‘ํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค.์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ผ ์ฝ”๋“œ์—์„œ ๋ณด์ด์ง€๋Š” ์•Š๋Š” componentN์ธ๋ฐ N์€ ๋ณ€์ˆ˜ ์œ„์น˜์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ๊ฐ’์ด๋‹ค. ๋ฌดํ•œํžˆ componentN์„ ์„ ์–ธํ• ์ˆ˜๋Š” ์—†๊ณ , 5๊ฐœ๊นŒ์ง€ ๊ฐ€๋Šฅํ•˜๋‹ค.

5๊ฐœ๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด

Destructuring declaration initializer of type List must have a 'component6()' function

์ด๋Ÿฐ ์—๋Ÿฌ๋ฌธ์„ ๋ณด์—ฌ์ค€๋‹ค.

for ((key, value) in map) {
        println("$key -> $value")
    }
 

์ด๋Ÿฐ ๊ตฌ์กฐ๋ถ„ํ•ด์„ ์–ธ์€ ๋ฃจํ”„์™€ map์„ ๊ฐ™์ด ์“ธ๋•Œ ์žฅ์ ์ด ๊ทน๋Œ€ํ™”๋œ๋‹ค. ์ž๋ฐ”์—์„œ๋Š” ์•ˆ๋˜๋˜ map ์ดํ„ฐ๋ ˆ์ด์…˜์ด ์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค.

์œ„์ž„ ํ”„๋กœํผํ‹ฐ delegated property

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

class Foo{
  private val delegate = Delegate()
  var p: Type
  set(value: Type) = delegate.setValue(..., value)
  get() = delegate.getValue(...)
}
 

์ ‘๊ทผ์€ p์— ํ•˜์ง€๋งŒ, ์‹ค์ œ๋กœ ๊ฐ’ ์ฒ˜๋ฆฌ๋Š” delegate์—์„œ ํ•˜๋Š” ๋ชจ์Šต์ด๋‹ค. ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์ด viewmodel์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•œ๋ฐ

private val _answerId = MutableLiveData<String?>()
val answerId: LiveData<String?> = _answerId
 

backing property๋ผ๋Š” ๊ธฐ๋ฒ•์ด๋‹ค.

java.beans์— ์žˆ๋Š” PropertyChangeSupport์™€ setter์˜ field๋ฅผ ์กฐํ•ฉํ•ด์„œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์œ„์ž„ํ”„๋กœํผํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋งŒ๋“ค์–ด๋ณด๊ฒ ๋‹ค.

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)

    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }

    fun removePropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.removePropertyChangeListener(listener)
    }
}
class ObservableProperty( // observer๋ฅผ ๊ตฌํ˜„ํ•ด๋‘” ๊ฒƒ.
    var propValue: Int, val changeSupport: PropertyChangeSupport
) {
    operator fun getValue(p: Person, prop: KProperty<*>): Int = propValue

    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}
 

๋„์šฐ๋ฏธ ํด๋ž˜์Šค์ธ๋ฐ, getValue, setValue๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•ด์„œ ์ ‘๊ทผ์ž๋ฅผ ๊ฐœ์กฐํ–ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ• ๋ง๊ณ  Delegates.observable์„ ์“ฐ๋Š” ๋ฐฉ๋ฒ•์ด ๋” ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋‚˜ ๋ณด๊ธฐ ์œ„ํ•ด ์ด ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์™”๋‹ค. ์ด๋•Œ ์‚ฌ์šฉ๋œ KProperty๋Š” ์ฝ”ํ‹€๋ฆฐ์—์„œ ํ”„๋กœํผํ‹ฐ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ํƒ€์ž…์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด get, set์„ ํ†ตํ•ด ๊ฐ„๋‹จํžˆ ๊ฐ’์„ ์˜ต์ €๋น™ํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค. by๋ฅผ ์‚ฌ์šฉํ•ด ์œ„์ž„ ๊ฐ์ฒด๋กœ ์ง€์ •ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ด์ „์— getter, setter์—์„œ field๋กœ ์ ‘๊ทผํ•ด ์ผ์ผ์ด ์—ฐ๊ฒฐํ•ด์ค˜์•ผํ–ˆ๋˜ ๊ฑธ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•ด์ค€๋‹ค.

var age: Int by ObservableProperty(age, changeSupport) // Delegates.observable(age, changeSupport)
var salary: Int by ObservableProperty(salary, changeSupport)
 

์ด๋•Œ ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ๋Š” ObservablePropertyํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ age์˜ ํ”„๋กœํผํ‹ฐ์— ์ €์žฅํ•˜๊ณ  ์ด๊ฑธ delegate, ํ”„๋กœํผํ‹ฐ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ KProperty๋Š” property๋กœ ์ƒ์„ฑํ•œ๋‹ค.

์ด๋ฒˆ์žฅ์€ ๋ญ”๊ฐ€ ์ •๋ณด์˜ ๋‚˜์—ด๋“ค์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ ๋‹ค.

"๋Œ“๊ธ€, ๊ณต๊ฐ ๋ฒ„ํŠผ ํ•œ ๋ฒˆ์”ฉ ๋ˆ„๋ฅด๊ณ  ๊ฐ€์ฃผ์‹œ๋ฉด ํฐ ํž˜์ด ๋ฉ๋‹ˆ๋‹ค"
๋ฐ˜์‘ํ˜•

'๐Ÿ“–๐Ÿ–‹๏ธ > ์ฝ”ํ‹€๋ฆฐ ์ธ ์•ก์…˜๐Ÿ“–' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Kotlin in action - ๋ถ€๋ก E. ์ฝ”๋ฃจํ‹ด๊ณผ Async/Await  (0) 2023.10.13
Kotlin in action(8)  (0) 2023.10.10
Kotlin in action(6)  (0) 2023.10.10
Kotlin in action(5)  (0) 2023.10.10
Kotlin in action(4)  (0) 2023.10.10
COMMENT