Heeyaa

기록

세션 스위칭 트러블슈팅

2026. 4. 2.

안녕하세요.

오늘은 운영 환경에서 발생한 심각한 버그 중 하나인 ‘세션 스위칭(다른 사용자로 세션이 바뀌는 현상)’에 대한 트러블 슈팅 과정을 기록해보려 합니다.

결제와 주문이라는 민감한 비즈니스 로직에서 발생한 문제라 원인 파악과 해결이 매우 긴박하게 진행되었습니다.

문제 상황

운영 환경의 결제 및 주문 프로세스 중, 현재 사용자의 세션이 갑자기 다른 사용자(예: 구매자가 판매자로)로 전환되는 문의가 들어왔습니다.

  • 증상: 제3자가 아닌, 거래 대상자인 상대방의 계정으로 세션이 바뀜
  • 영향: 상대방의 채팅 목록이 노출되고, 해당 계정의 모든 권한을 행사할 수 있는 보안상 매우 치명적인 상황
  • 특이사항: 내부 테스트 시 재현이 매우 어려웠으며, 특정 타이밍에 발생하는 이슈로 판단

원인 분석

근본 원인 : ensureMemberId 함수의 사이드 이펙트

코드 분석 결과, 여러 파일에서 공통으로 사용되던 ensureMemberId 함수가 문제였습니다. 이 함수는 단순히 ID 포맷을 맞추는 역할을 넘어, 내부 상태와 스토리지까지 직접 수정하는 사이드 이펙트를 가지고 있었습니다.

문제의 코드 패턴

ensureMemberId(rawMemberId) {
	// ... 
	
	this.memberId = normalized; // 컴포넌트 내부 변수 덮어쓰기
	
	try {
		CryptoHelper.setKeyData('memberId', normalized); // localStorage 영구 저장
		} catch (error) {
			console.error(error)
			}
			
			return normalized;
}

이런식으로 memberId에 다른 사용자의 memberId가 들어오면 memberId가 덮어씌워져 localStorage에도 덮어씌워지는 현상이 발생

버그 발생 시나리오

  1. ensureMemberId(orderMember.id) 같은 잘못된 호출 발생
  2. 현재 로그인한 사용자의 memberId가 주문자 Id로 덮어써짐
  3. localStorage에도 덮어씌워져, 페이지 이동 후 에도 유지
  4. 이후 모든 API 호출 및 체크가 잘못된 memberId로 나감
  5. lcoaStorage, memberId가 덮어씌워졌기 때문에 서버는 신뢰
  6. 다른 사용자의 채팅/주문 등 보이는 현상이 발생

방어 코드가 없었나 ?

실제로 방어 코드가 없었는지 찾아봤었는데, 방어 코드가 존재 했습니다.

if (this.memberId != this.orderMember.id) {
	alert('잘못된 접근입니다.')
	router.go(-1);
	return;
}

위의 ensureMemberId 함수가 체크 로직보다 먼저 실행되면서, 이미 this.memberIdorderMember.id와 같아지도록 오염된 상태였습니다.

결과적으로 if 조건문은 항상 false가 되어 방어 로직을 그대로 통과하게 된 것입니다.


수정 내용

ensureMemberId 함수 순수 함수화

this.memberId = normalized 할당도 제거해 함수가 어떤 상태도 변경하지 않는 순수 함수로 수정

ensureMemberId(rawMemberId) {

	const numericCandidate = Number(candidate);
	const noramlized = Number.isNan(numericCandidate) ? candidate : numericCandidate;

	return noramlized;
}

더이상 this.memberId를 변경하지 않습니다.

스토리지 수정 권환 일원화

localStoragememberId를 쓰는 행위는 오직 로그인 시점에만 수행되도록 StorageHelper.js로 로직을 모으고, 다른 곳에서의 쓰기 호출(setKeyData)을 모두 제거했습니다.

이제 나머지 컴포넌트에서는 값을 읽기만 합니다.

문제의 호출 제거

문제가 되었던 ensureMemberId(orderMember.id) 호출을 제거


이 코드는 왜 들어갔는가?

이 코드가 도대체 왜 들어갔고 언제 들어갔는지 궁금해 찾아보니

과거 커밋 내역을 추적해 본 결과, 과거 모바일 웹 환경에서 결제 시 세션이 끊기는 이슈가 있었습니다.

당시 개발자가 이를 유지하기 위해 결제 로직 곳곳에서 강제로 ID를 스토리지에 저장하고 갱신하도록 처리했던 흔적이었습니다.


결론

이번 트러블 슈팅을 통해 함수의 사이드 이펙트가 시스템 전체에 얼마나 치명적인 영향을 줄 수 있는지 체감했습니다.

  • 상태를 변경하는 코드는 반드시 예측 가능한 곳(로그인, 프로필 수정 등)에만 존재해야 한다.
  • 순수 함수를 지향하여 예측 가능성을 높여야 한다.
  • 방어 코드를 작성할 때는 그 이전에 상태를 변화시키는 전조 현상이 없는지 면밀히 살펴야 한다.

앞으로 코드를 작성할 때 "이 코드가 과연 내가 의도한 일만 하는가?"를 항상 질문하며 사이드 이펙트에 대한 경각심을 가져야겠습니다.