This story is based on a true story.
분명 소스상으로는 변경하는 코드가 없는데...
결제에 실패한 케이스를 분석하던 중에 뭔가 이상한 게 눈에 띄었어.
PG사 결제 파라미터 중에 "상점아이디"라는 걸 전달하고 있는데, 결제 방식에 따라 다른 값을 사용하고 있어.
예를 들어서 결제 방식이 인증이면 "A", 비인증이면 "B"를 사용하는 식이지.
String 상점아이디 = "";
if (결제방식 == "인증") {
상점아이디 = "A";
} else if (결제방식 == "비인증") {
상점아이디 = "B";
}
근데 인증방식(A)으로 요청된 결제건의 상점아이디가 실제 PG사에서는 비인증방식(B)으로 수신됐다는 거야.
인증, 비인증 방식으로 "상점아이디"를 결정하는 로직은 명확해서 중간에 실수할 게 없었고,
로직 외부에서도 "상점아이디"의 값을 변경하는 코드가 없었어.
그래서 어떻게 했냐면...
로그파일을 살펴봤어.
해당 결제 건이 발생한 시간대에 로그를 열어두고 추적한 거지.
근데 해당 결제 건에서 올바르게 선택된 "상점아이디"가 PG사로 요청을 하기 전에 값이 변경된 거야.
갑자기 싸한 느낌이 들어서 다시 소스를 살펴봤는데,
상점아이디를 저장하는 변수가 전역변수로 선언되어 있었어.
전역변수로 선언되어 있으면 변수가 변경될 수 있어...
현재 운영 중인 프로젝트는 스프링 프레임워크를 사용하고 있고,
문제가 발생한 클래스는 스프링 빈으로 등록해서 사용하고 있어.
근데 스프링에서는 빈으로 등록되는 클래스를 싱글톤방식으로 생성하거든.
싱글톤으로 생성되면 해당 프로그램상에서는 유일한 객체가 되는 거니까
다른 스레드에서 해당 인스턴스의 변수에 접근해서 값을 변경할 수 있다는 거지.
그래서 어떻게 했냐면... 2
그래서 지역변수로 변경했어.
지역변수는 다른 스레드에서 접근이 안되거든.
그리고 이걸 스레드세이프라고 얘기하지.
이걸 좀 더 깊게 이해하려면 JVM에서 메모리를 어떻게 관리하는지를 공부하면 돼
그리고 지역변수로 바꾸지 않고도 해결할 수 있는데
ThreadLocal이라는 클래스를 이용하면 돼
결국 모두가 행복해졌어!!!
PG사에서는 결제실패건의 발생이 줄어들었고
가맹점에서는 결제실패로 인한 이탈률을 줄일 수 있게 된 거지.
그리고 고객클레임을 처리하는 현업입장에서도 일거리가 줄어들었을 거야.
마지막으로 미약하지만 인터넷 트래픽 감소로 조금이나마 지구가 행복해지지 않았을까 싶어.
스레드 로컬(ThreadLocal)
동시성문제를 해결하기 위한 클래스스프링에 등록되는 빈은 싱글톤으로 생성된다.그래서 객체의 인스턴스가 애플리케이션에 한 개만 존재한다.그런데 여러 스레드에서 동시에 해당 인스턴스
aphrodisiac.tistory.com