시나리오 개요

개인 프로젝트를 진행하면서 2명 이상의 사용자가 같은 아이디를 가지고 회원가입을 진행한다면 과연 스프링은 어떤 결과를 낼 것인지 궁금해졌다. 현실에서는 일어날 확률은 매우 희박하지만 마른하늘에 날벼락도 맞는 사람도 있으니 일어나지 않을 것이라는 보장은 없기에 시나리오를 만들었다. 또한 그 해결방안도 생각해보자!

 

 

시나리오를 위한 준비물

Spring boot, spring data jpa, jmeter

 

코드

Controller에서 넘어온 데이터가 Service로 데이터를 넘기면 컨트롤러에서는 DB에 값이 존재하면 null을 보내고 존재하지 않는다면 저장하여 객체를 넘기는 간단한 코드이다.

 

Controller :

	@ResponseBody
	@GetMapping("/")
	public ResponseEntity<String> main(@RequestParam(value = "test") String test,
			HttpServletRequest httpServletRequest) {
		if (service.testSave(test) == null) {
			return new ResponseEntity<String>("fail", HttpStatus.BAD_REQUEST);
		}

		return new ResponseEntity<String>("success", HttpStatus.OK);
	}

 

Service :

public TestEntity testSaveService(String test) {
		if(testRepository.existsByTest(test)) {
			return null;
		}
		return testRepository.save(new TestEntity(test));
}

 

 

시나리오 1) 1초 간격으로 보낸 데이터

 

 

 

두 명의 사용자가 1초 간격으로 test=1234라는 데이터를 보낸다. 

 

 

 

시나리오 1) 결과 : 실패

역시 실패했다. 1초 간격으로 보냈지만 확실히 튕겨내는 모습을 보여주고 있다. 인간이 느끼기에는 짧은 시간이지만 스프링은 아닌 듯하다.

시나리오 2) 0.1초 간격으로 보낸 데이터

앞의 시나리오와 마찬가지로 2명의 사람이 0.1초 간격으로 데이터를 보낼 것이다.

 

시나리오 2) 결과 : 성공

성공했다. 0.1초 간격으로 보낸 데이터는 스프링이 해결을 하지 못하는 모습이다. 우연에 우연이 겹쳐 중복된 데이터가 db에 저장되고 두 명의 사용자가 자신이 만든 아이디로 로그인하게 된다면 재미있는 상황이 나오겠지만 나는 그런 상황은 보고 싶지도 않다.

 

왜 이런 상황이 생긴 것일까?

스프링은 요청이 들어온 순서대로 서비스를 처리해준다. 하지만 우연히 중복된 아이디가 들어올 경우 아주 짧은 시간의 요청은 처리하지 못하고 저장되는 모습을 보여줬다. 

여기서 발생된 문제는 스프링의 동시성 문제 때문일 것이다.. 두 명 이상의 유저가 하나의 객체에 접근해서 생기는 문제이다. 

 

※ 이후 해결방안 테스트 케이스 시간 간격은 0초 입니다.

해결방안 1) DB 레벨 : UNIQUE 

스프링이 동시에 데이터를 받아들여도 저장 단계에서는 분명 순서가 있을 것이라고 생각하기에 컬럼에 UNIQUE를 넣어 중복을 방지하는 것을 생각했고 확실히 중복을 막아주는 모습을 보여준다. 하지만 select select insert insert 문 순서로

원하는 모습은 select insert select insert 문 순서로 진행돼야 한다.

 

 

하지만 내가 원하던 모습은 아니다. 만일 unique 키워드를 붙여주지 않는다면 어떻게 해결해야 될까?

 

 

해결 방안 2) Service 레벨 : @Transactional(isolation = Isolation.SERIALIZABLE)

두 번째 해결 방안은 Service 영역부터 저장을 막는 것은 어떨까? 가장 강력한 트랜잭션을 걸어 동시성 문제를 해결하는 것이다. 성공적으로 막아내는 모습처럼 보이지만 아직도 select select insert insert 문 순서로 진행된다.

 

 

해결 방안 3) synchronized

가장 간단한 방법이지만 부하를 가중시키기 때문에 남발해서는 안 되는 키워드이다. 컨트롤러, 서비스 영역 상관없이 원하는 메서드에 붙여주면 된다. select insert select 문 순서로 나오기에 가장 이상적인 방법이지 않을까 한다.

 

 

 

결론

여러 가지 방법은 있을 것이다. 지식의 한계로 3개 정도 막을 방법을 생각했지만 아직 많이 부족한 것 같다. 가장 이상적인 방법은 synchronized이지만 남발하게 되면 요청 시간이 길어지게 될 것이다. 만일 대규모로 회원 가입이 이루어질 경우 시간 vs 데이터의 무결성이냐을 가지고 충분히 생각해야 될 것이다.

+ Recent posts