Slide 1

Slide 1 text

Kotlin, Gradle 그리고 AWS SAM 허재위(@importre), Riiid! ,PUMJO/JHIU4FPVM

Slide 2

Slide 2 text

About Me ˖ 핺퓒!JNQPSUSF ˖ 윊핂슪$PGPVOEFS ˖ "OESPJE <->#BDLFOE ˖ ,PUMJO,PSFB

Slide 3

Slide 3 text

이야기 할 내용 ˖ ,PUMJO (SBEMF믆읺몮4FSWFSMFTT"QQMJDBUJPO.PEFM4".픒핂푷 컪"84-BNCEB (BUFXBZ"1*읊짾쫓삖삲 ˖ 짾"1*읊칺푷쁢$-*팿픒잚슲펂쫓삖삲 ˖ 숞옪헫펞컪벦튾쁢슪읊빦옪쫓삖삲 펂썲뮪졶펞컪슮핞3&45짝8FC4PDLFU"1*읊캫컿 멚킪 퓮힎뫎읺 졶삖잏짝쫂쿦핖멚훊쁢"84컪찒큲 컪쩒읊뫎읺힎팘몮슪읊킲쿦핖멚훊쁢컪찒큲 푢킪펞잚킲  4"."84캏펞컪컪쩒읺큲팿픒잚슲믾퓒칺푷쿦핖쁢폲콚큲엖핒풚

Slide 4

Slide 4 text

그리고, Build Script 역시 Kotlin DSL

Slide 5

Slide 5 text

그림으로 보면 HSFFUJOH 'VODUJPO $-*

Slide 6

Slide 6 text

개발환경 ˖ +%, ˖ (SBEMF9 ˖ %PDLFS ˖ "84 ˖ $-*BQQT BXTDMJ BXTTBNDMJ ˖ $POHVSBUJPO$SFEFOUJBM'JMFT

Slide 7

Slide 7 text

진행 순서  옪헫캫컿  "1*(BUFXBZ)BOEMFS잚슲믾  4".tempalte.yml핟컿  옪큲  "84펞짾  묺"1*읊핂푷쁢$-*팿잚슲믾 뫃슪뫃퓮

Slide 8

Slide 8 text

프로젝트 생성

Slide 9

Slide 9 text

프로젝트 생성 › # ೐۽ં౟ ٣۩షܻ ࢤࢿ ߂ ੉ز › mkdir kotlin-night-seoul && cd $_ › # Gradle ೐۽ં౟ ୡӝച › gradle init --type basic --dsl kotlin Project name (default: kotlin-night-seoul): › # ࢲ࠳ ݽٕ `function` ࢤࢿ › mkdir function && touch "$_/build.gradle.kts"

Slide 10

Slide 10 text

프로젝트 생성 › # ೐۽ં౟ ٣۩షܻ ࢤࢿ ߂ ੉ز › mkdir kotlin-night-seoul && cd $_ › # Gradle ೐۽ં౟ ୡӝച › gradle init --type basic --dsl kotlin Project name (default: kotlin-night-seoul): › # ࢲ࠳ ݽٕ `function` ࢤࢿ › mkdir function && touch "$_/build.gradle.kts"

Slide 11

Slide 11 text

프로젝트 생성 › # ೐۽ં౟ ٣۩షܻ ࢤࢿ ߂ ੉ز › mkdir kotlin-night-seoul && cd $_ › # Gradle ೐۽ં౟ ୡӝച › gradle init --type basic --dsl kotlin Project name (default: kotlin-night-seoul): › # ࢲ࠳ ݽٕ `function` ࢤࢿ › mkdir function && touch "$_/build.gradle.kts"

Slide 12

Slide 12 text

프로젝트 생성 › # ೐۽ં౟ ٣۩షܻ ࢤࢿ ߂ ੉ز › mkdir kotlin-night-seoul && cd $_ › # Gradle ೐۽ં౟ ୡӝച › gradle init --type basic --dsl kotlin Project name (default: kotlin-night-seoul): › # ࢲ࠳ ݽٕ `function` ࢤࢿ › mkdir function && touch "$_/build.gradle.kts"

Slide 13

Slide 13 text

프로젝트 생성 › # ೐۽ં౟ ٣۩షܻ ࢤࢿ ߂ ੉ز › mkdir kotlin-night-seoul && cd $_ › # Gradle ೐۽ં౟ ୡӝച › gradle init --type basic --dsl kotlin Project name (default: kotlin-night-seoul): › # ࢲ࠳ ݽٕ `function` ࢤࢿ › mkdir function && touch "$_/build.gradle.kts"

Slide 14

Slide 14 text

프로젝트 구조 . ├── build.gradle.kts ├── function │ └── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts

Slide 15

Slide 15 text

프로젝트 구조 . ├── build.gradle.kts ├── function │ └── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts

Slide 16

Slide 16 text

프로젝트 구조 . ├── build.gradle.kts ├── function │ └── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts

Slide 17

Slide 17 text

프로젝트 구조 . ├── build.gradle.kts ├── function │ └── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts

Slide 18

Slide 18 text

프로젝트 구조 . ├── build.gradle.kts ├── function │ └── build.gradle.kts ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts

Slide 19

Slide 19 text

API Gateway Handler 만들기

Slide 20

Slide 20 text

서브 프로젝트 설정 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include(":function")

Slide 21

Slide 21 text

서브 프로젝트 설정 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include(":function")

Slide 22

Slide 22 text

핸들러 만들기 멾뫊쭎쫂졂 › http get http: //localhost:3000/greeting HTTP/1.0 200 OK Content-Length: 47 Content-Type: application/json { "message": "Kotlin Night Seoul, Hello!" }

Slide 23

Slide 23 text

핸들러 만들기 functions/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.amazonaws:aws-lambda-java-core:1.2.0") implementation("com.amazonaws:aws-lambda-java-events:2.2.5") implementation("com.amazonaws:aws-lambda-java-log4j2:1.0.0") testImplementation(kotlin("test-junit")) }

Slide 24

Slide 24 text

핸들러 만들기 functions/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.amazonaws:aws-lambda-java-core:1.2.0") implementation("com.amazonaws:aws-lambda-java-events:2.2.5") implementation("com.amazonaws:aws-lambda-java-log4j2:1.0.0") testImplementation(kotlin("test-junit")) }

Slide 25

Slide 25 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt class GreetingHandlerTest { private lateinit var app: GreetingHandler @BeforeTest fun setup() { app = GreetingHandler() } @Test fun testSuccessfulResponse() { // ... } }

Slide 26

Slide 26 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt class GreetingHandlerTest { private lateinit var app: GreetingHandler @BeforeTest fun setup() { app = GreetingHandler() } @Test fun testSuccessfulResponse() { // ... } }

Slide 27

Slide 27 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt class GreetingHandlerTest { private lateinit var app: GreetingHandler @BeforeTest fun setup() { app = GreetingHandler() } @Test fun testSuccessfulResponse() { // ... } }

Slide 28

Slide 28 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt class GreetingHandlerTest { private lateinit var app: GreetingHandler @BeforeTest fun setup() { app = GreetingHandler() } @Test fun testSuccessfulResponse() { // ... } }

Slide 29

Slide 29 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt @Test fun testSuccessfulResponse() { val result = app.handleRequest(null, null) assertEquals(result.statusCode, 200) assertEquals(result.headers["Content-Type"], "application/json") result.body ?.let { content -> //language=JSON assertEquals( expected = """ { "message": "Kotlin Night Seoul, Hello!" } """.trimIndent(), actual = content ) } }

Slide 30

Slide 30 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt @Test fun testSuccessfulResponse() { val result = app.handleRequest(null, null) assertEquals(result.statusCode, 200) assertEquals(result.headers["Content-Type"], "application/json") result.body ?.let { content -> //language=JSON assertEquals( expected = """ { "message": "Kotlin Night Seoul, Hello!" } """.trimIndent(), actual = content ) } }

Slide 31

Slide 31 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt @Test fun testSuccessfulResponse() { val result = app.handleRequest(null, null) assertEquals(result.statusCode, 200) assertEquals(result.headers["Content-Type"], "application/json") result.body ?.let { content -> //language=JSON assertEquals( expected = """ { "message": "Kotlin Night Seoul, Hello!" } """.trimIndent(), actual = content ) } }

Slide 32

Slide 32 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt @Test fun testSuccessfulResponse() { val result = app.handleRequest(null, null) assertEquals(result.statusCode, 200) assertEquals(result.headers["Content-Type"], "application/json") result.body ?.let { content -> //language=JSON assertEquals( expected = """ { "message": "Kotlin Night Seoul, Hello!" } """.trimIndent(), actual = content ) } }

Slide 33

Slide 33 text

핸들러 테스트 functions/ .../GreetingHandlerTest.kt @Test fun testSuccessfulResponse() { val result = app.handleRequest(null, null) assertEquals(result.statusCode, 200) assertEquals(result.headers["Content-Type"], "application/json") result.body ?.let { content -> //language=JSON assertEquals( expected = """ { "message": "Kotlin Night Seoul, Hello!" } """.trimIndent(), actual = content ) } }

Slide 34

Slide 34 text

핸들러 만들기 functions/ .../GreetingHandler.kt class GreetingHandler : RequestHandler { override fun handleRequest( input: APIGatewayProxyRequestEvent?, context: Context? ): APIGatewayProxyResponseEvent = APIGatewayProxyResponseEvent() .withStatusCode(200) .withHeaders(headers()) .withBody(body(input ?.queryStringParameters ?: emptyMap())) private fun headers(): Map = mapOf() private fun body(params: Map): String = "" }

Slide 35

Slide 35 text

핸들러 만들기 functions/ .../GreetingHandler.kt class GreetingHandler : RequestHandler { override fun handleRequest( input: APIGatewayProxyRequestEvent?, context: Context? ): APIGatewayProxyResponseEvent = APIGatewayProxyResponseEvent() .withStatusCode(200) .withHeaders(headers()) .withBody(body(input ?.queryStringParameters ?: emptyMap())) private fun headers(): Map = mapOf() private fun body(params: Map): String = "" }

Slide 36

Slide 36 text

핸들러 만들기 functions/ .../GreetingHandler.kt class GreetingHandler : RequestHandler { override fun handleRequest( input: APIGatewayProxyRequestEvent?, context: Context? ): APIGatewayProxyResponseEvent = APIGatewayProxyResponseEvent() .withStatusCode(200) .withHeaders(headers()) .withBody(body(input ?.queryStringParameters ?: emptyMap())) private fun headers(): Map = mapOf() private fun body(params: Map): String = "" }

Slide 37

Slide 37 text

핸들러 만들기 functions/ .../GreetingHandler.kt class GreetingHandler : RequestHandler { override fun handleRequest( input: APIGatewayProxyRequestEvent?, context: Context? ): APIGatewayProxyResponseEvent = APIGatewayProxyResponseEvent() .withStatusCode(200) .withHeaders(headers()) .withBody(body(input ?.queryStringParameters ?: emptyMap())) private fun headers(): Map = mapOf() private fun body(params: Map): String = "" }

Slide 38

Slide 38 text

핸들러 만들기 functions/ .../GreetingHandler.kt class GreetingHandler : RequestHandler { override fun handleRequest( input: APIGatewayProxyRequestEvent?, context: Context? ): APIGatewayProxyResponseEvent = APIGatewayProxyResponseEvent() .withStatusCode(200) .withHeaders(headers()) .withBody(body(input ?.queryStringParameters ?: emptyMap())) private fun headers(): Map = mapOf() private fun body(params: Map): String = "" }

Slide 39

Slide 39 text

핸들러 만들기 functions/ .../GreetingHandler.kt private fun headers() = mapOf( "Content-Type" to "application/json" ) private fun body(params: Map): String = params .getOrDefault("content", "Hello") .let { content -> //language=JSON """ { "message": "Kotlin Night Seoul, ${content.capitalize()}!" } """.trimIndent() }

Slide 40

Slide 40 text

tempalte.yml 작성

Slide 41

Slide 41 text

SAM 템플릿 작성 function/template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS ::Serverless-2016-10-31" Resources: GreetingFunction: Type: "AWS ::Serverless ::Function" Properties: Handler: "GreetingHandler ::handleRequest" CodeUri: "./build/libs/function-all.jar" Events: IndexApi: Type: "Api" Properties: Path: "/greeting" Method: "get" Runtime: "java8" Outputs: ProdEndpoint: Value: !Sub "https: //${ServerlessRestApi}.execute-api.${AWS ::Region}.amazonaws.com/Prod/"

Slide 42

Slide 42 text

SAM 템플릿 작성 function/template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS ::Serverless-2016-10-31" Resources: GreetingFunction: Type: "AWS ::Serverless ::Function" Properties: Handler: "GreetingHandler ::handleRequest" CodeUri: "./build/libs/function-all.jar" Events: IndexApi: Type: "Api" Properties: Path: "/greeting" Method: "get" Runtime: "java8" Outputs: ProdEndpoint: Value: !Sub "https: //${ServerlessRestApi}.execute-api.${AWS ::Region}.amazonaws.com/Prod/"

Slide 43

Slide 43 text

SAM 템플릿 작성 function/template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS ::Serverless-2016-10-31" Resources: GreetingFunction: Type: "AWS ::Serverless ::Function" Properties: Handler: "GreetingHandler ::handleRequest" CodeUri: "./build/libs/function-all.jar" Events: IndexApi: Type: "Api" Properties: Path: "/greeting" Method: "get" Runtime: "java8" Outputs: ProdEndpoint: Value: !Sub "https: //${ServerlessRestApi}.execute-api.${AWS ::Region}.amazonaws.com/Prod/"

Slide 44

Slide 44 text

SAM 템플릿 작성 function/template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS ::Serverless-2016-10-31" Resources: GreetingFunction: Type: "AWS ::Serverless ::Function" Properties: Handler: "GreetingHandler ::handleRequest" CodeUri: "./build/libs/function-all.jar" Events: IndexApi: Type: "Api" Properties: Path: "/greeting" Method: "get" Runtime: "java8" Outputs: ProdEndpoint: Value: !Sub "https: //${ServerlessRestApi}.execute-api.${AWS ::Region}.amazonaws.com/Prod/"

Slide 45

Slide 45 text

SAM 템플릿 작성 function/template.yml AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS ::Serverless-2016-10-31" Resources: GreetingFunction: Type: "AWS ::Serverless ::Function" Properties: Handler: "GreetingHandler ::handleRequest" CodeUri: "./build/libs/function-all.jar" Events: IndexApi: Type: "Api" Properties: Path: "/greeting" Method: "get" Runtime: "java8" Outputs: ProdEndpoint: Value: !Sub "https: //${ServerlessRestApi}.execute-api.${AWS ::Region}.amazonaws.com/Prod/"

Slide 46

Slide 46 text

로컬 테스트

Slide 47

Slide 47 text

로컬 테스트 function/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" id("aws.sam") version "0.1.0" } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.amazonaws:aws-lambda-java-core:1.2.0") implementation("com.amazonaws:aws-lambda-java-events:2.2.5") implementation("com.amazonaws:aws-lambda-java-log4j2:1.0.0") testImplementation(kotlin("test-junit")) } sam { template = file("template.yml") }

Slide 48

Slide 48 text

로컬 테스트 function/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" id("aws.sam") version "0.1.0" } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.amazonaws:aws-lambda-java-core:1.2.0") implementation("com.amazonaws:aws-lambda-java-events:2.2.5") implementation("com.amazonaws:aws-lambda-java-log4j2:1.0.0") testImplementation(kotlin("test-junit")) } sam { template = file("template.yml") }

Slide 49

Slide 49 text

id("aws.sam") aws-sam-gradle-plugin ˖ 5BTLT ˖ JOTUBMM"XT4BN › pip install awscli aws-sam-cli ˖ NBLF#VDLFU › aws s3 mb s3: // ... ˖ QBDLBHF4BN"QQ › sam package ... ˖ SVO-PDBM4UBSU"QJ › sam local start-api ... ˖ EFQMPZ4BN"QQ › sam deploy ... BXTTBNHSBEMFQMVHJOJNQPSUSFBXTTBNHSBEMFQMVHJO

Slide 50

Slide 50 text

id("aws.sam") aws-sam-gradle-plugin ˖ TIBEPX얺믆핆뺂핳 ˖ dependencies읊졶숞jar핊픒T펞폺읺믾퓒 ˖ $buildDir/lib/${project.name}-all.jar칺푷 ˖ 잚핊"84-BNCEB-BZFST읊핂푷삲졂 ˖ 믾쫆헏픊옪캫컿쇦쁢jar핊칺푷 BXTTBNHSBEMFQMVHJOJNQPSUSFBXTTBNHSBEMFQMVHJO

Slide 51

Slide 51 text

로컬 테스트 "1*컪쩒킪핟 › ./gradlew :function:runLocalStartApi

Slide 52

Slide 52 text

로컬 테스트 httpie › http get http: //localhost:3000/greeting HTTP/1.0 200 OK Content-Length: 47 Content-Type: application/json { "message": "Kotlin Night Seoul, Hello!" } IUUQJFbrew install httpie

Slide 53

Slide 53 text

로컬 테스트 httpie › http get http: //localhost:3000/greeting\?content\=2019 HTTP/1.0 200 OK Content-Length: 46 Content-Type: application/json { "message": "Kotlin Night Seoul, 2019!" } IUUQJFbrew install httpie

Slide 54

Slide 54 text

AWS에 배포

Slide 55

Slide 55 text

AWS에 배포 function/build.gradle.kts sam { template = file("template.yml") bucket = "YOUR_S3_BUCKET_NAME" stack = "YOUR_CLOUDFORMATION_STACK_NAME" } ⚠ 4#VFDLFU $MPVE'PSNBUJPO4UBDL핂읒뮪훊픦

Slide 56

Slide 56 text

AWS에 배포 function/build.gradle.kts sam { template = file("template.yml") bucket = "YOUR_S3_BUCKET_NAME" stack = "YOUR_CLOUDFORMATION_STACK_NAME" } ⚠ 4#VFDLFU $MPVE'PSNBUJPO4UBDL핂읒뮪훊픦

Slide 57

Slide 57 text

AWS에 배포 $POHVSBUJPO$SFEFOUJBM'JMFT핆 › ./gradlew :function:deploySamApp ... ... ... [ { "OutputKey": "ProdEndpoint", "OutputValue": "https: //???.execute-api.ap-northeast-2.amazonaws.com/Prod/" } ]

Slide 58

Slide 58 text

AWS에 배포 $POHVSBUJPO$SFEFOUJBM'JMFT핆 › ./gradlew :function:deploySamApp ... ... ... [ { "OutputKey": "ProdEndpoint", "OutputValue": "https: //???.execute-api.ap-northeast-2.amazonaws.com/Prod/" } ]

Slide 59

Slide 59 text

CLI 앱 만들기

Slide 60

Slide 60 text

CLI 앱 만들기 › # ࢲ࠳ ݽٕ `cli` ࢤࢿ › mkdir cli && touch "$_/build.gradle.kts"

Slide 61

Slide 61 text

CLI 앱 만들기 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include( ":function", ":cli" )

Slide 62

Slide 62 text

CLI 앱 만들기 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include( ":function", ":cli" )

Slide 63

Slide 63 text

CLI 앱 만들기 cli/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" application } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.squareup.retrofit2:retrofit:2.5.0") implementation("com.squareup.retrofit2:converter-gson:2.5.0") implementation("com.squareup.retrofit2:adapter-rxjava2:2.5.0") testImplementation(kotlin("test-junit")) } application { mainClassName = "MainKt" }

Slide 64

Slide 64 text

CLI 앱 만들기 cli/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" application } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) implementation("com.squareup.retrofit2:retrofit:2.5.0") implementation("com.squareup.retrofit2:converter-gson:2.5.0") implementation("com.squareup.retrofit2:adapter-rxjava2:2.5.0") testImplementation(kotlin("test-junit")) } application { mainClassName = "MainKt" }

Slide 65

Slide 65 text

CLI 앱 만들기 cli/ .../Main.kt data class GreetingData( val message: String ) interface GreetingService { @GET("/greeting") fun greeting( @Query("content") content: String? = null ): Single> }

Slide 66

Slide 66 text

CLI 앱 만들기 cli/ .../Main.kt data class GreetingData( val message: String ) interface GreetingService { @GET("/greeting") fun greeting( @Query("content") content: String? = null ): Single> }

Slide 67

Slide 67 text

CLI 앱 만들기 cli/ .../Main.kt data class GreetingData( val message: String ) interface GreetingService { @GET("/greeting") fun greeting( @Query("content") content: String? = null ): Single> }

Slide 68

Slide 68 text

CLI 앱 만들기 cli/ .../Main.kt private val service = Builder() .client(OkHttpClient()) .baseUrl("http: //127.0.0.1:3000") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(GreetingService ::class.java) fun main() { service.greeting() .map { it.body() ?.message ?: it.errorBody() ?: "???" } .subscribe( ::println, Throwable ::printStackTrace) }

Slide 69

Slide 69 text

CLI 앱 만들기 cli/ .../Main.kt private val service = Builder() .client(OkHttpClient()) .baseUrl("http: //127.0.0.1:3000") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(GreetingService ::class.java) fun main() { service.greeting() .map { it.body() ?.message ?: it.errorBody() ?: "???" } .subscribe( ::println, Throwable ::printStackTrace) }

Slide 70

Slide 70 text

CLI 앱 실행하기 › ./gradlew -q :cli:run Kotlin Night Seoul, Hello!

Slide 71

Slide 71 text

공통 모듈 만들기

Slide 72

Slide 72 text

Shared 모듈 만들기 # ࢲ࠳ ݽٕ `shared` ࢤࢿ › mkdir shared && touch "$_/build.gradle.kts"

Slide 73

Slide 73 text

Shared 모듈 만들기 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include( ":function", ":cli", ":shared" )

Slide 74

Slide 74 text

Shared 모듈 만들기 settings.gradle.kts rootProject.name = "kotlin-night-seoul" include( ":function", ":cli", ":shared" )

Slide 75

Slide 75 text

Shared 모듈 만들기 shared/build.gradle.kts plugins { kotlin("jvm") version "1.3.21" } repositories { jcenter() } dependencies { implementation(kotlin("stdlib-jdk8")) testImplementation(kotlin("test-junit")) }

Slide 76

Slide 76 text

공유 코드 shared/ .../GreetingData.kt 핂헒펞잚슮앦큲읊shared졶슖옪핂솧 data class GreetingData( val message: String )

Slide 77

Slide 77 text

shared 사용 cli/build.gradle.kts dependencies { implementation(project(":shared")) // ... }

Slide 78

Slide 78 text

shared 사용 function/build.gradle.kts dependencies { implementation(project(":shared")) implementation("com.google.code.gson:gson:2.8.5") // ... }

Slide 79

Slide 79 text

JVM Kotlin Plugin 젎옪헫 // build.gradle.kts plugins { kotlin("jvm") version "1.3.21" apply false } // shared/build.gradle.kts // function/build.gradle.kts // cli/build.gradle.kts plugins { kotlin("jvm") }

Slide 80

Slide 80 text

Response 변경 function/ .../GreetingHandler.kt private fun headers() = mapOf( "Content-Type" to "application/json" ) private fun body(params: Map): String = params .getOrDefault("content", "Hello") .let { content -> "Kotlin Night Seoul, ${content.capitalize()}!" } .let { message -> GreetingData(message = message) } .let { data -> Gson().toJson(data) }

Slide 81

Slide 81 text

Response 변경 function/ .../GreetingHandler.kt private fun headers() = mapOf( "Content-Type" to "application/json" ) private fun body(params: Map): String = params .getOrDefault("content", "Hello") .let { content -> "Kotlin Night Seoul, ${content.capitalize()}!" } .let { message -> GreetingData(message = message) } .let { data -> Gson().toJson(data) }

Slide 82

Slide 82 text

Response 변경 function/ .../GreetingHandler.kt private fun headers() = mapOf( "Content-Type" to "application/json" ) private fun body(params: Map): String = params .getOrDefault("content", "Hello") .let { content -> "Kotlin Night Seoul, ${content.capitalize()}!" } .let { message -> GreetingData(message = message) } .let { data -> Gson().toJson(data) }

Slide 83

Slide 83 text

Response 변경 function/ .../GreetingHandler.kt private fun headers() = mapOf( "Content-Type" to "application/json" ) private fun body(params: Map): String = params .getOrDefault("content", "Hello") .let { content -> "Kotlin Night Seoul, ${content.capitalize()}!" } .let { message -> GreetingData(message = message) } .let { data -> Gson().toJson(data) }

Slide 84

Slide 84 text

Response 변경 function/ .../GreetingHandlerTest.kt assertEquals( expected = Gson().toJson( GreetingData(message = "Kotlin Night Seoul, Hello!") ), actual = content )

Slide 85

Slide 85 text

요약

Slide 86

Slide 86 text

요약 ˖ "84-BNCEB "1*(BUFXBZ핟컿픒읾픊옪 ˖ BXTBXTMBNCEBKBWBMJCT ˖ JNQPSUSFBXTTBNHSBEMFQMVHJO ˖ BXTDMJ BXTTBNDMJ ˖ 찚슪큲잋솒읾픊옪 ˖ ,PUMJO%4- ˖ 컪쩒퐎않핂펆슪읊뫃퓮믾 ˖ "VUIPSJOH.VMUJ1SPKFDU#VJMET ˖ "QQMJDBUJPO1MVHJO

Slide 87

Slide 87 text

그리고..

Slide 88

Slide 88 text

우리는 채용 중!

Slide 89

Slide 89 text

!iiid!

Slide 90

Slide 90 text

!iiid! / get rid of sth /

Slide 91

Slide 91 text

Positions ˖ "OESPJE ❤ ,PUMJO ˖ #BDLFOE ❤ ,PUMJO ˖ "OE"* J04 8FC 핞퓮퍟킫픊옪핂엳컪읊쫂뺂훊켆푢 ˋSFDSVJU!SJJJEDP IUUQTSJJJEDPDBSFFS

Slide 92

Slide 92 text

고맙습니다

Slide 93

Slide 93 text

Q&A

Slide 94

Slide 94 text

!iiid!