예전에 나와 신입사원 2명이 한 달예정 프로젝트에 투입된 적이 있었다.
이 프로젝트가 아주 소규모에 한 달이라는 프로젝트였던 것은
이미 어느정도 개발이 된 상태의 프로젝트여서 였다
다만 엔티티와 dto의 변수명이 일본어여서 이것을 영문으로 바꿔달라는 의뢰였다.
분명히 이미 돌아간다고 했는데
받고나서보니 내부가 뒤죽박죽이었다.
컨트롤러에서 직접 sql을 날리는데 서비스와 리파지토리는 따로 있어 코드를 파악하기 힘들었다.
컨트롤러와 서비스와 리포지토리의 구분은 진짜 개발자들의 피땀의 산물이라 느꼈다.
규칙을 지키는 코드는 이후 수정할 개발자에 대한 배려와 매너임을 알았다.
이중에서 심각한건 바로 컨트롤러 필드의 접근시간을 가지고 화면의 최신화를 확인한다는 점이었다.
이 프로젝트는 관리자용 시스템이라 동시에 많은 사용자들이 오는 것은 아니었지만,
부서나 사용자에 따라 허용가능한 프로그램이나 권한등을 설정하는 것이었다.
관리자 A가 사용자B가 회계부의 일부에게만 접근가능한 중요한 회계 프로그램에 접근가능하도록 허가하기 위해
프로그램 권한 설정페이지를 열고 회계부임을 확인하는 중에
관리자 C가 사용자B의 부서를 영업부로 바꾼다면
관리자 A는 결국 영업부인 B에게 회계프로그램 접근권한을 주는 것이다.
근데 처음부터 코드가 안돌아가서 하나하나 뜯어보니 전혀 작동할 수 없는 코드였다.
일단 스프링에서 new로 필드를 생성해 곳곳에서 NP예외가 터지고 있었다.
보고한 뒤에 스프링을 제대로 적용해서 했는데
저 동시성 문제가 터진것이다.
의도한 바는 관리자 A와 관리자 C가 각각의 화면접근시간을 가지고,
변경 버튼을 누를 때 관련된 데이터의 갱신시간과 비교해 갱신이전이면 화면을 리프레시 하는 것이었겠지만,
실제로는 스프링 컨테이너에 의해 싱글톤으로 관리되는
컨트롤러의 필드는 계속해서 나중에 들어온 사용자의 갱신시간으로 바뀌었기에
제대로된 확인이 불가했다.
이 때 우리는 각 사용자의 세션이 다르니 그냥 세션에 화면접근시간을 저장해 비교하도록 변경하였다.
근데 사실 쓰레드로컬 데이터타입을 정의하면 더 간단하게 쓰레드별로 각 데이터를 가지게된다.
private final ThreadLocal<String> filed1= new ThreadLocal<>();
멀티 쓰레드 싱글톤인 스프링 빈의 필드를 쓰거나 변경하고 해당값을 참조할 때
일반적인 경우 각 쓰레드가 동시에 변경하게 되면 정확한 값을 보장할 수 없다.
따라서 각 쓰레드 별로 해당 필드의 값을 가질 수 있도록 하는 쓰레드 로컬을 적용할 수 있다.
자바환경에서 지원하는 쓰레드로컬을 활용하게 되면
동시에 싱글톤 빈의 필드에 접근하더라도 저장이나 참조하는 장소는 쓰레드 별로 관리되기 때문에
서로간에 간섭이 일어나지 않는다.
주의할 점은 쓰레드로컬 데이터를 사용한 뒤 반드시 remove해야한다.
왜냐하면 기본 적으로 spring은 스레드풀을 사용하는데 만약 이를 비워주지 않으면
재사용된 스레드가 이전의 데이터를 가지고 있어 정확한 처리를 하지 못할 수도 있기 때문이다.
'프로그래밍 > java' 카테고리의 다른 글
[spring] 스프링부트의 로그 시스템 (0) | 2023.05.02 |
---|---|
[spring] 스프링 AOP는 언제 사용하지 (0) | 2023.04.27 |
[spring AOP] 어드바이스의 종류 (0) | 2023.01.31 |
[spring] spring AOP (0) | 2023.01.19 |
[spring] spring data rest (0) | 2022.10.25 |