[NET-011][실무] 끊김에도 버티는 요청 설계하기 > IT 기술 공유

본문 바로가기
사이트 내 전체검색

IT 기술 공유

[NET-011][실무] 끊김에도 버티는 요청 설계하기

페이지 정보

profile_image
작성자 기술팀장
댓글 0건 조회 80회 작성일 26-09-05 16:35

본문

[이번 수업]
네트워크 요청은 느려지거나 끊기고 서버가 잠시 응답하지 못할 수 있습니다. 무한 대기와 즉시 반복은 장애를 키웁니다. 이번에는 타임아웃, 재시도, 지수 백오프와 지터로 일시적 실패를 다룹니다.

[선수지식]
TCP 연결과 HTTP 상태 코드, Python 예외 처리와 반복문, 터미널에서 프로그램 두 개를 실행하는 법을 알고 있어야 합니다.

[학습목표]
1. 타임아웃과 전체 마감 시간을 구분한다.
2. 재시도해도 안전한 요청을 고른다.
3. 백오프·지터·횟수 제한으로 요청 폭주를 막는다.

[핵심개념]
타임아웃은 연결이나 읽기 한 단계를 기다리는 한계입니다. 실무에서는 DNS, TCP, TLS, 응답 읽기와 모든 재시도를 포함한 전체 마감 시간도 둡니다. 라이브러리마다 `timeout`의 범위가 다르므로 문서를 확인합니다.

재시도는 연결 실패, 타임아웃, 503처럼 일시적인 실패에 제한합니다. 멱등(idempotent)은 같은 요청을 여러 번 보내도 의도한 서버 상태가 한 번 보낸 것과 같은 성질입니다. 조회는 대체로 재시도하기 쉽지만 주문 생성 같은 POST는 중복될 수 있으므로 고유 요청 ID와 서버 측 중복 방지가 필요합니다. `Retry-After` 응답이 있으면 그 시간도 고려합니다.

동시 재시도는 회복 중인 서버를 다시 압박합니다. 대기를 0.25초, 0.5초, 1초처럼 늘리는 지수 백오프에 작은 무작위 지연인 지터를 더하고, 최대 시도와 전체 시간을 제한합니다.

[따라하기]
server.py는 두 번 503을 보낸 뒤 200을 반환합니다.
```python
from http.server import BaseHTTPRequestHandler, HTTPServer

count = 0
class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        global count
        count += 1
        code = 503 if count < 3 else 200
        body = b"retry" if code == 503 else b"ok"
        self.send_response(code)
        if code == 503:
            self.send_header("Retry-After", "1")
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, *args):
        pass

HTTPServer(("127.0.0.1", 8000), Handler).serve_forever()
```
client.py를 만듭니다.
```python
import random, time
from urllib.error import HTTPError, URLError
from urllib.request import urlopen

for attempt in range(3):
    try:
        with urlopen("http://127.0.0.1:8000", timeout=1) as response:
            print(response.status, response.read().decode())
            break
    except HTTPError as error:
        if error.code != 503 or attempt == 2:
            raise
    except URLError:
        if attempt == 2:
            raise
    delay = min(0.25 * 2 ** attempt, 1) + random.uniform(0, 0.1)
    print(f"재시도 {attempt + 1}: {delay:.2f}초 뒤")
    time.sleep(delay)
```
첫 터미널에서 macOS·Linux는 `python3 server.py`, Windows는 `py server.py`를 실행합니다. 둘째 터미널에서 같은 방식으로 client.py를 실행하세요. 재시도 안내 두 번 뒤 `200 ok`가 나오면 성공입니다. 재시험 전 서버를 다시 켭니다.

[흔한 실수]
모든 오류를 재시도하거나 각 시도에만 타임아웃을 두어 전체 대기가 길어지곤 합니다. 마지막 원인과 시도 횟수를 기록하세요. 결과를 모르는 쓰기 요청을 반복할 때는 중복 처리 여부를 확인해야 합니다.

[보안 주의]
재시도 주소를 외부 입력 그대로 받아 내부 주소로 요청하지 마세요. TLS 인증서 검증을 끄지 않고 로그에 인증 헤더·토큰·개인정보를 남기지 않습니다. 횟수와 동시 요청 수를 제한하고, 실습은 본인 소유 로컬 서버에서만 진행합니다.

[직접 해볼 과제]
서버가 네 번 실패하게 바꾸되 최대 시도는 3회로 유지해 최종 오류와 걸린 시간을 기록하세요. 이어 전체 마감 시간이 1초를 넘으면 중단하도록 개선합니다.

[확인문제]
1. 개별 타임아웃과 전체 마감 시간은 어떻게 다른가요?
2. POST 재시도가 중복 처리를 만들 수 있는 이유는 무엇인가요?
3. 지수 백오프에 지터를 더하는 목적은 무엇인가요?

[다음 학습]
NET-012에서 DNS, TLS, 프록시, 방화벽, 타임아웃을 묶어 작은 웹서비스의 네트워크 구성도를 만듭니다.

[공식 참고 자료]
https://www.rfc-editor.org/rfc/rfc9110.html#name-idempotent-methods
https://www.rfc-editor.org/rfc/rfc9110.html#name-retry-after
https://docs.python.org/3/library/urllib.request.html
https://docs.python.org/3/library/urllib.error.html
https://docs.python.org/3/library/http.server.html
https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md

댓글목록

등록된 댓글이 없습니다.

회원로그인

회원가입

사이트 정보

회사명 : 회사명 / 대표 : 대표자명
주소 : OO도 OO시 OO구 OO동 123-45
사업자 등록번호 : 123-45-67890
전화 : 02-123-4567 팩스 : 02-123-4568
통신판매업신고번호 : 제 OO구 - 123호
개인정보관리책임자 : 정보책임자명

접속자집계

오늘
3,903
어제
6,862
최대
16,772
전체
769,854
Copyright © 소유하신 도메인. All rights reserved.