피드로 돌아가기
컬리 기술블로그Backend
원문 읽기
컬리 공통 암호화 모듈의 동시성 이슈 해결하기
컬리가 AWS KMS 암호화 모듈의 ByteBuffer 동시성 이슈를 duplicate() 메소드로 해결해 모든 연동 서비스의 간헐적 복호화 오류 제거
AI 요약
Context
컬리의 공통 암호화 SDK 모듈을 사용하는 1:1 문의 게시판 서비스에서 일부 요청에 대해 "newPosition > limit" 버퍼 연산 오류가 간헐적으로 발생했다. 오류 발생 시 고객이 마스킹 처리된 전화번호를 확인하지 못하는 문제로 이어졌으며, 암호화 모듈을 사용하는 모든 서비스에 동일한 문제가 발생할 가능성이 있었다.
Technical Solution
- AWS KMS의 GenerateDataKeyResult에서 제공하는 ByteBuffer를 clear() 메소드로 초기화한 후 SecretKey.createKey()에 전달: position을 0으로 리셋하고 limit을 capacity로 초기화하는 방식으로 버퍼 준비
- Caffeine 캐시를 통해 AWS KMS API 호출 빈도 감소: Data Key를 캐시에 보관해 재사용함으로써 KMS 요청 횟수 최소화
- 멀티스레드 환경에서 ByteBuffer의 position 상태가 스레드 간 간섭되는 동시성 이슈 진단: 100개 스레드로 암호화 로직을 동시 실행하면서 버퍼의 limit, position, length 값을 로깅해 position이 0이 아닌 32로 출력되는 현상 확인
- ByteBuffer.duplicate() 메소드를 호출해 스레드별 독립적인 버퍼 객체 생성: 각 스레드가 position 포인터를 독립적으로 관리하도록 변경
- getByteArray() 메소드에서 ByteBuffer 매개변수에 duplicate() 적용: GenerateDataKeyResult.getPlaintext()의 자바독에서 권장하는 asReadOnlyBuffer()와 동일한 동작 원리 적용
Key Takeaway
Java ByteBuffer는 스레드 안전하지 않으므로 멀티스레드 환경에서는 각 스레드가 독립적인 버퍼 인스턴스를 가지도록 duplicate() 또는 asReadOnlyBuffer() 메소드를 사용해야 하며, synchronized를 사용한 동기화는 성능 트레이드오프를 초래하기 때문에 버퍼 격리가 더 효율적인 해결책이다.
실천 포인트
AWS KMS 암호화를 캐싱하며 멀티스레드로 처리하는 Java 서비스에서 ByteBuffer의 position 상태 변화로 인한 간헐적 오류가 발생하면, 버퍼를 사용하는 메소드에 duplicate() 메소드를 호출해 스레드별 독립적인 버퍼 복사본을 생성하면 동시성 이슈를 성능 저하 없이 해결할 수 있다.