logo
logo
언어
Kotlin
자바가 실행되는 환경에서 자바를 대체할 수 있도록 개발된 언어
StackOverflow 질문 수: 98852
Github Stars : ★ 50279
사용 기업
직장
금융/보험
패션
교육
이커머스
소셜/컨텐츠
부동산/인테리어
여행
기타
푸드테크
모빌리티
헬스케어
인공지능
종합
블록체인
techstack-logo
플렉스
techstack-logo
렌딧
techstack-logo
토스랩
techstack-logo
핀다
techstack-logo
드라마앤컴퍼니
techstack-logo
딜리셔스
techstack-logo
뤼이드
techstack-logo
에이블리
techstack-logo
트렌비
techstack-logo
드림어스컴퍼니
techstack-logo
스푼
techstack-logo
클래스101
techstack-logo
디셈버앤컴퍼니
techstack-logo
직방
techstack-logo
당근
techstack-logo
마이리얼트립
techstack-logo
버킷플레이스
techstack-logo
와디즈
더 보기
기술 블로그 글
현대자동차그룹
Android SharedFlow, StateFlow 도입기
안녕하세요, 현대오토에버에서 내비게이션 개발 업무를 하고 있는 김혜성입니다.이번에 Android 기반의 새로운 내비게이션 엔진을 도입하면서 관련 로직을  Kotlin으로 개발하게 되었습니다.해당 프로젝트에서는 비즈니스 로직과 UI 로직을 별도의 repository로 개발하고 있습니다. 따라서 비즈니스 로직에는 Android UI 생명 주기와 직접적으로 관련된 데이터는 필요하지 않았고, 오히려 Android에 대한 의존성을 최대한 배제하여 개발한다면 추후 다른 플랫폼에서도 기존 로직을 그대로 사용할 수 있을 것 같았습니다.이런 점을 염두에 두고 보니 프로젝트 초기에 작성된 코드의 LiveData가 눈에 띄었고 이를 모두 SharedFlow와 StateFlow로 대체하기로 했습니다.LiveDataAndroid 공식 문서에 따르면 LiveData는 observer 패턴을 따르는 데이터 홀더 클래스인데, 데이터 관찰 시 특정 Lifecycle 객체를 파라미터로 넘겨 주어 해당 Lifecycle의 상태에 따라 관찰을 시작하거나 끝낼 수 있습니다.UI와 함께 개발할 때는 이런 점이 메모리 관리 측면에서 편리하지만 LiveData는 기본적으로 main 스레드에서 처리되기 때문에 비즈니스 로직만 개발하는 경우에는 이런 점이 오히려 좋지 않습니다. 또한 아키텍처 측면에서는 불필요하게 Android에 대한 의존성을 갖게 됩니다.이를 대체하기 위해 사용할 수 있는 것이 Kotlin의 Flow입니다.FlowFlow는 Kotlin의 공식 라이브러리인 kotlinx.coroutines에서 제공하는 클래스입니다. Kotlin 공식 문서에 따르면 Flow는 비동기적으로 계산된 값들을 반환하기 위해 만들어진 타입입니다.Flow가 LiveData와 구분되는 또 다른 특징이 바로 다양한 중간 연산자와 종단 연산자를 지원한다는 점입니다. 우선 중간 연산자로는 다음과 같은 것들이 있습니다.map: 해당 요소를 다른 형태로 변환transform: 해당 요소에 대해 함수 호출 등 복잡한 처리 가능take: 설정한 횟수에 이르면 플로우 실행 취소그리고 종단 연산자로는 다음과 같은 것들이 있습니다.collect: 가장 기본적인 종단 연산자로, Flow를 수집launchIn: Flow를 특정 CoroutineScope 내에서 수집toList, toSet: Collection으로 변환first: 첫 번째 값만 기다린 후 반환single: Flow가 종료되기를 기다린 후 하나의 값 반환reduce: 모든 요소를 더하여 하나의 값 반환fold: 초기값에 모든 요소를 더하여 하나의 값 반환Kotlin의 Collection 함수와 동일한 역할을 하는 익숙한 연산자가 많아서 다양하게 활용할 수 있습니다. 그리고 Flow는 LiveData의 MediatorLiveData보다 비교적 간단하게 데이터를 결합할 수 있습니다.zip: 두 Flow에서 모두 새로운 값이 생산되면 그때 1:1로 쌍을 이루어 방출combine: 두 Flow 중 하나라도 새로운 값이 생산되면 가장 최신 값끼리 쌍을 이루어 방출merge: 여러 Flow를 하나의 Flow로 만들어서 먼저 생산되는 것부터 방출하지만 Flow는 cold stream이기 때문에 실제 애플리케이션 개발에서는 이의 하위 타입인 StateFlow와 SharedFlow를 주로 사용하고 있습니다.SharedFlowSharedFlow는 Flow를 상속한 인터페이스로, 초기값이 없어도 됩니다. 새로운 구독자가 생기면 그 구독자가 구독을 시작한 이후에 새로 업데이트된 값부터 알림을 받기 때문에 이벤트 처리에 적합하고 replay 옵션에 따라 이전 데이터를 캐싱할 수 있습니다.private val _hasArrived = MutableSharedFlow(replay = 1)val hasArrived: SharedFlow = _hasArrived.asSharedFlow()// (코루틴 내에서) 데이터 생산_hasArrived.emit(true)// (코루틴 내에서) 데이터 구독(수집)hasArrived.collect { // 도착 여부에 따른 처리}// 캐싱된 데이터에 접근hasArrived.replayCache.firstOrNull()emit()은 suspend fun이기 때문에 코루틴 내에서만 호출할 수 있습니다. 하지만 tryEmit()을 사용하면 기다리지 않고 즉시 반환되게 할 수 있습니다. 이때는 onBufferOverflow 옵션에 따라 동작하게 됩니다.BufferOverflow.SUSPEND: MutableSharedFlow의 버퍼 오버플로우 시 기본 정책으로, 버퍼에 자리가 날 때까지 기다림BufferOverflow.DROP_OLDEST: 버퍼에서 가장 오래된 값을 제거하고 새로운 값을 즉시 추가함BufferOverflow.DROP_LATEST: 새로운 값을 추가하지 않고 버퍼를 유지함BufferOverflow.SUSPEND일 때는 일반적인 emit()처럼 동작하고 false를 반환합니다. 그리고 나머지 설정값은 캐시나 버퍼가 있을 때만 가능하므로 replay나 extraBufferCapacity 옵션을 1 이상의 값으로 설정해야 합니다.launchIn을 사용하면 다음과 같이 간단한 형태로 구독할 수 있습니다.hasArrived .onEach { /* 도착 여부에 따른 처리 */ } .launchIn(coroutineScope)StateFlowStateFlow는 SharedFlow의 구현체로, 항상 한 가지 값(상태)을 갖고 있어야 해서 초기값이 필요합니다. 새로운 구독자에게 현재 상태를 즉시 알리고 이후에 새로 업데이트된 상태를 알리기 때문에 UI 업데이트 용도로 사용할 수 있습니다.private val _isAlertShowing = MutableStateFlow(false)val isAlertShowing: StateFlow = _isAlertShowing.asStateFlow()launch { // (코루틴 내에서) 데이터 생산 _isAlertShowing.emit(true)}launch { // (코루틴 내에서) 데이터 구독(수집) isAlertShowing.collect {
kotlin
카카오페이
함수형 프로그래밍과 Effect System을 이용한 의도가 명확한 코드 작성하기
tei.fxpark 자칫 어려울 수 있는 Effect System 개념을 예제와 같이 쉽게 풀어서 알려주고, Effect System을 만들기 위해서 과거부터 어떤 고민이 있었는지 알아볼 수 있었습니다. Side Effect가 없는 안전한 코드를 만들고 싶은 분들께 이 글을 추천합니다.hyeoni.c Side Effect라는 불확실성을 Effect System을 활용하여 안정성과 유지보수성을 모두 잡을 수 있는 방법을 쉽게 알려줍니다. 더 나아가 Effect System의 미래까지 확인하고 싶다면 이 글을 추천합니다!여러분은 위 코드를 보고 어떤 동작을 수행하는지 알 수 있나요? 메서드명만 보면 어딘가에 이벤트를 보내는 것 같습니다. 혹시 여러분도 저처럼 메서드의 이름만 보고 동작을 유추하고 있지는 않나요?이해를 돕기 위해 실제로 서비스에 적용해보겠습니다.만약 내가 담당하고 있는 서비스에 위 코드를 적용한다고 가정해 보겠습니다. 적용하려는 곳이(사용자 결제와 직접적으로 연관 있는) 크리티컬한 영역이라면 어떨까요? 아마 자연스럽게 의심부터 시작할 것입니다.타 팀에서 sendEvent 메서드를 서비스에 적용하는 과정에서 아래와 같은 질문을 받았습니다.• sendEvent를 호출하면 이벤트는 어디로 어떻게 전송하는 것인가요?• sendEvent만 호출했다 하면 간헐적으로 알 수 없는 Exception이 터져서 이후에 있는 기존의 로직이 실행이 되질 않습니다. 매출에 영향이 있으니 얼른 고쳐주세요.• sendEvent는 얼마나 오래 걸리나요? 우리 서비스는 사용자 결제 UX와 직접적으로 연관이 있기 때문에 빠르게 처리돼야 합니다. 많은 시간을 소모하지 않았으면 합니다.적용을 했는데 간헐적으로 NPE(NullPointerException)가 발생한다거나 CPU, 메모리 사용량이나 Latency가 증가하는 등 운영을 하기 전까지는 알 수 없는 문제가 발생할 수 있습니다. 특히 개발 환경에서는 문제가 없었는데 운영 환경에만 배포하면 문제가 발생할 경우 개발자를 야근의 늪으로 빠뜨리는 하나의 요소가 될 수 있습니다.왜 우리는 코드만 보고도 숨은 의도나 이슈를 파악하지 못해 늘 문제가 터진 뒤에야 대응하게 되는 것일까요? 왜 매번 Null check를 빼먹어서 NPE를 맞이하는 걸까요? 문서화, 테스트 코드 작성 등의 활동으로 어느 정도 이슈를 커버할 수는 있지만 모든 불확실성을 커버하기란 쉽진 않습니다. 뭔가 더 나이스한 방법은 없는 것일까요?이 문서에서는 Effect System을 이용하여 사이드 이펙트를 명시적으로 드러내고 조합하는 방법을 살펴보고자 합니다. Effect System을 처음 듣는 사람도 있고 경험해 본 독자도 있을 텐데요. Effect System이 뭔지는 뒤에 가서 좀 더 살펴보도록 하겠습니다.이 글은 Effect System의 기법들과 개념들을 간략하게 살펴보는 입문용 글로서 주로 Scala 언어로 설명하지만 상황에 따라 Java나 Kotlin 언어를 사용하여 설명합니다. 따라서 Scala, Kotlin 언어를 다뤄봤으면서 좀
kotlin
scala
카카오페이
Ktor로 팀 환경에 맞는 API 서버 구현하기
bri.ghten 다른 기본적인 예제 포스팅과 다르게 실제로 팀 내에서 Ktor를 서비스로 구성하는 과정을 같이 따라가 보면서 학습할 수 있었던 좋은 글입니다.dory.m Ktor 써보고 싶은 사람 손! Ktor를 활용해 서비스를 구성할 때 필요한 요소가 꾹꾹 눌러 담겨 있는 워니의 글 한번 보고 가세요~ 직렬화, 로깅, 메트릭 등에 대한 길잡이가 되어 줄 것입니다.안녕하세요. 카카오페이 후불결제TF 워니입니다.카카오페이는 혁신금융서비스로 소액후불결제업무(BNPL, Buy Now Pay Later)를 제공하고 있습니다. 저희 팀은 사용자들이 후불교통카드로 사용한 금액에 대해서 승인/청구/납부/연체 등을 관리하는 플랫폼을 개발/운영하고 있습니다.저는 새로운 환경을 프로젝트에 도입하는 것을 선호합니다. 이번에도 Ktor 환경을 팀 내에 도입해보고 싶었습니다. 하지만 도입해보기 전에 카카오페이의 환경과 팀의 개발 컨벤션에 맞게 구현될 수 있는지 확인이 필요했습니다. 구현 과정과 구현하며 느꼈던 점을 공유하고자 이 글을 작성했습니다. 이 글에서는 Ktor 환경으로 구현한 내용만 다루고 있습니다. Spring 프레임워크와 비교 분석한 내용은 Spring 공화국에서 Ktor 사용하기를 참고하시기 바랍니다.Ktor 애플리케이션을 만드는 방법은 크게 두 가지가 있습니다.• 직접 코틀린 프로젝트를 생성해서 의존성을 설정하는 방법• Ktor에서 제공하는 Ktor Generator 혹은 IntelliJ에서 Ktor Generator를 활용해 생성하는 방법기능별로 어떤 의존성이 필요한지 명확하게 알고 싶어 코틀린 프로젝트를 생성해서 의존성을 설정하는 첫 번째 방법을 선택했습니다.Ktor 애플리케이션을 만들기 위해 IntelliJ에서 Kotlin 프로젝트를 생성하고 Ktor Server 의존성을 설정했습니다.프로젝트 생성 다음으로는 서버에 대한 설정이 필요합니다. 서버 설정에도 두 가지 방법이 있습니다.• embeddedServer 방식: 서버를 실행하는 코드와 설정을 같이 작성(예시)저는 서버 설정의 가시성을 위해 EngineMain 방식을 선택했습니다.설정 파일을 에 작성했습니다. yaml로 작성한 설정 파일을 읽기 위해서는 추가 의존성이 필요했습니다.기본 구성을 마친 상태에서 main 함수를 실행하면 서버가 실행됩니다. 서버를 실행하고 을 호출하면 아래처럼 응답을 받을 수 있습니다.애플리케이션 로직과 별도로 공통된 처리 기능을 Ktor에서는 plugin이라고 부릅니다. 예를 들면 요청/응답 body의 json serialize, deserailize 기능이 plugin으로 제공됩니다.이렇게 제공되는 Ktor의 plugin들을 사용해서 카카오페이와 팀 개발 환경에 맞는 서버를 만들어 보겠습니다.Ktor에서 json 형식을 사용하기 위해서는 ContentNegotiation 의존성 설정을 하고 plugin을 설치해야 합니다. ContentNegotiation plugin은 json 뿐만 아니라 다양한 포맷의 serializer를 적용할 수 있습니다.카카오페이
kotlin
ktor
하이퍼커넥트
Spring Transactional Rollback Deep Dive
안녕하세요. Azar API Dev Team의 Ledger입니다. 이번 글에서는 Spring Transactional 동작에서 Checked Exception과 Unchecked Exception의 롤백(rollback) 처리에 관한 내용을 다뤄보겠습니다. 여러 사례를 통해 예외 처리 코드를 작성해 보고, 자주 혼동되는 부분들을 정리해 보았습니다.트랜잭션 범위 내에서 예외가 발생하면 롤백 되는건 익히 알고 있지만, 예외 처리를 해도 롤백 될 때가 있습니다. 정확히 언제 롤백이 될까요? 보통 Unchecked Exception과 Checked Exception 관련된 내용을 위주로 떠올리지만, 앞으로 나올 문제들을 모두 해결하려면 트랜잭션 프록시의 세부 동작이나 트랜잭션과 스레드의 상관관계와 같은 더 많은 내용을 이해하고 있어야 합니다.먼저 Unchecked Exception과 롤백 마킹에 대해서 간단히 살펴보겠습니다.Spring에서는 트랜잭션 진행 중 예외가 발생할 경우 rollbackOn에서 Unchecked 인지 체크합니다. Unchecked일 경우 processRollback 부분에서 각 기본 설정값의 영향으로 참여 중인 트랜잭션을 로 마킹합니다. 의도를 이해해 보자면 일부 트랜잭션이 실패할 경우, 전체 트랜잭션을 롤백 하는 것입니다. Checked Exception은 예상된 예외로 이를 처리하도록 의도된 것이고, Unchecked Exception은 예상치 못한 예외로 발생 시 롤백을 시도합니다.롤백 마킹에 대한 설정은 Spring 의 설정을 보면 확인할 수 있고, default 설정은 Unchecked Exception인 RuntimeException과 Error입니다.그렇다면 Unchecked Exception은 try catch로 잡아도 무조건 롤백이 될까요? 그렇지 않습니다. 이걸 이해하려면 롤백이 마킹된다는 개념을 이해하고 있어야 합니다. 이어서 아래 문제들을 통해 확인해 보겠습니다. 아래 문제들은 @Transactional 메소드를 제외하고 별도로 설정된 Transaction Advice는 없다고 가정합니다.같은 서비스 내에서 @Transactional 호출 시 동작문제 1. eatPizza 메소드 내의 pizza가 호출되면 새로운 트랜잭션이 열릴까요?정답은 X입니다. 이유는 스프링 어노테이션은 Spring AOP 기반으로 동작하는데 동일한 클래스 내에서 이 적용된 내부 메서드를 호출하는 경우, 호출되는 메서드는 프록시 객체를 거치지 않고 직접 호출되기 때문에 프록시(TransactionInterceptor)가 동작하지 않습니다.위 트랜잭션을 실행시키고 싶다면 pizza를 별도의 서비스로 분리하거나 트랜잭션 템플릿을 사용해 직접 트랜잭션을 열고 닫게 구현하면 됩니다. 아래 그림 1은 프록시 호출 구조에 대해 간단히 정리합니다.그림 1에서 프록시 동작을 코드로 간단히 표현하면 아래와 같습니다.위의 방법 대신 AspectJ를 활용할 수도 있지만, 본문에서는 Spring AOP 사용 사례에 대해서만 다룹니다. 이어서 아래 문
java
kotlin
spring
Copyright © 2025. Codenary All Rights Reserved.