성공·누락·실패 구분하기
65분 안팎
학습 목표
응답 오류와 연결 실패를 구분합니다.
개념
오류도 응답인지부터 판단합니다
독서 보고서를 확인하는 사람이 오류 화면을 보았다고 알려 왔습니다. 첫 질문은 서버가 HTTP 응답을 보냈는지입니다. 상태 404를 받은 상황과 연결 자체가 거절된 상황은 조사 출발점이 다릅니다. 전자는 요청 대상과 서버의 경로 약속을 대조하고, 후자는 서버 실행과 주소·포트를 대조합니다. 모두 실패라는 한 단어로 기록하면 동료가 어느 계층부터 확인할지 알 수 없습니다.
상태 코드는 서버가 HTTP 응답에 담는 결과 분류입니다. 200은 이 조회 요청이 성공했다는 표시이며 보고서 합계까지 검증한 증거는 아닙니다. 404는 이 서버에서 요청 대상 경로를 찾지 못했다는 의미로 사용합니다. 500은 서버 쪽 처리 실패를 표현합니다. 이번 /fixture-error는 의도적으로 고정 500을 보내는 경로입니다. 실제 SQL 장애를 일으키거나 운영 장애를 재현했다고 해석하지 않습니다.
상태 숫자와 본문은 다른 정보입니다. 404 응답에도 NOT_FOUND라는 JSON 본문과 Content-Type이 있습니다. 500에도 FIXTURE_ERROR라는 본문이 있습니다. 본문이 읽힌다고 성공 응답은 아니고 JSON이라는 이유로 status를 무시할 수도 없습니다. 관찰표에서는 상태, 자료 형식, 오류 코드를 독립된 칸에 적어 같은 JSON 형식의 성공·실패를 구분합니다.
같은 서버에서 조건을 하나씩 바꿉니다
view_http.py를 실행해 표시된 기본 주소를 유지합니다. /report.json에서 /missing으로 경로만 바꾸면 포트와 서버 수명은 같고 대상 경로만 달라집니다. 이어 /fixture-error로 경로를 바꿉니다. 각각 새 요청 행을 선택하고 상태와 본문을 읽습니다. 실행마다 임시 포트가 달라질 수 있으므로 다른 실행의 주소를 섞어 비교하지 않습니다.
연결 실패는 터미널에서 Enter로 서버를 정상 정리한 뒤 같은 주소를 요청해 관찰합니다. 실행하지 않는 임의의 포트를 찍으면 우연히 다른 프로그램이 응답할 수 있습니다. 자동 검사는 컨텍스트가 닫힌 주소에 요청하여 응답 없는 상황을 확인합니다. 일반적인 로컬 실습 조건에서 확인하는 방식이며 오류 메시지의 문자열은 운영체제와 브라우저마다 달라질 수 있습니다.
개발자 도구가 연결 실패 행에 failed 또는 net::ERR_CONNECTION_REFUSED 같은 표현을 보여줄 수 있습니다. 이것은 서버가 보낸 HTTP 상태가 아닙니다. 상태 칸을 0, 404, 500으로 임의로 채우지 않고 HTTP 응답 없음이라고 적습니다. 캐시나 과거 화면과 혼동되지 않게 새 요청의 시각과 오류 문구를 남깁니다. 받은 본문이 없으면 오류 JSON을 만들어 넣지 않습니다.
Python 클라이언트의 예외를 해석합니다
urllib.request는 HTTP 오류 상태를 HTTPError로 알릴 수 있습니다. 예외가 발생했다는 이유만으로 연결 실패로 처리하면 404와 500의 실제 응답을 잃습니다. HTTPError는 응답 상태와 헤더·본문을 읽을 수 있는 객체이므로 observe는 이를 response로 받아 with 안에서 읽습니다. 그 다음 반환값의 kind는 http이고 status는 실제 숫자가 됩니다.
URLError는 연결이나 이름 해석 등 요청 실패를 감싸는 경우에 사용됩니다. HTTPError가 URLError의 하위 유형이므로 HTTPError를 먼저 잡아야 합니다. 넓은 URLError를 앞에서 잡으면 HTTP 오류도 응답 없는 실패로 분류됩니다. starter는 이 구분이 잘못되어 있으므로 test_missing과 test_error의 실제 kind와 기대 kind를 대조한 뒤 응답 객체를 보존하도록 수정합니다.
이 실습 observe는 외부 URL을 거부하고 환경 프록시를 끕니다. 반환하는 connection 분류는 제한된 루프백 실습에서 HTTP 응답을 받지 못한 경우를 나타냅니다. 그 분류만으로 모든 실패의 근본 원인을 확정할 수는 없습니다. 실무에서는 오류의 원인 객체와 시각 등 더 많은 진단 정보를 확보해야 합니다. 여기서는 개인정보가 있는 입력을 로그에 남기지 않고 요청 단계의 차이를 우선 익힙니다.
재시도 전에 바꿀 조건을 정합니다
/missing의 404에서 같은 잘못된 경로를 계속 새로고침하는 것은 경로 약속을 고치지 못합니다. /report.json과 비교해 오타와 대소문자를 먼저 봅니다. 고정 오류 fixture의 500 역시 기다리면 정상으로 바뀌는 서버가 아닙니다. fixture의 목적이 상태 관찰이라는 사실을 문서에 쓰고 보고서 경로와 구분합니다. 재시도 판단은 숫자 하나보다 요청의 의미와 실패 조건을 함께 봅니다.
서버가 정리된 뒤 연결 실패가 나왔다면 실행 도구를 다시 열고 새 주소를 사용합니다. 정리된 옛 포트에서 계속 요청하는 것은 재현 조건을 바꾸지 못합니다. 반대로 보고서 200에 잘못된 합계가 있다면 서버 생존 확인을 반복하기보다 기존 SQL 결과와 HTTP 본문을 비교합니다. 상태 분류는 조사 범위를 좁혀 주지만 업무 검증을 대신하지 않습니다.
검사에서 test_exception_cleanup은 관찰 도중 예외가 발생해도 서버가 닫히는지 확인합니다. finally 안에서 shutdown, thread.join, server_close를 실행하는 이유를 읽어 봅니다. 단순히 정상 종료 때만 닫으면 실패 재현 후 남은 서버가 다음 관찰을 오염시킬 수 있습니다. 새 기능을 고칠 때 기존 61개 테스트도 유지하여 통신 수정 때문에 파일 보존과 공개 열 정책이 약해지지 않게 합니다.
제출 전 관찰표에서 성공 200, 없는 경로 404, fixture 500, 연결 실패 네 행을 비교합니다. 앞의 세 행에는 HTTP 상태와 응답 정보가 있고 마지막 행에는 응답이 없습니다. 자동 문서 검사는 키워드만 확인하므로 실제로 관찰한 시각·주소·본문의 대응을 사람이 검토합니다. 각 행에 다음 조사 위치를 한 줄씩 적으면 오류를 설명하는 데서 조치를 고르는 단계까지 이어집니다.
HTTPError가 URLError의 하위 유형이면서 응답처럼 읽힐 수 있다는 설명은 Python 공식 urllib.error 문서에서 확인할 수 있습니다.
따라하기
정상과 고정 오류 비교
view_http.py가 출력한 같은 호스트와 포트에서 /report.json, /missing, /fixture-error를 차례로 엽니다. 각각 새 Network 행의 상태와 JSON 오류 코드를 비교합니다. 외부 HTTP 실행이 필요한 단계이므로 출력은 기재하지 않습니다.
응답 없는 경우 만들기
Enter로 서버를 정리하고 같은 URL로 다시 요청합니다. 오류 문구를 그대로 적고 HTTP 상태 칸에는 응답 없음을 씁니다. 브라우저가 이전 본문을 남겼다면 새 요청 행을 기준으로 판단합니다.
예외 상속 확인
HTTPError를 먼저 처리해야 실제 HTTP 응답을 잃지 않는 이유를 확인합니다.
from urllib.error import HTTPError, URLError
print(issubclass(HTTPError, URLError))실행 결과
True
구현과 검사
http_lab.py에서 없는 경로의 상태와 HTTPError 처리 TODO를 고칩니다. 압축 폴더에서 python3 -m unittest discover -s tests -p "test_*.py"를 실행합니다. FAIL의 실제 kind·status와 기대값을 비교하고 환경 ERROR는 구현 실패와 구분합니다.
확인 문제
실습
같은 실습의 없는 경로 상태와 HTTPError 처리를 고칩니다. 200·404·500·서버 종료 후 연결 실패를 비교하여 docs/http.md 관찰표를 완성합니다. README의 실행 절차를 사용합니다. 테스트는 수정하지 않습니다.
실행 명령
python3 -m unittest discover -s tests -p "test_*.py"
기대 결과
외부 환경에서 총 72개 검사 통과와 서버 정리를 확인합니다. 샌드박스에서는 소켓 바인딩 제한으로 PENDING입니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- HTTP 오류 응답과 연결 실패의 관찰 차이를 설명합니다.