Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Kopring으로 효율적인 백엔드 구성하기
Search
byeongsu
May 13, 2023
Programming
0
240
Kopring으로 효율적인 백엔드 구성하기
KotlinConf 2023 Global in Songdo에서 Java+Spring에서 Kotiln 으로 언어 변경 시 얻을 수 있는 이점을 소개한 자료입니다.
byeongsu
May 13, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
Fibonacci Function Gallery - Part 2
philipschwarz
PRO
0
200
GitHubで育つ コラボレーション文化 : ニフティでのインナーソース挑戦事例 - 2024-12-16 GitHub Universe 2024 Recap in ZOZO
niftycorp
PRO
0
1.1k
Package Traits
ikesyo
1
150
「とりあえず動く」コードはよい、「読みやすい」コードはもっとよい / Code that 'just works' is good, but code that is 'readable' is even better.
mkmk884
6
1.3k
Jaspr Dart Web Framework 박제창 @Devfest 2024
itsmedreamwalker
0
140
PHPで作るWebSocketサーバー ~リアクティブなアプリケーションを知るために~ / WebSocket Server in PHP - To know reactive applications
seike460
PRO
2
720
iOS開発におけるCopilot For XcodeとCode Completion / copilot for xcode
fuyan777
1
1.2k
QA環境で誰でも自由自在に現在時刻を操って検証できるようにした話
kalibora
1
120
毎日13時間もかかるバッチ処理をたった3日で60%短縮するためにやったこと
sho_ssk_
1
500
Scalaから始めるOpenFeature入門 / Scalaわいわい勉強会 #4
arthur1
1
380
コンテナをたくさん詰め込んだシステムとランタイムの変化
makihiro
1
180
KubeCon NA 2024の全DB関連セッションを紹介
nnaka2992
0
110
Featured
See All Featured
Building Flexible Design Systems
yeseniaperezcruz
328
38k
Automating Front-end Workflow
addyosmani
1366
200k
Music & Morning Musume
bryan
46
6.3k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.3k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
VelocityConf: Rendering Performance Case Studies
addyosmani
327
24k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Speed Design
sergeychernyshev
25
720
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
120k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
Raft: Consensus for Rubyists
vanstee
137
6.7k
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