Retry 로직 개발 시 고려해야할 사항
꼬리 질문
- Retry가 불가능한 로직이란 구체적으로 어떤 경우들인가요? 그리고 이런 케이스들을 어떻게 구분해서 처리하셨나요?
- 낙관적 락 외에 네트워크 호출(외부 API, DB 연결 등)에서 retry를 적용한다면, 어떤 예외나 응답 코드에 대해서만 retry를 수행해야 할지 기준이 있으실까요?
- API Rate Limit의 경우
429 Too Many Requests
응답이 오는데, 이때 응답 헤더에Retry-After
가 포함되어 있을 수 있어요. 이런 동적 대기 시간은 어떻게 처리하실 건가요? - HTTP Timeout의 경우도
Connection Timeout
과Read Timeout
이 다른데, 둘 다 retry 대상으로 보시나요? 특히 Read Timeout은 서버에서 처리가 완료되었을 수도 있는 상황인데, 이때 멱등성이 보장되지 않는 요청(POST로 데이터 생성 등)은 어떻게 처리하시겠어요? - 멱등성(Idempotency)에 대해서는 어떻게 생각하시나요? 예를 들어, 결제 API를 호출했는데 Read Timeout이 발생했다면, 실제로는 결제가 성공했을 수도 있잖아요. 이런 상황에서 안전하게 retry를 구현하려면 어떤 방법들이 있을까요?
- 멱등키를 지원하지 않는 레거시 API나 외부 시스템과 연동해야 하는 상황이라면 어떻게 하시겠어요? 예를 들어, 이메일 발송 API나 SMS 발송 같은 경우 말이죠?
- 멱등키를 사용할 때 키 생성 전략도 중요한데, 보통 어떤 방식으로 생성하시나요? UUID를 쓸지, 요청 내용의 해시값을 쓸지, 아니면 다른 방법이 있을지요?
- "응답이 올 때까지 재시도"라고 하셨는데, 만약 정말 오랫동안 응답이 안 온다면? 최대 대기 시간이나 총 retry 시간에 대한 제한도 필요할 것 같은데 어떻게 생각하시나요?
답변 보기
-
✅ 실무 경험: 낙관적 락 적용 시 CAS 연산 실패로 인한 Exception 발생 상황에서 Retry 로직을 구현했습니다 🔄
-
✅ 핵심 고려사항:
- 백오프 전략: 동시 트래픽 몰림 시 Retry 시간을 랜덤하게 줘서 재충돌 방지
- 최대 재시도 횟수 제한: 무한 반복 방지를 위한 필수 요소
-
✅ 꼬리질문: Retry가 불가능한 로직이란 구체적으로 어떤 경우들인가요?
- DB가 완전히 다운된 상황
- API Rate Limit에 걸린 상황
- 화이트리스트 방식 채택: Retry 가능한 에러들만 명시적으로 관리하는 것이 안전
- HTTP Timeout
- Resource Exhausted
- CAS로 인한 Update Fail
- 예상치 못한 에러에 대한 무분별한 retry 방지 가능
-
✅ 꼬리질문: API Rate Limit 상황에서 Retry-After 헤더가 있을 때 동적 대기 시간 처리 방법
- Circuit Breaker 패턴 활용이 적절 🔌
429 Too Many Requests
응답 시 Circuit Breaker를 열고, Retry-After 헤더의 시간만큼 대기 후 Half-Open 상태로 전환- 단순 retry보다 서버 부하를 줄이는 효과적인 방법
-
❌ 꼬리질문: Connection Timeout vs Read Timeout 처리 방식
-
❌ Connection Timeout은 단순히 연결 실패만이 아닙니다 🔌
- DNS 해석 실패: 완전히 안전, retry 가능
- TCP 3-way handshake 실패: 안전, retry 가능
- SSL/TLS handshake 실패: 안전, retry 가능
- HTTP 요청 전송 중 실패: ⚠️ 위험할 수 있음
-
❌ Read Timeout은 가장 복잡한 상황입니다 ⏰
- 서버에서 처리가 완료되었을 수도, 진행 중일 수도 있는 상태
- 단순 retry는 위험할 수 있음
- Connection Timeout: 서버 연결 자체 실패, 안전하게 retry 가능
- Read Timeout: 요청은 전송되었으나 응답 지연, 서버에서 처리 중일 가능성
-
-
✅ 꼬리질문: 멱등성을 활용한 안전한 retry 구현
- 멱등키(Idempotency Key) 활용: 동일한 키로 여러 번 호출해도 한 번만 실행되도록 보장 🔑
- 결제 API 같은 중요한 작업에서 필수적
- 응답이 올 때까지 재시도해서 확실한 결과를 클라이언트에 전달
-
✅ 꼬리질문: 멱등키 생성 전략
- 기본적으로 UUID 사용
- User-specific한 경우: User ID를 prefix로 붙여서 사용자별 구분 👤
- 동시 처리 시 유용하고 중복 생성 감지 가능
-
❌ 꼬리질문: 멱등키 미지원 시스템 대응 방안 (이메일/SMS 발송 등)
- 애플리케이션 레벨 중복 방지:
- 데이터베이스에 발송 이력 테이블 생성
- (user_id, message_type, content_hash, created_at) 조합으로 중복 체크 📧
- 상태 조회 API 활용:
- 요청 ID나 transaction ID를 저장 후 상태 조회로 결과 확인
- 예: SMS 발송 후 message_id로 delivery status 조회
- 보상 트랜잭션 패턴:
- 중복 발송 시 롤백 메커니즘 준비 (사과 메일 발송 등)
- 애플리케이션 레벨 중복 방지:
-
❌ 꼬리질문: 총 retry 시간 및 대기 시간 제한 전략
- 총 실행 시간 제한 (Total Timeout):
- 전체 retry 프로세스가 30초를 넘지 않도록 제한 ⏰
- 개별 재시도 간격이 늘어나더라도 총 시간 초과 시 중단
- 단계별 백오프 + 최대 대기 시간:
- 1회: 100ms, 2회: 200ms, 3회: 400ms...
- 최대 5초까지만 대기, 그 이후는 5초 고정
- Dead Letter Queue 활용:
- 최종 실패 요청들을 별도 큐에 저장
- 수동 처리 또는 배치 재처리 📥
- 모니터링 및 알람:
- retry 실패율 임계치 초과 시 즉시 알람
- 시스템 전체 장애 가능성 빠른 감지
- 총 실행 시간 제한 (Total Timeout):
자바 synchronized의 락 범위와 종류
꼬리 질문
- static synchronized method와 인스턴스 synchronized method의 락 범위 차이는?
- synchronized method와 synchronized block의 락 객체 차이는?
- 서로 다른 락 객체를 사용하는 synchronized block들의 동시 실행 가능성은?
답변 보기
- ✅ 인스턴스 synchronized method들은 같은 인스턴스에서 서로 블로킹됨
- ❌ 꼬리질문: static synchronized method와 인스턴스 synchronized method의 락 범위 차이는?
- static synchronized method는 Class 객체를 락으로 사용 (클래스 레벨 락)
- 인스턴스 synchronized method는 this 객체를 락으로 사용 (인스턴스 레벨 락)
- 따라서 static과 인스턴스 메서드는 서로 다른 락이므로 동시 실행 가능 🔄
- ❌ 꼬리질문: synchronized method와 synchronized block의 락 객체 차이는?
- synchronized method: 자동으로 this(인스턴스) 또는 Class(static) 객체를 락으로 사용
- synchronized block: 개발자가 명시적으로 지정한 객체를 락으로 사용
- synchronized(lock1), synchronized(lock2)처럼 다른 객체 사용 시 동시 실행 가능 ⚡
- ❌ 꼬리질문: 서로 다른 락 객체를 사용하는 synchronized block들의 동시 실행 가능성은?
- 서로 다른 락 객체를 사용하면 동시 실행 가능
- 각 락 객체는 독립적인 모니터를 가지므로 상호 간섭하지 않음
ReentrantLock의 고급 기능과 활용
꼬리 질문
- tryLock(timeout) 기능이 언제 유용한가?
- ReentrantLock이 synchronized보다 유리한 상황들은?
답변 보기
- ✅ 큐 방식은 락 대기 스레드들이 대기큐에서 순서대로 대기하는 방식
- ❌ 꼬리질문: tryLock(timeout) 기능이 언제 유용한가?
- 데드락 방지: 일정 시간 후 락 획득을 포기하고 다른 로직 수행 🚫
- 응답성 향상: 사용자 요청이 무한정 대기하지 않도록 타임아웃 설정
- 장애 격리: 특정 리소스 문제가 전체 시스템을 멈추지 않도록 방어
- ❌ 꼬리질문: ReentrantLock이 synchronized보다 유리한 상황들은?
- 타임아웃이 필요한 경우 (tryLock)
- 락 획득을 중단해야 하는 경우 (lockInterruptibly)
- 공정성이 중요한 경우 (fair lock 옵션)
- 조건 변수가 필요한 경우 (Condition 객체 활용)
- 락 상태를 확인해야 하는 경우 (isLocked, getQueueLength 등)
Atomic 클래스의 CAS 연산 처리
꼬리 질문
- CAS 연산이 실패했을 때 어떻게 처리되는가?
답변 보기
- ✅ Atomic 클래스는 CAS 연산을 통해 동시성 보장
- ✅ volatile을 활용해 메모리 가시성 문제 해결
- ❌ 꼬리질문: CAS 연산이 실패했을 때 어떻게 처리되는가?
- CAS 실패 시 스핀 루프(spin loop)를 통해 재시도 🔄
- 현재 값을 다시 읽어와서 새로운 값을 계산하고 CAS 재시도
- 성공할 때까지 반복하는 lock-free 알고리즘
- 예: AtomicInteger.incrementAndGet()은 내부적으로 CAS 루프 사용