Spring Camp 늦은 후기
2024년 5월 한국 스프링 사용자 모임(KSUG)이 주관하는 스프링 캠프 컨퍼런스에 다녀왔다. 선착순이라 큰 기대하지 않았는데 운 좋게도 성공을 했다. 총 두가지의 트랙으로 진행이 되었기 때문에 관심이 있는 트랙을 선택하며 들을 수밖에 없었다.
이외에 세션들은 유튜브를 통해서 공개될 예정으로 알고 있어서 크게 걱정하지 않았다.
1. 동시성의 미래 - 코루틴과 버츄얼 스레드 (이상훈 님)
이상훈 님은 수천, 수만의 동시 요청을 처리하는 서버를 구현하기 위한 다양한 기술 중, 전통적인 웹 방식(thread per request)부터 시작해 리액티브 프로그래밍, 그중에서 코틀린의 코루틴과 JDK 21에서 정식 릴리즈된 버추얼 스레드를 중점적으로 기술의 차이와 통합 방법에 대해 설명해 주셨다.
코루틴
핵심 내용 중 하나는 Kotlin 코루틴의 작동 방식을 이해하는 것이었다. 다수의 코루틴은 플랫폼 스레드에 매핑되어 사용되기 때문에 매우 가벼워서 기존 스레드보다 더 효율적이다. 그러나 코루틴은 여전히 플랫폼 스레드에서 실행되므로 결국 스레드가 블로킹되는 환경에서 성능이 저하될 수 있다.
버츄얼 스레드
그다음으로는 버츄얼 스레드를 설명해 주셨는데 해당 키워드를 이 강의를 통해 처음 알게 되었다. 버츄얼 스레드는 jdk 21에 릴리즈 된 기능으로 코루틴과 유사하게 JVM 레벨의 경량 스레드로 다수의 버츄얼 스레드가 캐리어 스레드에 매핑되어 사용된다는 것이다. 따라서 코루틴과 다르게 전통적인 동기식으로 코드 작성이 가능하며 논블로킹 환경의 코루틴 성능과 비슷하다는 것을 알았다. 하지만 아직 성능을 100% 내기는 어려운 환경이므로 리서치가 필수적으로 보인다.
코루틴과 버츄얼 스레드의 통합
이뿐만 아니라, 코루틴의 환경과 버츄얼 스레드를 통합하여 사용하면 코루틴의 단점인 블로킹 상황의 성능 하락 이슈를 버츄얼 스레드가 해결 가능할 것으로 보이며 상호운용을 통해 서로의 단점을 해결할 수 있어 보인다는 말씀도 주셨는데 굉장히 인상 깊었다.
특히 이번 스프링 캠프에서 전반적으로 코루틴에 대한 이야기가 꽤 많았다. 커지는 서비스에 비례하는 요청량, 다수의 MSA로 분리되는 서비스들, 길어지는 네트워크 구간을 해결하기 위해 모두 동시성 기법을 많이 채택하여 사용하는 것 같아 코루틴에 관심이 많이 가지게 되었다.
코루틴은 다음 포스팅을 통해 살펴보며 프로젝트에 사용해 볼 생각이다.
2.Spring Coroutine in action (최진영 님)
최진영 님은 spring에서 kotlin coroutine 활용에 초첨을 맞추어, 비동기 프로그래밍과 coroutine의 동작구조부터 spring 프레임워크에서의 실제 적용을 중점적으로 이야기해 주셨다.
코루틴과 구조적 프로그래밍
기존의 비동기 코드는 새 스레드의 실행이 새로운 실행 흐름으로 넘어가므로 흐름의 제어가 되고 있지 않기 때문에 이러한 관점에서 구조적 프로그래밍이라고 볼 수 없다는 것이다.
하지만 비동기를 사용하지 않을 수 없는 것은 모두 다 알 것이다.
코루틴은 각 스레드의 실행 후 실행한 부모 스레드의 흐름으로 돌아오게끔 보장하여 Structured Concurrency로 인해 코루틴이 손실되거나 누수되지 않음 등 코루틴의 전반적인 내용들은 굉장히 상세하게 말씀 주셨다.
해당 강의는 코루틴에 대해 공부할 때 한번 더 자세히 파악해보려고 한다.
3. 왜 나는 테스트를 작성하기 싫을까? (조성아 님)
조성아 님은 정말 직관적인 강의 제목으로 이론적이거나 추상적인 테스트 이야기 대신에, 간단한 예제와 실제 경험을 토대로 빠르게 작성하고 피드백을 받을 수 있는 테스트에 대해 얘기해 주셨다.
특히 픽스쳐 몽키란 테스트 관련 오픈소스 라이브러리를 개발하신 분으로 픽스쳐 몽키를 사용해 본 적 있던 나에게는 내적친밀감을 주셨다.
테스트 코드를 작성하기 싫은 이유
특히 요즘은 테스트 코드에 중요성에 대해 모두 다 알고 있는 만큼 왜 작성하기 싫은지에 대해 아주 간결하게 말씀 주시는 부분이 인상 깊었다. 테스트 코드란 무안단물이 아니며 테스트 코드를 작성하기 싫은 이유는 비용이 이득보다 크기 때문이라고 하셨다.
테스트 코드의 비용이란 작성 비용과 유지 비용이 있는데 테스트에 많은 기대를 하면 그게 모두 비용이 되며 각 테스트 단계마다 기대하는 정도가 다르기 때문이라고 하셨다.
비용을 결정하는 요소
특히 가장 공감이 됐던 부분은 테스트 코드 작성 비용을 줄일 수 있는 부분을 말씀 주셨는데 그게 바로 given과 then이었다.
필요하지 않은 복잡한 테스트 작성(given) 많은 검증(then)하려 하는 부분들을 줄여야 한다고 말씀 주셨는데 통합테스트와 인수테스트에 무거운 given, then 공감하고 있던 나에게 의미가 깊던 강의였다.
물론 각 프로젝트와 조직에 맞춰서 적절한 테스트 비용을 결정하는 것이 가장 좋은 방법일 것 같다.
끝으로 픽스쳐 몽키의 유용성에 대해서도 굉장히 많이 말씀 주셨는데 한번 사용해 볼 때 굉장히 간단히 기능들을 사용해 보았으나 꼭 필요한가? 란 생각이 들던 나에게 다시 한번 해당 라이브러리의 자세히 기능들을 살펴봐야겠다고 생각이 들었던 강의였다.
4. 실전! MSA 개발 가이드 (김용욱 님)
김영욱 님은 마이크로서비스를 개발할 때 가장 어려워하는 부분인 서비스 별로 데이터베이스를 분리하여 API로 대체한다는 점에 걱정하는 부분과 실전에서 마이크로서비스를 개발할 때의 구현 이슈를 소개하고, 실전에서는 어떻게 풀어내는지 소개해주셨다.
API로 속도가 나올까??
서비스가 api로 데이터를 많이 조회하고 많이 조합할 때 속도 저하가 일어날 수 있다. 그러나 이런 부분도 데이터의 성격을 데이터의 참조빈도와 변경 빈도 를 확인해보아야 한다.
참조 빈도가 높고, 변경 빈도가 높다면 매우 다루기 까다롭다. 하지만 이런 유형의 데이터의 종류는 다행히 많지 않으며 잘 알려진 분야이기에 솔루션도 많다
ex) 로그인 상태 정보 -> JWT 등을 이용
참조 빈도가 높고, 변경 빈도가 낮다면 다양한 방법을 통해 튜닝을 고려해볼 수 있다.
- 데이터 복제
- 동기화 하는 부분은 물론 단점이다.
- 하지만 “필요한 부분”만 복사하는 방향은 괜찮을 수 있다.
- 원본 소스의 서비스가 다운되어도 문제없기 때문이다.
- 모델링 변경
- 일괄 조회
- n+1 problem 같은 경우
- api 를 매번 조회 하지 말아야 한다.
- 마치 in query 같은 경우
- 병렬 조회?
- 대부분 안티 패턴이다.
- 순간적으로 큰 부하가 생길 수 있다.
- 꼭 필요한 경우에만, “일괄 조회”를 병렬로 실행해야 한다.
- 로컬 캐시
- ex) eh cache
- 실질적 네트워크 호출을 줄여주기 때문에 성능적으로 좋아지는 경우 많다.
- 사이즈는 작게 유지하는게 좋고 모니터링 하는게 좋다.
- 로컬 캐시는 동기화하지 않는다.
- 안티 패턴이다.
- 노드가 늘어날수록 동기화의 효율이 적기 때문이다.
트랜잭션 없이 정합성이 보장될까??
마찬가지로 API를 사용하는 경우 원자성과 독립성이 보장이 안된다.
- 원자성: db rollback을 못함
- 독립성: read committed이 안되고 read uncommited 수준으로 떨어진다.
원자성 보완이 필요한 경우
DB 롤백이 불가능하기 때문에 원자성이 보장되지 않는다.
- ex) 만약 A 서비스에 쓰기 요청에 B서비스의 쓰기 요청이 의존되는 경우
- 원칙적으로는 쓰기에 실패하면 타 서비스에서 커밋된 데이터를 API로 직접 삭제하고, 로컬에 생성된 데이터는 롤백하여 직접 처리해야 한다.
- 하지만 만약 B서비스의 데이터 삭제하다 실패하면?
- 그럼 A 서비스에서는 롤백되기 때문에 정합성 깨진다.
- API Retry??
- 어차피 재시도를 바로 해도 이미 안될 가능성이 높다.
- 안티 패턴
- 따라서 차라리 이벤트의 재시도하는 방식이 나을 수 있다.
- 이벤트는 무조건 전달 보장한다.
- 간혹 여러번 전달될수도 있을 수 있다.
- 그러므로 여러번 이벤트가 일어나도 결과는 동일하도록 구현해야한다.
- 결국 네트워크로 쓰기 작업을 하면 멱등성을 보장해야한다.
- 긴 TX 나누기
- 실패해도 전체를 취소할 필요가없다면 이벤트로 분리한다.
- 취소할 수 없는 쓰기는 이벤트로 분리한다.
- 역할 분리
- 다른 서비스들이 각자 알아서 하는 경우가 좋을수도 있다.
- 책임 분리, 의존 분리
- 모델링 변경
- 고객 <> 상담
- 상담 유의사항 속성이 필요하여 고객 참고 정보 테이블이 필요한 상황
- 해당 테이블을 고객 디비에 배치하는 것보단, 상담 서비스 디비에 하는것이 좋음
- DDD의 바운더리 컨텍스트 단위 개념과 비슷
- 즉 데이터는 오너십 가진 서비스에서 가지고 있기
- 서비스 경계 변경
- 너무 구현하기 힘든 상황이라면 서비스를 합치거나 경계를 변경해도 괜찮다.
독립성 보완이 필요한 경우
MSA는 서비스 간의 트랜잭션 격리 레벨은 Read Uncommitted 이다.
- 따라서 서비스 간의 데이터가 순간적으로 일치하지 않을 수 있다
- 정교하게 맞아야 한다면 어플리케이션 코드레벨에서 조절해야함
- 데이터베이스의 동기화 메커니즘을 사용할 수 없음
- select for update, 등 db 락 사용
- 원래도 사실 안티패턴이다.
- 어플리케이션 lock
- msa는 이 경우만 사용한다.
- select for update, 등 db 락 사용
평소 MSA에 대해서 많은 정의가 있지만 구체적으로 어떤 것이 MSA를 의미하는지 항상 헷갈리고 구체적인 구현측면에서 어떤점이 맞는지 헷갈려왔던 나에게 어느정도 판단할 수 있는 기준점이 되는 내용들이라고 생각이 들었고 많은 의미가 있다고 생각이 들었다. 특히 안티패턴을 피하는 것만으로도 큰 도움이 될 것 같다.
5. 구해줘 홈즈! 은행에서 3천만 트래픽의 홈 서비스 새로 만들기 (이영규님)
이영규님은 안정성이 중요한 은행 환경에서 트래픽이 많은 홈 서비스를 새로 개발하며, 동료들과 함께 고민하고 결정했던 경험과 회고들을 중점적으로 공유해주셨다.
특히 다음과 같이 구조적 문제와 성능 문제를 중점적으로 해결하려 하셧다고 한다.
구조적 문제
- 레거시 프로젝트로 인한 계층간 의존성 꼬여 있음
- 외부 의존성과 도메인 정책이 혼재되어 섞여 있음
이러한 문제들을 헥사고날 아키텍쳐를 기반으로 도메인 계층을 외부 의존성과 독립되도록 분리하셨다.
성능 문제 해결 - 기술 부채
- 외부 서비스 호출 증가에 따른 성능 이슈
- MSA로 인한 길어지는 네트워크 구간
마찬가지로 코루틴을 사용하셨고 async - await 패턴을 통해 해결하셨다고 말씀 주셨다.
안정적 이관 전략
개인적으로 이쪽 파트를 말씀주실때 가장 크게 인상깊게 다가왔던 것 같다.
서버만의 분리 작업이기 때문에 외부 인터페이스는 변하면 안되므로 안정적인 이관을 위해 정말 다양한 전략들을 사용하셨는데 모두 도움이 되었다고 하셨다.
- 응답 비교
- 기존 서비스 → (응답 전달(async), 응답 비교서비스) → 신규 서비스
- 기존 서비스에서 응답 비교 서비스 호출 한다.
- 응답 비교 서비스에서 신규 서비스 호출하여 비교 한다.
- 단점
- 응답 비교 서비스에서 호출하는 찰나의 순간 데이터 변경으로 달라지는 경우 있었다.
- 기존 다른 서비스들의 트래픽 2배가 된다.
- 표본 검사
- 하루 약 3천만 호출
- 검증 비율을 동적으로 변경할 수 있도록 설정
- 점진적으로 응답 비교 비율을 확대 및 모니터링
- A/B
- 게이트웨이에서 A/B 테스트 하듯이 트래픽을 일정 비율로 전환 가능하도록 설계
- 기존서비스, 신규서비스
- 단점
- 하이럼의 법칙
- 별칭 보정 기능 이슈
- 별칭이 존재하지 않으면 보정해주는 기능이 있었음.
- 신규 서비스에서는 이 기능을 제거
- 만약의 상황을 위한 기능이었기 떄문에
- 최근 호출 기록도 확인
- 하지만 나중에 다른 시스템에서 이 별칭 보정 기능을 의존하고 있었다는 걸 알게 됨
- 게이트웨이에서 A/B 테스트 하듯이 트래픽을 일정 비율로 전환 가능하도록 설계
- fallback
- 신규 서비스에서 예외가 발생하면 기존 서비스 응답으로 fallback
- nginx가 다운된 사례가 있었는데 fallback 기능을 통해 보완
이러한 안정적 이관을 위해 사용하셨던 전략들을 알게 되어서 굉장히 의미 있었고 아주 무거운 레거시 프로젝트를 전환하게 된다면 안정적인 이관을 위해 노력을 많이 쏟아야 한다는 것을 알게 되었다. 물론 이관용 서비스를 구축하는 것이 쉽지만은 않은 것은 알지만 안정성이 매우 중요한 서비스라면 필요해보인다고 생각이 들었다.
후기
이렇게 인상깊었던 세션들의 내용을 정리해봤는데 오프라인 세션에서는 사진 촬영과 한정된 시간으로 인해 모두 이해하고 맥락을 짚기엔 어려운 부분들도 많았지만 오프라인으로 참여하여 직접 들으며 경험과 노하우를 들을 수 있던 부분들이 좋았다. 아무래도 QnA는 오프라인 참여자들의 가장 큰 장점이지 않을까 싶다.
이 외에도 IT 회사들의 부스들에서 진행하는 채용 관련 상담과 굿즈, 간식 타임까지 있어서 준비를 많이 하셨다고 생각이 들었고 1분도 안되어 선착순 마감에 오프라인 세션을 열심히 듣던 개발자 분들을 보며 많은 자극을 얻었고, 유튜브를 통해 자세히 한번 볼 예정이다.