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
280
Kopring으로 효율적인 백엔드 구성하기
KotlinConf 2023 Global in Songdo에서 Java+Spring에서 Kotiln 으로 언어 변경 시 얻을 수 있는 이점을 소개한 자료입니다.
byeongsu
May 13, 2023
Tweet
Share
Other Decks in Programming
See All in Programming
脳の「省エネモード」をデバッグする ~System 1(直感)と System 2(論理)の切り替え~
panda728
PRO
0
130
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
0
180
Kotlin Multiplatform Meetup - Compose Multiplatform 외부 의존성 아키텍처 설계부터 운영까지
wisemuji
0
170
これならできる!個人開発のすゝめ
tinykitten
PRO
0
150
ゆくKotlin くるRust
exoego
1
200
チームをチームにするEM
hitode909
0
440
從冷知識到漏洞,你不懂的 Web,駭客懂 - Huli @ WebConf Taiwan 2025
aszx87410
2
3.3k
Cap'n Webについて
yusukebe
0
160
Unicodeどうしてる? PHPから見たUnicode対応と他言語での対応についてのお伺い
youkidearitai
PRO
0
420
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
16
5k
それ、本当に安全? ファイルアップロードで見落としがちなセキュリティリスクと対策
penpeen
6
2k
LLMで複雑な検索条件アセットから脱却する!! 生成的検索インタフェースの設計論
po3rin
4
1.1k
Featured
See All Featured
Code Review Best Practice
trishagee
74
19k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
3.3k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
51k
30 Presentation Tips
portentint
PRO
1
180
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
110
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Mind Mapping
helmedeiros
PRO
0
47
Color Theory Basics | Prateek | Gurzu
gurzu
0
170
Thoughts on Productivity
jonyablonski
74
5k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.4k
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
81
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
270
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