SpringBootProject

SpringBoot : 동시성 문제

dding-shark 2025. 7. 9. 15:55
728x90

동시성 문제 정리

  • 보통, 스프링부트 프로젝트를 만들다보면, 자주 사용되는 객체를 계속해서 재생성 하기에는 메모리 낭비도 심하고, 지속해서 생성/삭제 하는 번거로움과, JIT 컴파일러가
    최적화 시켜주기 애매한? 좀더 심화해서는 에덴에 있음직한 친구가 에덴으로 못가는 문제가 있어서. 자주 이용해야하는 객체나 컴포넌트들을 싱글톤 패턴으로 생성하게 되는 경우가 많다
  • 싱글톤 패턴이란, 전역변수를 만드는 거랑 비슷하다고 보면 되는데, 객체를 하나 만들어 놓고 만들어진 객체를 여러 클래스들이 이용하게 하는 구조 패턴이다,
  • 예를 들면, DB에 접속하는 클래스나, 외부 API에서 정보를 가져오는 등... 여러 곳에서 필자또한 사용하고 있는데,
  • 스프링 심화편 강의를 듣다가 로그추적기를 만드는 실습을 진행하다가, 강사님 께서 로그추적기를 싱글톤 패턴으로 구조를 잡고, 만드시면서 동시성 문제를 지적하셨다.

문제의 원인

  • 문제의 원인은 각 트랜잭션 별로 서로다른 ID가 부여되고, LEVEL에 맞게 로그가 계층형으로 출력되게 하길 원하셨는데, 트랜잭션이 완료되기전에 다른 요청이
    들어오게 되면 싱글톤 내부에 있는 변수와 레벨을 새로들어온 요청이 간섭하게되어 로그가 원하는대로 출력이 되지 않는 문제가 발생했다.

상어의 처음 Solution

  • 처음 설계 하시자마자, traceId를 파라미터로 넘기는 문제를 어떻게 해결할건지와 동시성문제, 이미 완성되어있는 코드들이 있을탠대 이 코드들을 최대한 수정하지 않으면서
    이 기능을 어떻게 이식시킬건지가 내가 느낀 가장 첫 궁금증이었다.
  • 필자가 만약 이 기능을 만든다면(사실... 만든적이 있지만...) 당연하게 파라미터를 넘기거나... new 연산을 남발하거나.... 해서 구현한뒤에 임시 이중 버퍼전략(정확한 네이밍인지는 모르겠다)을 사용하여,
    기준 단위마다 txt 파일이나 데이터베이스에 업로드하는 그런 기능의 시스템으로 만들었을거같다.
  • 하지만, 이러면 동시성 문제와 파라미터 문제는 어느정도 해결이 될거 같은데, 서비스코드들을 수정해야한다는 것은, 생각보다 큰 비용을 요구한다는 건 자명해서
    사실 이부분을 좀더 보고싶었다. 데코레이트 패턴이라든지,, 등등 솔루션 몇개는 생각이 나지만, 완벽한 솔루션은 조금더 고민을 해봐야겠다.

강의를 듣고 난 후

  • 아직 동시성문제해결 까지만 영상을 봐서 그런지, 서비스에 잘 이식시키는 방법은 아직 알려 주시지 않으셨다,
  • 당연하게도 강사님은 로그추적기라는 기능을 개발하고 계시지만, MSA 구조에서 개발자가 가져야 하는 덕목중에 하나는,
  • 최소단위로 쪼개져 있는 여러 시스템에 영향을 최소화 하면서, 새로운 핵심기능이 아닌 부가기능들을 더하는 것이 가능 핵심적인 능력이라고 생각한다.
  • 싱글톤 패턴으로 어떠한 최소단위의 서비스를 만들고, 이를 서비스코드의 영향이 없게 추가하는 방법을 이 강의를 통해 배웠으면 좋겠다.

강의를 듣고 난 후 궁금증

  • 문득 이런 궁금증이 들었다,
    • Q1. 스프링부트 로그를 보면, WAS 에서 가져오는 쓰래드또한 가상 쓰래드이고, 필요할때마다 하나씩 이름붙혀서 나오는거 같은데... 안지우고 넣어놓으면... 가비지콜랙터가... 연결이 끊긴 객체니까 알아서 지워주지 않을까..?
      라는 생각이 들었다.
      • A1.
        1. WAS의 쓰레드 풀은 항상 현재 실행 중인(또는 대기 중인) 쓰레드에 대한 참조를 유지합니다. 따라서 쓰레드 객체는 GC의 수거 대상이 아닙니다.
        2. 살아있는 쓰레드 객체는 내부의 ThreadLocalMap에 대한 참조를 유지합니다.
        3. ThreadLocalMap은 우리가 ThreadLocal을 통해 저장한 데이터 객체에 대한 강한 참조(Strong Reference)를 가지고 있습니다.
        4. 결과적으로, remove()를 호출하기 전까지는 '쓰레드 풀 → 쓰레드 객체 → ThreadLocalMap → 데이터 객체' 로 이어지는 참조 사슬이 끊어지지 않습니다. GC의 관점에서는 이 데이터 객체가 여전히 '사용 중'인 것으로 보이므로 메모리에서 수거해가지 못합니다.
      • 만들어 쓰는게 아니라.. 진짜 걍 꺼내 오는거였네...;;;;;
      • 가상 쓰래드랑 햇갈린거같다... 아직 공부할게 너무 많다...;;
728x90