구글 애드센스 광고 게재 제한 해결|무효 트래픽 차단 실전 (2편)

구글 애드센스 광고 게재 제한 해결 |무효 트래픽 원인과 대처 (1편)에서 불량 리퍼러 차단과 무효 트래픽 문의 양식 제출을 시도했지만, 방향이 맞지 않았는지 받아들여지지 않았고 애드센스 광고 게재 제한은 계속되었다. 구글의 자동 트래픽 모니터링에서 일시적으로 이상 트래픽을 감지한 것으로 계정 정지 등의 큰 위험은 없다고 판단해 조금 더 기다려 보기로 했다. 인터넷을 검색해보니 1개월 전후로 광고 게재 제한이 풀렸다는 글이 많았고, 길게는 6개월 정도 걸렸다는 글도 있었다.
다른 일로 바빠서 한동안 잊고 지내다 보니 며칠이 흘렀고, 광고 게재 제한이 풀릴 기미가 보이지 않아 다시 무효 트래픽 문제점을 직접 확인해 보기 시작했다.

워드펜스 라이브 트래픽 로그 확인

먼저 워드펜스(Wordfence)의 라이브 트래픽 로그를 확인해보았다. 주로 해외에서 비정상적인 접근들이 있었으나 잘 차단되고 있었다. 접속 수도 그리 많지 않아 해당 비정상적 접근들은 애드센스 광고 게재 제한과는 직접적인 관련은 없어 보였다. 그러나 이러한 접근들은 특정 IP에서 비정상적인 트래픽을 발생시킬 수 있으므로 차단 조치가 필요해 보였다.

워드펜스 라이브 트래픽 로그
 워드펜스 라이브 트래픽 로그


비정상적인 접근을 보다 효과적으로 차단하기 위해, IP 주소가 규칙을 위반할 때 차단되는 기간을 기존보다 늘려 1시간으로 재설정했다.

워드펜스 IP 주소 차단 기간 설정 화면
 워드펜스 IP 주소 차단 기간 설정 화면

구글 애널리틱스로 국가별 트래픽 확인

구글 애널리틱스의 인구통계 세부정보 메뉴에서 국가별 트래픽 유입을 확인해보았다. 97% 이상이 국내 유입이고 해외 유입은 3%도 되지 않았다. 단순히 해외 유입 비율만 보면 크게 문제가 없어 보이지만, 실제 무효 트래픽은 수치보다 질적인 부분에서 발생할 수 있어 더 정밀한 분석이 필요했다.

구글 애널리틱스 인구통계 세부정보 화면
 구글 애널리틱스 인구통계 세부정보 화면

웹로그 분석 — Python 스크립트 활용

좀 더 자세히 확인하기 위해서는 웹 서버 로그 직접 분석이 필요했다. 로그 내용이 방대하고 IP나 URL 등 반복적인 내용이 많아 육안으로 분석하기가 쉽지 않았다.

웹 로그를 Visual Studio Code로 확인한 모습
 웹 로그를 Visual Studio Code로 확인한 모습

Python 스크립트를 이용해 IP별 접속 횟수 및 위치 정보를 출력하도록 작성하고 실행하니, 의심되는 IP와 해당 국가를 한눈에 확인할 수 있었다. IP 주소 위치가 국내이거나 신뢰할 수 있는 경우(Google, Microsoft 등)를 제외하고 보니, 특정 국가(중국, 러시아 등)에서 정상적이라고 보기 어려운 트래픽들이 확인되었다.

'''
LOG_FILE_PATHS에 지정된 웹로그 파일들을 읽어서
접속 총 횟수 상위 n개를 표시한다
'''

import collections
import os
import requests
from time import sleep

# IP 조회 결과를 캐싱할 딕셔너리
IP_CACHE = {}

def get_geolocation(ip_address):
    """
    ipinfo.io API를 사용하여 IP 주소의 지리적 위치를 조회합니다.
    """
    if ip_address in IP_CACHE:
        return IP_CACHE[ip_address]
        
    # 내부 IP (예: 127.0.0.1) 또는 예약된 IP는 조회하지 않음
    if ip_address.startswith(('10.', '172.', '192.', '127.')):
        return "Local/Reserved IP"

    # ipinfo.io API 엔드포인트 사용 (무료 사용 시 제한이 있을 수 있음)
    api_url = f"https://ipinfo.io/{ip_address}/json"

    try:
        # API 요청
        response = requests.get(api_url, timeout=5) # 5초 타임아웃 설정
        data = response.json()

        # API 제한 방지를 위해 1초 대기 (무료 서비스 이용 시 중요)
        sleep(1) 

        # 응답 처리
        if response.status_code == 200:
            if data.get('bogon'): # 예약된 IP 주소나 내부 IP인지 확인
                result = "Local/Reserved IP"
            elif data.get('country'):
                country = data.get('country', 'Unknown')
                city = data.get('city', 'Unknown')
                org = data.get('org', '').split(' ', 1)[-1] # ISP/Organization 정보
                
                # 최종 결과 문자열 생성
                result = f"{country} ({city}) / {org}"
            else:
                result = f"Error: IP Not Found or Bad Request"

            IP_CACHE[ip_address] = result
            return result
        
        elif response.status_code == 429:
            # 429: Too Many Requests (API 요청 제한 초과)
            return "Error: API 요청 제한 초과 (잠시 후 다시 시도)"
        
        else:
            return f"Error: HTTP {response.status_code}"
            
    except requests.exceptions.Timeout:
        return "네트워크 오류: API 요청 시간 초과"
    except requests.exceptions.RequestException as e:
        return f"네트워크 오류: {e}"
    except Exception as e:
        return f"처리 오류: {e}"

def analyze_multiple_log_files(file_paths, top_n_ips=5):
    """
    여러 로그 파일을 읽어 IP 접속 횟수를 집계하고, 상위 N개 IP의 위치를 조회합니다.
    """
    all_ips = []
    
    print("📝 로그 파일 분석을 시작합니다.")

    # --- 1. 로그 파일에서 IP 추출 및 집계 (이전 코드와 동일) ---
    for file_path in file_paths:
        if not os.path.exists(file_path):
            print(f"⚠️ 경고: 파일을 찾을 수 없습니다. 경로를 확인해 주세요: {file_path}")
            continue
        # ... (파일 읽기 및 IP 추출 로직 생략, 이전 코드와 동일) ...
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            
            file_ips_count = 0
            for line in lines:
                line = line.strip()
                if line:
                    try:
                        ip_address = line.split(' ')[0]
                        all_ips.append(ip_address)
                        file_ips_count += 1
                    except IndexError:
                        continue
            
            print(f"✅ 파일 분석 완료: {file_path} (총 {file_ips_count}개 로그 항목 처리)")

        except Exception as e:
            print(f"❌ 오류: {file_path} 파일을 처리하는 중 예외가 발생했습니다: {e}")
            continue


    ip_counts = collections.Counter(all_ips)
    if not ip_counts:
        print("\n분석 가능한 로그 항목이 없습니다.")
        return

    # --- 2. 상위 N개 IP에 대해 위치 정보 조회 ---
    top_ips = ip_counts.most_common(top_n_ips)
    print("\n" + "=" * 40)
    print(f"🌍 상위 {top_n_ips}개 IP에 대한 지리적 위치 정보 조회 중... (1초 간격)")
    print("=" * 40)
    
    ip_with_location = []
    for ip, count in top_ips:
        location = get_geolocation(ip)
        ip_with_location.append((ip, count, location))
        print(f"-> {ip}: {location}") 

    # --- 3. 최종 결과 출력 (이전 코드와 동일) ---
    print("\n" + "=" * 80)
    print("📈 최종 IP별 접속 횟수 및 위치 정보 (접속 많은 순)")
    print("=" * 80)
    print(f"{'IP 주소':<15} | {'총 횟수':<10} | {'조회된 위치 (국가/지역/ISP)'}")
    print("-" * 80)
    
    for ip, count, location in ip_with_location:
        print(f"{ip:<15} | {count:<10} | {location}")

    if len(ip_counts) > top_n_ips:
        other_ips_count = sum(count for ip, count in ip_counts.most_common()[top_n_ips:])
        print("-" * 80)
        print(f"{'기타 IP (합산)':<15} | {other_ips_count:<10} | {'- (총 {len(ip_counts) - top_n_ips}개 IP)'}")
        
    print("=" * 80)

# 📌 사용 방법 설정:
LOG_FILE_PATHS = [
    "xxx.com_2026-01-27.log",
    "xxx.com_2026-01-28.log"
]

# 함수 실행 (상위 n개 IP에 대해 위치 정보를 조회합니다.)
analyze_multiple_log_files(LOG_FILE_PATHS, top_n_ips=30)

무효 트래픽이란 의도적으로 광고주의 비용 또는 게시자의 수입을 부풀리기 위한 클릭이나 노출을 말한다. 이러한 특정 국가의 특정 IP에서 비정상적으로 발생하는 비정상적인 트래픽을 구글이 무효 트래픽으로 인식했을 가능성이 있다고 판단했다.

무효 트래픽 분류를 위한 Python 스크립트 실행 결과
무효 트래픽 분류를 위한 Python 스크립트 실행 결과

국가별 차단으로 대응

의심 트래픽을 매번 IP 단위로 차단하는 것은 번거로운 데다, IP도 매번 변경되기 때문에 국가별 차단 방식으로 대응하기로 했다. 해당 국가의 유효한 트래픽까지 차단되는 부작용이 있지만, 블로그의 특성상 해외 트래픽이 거의 없고, 비정상 트래픽에 확실하게 대응할 수 있는 가장 현실적인 방법이라 판단했다.

결과 — 약 1주일 후 광고 게재 제한 해제

국가별 차단으로 대응한 뒤 대략 1주일 후에 애드센스 광고 게재 제한이 해제되었다. 국가별 차단의 효과인지 아닌지 확신할 수 없는 상황이어서 해결했다는 느낌은 들지 않았다.

전체 유입 트래픽이 많지 않은 데다 해외 트래픽은 전체의 3%에 불과하고, 테스트를 정밀하게 진행한 것도 아니며 다른 외부 요인이 있을 수 있어서, 이번 애드센스 광고 게재 제한 해제가 국가별 차단 때문이었다고 지금도 단정할 수는 없다. 다만 불량 리퍼러 차단, 비정상 접근 차단, 해외 트래픽 차단이 사이트 유입 트래픽에 변화를 가져왔고, 구글 모니터링에서 변화를 감지한 것이 아닌가 추측할 뿐이다.

해외 트래픽은 크게 염두에 두지 않았고, 실제 유입 트래픽도 많지 않아서 총 트래픽에 큰 변화가 없고, 스팸성 댓글도 현저히 줄어들어서, 당분간은 국가별 차단을 유지해 두고, 추후 전체 트래픽이 충분히 증가하면 단계적으로 심각도가 덜한 국가를 우선으로 차단을 해제해볼 생각이다.

마치며

구글 애드센스 광고 게재 제한 해결 |무효 트래픽 원인과 대처 (1편)에 이어 이번 2편에서는 웹 로그 분석과 Python 스크립트를 활용한 무효 트래픽 추적, 그리고 국가별 차단을 통한 애드센스 광고 게재 제한 해결 과정을 정리해보았다. 완벽한 원인 규명은 어렵지만, 비정상적인 해외 트래픽을 차단하는 것이 무효 트래픽 문제 대응에 있어 유효한 방법 중 하나일 수 있다고 짐작 할 수 있었다.

혹시 구글 애드센스 광고 게재 제한 조치를 겪고 있다면 구글 애널리틱스와 웹 로그 분석부터 시작해보길 권한다. 막막하게 기다리는 것보다는, 직접 원인을 찾아보는 과정 자체가 블로그 보안 강화에도 도움이 된다.

댓글 남기기