Skip to Content
Backend네이버페이 연동의 동시성 파싱 오류 수정
💻 Backend2024년 4월 25일

네이버페이 연동의 동시성 파싱 오류 수정

#e-commerce#payment-expansion#backend#naverpay
Payment Expansion · Series 4 · Stabilization & Propagation12 / 14Backend
여러 스레드가 공유하는 파서에서 발생한 간헐 오류를 살펴보고, 날짜 파싱 객체의 공유 방식을 정리합니다.

네이버페이 연동 서비스에서 간헐적인 파싱 오류를 수정한 적이 있습니다. 비동기 워커의 여러 스레드가 같은 코드를 실행하는 환경에서 스레드에 안전하지 않은 파서 객체를 공유하고 있었습니다.

한 번씩 직접 호출하는 테스트에서는 잘 드러나지 않았습니다. 여러 호출이 겹치는 상황과 공유 객체의 상태를 함께 살펴봐야 하는 문제였습니다.

공유된 날짜 파싱 객체

문제가 된 부분은 날짜와 시간을 읽는 SimpleDateFormat 인스턴스였습니다. 클래스 필드에 두고 공유하면 여러 스레드가 같은 객체의 parse()나 format()을 동시에 호출할 수 있습니다.

SimpleDateFormat은 스레드에 안전하지 않습니다. Java 공식 문서 도 스레드마다 별도 인스턴스를 사용하거나 외부에서 동기화하도록 안내합니다. 한 스레드에서 정상 동작했다는 결과로 동시 호출까지 안전하다고 볼 수는 없습니다.

파서 공유 방식 수정

수정의 핵심은 같은 가변 파서를 여러 스레드가 동시에 사용하지 않도록 하는 것이었습니다. 방법은 스레드별로 인스턴스를 나누거나, 동기화하거나, 스레드에 안전한 날짜 처리 API를 사용하는 식으로 나눠 볼 수 있습니다.

아래는 DateTimeFormatter를 사용하는 설명용 예시입니다. 당시 수정 코드를 그대로 옮긴 것은 아닙니다. DateTimeFormatter 는 불변이며 여러 스레드가 공유할 수 있습니다.

java
sample/payment-time-parser.java
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("uuuuMMddHHmmss") .withResolverStyle(ResolverStyle.STRICT); private static final ZoneId PAYMENT_ZONE = ZoneId.of("Asia/Seoul"); public Instant parseApprovedAt(String raw) { return LocalDateTime.parse(raw, FORMATTER) .atZone(PAYMENT_ZONE) .toInstant(); }

예시는 입력 문자열이 서울 기준 시각이라는 가정입니다. 실제 적용할 때는 응답이 어느 시간대인지와 날짜 형식을 맞춰야 합니다. 파서를 교체하면서 같은 문자열을 다른 시각으로 해석하면 동시성 오류와 별개의 문제가 생깁니다.

문서화와 코드 확인

같은 시기에 저장결제 등록·해지·인증 API도 문서화했습니다. 요청과 응답을 코드에 맞춰 정리하는 과정은 각 처리에서 어떤 객체를 사용하는지 다시 살펴보는 계기가 됐습니다.

이번 문제에서는 필드 하나의 값보다 그 값을 읽는 객체가 어디서 만들어지고 누구와 공유되는지가 중요했습니다. 메서드가 짧아도 내부에서 사용하는 객체까지 멀티스레드 호출에 안전한 것은 아니었습니다.

네이버페이 도메인 변경 대응은 별도 글에서 다룹니다. 다음 글에서는 결제 로그 설정을 여러 서비스에 전달한 과정을 정리합니다.

Last updated on