파이썬 예외 처리데이터 검증파이썬
잘못된 광고 데이터를 막는 검증과 예외 처리
그로스앤디 · 2026-08-25 발행· 9분 읽기
목차
코드 해부 1편에서는 광고 데이터가 JSON 파일에서 Python으로 들어와 결과 파일로 나가는 흐름을 살펴봤습니다. 2편에서는 들어온 목표와 캠페인을 딕셔너리·리스트·집합에 어떻게 담는지 알아봤고요.
이제 데이터가 들어올 길과 담길 자리는 마련됐습니다. 하지만 계산을 시작하기 전에 한 가지를 더 확인해야 해요. 그 데이터가 실제 광고 성과로 믿을 만한 값인지입니다.
실무에서는 수동 입력, 매체별 파일 병합, API 수집 과정에서 누락값·음수·중복 캠페인·잘못된 퍼널 수치가 섞일 수 있습니다. 이런 값을 그대로 자동 계산하면 오류 메시지보다 더 위험한 결과가 나옵니다. 잘못된 숫자가 정상적인 리포트처럼 대시보드와 의사결정에 전달되기 때문인데요.
이번 글에서는 검증과 예외 처리를 이용해 잘못된 입력을 계산 전에 걸러내고, 파일 전체를 멈춰야 할 오류와 캠페인 한 건만 제외할 오류를 구분하는 방법을 함께 확인해봅니다. 이 원리는 광고 리포트 자동화뿐 아니라 CSV 업로드, API 데이터 수집, 여러 소스를 합치는 데이터 파이프라인에도 그대로 적용할 수 있어요.
광고 데이터가 오염된 경우를 가정해보자
먼저 JSON 파일을 성공적으로 열었지만, 광고 데이터로는 사용할 수 없는 사례부터 보겠습니다.
다음 데이터의 JSON 문법에는 문제가 없어요.
{
"campaign": "여름 세일",
"spend": -300000,
"impressions": 1000,
"clicks": 1200,
"conversions": 1300,
"revenue": 1050000
}
하지만 광고 데이터로는 세 가지 문제가 있습니다.
- 광고비가 음수입니다.
- 클릭 수가 노출 수보다 많습니다.
- 전환 수가 클릭 수보다 많습니다.
이 값을 아무런 제한 없이 그대로 입력했을 때, Python은 아무 사고 없이 숫자를 나눕니다. (당연히 도구이기 때문에 우리가 시킨 일만 한답니다. 사고는 우리가 해야해요) 문제는 계산 결과가 현실을 설명하지 못한다는 점이에요.
데이터 검증의 목적은 단순히 결측데이터나 이상 데이터를 거르는데 있지 않아요. 중요한 건 우리가 이 데이터를 통해 얻고 싶은 정보들을 오염시키는 걸 막는데 있습니다.
오류는 한 종류가 아닙니다
이 프로그램은 오류를 네 단계로 나눠 확인합니다.
1. 파일을 열 수 있는가?
2. JSON 문법이 올바른가?
3. 목표와 전체 구조가 실행 가능한가?
4. 캠페인 각각이 정보규칙을 지키는가?
각 단계의 실패가 프로그램에 미치는 영향도 다릅니다.
| 오류 위치 | 예시 | 처리 방식 |
|---|---|---|
| 파일 | 경로에 파일이 없음 | 전체 실행 중단 |
| JSON 문법 | 쉼표·괄호 오류 | 전체 실행 중단 |
| 목표·최상위 구조 | goals 없음, 빈 campaigns |
전체 실행 중단 |
| 캠페인 | 음수 광고비, 중복 이름 | 해당 캠페인만 오류로 기록 |
목표값이 없다면 모든 캠페인의 판정 기준이 사라지므로 실행을 계속할 수 없죠. 반면 캠페인 열 건 중 한 건이 잘못됐다고 나머지 아홉 건까지 버릴 이유는 전혀 없습니다.
파일 오류를 프로그램 언어로 작성하기
load_json()은 파일을 읽는 과정에서 생기는 기술적인 예외를 잡습니다.
def load_json(file_path):
try:
with open(file_path, "r", encoding="utf-8") as file:
return json.load(file)
except FileNotFoundError:
raise ValueError(f"입력 파일을 찾을 수 없습니다: {file_path}")
except json.JSONDecodeError as error:
raise ValueError(
f"JSON 형식이 올바르지 않습니다: {error.msg} (line {error.lineno})"
)
except OSError as error:
raise ValueError(f"입력 파일을 읽을 수 없습니다: {error}")
try에는 정상적으로 실행할 코드를 넣고, except는 예상한 실패를 종류별로 처리합니다.
FileNotFoundError: 입력 경로에 파일이 없음JSONDecodeError: JSON 문법을 해석할 수 없음OSError: 권한이나 파일 시스템 문제로 읽을 수 없음
여기서 raise ValueError(...)는 오류를 없애지 않습니다. 파일 시스템이 만든 예외를 “입력값을 확인해야 한다”는 프로그램의 언어로 바꾸어 다시 전달합니다.
따라서, 예외처리라는 방식은 최소한의 안전장치라고 할 수 있어요. 실제로 코드를 돌려보면서 더 많은 예외처리의 규칙을 만들고 적용할 때도 있죠.
숫자처럼 보인다고 모두 광고 수치는 아닙니다
광고비·매출·목표값은 숫자여야 합니다. 하지만 Python에서는 True와 False도 정수의 하위 타입으로 취급됩니다.
isinstance(True, int) # True
따라서 숫자 타입만 확인하면 True가 광고비 1처럼 통과할 수 있어요. is_number()는 이 경우와 무한값을 함께 제외합니다.
def is_number(value):
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(value)
)
검증은 단순히 Python 타입을 확인하는 데서 끝나지 않습니다. 광고비와 매출은 0 이상이어야 하고, CPA 목표는 0보다 커야 한다는 업무 범위도 확인합니다.
목표 오류는 전체 실행을 중단합니다
validate_goals()는 목표 데이터의 구조와 값을 검사합니다.
def validate_goals(goals):
errors = []
if not isinstance(goals, dict):
return ["goals는 딕셔너리여야 합니다."]
target_roas = goals.get("target_roas")
target_cpa = goals.get("target_cpa")
if not is_number(target_roas) or target_roas < 0:
errors.append("target_roas는 0 이상의 숫자여야 합니다.")
if not is_number(target_cpa) or target_cpa <= 0:
errors.append("target_cpa는 0보다 큰 숫자여야 합니다.")
return errors
오류가 발견될 때마다 바로 멈추지 않고 errors 리스트에 모읍니다.
덕분에 ROAS와 CPA 목표가 모두 잘못됐다면 두 문제를 한 번에 알려줄 수 있어요.
main()은 오류 목록을 하나의 메시지로 합쳐 전체 실행을 끝냅니다.
goal_errors = validate_goals(goals)
if goal_errors:
raise ValueError(" ".join(goal_errors))
목표가 유효하지 않으면 모든 판정 결과가 의미를 잃기 때문입니다.
캠페인 오류는 한 건만 격리합니다
validate_campaign()은 캠페인마다 일곱 종류의 조건을 확인합니다.
딕셔너리인가?
→ 필수 항목이 모두 있는가?
→ 이름이 비어 있지 않은가?
→ 같은 이름이 이미 나왔는가?
→ 광고비·매출이 0 이상인가?
→ 노출·클릭·전환이 0 이상의 정수인가?
→ 전환 ≤ 클릭 ≤ 노출인가?
필수값은 REQUIRED_FIELDS를 기준으로 찾습니다.
missing_fields = [
field for field in REQUIRED_FIELDS if campaign.get(field) is None
]
퍼널의 업무 관계도 코드로 확인합니다.
if clicks > impressions:
errors.append("clicks는 impressions보다 클 수 없습니다.")
if conversions > clicks:
errors.append("conversions는 clicks보다 클 수 없습니다.")
이 조건은 Python 문법에서 자동으로 알려주지 않습니다. 광고 성과 데이터가 따라야 할 업무 규칙을 사람이 코드로 정의한 것입니다.
continue는 오류 데이터가 계산으로 내려가는 것을 막습니다
캠페인을 반복하는 diagnose_campaigns()는 검증 결과에 따라 경로를 나눕니다.
for number, campaign in enumerate(campaigns, start=1):
errors = validate_campaign(campaign, seen_names)
if errors:
results.append({
"number": number,
"status": "입력 오류",
"errors": errors,
})
continue
metrics = calculate_metrics(campaign)
오류가 있으면 결과에 원인을 기록한 뒤 continue로 다음 캠페인으로 이동합니다. 따라서 잘못된 값은 아래의 calculate_metrics()에 전달되지 않습니다.
캠페인 한 건
→ 검증
├─ 오류 있음 → 오류 기록 → 다음 캠페인
└─ 오류 없음 → 지표 계산 → 목표 판정
검증과 계산을 분리하면 정상 데이터만 계산 단계에 들어간다는 규칙을 만들 수 있습니다.
오류를 직접 만들어보세요
예제 JSON을 복사한 뒤 한 번에 하나씩 값을 바꿔보세요.
| 변경 | 예상 결과 |
|---|---|
| 입력 파일명을 틀리게 입력 | 전체 실행 중단, 파일 없음 메시지 |
| JSON의 쉼표 하나 삭제 | 전체 실행 중단, JSON 줄 번호 표시 |
target_cpa를 0으로 변경 |
전체 실행 중단, 목표 오류 메시지 |
spend를 음수로 변경 |
해당 캠페인만 입력 오류 |
clicks를 impressions보다 크게 변경 |
해당 캠페인만 입력 오류 |
| 캠페인 이름을 중복 입력 | 두 번째 캠페인 입력 오류 |
실행하기 전에 어떤 메시지가 나올지 먼저 적어보는 것이 중요합니다. 예측과 실제 결과가 다르다면 그 차이가 다시 살펴볼 코드입니다.
좋은 프로그램은 정상 입력에서만 작동하는 프로그램이 아닙니다. 잘못된 입력을 만났을 때 어디까지 처리하고, 무엇을 제외하며, 사용자에게 무엇을 고치라고 알려줄지 결정할 수 있어야 해요.
다음 글인 0으로 나눌 수 없는 광고 지표는 어떻게 계산할까에서는 검증을 통과한 정상 캠페인을 계산 단계로 보냅니다.
0으로 나눌 수 없는 상황을 None으로 처리하면서 CTR·CVR·CPC·CPA·ROAS를 만드는 함수를 살펴볼게요.
관련 키워드
파이썬 예외 처리데이터 검증try exceptraise광고 데이터 오류