Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Kopring으로 효율적인 백엔드 구성하기
Search
byeongsu
May 13, 2023
Programming
310
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Kopring으로 효율적인 백엔드 구성하기
KotlinConf 2023 Global in Songdo에서 Java+Spring에서 Kotiln 으로 언어 변경 시 얻을 수 있는 이점을 소개한 자료입니다.
byeongsu
May 13, 2023
Other Decks in Programming
See All in Programming
golang-bob.pdf
iwatsukayura
0
130
AIに “答え“を書かせるのを やめてみた
___akari11
0
120
Workers Cache を知る
syumai
0
370
コードレビューのボトルネックを"する側"と"される側"の両面から解消する
yub0n
2
1.6k
S3 TablesのIcebergデータに、AIエージェントからアクセスしてみた / 20261016developersio2026joetsu-accessing-iceberg-in-s3tables-via-agent
kasacchiful
PRO
0
160
Simple Storage Service(S3) is not simple
iwatsukayura
0
290
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
690
Jetpack Compose Mechanisms
skydoves
3
820
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
500
残高管理から台帳サービスへの進化
artoy
1
200
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
300
IBM Bob でAI駆動開発『QC-Playbook』
ichirokano2
0
110
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
Reflections from 52 weeks, 52 projects
jeffersonlam
356
22k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
480
Mind Mapping
helmedeiros
1
400
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
360
The SEO identity crisis: Don't let AI make you average
varn
0
590
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
The World Runs on Bad Software
bkeepers
PRO
72
12k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
Transcript
김병수 KotlinConf’23 Global in Songdo GDG Songdo X Incheon Kopring으로
효율적인 백엔드 구성하기
Autocrypt - Backend Engineer - Data Engineer - Kotlin +
Spring - 정의에 대한 고민 - 코드의 아름다움
Java + Spring Boot => Kotlin + Spring Boot 코드
개선 여정
서비스 요구사항 - 기사의 주행 데이터 수집 - 1초당 위치
정보 수집 - 1분당 주소 정보 저장 - 주소 정보 조회 시 1sec 소요 - 일반적으로 주행은 약 20분 소요
Java + Spring Code
Kotlin으로 언어를 변경한다면 어떤 장점이 있을까요?
1. 가독성 향상 2. 유용한 Extension function 3. 간단한 비동기
처리
다크모드 전환
Entity 설계
@Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Driver { @Id
@Column private String id; @Column private String name; @Column private String mobileNumber; @Column private LocalDateTime createDate; } Entity 설계
@Entity class Driver ( @Id @Column val id: String =
StringUtil.uuid(), @Column var name: String = "", @Column var mobileNumber: String? = null, @Column val createDate: LocalDateTime = LocalDateTime.now(), ) @Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Driver { @Id @Column private String id; @Column private String name; @Column private String mobileNumber; @Column private LocalDateTime createDate; } Entity 설계 Java Kotlin
@Entity class Driver ( @Id @Column val id: String =
StringUtil.uuid(), @Column var name: String = "", @Column var mobileNumber: String? = null, @Column val createDate: LocalDateTime = LocalDateTime.now(), ) @Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Driver { @Id @Column private String id; @Column private String name; @Column private String mobileNumber; @Column private LocalDateTime createDate; } Entity 설계 Java Kotlin
Entity 설계 @Entity class Driver ( @Id @Column val id:
String = StringUtil.uuid(), @Column var name: String = "", @Column var mobileNumber: String? = null, @Column val createDate: LocalDateTime = LocalDateTime.now(), ) @Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Driver { @Id @Column private String id; @Column private String name; @Column private String mobileNumber; @Column private LocalDateTime createDate; } Java Kotlin
@Entity class Driver ( @Id @Column val id: String =
StringUtil.uuid(), @Column var name: String = "", @Column var mobileNumber: String? = null, @Column val createDate: LocalDateTime = LocalDateTime.now(), ) @Entity @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class Driver { @Id @Column private String id; @Column private String name; @Column private String mobileNumber; @Column private LocalDateTime createDate; } Entity 설계 Java Kotlin
val driver = Driver( id = StringUtil.uuid(), name = "",
mobileNumber = null, createDate = LocalDateTime.now() ) Driver driver = new Driver( StringUtil.uuid(), "", null, LocalDateTime.now() ); Java Kotlin Entity 설계
val driver = Driver( id = StringUtil.uuid(), name = "",
mobileNumber = null, createDate = LocalDateTime.now() ) Driver driver = new Driver( StringUtil.uuid(), "", null, LocalDateTime.now() ); Java Kotlin Entity 설계
Entity 설계 class Driving ( val id: String = StringUtil.uuid(),
var startTime: LocalDateTime = LocalDateTime.now(), var endTime: LocalDateTime? = null, var driver: Driver? = null, var vehicle: Vehicle? = null, var departureAddress: String = "", var destinationAddress: String? = null, // ... // ... var customerName: String = "", var customerMobileNumber: String? = null, var customerEmailAddress: String? = null, var drivingDistance: Double? = null, val createDate: LocalDateTime = LocalDateTime.now(), var updateDate: LocalDateTime = LocalDateTime.now(), )
val driving = Driving( startTime = startTime, endTime = endTime,
customerName = customerName, departureAddress = departureAddress, destinationAddress = destinationAddress, ) Driving driving = new Driving( StringUtil.uuid(), startTime, endTime, null, null, departureAddress, destinationAddress, // ... customerName, null, null, null, LocalDateTime.now(), LocalDateTime.now() ) Java Kotlin Entity 설계
val driving = Driving( startTime = startTime, endTime = endTime,
customerName = customerName, departureAddress = departureAddress, destinationAddress = destinationAddress, ) Driving driving = new Driving( StringUtil.uuid(), startTime, endTime, null, null, departureAddress, destinationAddress, // ... customerName, null, null, null, LocalDateTime.now(), LocalDateTime.now() ) Java Kotlin Entity 설계 ???
val driving = Driving( startTime = startTime, endTime = endTime,
customerName = customerName, departureAddress = departureAddress, destinationAddress = destinationAddress, ) Driving driving = new Driving( StringUtil.uuid(), startTime, endTime, null, null, departureAddress, destinationAddress, // ... customerName, null, null, null, LocalDateTime.now(), LocalDateTime.now() ) Java Kotlin Entity 설계
1. Getter & Setter 제거 a. 주의: Lombok 사용 불가
2. Named Parameter 3. Default Constructor Entity 설계 => 가독성 향상
참고 Kotlin 전환 시 Lombok 에 대하여
dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.springframework.boot:spring-boot-starter-data-jdbc") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.jetbrains.kotlin:kotlin-reflect") // mariadb implementation
("org.mariadb.jdbc:mariadb-java-client") // encoding decoding codec implementation ("commons-codec:commons-codec:1.15") // coroutine implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") testImplementation("org.springframework.boot:spring-boot-starter-test") } 적용한 dependencies
dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.springframework.boot:spring-boot-starter-data-jdbc") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.jetbrains.kotlin:kotlin-reflect") // mariadb implementation
("org.mariadb.jdbc:mariadb-java-client") // encoding decoding codec implementation ("commons-codec:commons-codec:1.15") // coroutine implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") testImplementation("org.springframework.boot:spring-boot-starter-test") } 적용한 dependencies No Lombok!
주의할 점 Kotlin Java … Annotation Processing … .class .class
1 2 No Code, Lombok Processed
주의할 점
Business Logic 구현
Driving - foo - drivingPath - bar Location - drivingId
- ord - lat - lng - address? Business Logic 구현
fun doneDriving( drivingId: String ): DoneDrivingRes { val driving =
drivingRepository .findFirstById( drivingId ) ?: return DoneDrivingRes(null) val path = locationService .getLocationListByDrivingId( drivingId = driving.id ) driving.setDrivingDistance(path) /* other business logic */ /* other business logic */ drivingRepository.save(driving) locationService.updateAddress(path) return DoneDrivingRes( buildDrivingDto(driving) ) } Business Logic 구현
fun doneDriving( drivingId: String ): DoneDrivingRes { val driving =
drivingRepository .findFirstById( drivingId ) ?: return DoneDrivingRes(null) val path = locationService .getLocationListByDrivingId( drivingId = driving.id ) driving.setDrivingDistance(path) /* other business logic */ /* other business logic */ drivingRepository.save(driving) locationService.updateAddress(path) return DoneDrivingRes( buildDrivingDto(driving) ) } Business Logic 구현
Location - drivingId - ord - lat - lng -
address? Business Logic 구현 drivingId ord lat lng address testId 0 37.387540 126.638847 null testId 1 37.386466 126.639485 null … … … … … testId 59 37.392561 126.635330 null testId 60 37.393679 126.634572 null testId 61 37.395486 126.633526 null … … … … …
Location - drivingId - ord - lat - lng -
address? Business Logic 구현 drivingId ord lat lng address testId 0 37.387540 126.638847 null testId 1 37.386466 126.639485 null … … … … … testId 59 37.392561 126.635330 null testId 60 37.393679 126.634572 null testId 61 37.395486 126.633526 null … … … … …
Location - drivingId - ord - lat - lng -
address? Business Logic 구현 drivingId ord lat lng address testId 0 37.387540 126.638847 인천 연수구 … testId 1 37.386466 126.639485 null … … … … … testId 59 37.392561 126.635330 null testId 60 37.393679 126.634572 인천 연수구 … testId 61 37.395486 126.633526 null … … … … …
private void updateAddress( List<Location> path ) throws InterruptedException { for(int
i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } Business Logic 구현
private void updateAddress( List<Location> path ) throws InterruptedException { for(int
i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } Business Logic 구현
private String findAddress( Double lat, Double lng ) throws InterruptedException
{ Thread.sleep(1000); return "address"; } Business Logic 구현
private void updateAddress( List<Location> path ) throws InterruptedException { for(int
i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } Business Logic 구현
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } Business Logic 구현
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } Business Logic 구현
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } 다시, 주행 종료 API로 돌아가봅시다 Business Logic 구현
fun doneDriving( drivingId: String ): DoneDrivingRes { val driving =
drivingRepository .findFirstById( drivingId ) ?: return DoneDrivingRes(null) val path = locationService .getLocationListByDrivingId( drivingId = driving.id ) driving.setDrivingDistance(path) /* other business logic */ /* other business logic */ drivingRepository.save(driving) locationService.updateAddress(path) return DoneDrivingRes( buildDrivingDto(driving) ) } Business Logic 구현
fun doneDriving( drivingId: String ): DoneDrivingRes { val driving =
drivingRepository .findFirstById( drivingId ) ?: return DoneDrivingRes(null) val path = locationService .getLocationListByDrivingId( drivingId = driving.id ) driving.setDrivingDistance(path) /* other business logic */ /* other business logic */ drivingRepository.save(driving) locationService.updateAddress(path) return DoneDrivingRes( buildDrivingDto(driving) ) } Business Logic 구현
Location - drivingId - ord - lat - lng -
address? Business Logic 구현 drivingId ord lat lng address testId 0 37.387540 126.638847 null testId 1 37.386466 126.639485 null … … … … … testId 59 37.392561 126.635330 null testId 60 37.393679 126.634572 null testId 61 37.395486 126.633526 null … … … … …
Business Logic 구현 drivingId ord lat lng address testId 0
37.387540 126.638847 null testId 1 37.386466 126.639485 null … … … … … testId 59 37.392561 126.635330 null testId 60 37.393679 126.634572 null testId 61 37.395486 126.633526 null … … … … … +Distance +Distance +Distance Driving Distance
driving .setDrivingDistance( getDistanceOfPath( path ) ); driving.setDrivingDistance(path) // 후략 }
fun Driving.setDrivingDistance( path: List<Location> ) { this.drivingDistance = locationService .getDistanceOfPath(path) } Java Kotlin Business Logic 구현
driving.setDrivingDistance(path) // 후략 } fun Driving.setDrivingDistance( path: List<Location> ) {
this.drivingDistance = locationService .getDistanceOfPath(path) } driving .setDrivingDistance( getDistanceOfPath( path ) ); Java Kotlin Business Logic 구현
driving .setDrivingDistance( getDistanceOfPath( path ) ); Java Kotlin driving.setDrivingDistance(path) //
후략 } fun Driving.setDrivingDistance( path: List<Location> ) { this.drivingDistance = locationService .getDistanceOfPath(path) } Business Logic 구현
public Double getDistanceOfPath( List<Location> path ) { double distance =
0.0; for(int i = 1; i < path.size(); i++) { distance += GeoUtil .calculateDistance( path.get(i-1), path.get(i) ); } return distance; } fun getDistanceOfPath( locationList: List<Location> ): Double = locationList .calculateDistanceOfPath() Java Kotlin Business Logic 구현
public Double getDistanceOfPath( List<Location> path ) { double distance =
0.0; for(int i = 1; i < path.size(); i++) { distance += GeoUtil .calculateDistance( path.get(i-1), path.get(i) ); } return distance; } Java Kotlin fun getDistanceOfPath( locationList: List<Location> ): Double = locationList .calculateDistanceOfPath() Business Logic 구현
fun List<Location> .calculateDistanceOfPath(): Double = List(this.size) { index -> if(index
< 1) return 0.0 GeoUtil.calculateDistance( this[index - 1], this[index] ) } .sum() fun getDistanceOfPath( locationList: List<Location> ): Double = locationList .calculateDistanceOfPath() Business Logic 구현
• forEach / map / let • custom Extensions =>
유용한 Extension Functions Business Logic 구현
성능 개선
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } updateAddress 기능을 살펴봅시다 성능 개선
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } 성능 개선
fun updateAddress( path: List<Location> ) { path .chunked(6) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=6) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } 약 20분 주행, 1초에 한 번 위치 수집, 1분에 한 번 주소 저장 => 1200개 위치 데이터, 20s 소요 성능 개선
fun updateAddress( path: List<Location> ) { path .chunked(6) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=6) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } 약 20분 주행, 1초에 한 번 위치 수집, 5초에 한 번 주소 저장 => 1200개 위치 데이터, 240s 소요 성능 개선
fun updateAddress( path: List<Location> ) { path .chunked(60) .map {
chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 Java Kotlin private void updateAddress( List<Location> path ) throws InterruptedException { for(int i=0; i<path.size(); i+=60) { Location location = path.get(i); location.setAddress( findAddress( location.getLat(), location.getLng() ) ); } } 성능 개선
fun updateAddressAsync( path: List<Location> ) { CoroutineScope(Dispatchers.IO).launch { val deferredPath
= path .chunked(60) .map { locationChunk -> async { val top = locationChunk .first() top.address = findAddress( lat = top.lat, lng = top.lng ) top } } val addressedPath = deferredPath.awaitAll() fun updateAddress( path: List<Location> ) { path .chunked(60) .map { chunk -> val location = chunk .first() location.address = findAddress( lat = location.lat, lng = location.lng, ) location } // 후략 성능 개선
• Coroutine 성능 개선 => 간단한 비동기 처리
다크모드 종료
정리
1. 가독성 향상 2. 유용한 Extension function 3. 간단한 비동기
처리
Come with me, Use Kopring
QnA & EOF