오늘도 끄적끄적

느리더라도 꾸준하게

Java Virtual Threads 훑어보기에서 Virtual Thread에 대해 대충 훑어봤었고, 2023/09/19에 나오는 JDK21(심지어 LTS)에서 Virtual Threads (JEP 444)가 정식으로 출시된다는 소식을 들었다.
그러다보니 코틀린의 코루틴의 소식이 궁금했다.

출처: https://www.youtube.com/live/QxxG66eQoTc?feature=share&t=3652
많은 사람들이 Virtual Thread가 코루틴과 같은 역할을 할 수 있을지 궁금해하는 것 같았고, 나 또한 코루틴을 잘 모르는 입장에서 ‘결국 코루틴은 사장되는 게 아닐까?’란 생각이 들었다.

그러던 중 Coroutines and Loom behind the scenes by Roman Elizarov라는 영상을 보게 되어서 이해한 내용을 겸사겸사 정리해보았다. (나중에 회사에서 Virtual Thread 적용 할 때 고민해야할 부분도 함께 적어두기 위해서도 있지만…)
우선 해당 포스트에서는 Virtual Thread에 대한 기본적인 내용들은 알고 있다는 전제 하에 작성했다.

더 읽어보기 »

Java의 Virtual Threads는 JDK 19에 Preview Features로 추가되었다.
프로젝트 룸(Loom)에서 개발한 기능으로 알고있는데 사실 큰 관심도 없던(뭐하는 지도 모르던) 프로젝트였고, JDK 19가 LTS도 아니기 때문에 회사에서 바로 써볼 수도 없기에 JDK 19는 큰 관심도 가지고 있지 않았다.
하지만 최근 스프링 블로그에서 Embracing Virtual Threads 라는 포스트가 올라온 걸 보고 살짝 관심 가지게 되었다.

Spring Web MVC와 같이 전형적인 1 Request per 1 Thread 모델의 한계(쓰레드 자체가 많은 메모리를 소비하고, 컨텍스트 스위칭에 따른 불필요한 시간 소요 등등)를 극복하기 위해
Spring Webflux(가 의존하는 Netty)에서는 코어 갯수 * 2개만의 쓰레드를 만듦으로 인해 그 한계를 극복하였지만 하나의 요청을 하나의 쓰레드가 온전히 처리하는 것이 아니기 때문에 스택트레이스를 봐도 파편화된 정보가 남아 트러블 슈팅에 문제가 있었고,
MonoFlux와 같은 Publisher 타입으로 값을 감싸야 하기 때문에 코드가 매우 보기 힘들고, 어디서 쓰레드를 블락하는 코드를 호출하는 건 아닌지 항상 불안에 떨었어야했다.

그러다보니 Webflux가 더 고성능을 보장하더라도 유지보수하기가 힘들고 러닝커브 또한 존재하기 때문에 어지간한 경우가 아니면 Web MVC로 프로젝트를 만들었다.
사실 프로덕션에서 RDBMS를 안 쓰는 곳이 거의 없는데 R2DBC를 사용하기에는 너무 불안정해 보이기도 했고, 그리고 TPS가 안 나오면 대부분 스케일 아웃하는 형태로 해결을 많이 했다.
서버보다는 사람이 가장 비싼 자원이라고 생각되기에 유지보수 측면으로만 생각하다보니 Webflux는 거의 사용한 적이 없는 것 같다.
결정적으로 Web MVC(Spring Boot를 사용한다면 톰캣의 최대 쓰레드인 200개)만으로도 부족함이 없는 서비스도 많았고, 단일 서버가 아닌 이중화 등등으로 인해 서버가 다중으로 뜨기에 Webflux를 써야할 만큼의 처리를 단일서버에서 하지 않는 경우가 대다수였다.

그럼에도 불구하고 Virtual Threads는 어떠한 문제를 해결해주는 것인지, Spring과 함께 사용하면 어떤 시너지를 낼 수 있을지 궁금해서 살짝만 훑어보았다.

Platform Threads

더 읽어보기 »

Spring Boot 2에서 제공하는 RDB 관련 의존성을 추가하면 DB의 커넥션 풀을 관리하기 위해 기본적으로 사용하는 HikariCP는 시간 관련해서 다양한 설정들이 있다.
하지만 그냥 설명하는 것만 봐서는 무슨 내용인지 헷갈리는 설명들이 있어서 요번에 프로젝트에 도입된 설정들을 포함해 몇가지 정리를 해보았다.

maxLifeTime

⏳maxLifetime
This property controls the maximum lifetime of a connection in the pool.
An in-use connection will never be retired, only when it is closed will it then be removed.
On a connection-by-connection basis, minor negative attenuation is applied to avoid mass-extinction in the pool.
We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit.
A value of 0 indicates no maximum lifetime (infinite lifetime), subject of course to the idleTimeout setting.
The minimum allowed value is 30000ms (30 seconds). Default: 1800000 (30 minutes)

커넥션 풀에서 idle 커넥션이 최대 얼마동안 생존할 수 있냐는 설정이다. (단위는 ms, 기본값은 30분, 최소값은 30초, 0으로 설정하면 무제한)
반환될 때마다 idle time은 다시 0으로 초기화 될테니 트래픽이 많이 들어와서 커넥션이 계속 사용되는 서비스라면 이 설정에 의해 커넥션이 종료될 일은 적을 것이다.

sequenceDiagram
  autonumber
  participant Server
  participant Hikari as HikariCP (maxLifeTime: 30000ms(30s))
  participant C1 as Connection 1 (current idle time: 20s)
  participant C2 as Connection 2 (current idle time: 15s)
  
  Server ->> Hikari: getConnection()
  Hikari ->> C1: getConnection()
  C1 ->> Hikari:  Connection 1
  Hikari ->> Server: Connection 1
  Server ->> Hikari: releaseConnection(Connection 1)
  Hikari ->> C1: release (reset idle time to 0s)
더 읽어보기 »

1~2년 전 쯤 존경하는 개발자 분과 함께 A Brief History of Mock Objects라는 아티클을 함께 본 적이 있다.
개인적으로 이렇게 특정 개념의 근본이 된다던지, 해당 분야의 대가들이 쓴 아티클들을 함께 보는 것으로 인해 굉장히 많은 인사이트들이 생겼던 것 같다.
객체의 테스트 때문에 추가했던 getter로부터의 해방을 위해 고민하던 것으로 시작한 게 Mock의 탄생이라는 사실을 알게 되었을 때는 정말 위대한 탄생이라고 생각했다.

그리고 문득 시간이 지나 해당 아티클을 다시 보고 싶어졌다. (물론 해당 아티클은 테스트 주도 개발로 배우는 객체 지향 설계와 실천 책의 후기에 한글로 적혀있다.)
아티클을 보던 중에 후반 부분에 Mock과 관련해서 Mock Roles, not Objects라는 논문까지 썼다는 걸 보고 해당 논문까지 봐야 Mock에 대해 정확한 이해를 할 수 있을 것 같아 해당 논문을 보게 되었다.
그리고 목의 역사와 마찬가지로 해당 논문도 너무나 감명이 깊어 한 번 느낀점이나 내용을 정리해보고 싶었다.

Writing tests is a design activity

먼저 TDD(Test Driven Development)에는 크게 두 가지 관점이 존재할 것 같다.

첫 번째로 “검증”이다.
코드들이 의도대로 동작하는지, 버그는 없는지 검증하는 것이다.
이를 통해 프로덕션에 코드를 내보내도 된다는 자신감이 올라가고, 리팩토링을 하거나 신규 기능을 추가하더라도 코드의 동작은 변하지 않았음에 확신을 가질 수 있다.

더 읽어보기 »

서버에서 아주 가끔가다가 ClientAbortException(java.io.IOExceiption: Broken pipe)이 발생해서 어떨 때 발생하는지 딥다이브 해봄.

적다보니 글이 길어져 글을 나누었는데 해당 글을 읽기 전에 (Tomcat) ClientAbortException은 왜 발생할까? (Part 1)을 먼저 보는 것을 추천함.


https://tomcat.apache.org/tomcat-9.0-doc/api/org/apache/catalina/connector/ClientAbortException.html

더 읽어보기 »

서버에서 아주 가끔가다가 ClientAbortException(java.io.IOExceiption: Broken pipe)이 발생해서 어떨 때 발생하는지 딥다이브 해봄.

적다보니 글이 길어져 글을 나누었는데 해당 글을 읽고 난 후에 (Tomcat) ClientAbortException은 왜 발생할까? (Part 2)를 마저 보는 것을 추천함.


https://tomcat.apache.org/tomcat-9.0-doc/api/org/apache/catalina/connector/ClientAbortException.html

더 읽어보기 »

들어가기에 앞서

(Gradle) implementation vs api에서는 compile/runtime 의존성을 관리하는 방법에 대해 정리했다.
하지만 이는 실제 src/main 경로에 대해서만 의존성을 관리하는 것이지 src/test 경로에서 사용하는 테스트 의존성(testCompileClasspath, testRuntimeClasspath)에 대해서는 딥하게 다루지 않았다.
테스트도 관리해야할 대상이고 하나의 소프트웨어라는 관점에서 테스트의 의존성 조차도 신경을 써줘야한다.

testImplementation

더 읽어보기 »

3줄 요약

  1. implementation을 사용하자
  2. implementation을 사용하더라도 라이브러리를 사용하는 consumer 측의 runtimeClassPath에 추가되기 때문에 런타임 의존성 충돌이 발생할 수 있으니 의존성은 최대한 적게 추가하자.
  3. api가 필요한 건지 100번 고민하고 설계가 잘못된 건 아닌지 의심해 본 후 api를 사용한다. (api는 consumer의 compile/runtimeClassPath에 모두 추가된다.)

들어가기에 앞서

의존성(라이브러리/프레임워크)을 추가하기 위해 build.gradle(or build.gradle.kts)에 아래와 같이 디펜던시들을 추가하게 된다.

1
2
3
4
dependencies {
api("org.springframework.boot:spring-boot-starter-web")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
}
더 읽어보기 »

평상 시 독서를 거의 안 하고, 짧은 글, 짧은 영상 위주의 인스턴트 식으로 살다보니 독해능력이 엄청나게 떨어졌다.
글을 읽어도 제대로 이해하지 못하고, 한 2~3문장 이전에 읽은 글이 기억나지 않는다.
책을 읽고, 해당 책이 좋다는 사실까지는 알았는데 이 책이 그래서 하고자 하는 말은 무엇이지? 무슨 내용의 책이지?
를 남들에게 요약해서 설명해주려해도 설명할 수가 없었다.
책을 읽긴 읽었는데 그래서 뭐? 책을 읽기 전과 책을 읽은 후의 나는 어떤 변화가 있나? 어떤 발전이 있나?… 그냥 책 하나 읽었다는 자기 만족 밖에 되지 않았다.
빠르게 정보를 습득하는 것도 중요하지만, 정보가 남아있지 않다면 그 시간마저 버린 것이기 때문에 이렇게 기록이라도 하는 의식적인 노력을 들여야 조금이나마 내 자신이 개선될 것 같아 노트를 남긴다.
+로 비판적 책읽기(책에 있는 내용을 무지성으로 수용하는 게 아니라 문장 하나, 단어 하나하나가 의미하는 게 무엇인지 곱씹어보기, 작가가 헛소리 하는 건 없는지 의심해보기)를 통해 작가와 소통하며 책을 읽는 듯한 느낌을 느껴보려고 하는 것도 있음.

제로 투 원 (ZERO to ONE)

참고로 나는 개정되기 이전 버전을 읽었음.
읽게 된 계기는 회사 동료가 이 책을 읽고 가슴이 설렜다고 함.
그래서 사내에 기증된 도서에도 있길래 읽었음.
나의 난독+독해능력이 너무 딸려서 줄을 치면서(그나마 내용을 기억하기 위한 최소한의 행위/노력) 읽고 싶었지만 회사 책이라 그러지는 못함.
그러다보니 그냥 읽기만 하고 다음날 되면 전날 내용 다 까먹음.
그러다보니 내가 이 책을 읽고 있기는 한데 남들한테 이 책이 무슨 내용이고 왜 좋고 어떤 영감을 받았는지 왜 추천하는지 하나도 설명하지 못함.
그래서 2독을 결심하고 조금이나마 노력을 들여 나의 기억력 + 독해력 향상을 위해 노트에 받아적다가 팔도 아프고, 아무 노트에 대충 정리해놓다보니 나중에 잊혀질까 아까워서 그냥 블로그에 적기로 결심함.
물론 손으로 적었을 때가 노력이 제일 많이 들어가다보니 기억이나 독해력 향상에는 도움이 많이 되는 것 같지만,
노트에 적고 또 장기보존을 위해 블로그에 또 적자니 시간도 아깝고… 시간이 무한정 한 것이 아니기 때문에 걍 블로그에 적기로 결심.

참고로 이 책은 새로운 것을 창조하는 회사를 만드는 방법을 다루는 책임.
따라서 스타트업 창업을 생각하거나 본인의 야망을 어떻게 실현시킬지, 어떤 생각으로 일을 하거나 인생을 살아가야하는지에 대한 도움이 될만한 책이라고 생각함.
굳이 창업 안 하더라도 성공한 사람, 혁신을 이뤄낸 사람들은 어떻게 생각하고 어떻게 행동했는지 를 통해 배울 수 있는 점이 많음.

더 읽어보기 »

회사에서 좋은 기회가 생겨 AWS re:invent(2021/11/29 ~ 2021/12/03)에 참석할 기회가 생겼다.
영어도 잘 못하고, 평상시 AWS를 직접 쓰지 않은지 오래 되기도 했지만 견문을 넓히자는 차원에서 지원하여 갔다오게 되었다.
살면서 미국에 처음 가보는 것이다보니 미국에서만 할 수 있는 걸 해보자라는 목표를 세우고 갔으나 많은 실패들이 있었고, 영어가 잘 안되다보니 aws reinvent 컨벤션 후기 보다는 라스베가스 여행기가 되어버린 것 같았다.
기술적인 부분에서 인사이트를 크게 얻지 못해 창피하여 aws reinvent 후기는 적지 못하고, 미국이라는 기회의 땅에 가본 경험을 휘발성 데이터로 냅두기 아까워 기억들이 더이상 날아가기 전에 이렇게라도 기록을 해둬야할 거 같아서 이 글을 쓰게 되었다.
쓰다보니 사진이 많아서인지 글이 좀 루즈해지는 감이 없잖아 있어 파트를 좀 쪼개보았다.

용기내어 한마디라도 건네보기

어젯 밤 re:Play 행사를 갔다오고 나서 없던 자신감이 샘솟고 좀 더 미국에서만 경험할 수 있는 것을 경험해보고 싶다는 생각에 가득찼다.
그러다보니 동료 한 분과 아침식사를 하면서 무조건 외국 엔지니어들과 대화를 해보겠다는 목표를 세웠다.

일단 테이블에 앉을 때도 2명 정도 앉아있으면서 우리한테 대답해줄 거 같은 착한 사람을 물색하였다.
목표를 포착하고 앉아서 말없이 우리끼리만 대화를 하였다.
그러다 동료가 용기내어 말을 걸었고, 알고보니 그들은 United 항공사 소프트웨어 엔지니어들이었다.
대충 뭔 이야기를 했던 것 같은데 잘 기억은 안 나고 ‘우리 샌프란시스코에서 너네 항공사 타고 라스베가스로 왔어~’와 같은 시덥잖은 대화를 했던 것 같다.

더 읽어보기 »
0%