와.. 항상 코틀린이 문법적으로 더 깔끔하다고 생각했는데.. StructuredTaskScope 보고, 그렇지만은 안다는걸 느꼈다..
코틀린을 하면서, 어렵고 난해하다고 생각하는 부분/기능이 코루틴이였다.
그 개념을 이해하는데, 정말 시간이 걸렸다.
이해한거같은데, 모르겠고, 모르겠는데, 이해되는 부분도있고 하는 애매한 상태가 오래됬는데,
관련 코틀린책을 3번 정도 읽으니, 이해를 마침내 되었다.
그래도 코루틴에서 난해한 부분이 "구조화된 동시성"에 대한 기능에 대한 이해였다.
이해는 되지만, 사용법이 깔끔하지 않다고 할까?
특히 코드 블럭이 중첩되는 경우에 "구조화된 동시성"에 따른 동작 흐름을 판단하는건 많이 복잡했다.
DB 트랜잭션 전파(Propagation)와도 비슷한 맥락이랄까? ㅋ
암튼, 코틀린은 개인적으로 생각할때,
코틀린의 설계가 그렇게 썩 나이스한 설계는 아닌것 같다는 생각은 평상시에 자주한다.
중첩되는 경우나 suspend에 의한 스코프 변경에 따른 구조화된 동시성 구조를 파악하기가 너무 어렵다.
아마 함수단위로 코루틴을 설정한 부분에서 트레이드 오프가 아닐까 한다.
함수단위로 코루틴을 구현하면,
스택머신의 상태값을 함수인자로 전달받게 비교적 간단하게 구현을 할수있다는 장점이 있지만,
중첩된 스코프에서 오는 복잡함은 단점이 된다고 생각한다.
(함수와 함수의 연결된 흐름이 결국 비즈니스 로직이니..복잡할 수밖에)
개인적인 생각이 하나있다.
코루틴을 정말 잘모르는 사람일수록, 쉽게 사용하려하고, 별것아니라고 생각한다..
하지만 정말 코루틴을 잘 이해하는 사람은 코루틴을 사용하는걸 꺼리게 된다...그 위험성과 어려움을 잘 알기때문이다..
암튼, 간단하게 코루틴에서는 "구조화된 동시성"을 제공하는 스코프 함수가 크게 3가지 있다.
여기서 "구조화된 동시성"이란 여러 함수가 다른 스코프(함수/쓰래드등)에서 병렬/동시 처리가 되는 경우,
구조적으로 에러에 대한 전파나, 함수의 성공여부를 어떻게 전파하고 처리하느냐에 대한 부분이다.
1) GlobalScope
- 해당 스코프안에 코루틴은 에러발생시, 부모에게 에러를 전파하지 않는다.
(더 정확히는, 구조화가 안되있어서, 사실 부모가 없다, 그냥 모두가 자식인 상태이다)
(즉, 부모 스코프에서 에러를 알수없기에, 에러처리가 불가하다)
2) CoroutineScope
- 해당 스코프안에 코투틴에서 에러발생시, 부모에게 에러가 전파된다.
(부모 스코프에서 에러가 전파되기에, 에러에 대한 성공/실패처리 가능하다)
3) SupervisorScope
- 해당 스코프안에 코루틴에서 에러발생시, 부보에게 에러가 전파되지 않는다.
이런 개념을 알아야, 구조화된 동시성에 맞게 코루틴을 잘 사용할수가 있다.
그런데, 비슷한 개념으로 자바에서는 StructuredTaskScope 라는 기능으로 구조화된 동시성을 제공하고 있다.
뭐랄까..
훨씬, 문법적으로 깔끔한 느낌이다. 와!! 정리가 잘되있고, 명시적이고 직관적이랄까?!
StructuredTaskScope의 전략은 크게 2가지가 존재한다. 2가지만 잘 사용하면 된다. 깔끔하다!!
1) ShutdownOnFailure
- 자식이 여러개가 존재할때 하나라도 실패하면, 나머지 자식도 모두 실패, 취소된다.
2) ShutdownOnSuccess
- 자식들중 하나라도 성공하면, 나머지 자식들은 모두 실패, 취소된다.
- 동일한 API가 있는데, 먼저성공한걸 사용하는 기능만들때 참 좋겠다
(ex: 주소API가 2가지가 있는데, 2가지 호출해서 먼저 성공한 호출을 사용하도록 구현)
실제 사용예)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var user = scope.fork(() -> getUser());
var order = scope.fork(() -> getOrders());
scope.join();
scope.throwIfFailed();
return combine(user.resultNow(), order.resultNow());
}
충격이다..너무 깔끔하다 ㅠㅠ
그리고 가장 중요한 ScopedValue와 StructuredTaskScope를 같이 사용하여,
ScopedValue를 다른 코드블록으로 전파하여 사용가능하다.
ScopedValue.where(USER, "테스터").run(() -> {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
scope.fork(() -> {
return USER.get(); // 전달됨
});
}
});
아마 위의 코드 보면, 아!! 하지않을까?...
미쳤다..
ScopedValue와 StructuredTaskScope를 같이 사용하면 엄청난 시너지가 나는구나...
'MY개발생각' 카테고리의 다른 글
| [개발생각] 게시판 JPA로 구현할때, 정해야 할것들 (0) | 2026.03.30 |
|---|---|
| [개발생각] 공지글 읽음 처리에 대해서.. (0) | 2026.03.26 |
| [개발생각] 스크럼의 진정한 목적이 무엇일까? (0) | 2026.03.22 |
| [개발생각] REDIS에서 아토믹처리를 하려면 (0) | 2026.03.22 |
| [개발생각] 붉은사막...펄어비스... (2) | 2026.03.19 |
