Slide 1

Slide 1 text

,05-*/$0/' *4#"$, BGUFSUIFQBSUZ 박 용 권

Slide 2

Slide 2 text

੗೐݂֙ର ௏೐݂ѐਘର commit 543f02d23 Date: Sun Oct 10 19:34:30 2021 +0900 Add generated projects with spring and kotlin commit 226a51966 Date: Wed Oct 13 16:31:57 2021 +0900 Add Dockerfile and deploy manifest file commit 1d0525b88 Date: Wed Oct 13 18:31:41 2021 +0900 Make github actions commit 7476a1192 Date: Sat Oct 23 20:13:09 2021 +0900 Add servlet-filter based authentication processing commit 6390a5935 Date: Sat Oct 23 20:44:12 2021 +0900 Add meetup creation process and web-api ߅ਊӂ.apply { ࣗࣘ = "׼Ӕ݃௄" ౠ૚ = "݈ ݆਺, nߓࣘ ੤ࢤ" }

Slide 3

Slide 3 text

যցف ঠաف Github projects use Kotlin as their primary language Increase in Multiplatform libraries in 2002 of Android users are satisfied with Kotlin of the top 1,000 Android apps use Jetpack Compose of all kotlin users are satisfied with Kotlin of the top 1,000 Android apps are in Kotlin 1M 86 95 60 97 23 of the all daangn projects use Multiplatform 1 of the all daangn projects use Kotlin 10

Slide 4

Slide 4 text

فߓ؊ࡅܲ,ஹ౵ੌ۞о ,PUMJOਵ۽ য়Ҋ੓যਃ https://techblog.woowahan.com/2532/

Slide 5

Slide 5 text

,ஹ౵ੌ۞ܳ݅ա۰ݶCVJMEHSBEMFLUTܳ଺ইоࣁਃ https://techblog.woowahan.com/2532/ // 1.8.20 ੉࢚ kotlin { sourceSets.all { languageSettings { languageVersion = "2.0" } } } // 1.8.0 ੉ೞ tasks.compileKotlin { kotlinOptions { useK2 = true } } tasks.compileTestKotlin { kotlinOptions { useK2 = true } } tasks.withType { kotlinOptions { ... } } tasks.named("compileKotlin", org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile::class.java) { kotlinOptions { ... } } 4BNFBT

Slide 6

Slide 6 text

؊਌׳௓ೞҊ߄࢏ೠ࢜۽਍ޙߨ4ZOUBY ੉ળ࠺غҊ੓ؘਃ Explicit Fields Context Receivers Name Based Destructuring Collection Literals Static Extensions

Slide 7

Slide 7 text

4UBUJD&YUFOTJPOT,5 val courses = File.readCSV(pathname = "inflearn/courses.csv") https://youtrack.jetbrains.com/issue/KT-11968

Slide 8

Slide 8 text

4UBUJD&YUFOTJPOT,5 val courses = File.readCSV(pathname = "inflearn/courses.csv") fun File.Companion.readCSV(pathname: String): File { // processing } https://youtrack.jetbrains.com/issue/KT-11968

Slide 9

Slide 9 text

4UBUJD&YUFOTJPOT,5 val courses = File.readCSV(pathname = "inflearn/courses.csv") fun File.Companion.readCSV(pathname: String): File { // processing } https://youtrack.jetbrains.com/issue/KT-11968

Slide 10

Slide 10 text

4UBUJD&YUFOTJPOT,5 val courses = File.readCSV(pathname = "inflearn/courses.csv") fun File.Companion.readCSV(pathname: String): File { // processing } fun File.static.readCSV(pathname: String): File { // processing } https://youtrack.jetbrains.com/issue/KT-11968

Slide 11

Slide 11 text

&YQMJDJU'JFMET,5 class Meetup { val members: Set get() = _members.toSet() private val _members = mutableSetOf() fun join(member: Member): Meetup { _members.add(member) return this } // methods }

Slide 12

Slide 12 text

&YQMJDJU'JFMET,5 class Meetup { val members: Set get() = _members.toSet() private val _members = mutableSetOf() fun join(member: Member): Meetup { _members.add(member) return this } // methods } class Meetup { val member: Set field = mutableSetOf() // methods }

Slide 13

Slide 13 text

$POUFYU3FDFJWFST,5 fun processRequest(context: ServiceContext, request: ServiceRequest) { val payload = request.loadPayload(context) // processing } context(ServiceContext) fun processRequest(request: ServiceRequest) { val payload = request.loadPayload() // processing } context(ServiceContext) fun ServiceRequest.loadPayload(): Payload { // returning payload }

Slide 14

Slide 14 text

/BNF#BTFE%FTUSVDUVSJOH,5 data class Person( val firstName: String, val lastName: String ) val (firstName, lastName) = Person("Brie", "Park")

Slide 15

Slide 15 text

/BNF#BTFE%FTUSVDUVSJOH,5 data class Person( val firstName: String, val lastName: String ) val (firstName, lastName) = Person("Brie", "Park") val (lastName, firstName) = Person("Heidi", "Oh")

Slide 16

Slide 16 text

$PMMFDUJPO-JUFSBMT,5 val courses = listOf("੗߄ ѐߊ੗ܳ ਤೠ ௏ౣܽ ੑޙ", "Jetpack Compose ੑޙ")

Slide 17

Slide 17 text

$PMMFDUJPO-JUFSBMT,5 val courses = listOf("੗߄ ѐߊ੗ܳ ਤೠ ௏ౣܽ ੑޙ", "Jetpack Compose ੑޙ") val courses = ["੗߄ ѐߊ੗ܳ ਤೠ ௏ౣܽ ੑޙ", "Jetpack Compose ੑޙ"] val scores = MutableSet [5.0, 4.8]

Slide 18

Slide 18 text

௏ౣܽ਷ݽٚ೒ۖಬਸ؀࢚ਵ۽࠺ૉפझ֤ܻܳҕਬ೧ਃ

Slide 19

Slide 19 text

<খҟҊ>௏ౣܽݣ౭೒ۖಬ੉ҾӘೞदભ https://www.inflearn.com/course/%EC%BD%94%ED%8B%80%EB%A6%B0-%EB%A9%80%ED%8B%B0%ED%94%8C%EB%9E%AB%ED%8F%BC

Slide 20

Slide 20 text

Ӿݣ౭೒ۖಬ੉ҍউ੿ചػؘਃ 2020 2021 2023 2024 2022

Slide 21

Slide 21 text

"OESPJE %FTLUPQਸֈযJ04 8FC੄৔৉ө૑$PNQPTFೞࣁਃ

Slide 22

Slide 22 text

@Composable internal fun GalleryScreen( galleryPage: GalleryPage, photoGallery: PhotoGallery, dependencies: Dependencies, onClickPreviewPicture: (PictureData) -> Unit, onMakeNewMemory: () -> Unit ) { val pictures = dependencies.pictures var selected: PictureData by remember { mutableStateOf(pictures.first()) } LaunchedEffect(Unit) { ... } Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) { ... } ... } @Composable private fun SquaresGalleryView( images: List, selectedImage: PictureData, onSelect: (PictureData) -> Unit, ) { LazyVerticalGrid( modifier = Modifier.padding(top = 4.dp), columns = GridCells.Adaptive(minSize = 130.dp), verticalArrangement = Arrangement.spacedBy(1.dp), horizontalArrangement = Arrangement.spacedBy(1.dp) ) { ... } } ... $PNQPTF.VMUJQMBUGPSN6*೐ۨ੐ਕ௼ܳࣗѐೡѱਃ https://www.jetbrains.com/lp/compose-multiplatform/

Slide 23

Slide 23 text

8FC"TTFNCMZоޤջҳਃ https://blog.logrocket.com/webassembly-how-and-why-559b7f96cd71/ C , C or Kotlin, Rust...

Slide 24

Slide 24 text

ؘݽܳળ࠺೮૑݅അ੢ৈѤ੉ৈ੄஖ঋই੢಴৬৔࢚ਵ۽ࠁৈ٘۰ਃ ਫ਼द௏٘ખࠁ۞оपѱਃ

Slide 25

Slide 25 text

HSBEMFQSPQFSUJFT # Kotlin kotlin.code.style=official # MPP kotlin.mpp.stability.nowarn=true kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.androidSourceSetLayoutVersion=2 # Native kotlin.native.cacheKind=none kotlin.native.useEmbeddableCompilerJar=true kotlin.native.cocoapods.generate.wrapper=true kotlin.native.binary.memoryModel=experimental # Compose org.jetbrains.compose.experimental.uikit.enabled=true org.jetbrains.compose.experimental.macos.enabled=true org.jetbrains.compose.experimental.jscanvas.enabled=true # Android android.useAndroidX=true android.compileSdk=33 android.targetSdk=33 android.minSdk=24 # Versions jdk.version=11 kotlin.version=1.8.20 compose.version=1.4.0-dev-wasm06 ktor.version=2.3.0 agp.version=7.4.2

Slide 26

Slide 26 text

DPNNPOCVJMEHSBEMFLUT plugins { kotlin("multiplatform") kotlin("native.cocoapods") id("com.android.library") id("org.jetbrains.compose") } kotlin { android() iosX64() iosArm64() iosSimulatorArm64() jvm("desktop") { ... } js(IR) { ... } wasm { ... } cocoapods { ... } sourceSets { val commonMain by getting { dependencies { implementation(compose.runtime) ... implementation("io.ktor:ktor-client-core:$ktorVersion") } } val commonTest by getting { ... } val androidMain by getting val androidInstrumentedTest by getting val iosMain by creating val desktopMain by getting val desktopTest by getting val wasmMain by getting val wasmTest by getting ... } } android { ... }

Slide 27

Slide 27 text

DPNNPOBQQMJDBUJPO(SFFUJOH0QFSBUJPOTLU package kotlinconf.application import kotlinconf.net.URI import kotlinconf.net.http.ClientHttpRequestFactory import kotlinconf.net.http.HttpMethod interface GreetingOperations { fun greeting(username: String): String } class LocalGreetingOperations : GreetingOperations { override fun greeting(username: String): String { return "Hello, $username" } } class RemoteGreetingOperations( private val clientHttpRequestFactory: ClientHttpRequestFactory, private val serverSchema: String = "http", private val serverHost: String = "localhost", private val serverPort: Int = 8080, ) : GreetingOperations { override fun greeting(username: String): String { val request = clientHttpRequestFactory.createRequest( uri = URI.parse("$s..Schema://$s..Host:$s..Port$PATH?username=$username"), httpMethod = HttpMethod.GET() ) return request.execute().body.decodeToString() } companion object { private const val PATH = "/greeting" } }

Slide 28

Slide 28 text

DPNNPOVJ(SFFUJOH$PNQPTF"QQLU package kotlinconf.ui import androidx.compose..* import kotlinconf.application.GreetingOperations import kotlinconf.getPlatformName @Composable fun GreetingComposeApp( greetingOperations: GreetingOperations, modifier: Modifier ) { val platformName = getPlatformName() Box(modifier = modifier, contentAlignment = Alignment.Center) { Column(modifier = Modifier.wrapContentSize()) { val greetingText = remember { mutableStateOf("Welcome...") } WriteBox( onSearchClicked = { username -> greetingText.value = try { greetingOperations.greeting(username = username) } catch (error: Throwable) { if (error.message == null) { "An unknown error occurred" } else { "Error: ${error.message?.lowercase()}" } } } ) TextPanel(text = greetingText) } } } @Composable private fun WriteBox(onSearchClicked: (String) -> Unit) { ... } @Composable private fun TextPanel(text: MutableState) { ... }

Slide 29

Slide 29 text

TFSWFS4FSWFS"QQMJDBUJPOLU package kotlinconf import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.support.beans import org.springframework.web.servlet.config.annotation.CorsRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer @SpringBootApplication class ServerApplication fun main(args: Array) { val applicationBeans = beans { } val webBeans = beans { bean { object : WebMvcConfigurer { override fun addCorsMappings(registry: CorsRegistry) { registry .addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*") } } } } runApplication(*args) { addInitializers(applicationBeans, webBeans) } }

Slide 30

Slide 30 text

TFSWFSXFC(SFFUJOH3FTU$POUSPMMFSLU package kotlinconf.web import kotlinconf.application.GreetingOperations import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController class GreetingRestController : GreetingOperations { @GetMapping("/greeting") override fun greeting(@RequestParam("username") username: String): String { return "Hello, $username" } }

Slide 31

Slide 31 text

BOESPJE"OESPJE"QQMJDBUJPOLU package kotlinconf import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.MaterialTheme import androidx.compose.ui.Modifier import kotlinconf.application.RemoteGreetingOperations import kotlinconf.net.http.DefaultClientHttpRequestFactory import kotlinconf.ui.GreetingComposeApp class AndroidApplication : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MaterialTheme { GreetingComposeApp( greetingOperations = RemoteGreetingOperations( clientHttpRequestFactory = DefaultClientHttpRequestFactory(), serverHost = "10.0.2.2" ), modifier = Modifier.fillMaxSize() ) } } } }

Slide 32

Slide 32 text

EFTLUPQ%FTLUPQ"QQMJDBUJPOLU package kotlinconf import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.compose.ui.window.Window import androidx.compose.ui.window.application import kotlinconf.application.RemoteGreetingOperations import kotlinconf.net.http.DefaultClientHttpRequestFactory import kotlinconf.ui.GreetingComposeApp fun main() = application { Window(title = "Compose for Desktop", onCloseRequest = ::exitApplication) { GreetingComposeApp( greetingOperations = RemoteGreetingOperations( clientHttpRequestFactory = DefaultClientHttpRequestFactory() ), modifier = Modifier.fillMaxSize() ) } }

Slide 33

Slide 33 text

XFC8FC"QQMJDBUJPOLU package kotlinconf import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.window.CanvasBasedWindow import kotlinconf.application.RemoteGreetingOperations import kotlinconf.net.http.DefaultClientHttpRequestFactory import kotlinconf.ui.GreetingComposeApp @OptIn(ExperimentalComposeUiApi::class) fun main() { CanvasBasedWindow { GreetingComposeApp( greetingOperations = RemoteGreetingOperations( clientHttpRequestFactory = DefaultClientHttpRequestFactory() ), modifier = Modifier.fillMaxSize() ) } }

Slide 34

Slide 34 text

XFCJOEFYIUNM Compose for Web with WebAssembly --------------------------------------------------------------------------------------- # load.mjs import { instantiate } from './kotlinconf-is-back-web-wasm.uninstantiated.mjs'; await wasmSetup; instantiate({ skia: Module['asm'] });

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

J04 ૑ӘࠗఠJ04

Slide 37

Slide 37 text

DPNNPOJPTNBJOJPTLU import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.ui.Modifier import androidx.compose.ui.window.ComposeUIViewController import kotlinconf.application.RemoteGreetingOperations import kotlinconf.net.http.DefaultClientHttpRequestFactory import kotlinconf.ui.GreetingComposeApp import platform.UIKit.UIViewController fun MainViewController() : UIViewController = ComposeUIViewController { GreetingComposeApp( greetingOperations = RemoteGreetingOperations( clientHttpRequestFactory = DefaultClientHttpRequestFactory(), serverHost = "127.0.0.1" ), modifier = Modifier.fillMaxSize() ) }

Slide 38

Slide 38 text

DPNNPOCVJMEHSBEMFLUT plugins { kotlin("multiplatform") kotlin("native.cocoapods") id("com.android.library") id("org.jetbrains.compose") } kotlin { android() iosX64() iosArm64() iosSimulatorArm64() jvm("desktop") { ... } js(IR) { ... } wasm { ... } cocoapods { version = "1.0.0" summary = "common module for kotlinconf multiplatform apps" homepage = "https://kotlinconf.com/" ios.deploymentTarget = "14.1" podfile = project.file("../ios/Podfile") framework { baseName = "common" isStatic = true } extraSpecAttributes["resources"] = "['src/commonMain/resources/**']" } sourceSets { ... val iosMain by creating ... } } android { ... }

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

੉ઁCVJMEHSBEMFLUTܳݢ੷݅աࠁࣁਃ https://blog.gradle.org/kotlin-dsl-is-now-the-default-for-new-gradle-builds

Slide 42

Slide 42 text

,PUMJO௏٘ఐ࢝߂प೷ਸਤೠ؀ചഋਕ௼द౟ੋ௏ౣ֢ܽ౟࠘ਸࣗѐೡѱਃ https://plugins.jetbrains.com/plugin/16340-kotlin-notebook

Slide 43

Slide 43 text

(PPHMFß⃛ ,PUMJO 70 Google apps built and counting

Slide 44

Slide 44 text

௏ౣܽ੤ױ੉࢜۽਍धҳܳݏ੉೧ਃ

Slide 45

Slide 45 text

,PUMJO$POGীળ࠺ػ੉ঠӝࠁٮܻחޤо੓ਸөਃ <> -BOHVBHFBOE1SPHSBNNJOH <> .VMUJQMBUGPSNBOE$PNQPTF <>8FCBOE#BDLFOE <>&DPTZTUFN

Slide 46

Slide 46 text

&/%

Slide 47

Slide 47 text

ଵҊ੗ܐ KotlinConf 23 Keynote KotlinConf 2023 A Look at the Opening Keynote