# 🥕 당근지역코드 일괄 가져오기 (Bulk Export API) 연동 가이드

본 문서는 파이썬(Python), 노드(Node.js), PHP, C#, 쉘 스크립트 등 **외부 프로그램 및 시스템에서 Korea ZipCode Hub의 API를 호출하여 전체 당근마켓 지역코드 및 매핑 데이터를 일괄로 가져오는(Bulk Export) 방법**을 안내합니다.

---

## 📌 1. API 개요 및 통신 규격

당근지역코드 일괄 내보내기 API는 외부 시스템과의 원활한 연동을 위해 **CORS(Cross-Origin Resource Sharing)가 전면 개방**되어 있으며, 복잡한 인증 토큰(API Key) 없이 즉시 호출할 수 있습니다.

| 항목 | 내용 |
| :--- | :--- |
| **기본 엔드포인트** | `GET /api/export.php?type=carrot` |
| **통신 프로토콜** | HTTP / HTTPS (REST GET) |
| **인증 방식** | 인증 불필요 (Public Open API) |
| **CORS 설정** | `Access-Control-Allow-Origin: *` (모든 도메인 및 앱 허용) |
| **지원 응답 포맷** | `JSON` (`format=json`), `CSV` (`format=csv`) |
| **최대 호출 건수** | 기본 50,000건 / 파라미터 설정 시 최대 100,000건 |

---

## 📥 2. 요청 파라미터 규격

외부 프로그램에서 `GET` 쿼리스트링으로 전달할 수 있는 옵션 파라미터 목록입니다.

| 파라미터명 | 타입 | 기본값 | 필수 여부 | 상세 설명 |
| :--- | :---: | :---: | :---: | :--- |
| `type` | String | `zipcode` | **필수** | 당근 지역코드 추출 시 반드시 `carrot`으로 지정합니다. |
| `format` | String | `csv` | 선택 | 응답 데이터 형태: `json` 또는 `csv` |
| `q` | String | (빈값) | 선택 | 특정 지역(시도, 시군구, 동, 단지명) 또는 당근ID 필터링 검색어 |
| `limit` | Integer | `50000` | 선택 | 한 번에 가져올 최대 레코드 수 (최소 1 ~ 최대 100,000) |
| `missing_only`| String | `n` | 선택 | `y`로 지정 시 아직 당근 ID가 매칭되지 않은 지역만 선별 반환 |
| `download` | String | `n` | 선택 | 브라우저 파일 다운로드 헤더 강제 출력 여부 (`y` / `n`) |

---

## 📤 3. 데이터 응답 스키마

### 3.1 JSON 포맷 (`format=json`)

REST API 통신 및 애플리케이션 내 객체 매핑에 최적화된 형식입니다.

```http
GET /api/export.php?type=carrot&format=json&limit=50000
```

#### JSON 응답 예시:
```json
{
  "status": 200,
  "message": "success",
  "type": "carrot",
  "count": 2,
  "data": [
    {
      "id": 481,
      "sido": "서울특별시",
      "sigungu": "강남구",
      "legal_dong_name": "역삼1동",
      "complex_name": "",
      "carrot_region_id": "6110",
      "target_name": "역삼1동",
      "is_complex": "n"
    },
    {
      "id": 502,
      "sido": "서울특별시",
      "sigungu": "송파구",
      "legal_dong_name": "잠실동",
      "complex_name": "잠실엘스",
      "carrot_region_id": "8432",
      "target_name": "잠실엘스",
      "is_complex": "y"
    }
  ]
}
```

#### 필드 명세:
* `id`: 고유 데이터 시퀀스 ID (Integer)
* `sido`: 특별시/광역시/도 (예: `서울특별시`, `경기도`)
* `sigungu`: 시/군/구 (예: `강남구`, `수원시 팔달구`)
* `legal_dong_name`: 법정동/읍/면 단위 명칭 (예: `역삼1동`, `정자동`)
* `complex_name`: 아파트 단지 또는 복합 건물명 (단지 단위 코드인 경우 입력됨, 일반 동이면 빈 문자열)
* `carrot_region_id`: **당근마켓 내부 고유 지역 ID** (문자열)
* `target_name`: 실제 서비스 표출 대상 명칭 (`complex_name`이 있으면 단지명, 없으면 법정동명)
* `is_complex`: 아파트/단지 여부 (`y`: 아파트단지 단위, `n`: 일반 법정동 단위)

---

### 3.2 CSV 포맷 (`format=csv`)

엑셀 분석, 로컬 파일 보관, 대용량 일괄 임포트에 적합한 UTF-8 with BOM 스트리밍 형식입니다.

```http
GET /api/export.php?type=carrot&format=csv&download=y
```

#### CSV 헤더 구성:
```csv
ID,시도,시군구,법정동,단지/건물명,당근지역ID,대상지역명
481,서울특별시,강남구,역삼1동,,6110,역삼1동
502,서울특별시,송파구,잠실동,잠실엘스,8432,잠실엘스
```

---

## 💻 4. 프로그래밍 언어별 실제 연동 예제

각 언어 환경에서 즉시 실행하여 전체 당근지역코드를 로컬 메모리나 파일로 저장할 수 있는 실제 코드입니다.

### 4.1 Python (파이썬)

`requests` 라이브러리를 사용하여 JSON으로 일괄 수신 후 로컬 JSON 파일 및 Pandas 데이터프레임으로 처리하는 예제입니다.

```python
import json
import requests

# 1. API 요청 URL 및 파라미터 설정
API_URL = "http://your-domain.com/api/export.php"
params = {
    "type": "carrot",
    "format": "json",
    "limit": 100000  # 최대 수량 요청
}

try:
    print("당근지역코드 데이터를 수신 중입니다...")
    response = requests.get(API_URL, params=params, timeout=60)
    response.raise_for_status()
    
    result = response.json()
    
    if result.get("status") == 200:
        regions = result.get("data", [])
        total_count = result.get("count", 0)
        print(f"✅ 수신 완료: 총 {total_count:,}개의 지역코드를 가져왔습니다.")
        
        # 로컬 JSON 파일로 저장
        with open("carrot_regions.json", "w", encoding="utf-8") as f:
            json.dump(regions, f, ensure_ascii=False, indent=2)
        print("💾 'carrot_regions.json' 파일로 저장을 완료했습니다.")
        
        # 첫 3건 샘플 출력
        for item in regions[:3]:
            print(f"- [{item['carrot_region_id']}] {item['sido']} {item['sigungu']} {item['target_name']}")
    else:
        print(f"❌ API 오류: {result.get('message')}")

except requests.exceptions.RequestException as e:
    print(f"❌ 네트워크 통신 실패: {e}")
```

---

### 4.2 Node.js / JavaScript

Node.js 환경(v18+ 기본 Fetch) 또는 브라우저 프론트엔드에서 비동기로 데이터를 수신하는 예제입니다.

```javascript
const fs = require('fs');

async function fetchCarrotRegions() {
  const endpoint = 'http://your-domain.com/api/export.php?type=carrot&format=json&limit=100000';
  
  try {
    console.log('당근지역코드 데이터를 요청 중...');
    const res = await fetch(endpoint, {
      method: 'GET',
      headers: { 'Accept': 'application/json' }
    });

    if (!res.ok) {
      throw new Error(`HTTP error! status: ${res.status}`);
    }

    const json = await res.json();
    if (json.status === 200) {
      console.log(`✅ 수신 완료: 총 ${json.count.toLocaleString()}건`);
      
      // 파일 저장 (Node.js)
      fs.writeFileSync('carrot_regions.json', JSON.stringify(json.data, null, 2), 'utf-8');
      console.log('💾 carrot_regions.json 저장 완료');
    } else {
      console.error('API 응답 에러:', json.message);
    }
  } catch (err) {
    console.error('호출 실패:', err.message);
  }
}

fetchCarrotRegions();
```

---

### 4.3 PHP

외부 PHP 애플리케이션 또는 배치 작업에서 cURL을 사용하여 전체 데이터를 수신하고 로컬 배열로 가공하는 예제입니다.

```php
<?php
$apiUrl = 'http://your-domain.com/api/export.php?type=carrot&format=json&limit=100000';

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $apiUrl,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 15,
    CURLOPT_TIMEOUT => 60,
    CURLOPT_SSL_VERIFYPEER => true
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);

if ($httpCode === 200 && $response) {
    $result = json_decode($response, true);
    if (($result['status'] ?? 0) === 200) {
        $regions = $result['data'] ?? [];
        echo "✅ 당근 지역코드 " . count($regions) . "건 수신 완료\n";
        
        // 예: 로컬 DB 적재 또는 가공 처리
        // foreach ($regions as $row) { ... }
    } else {
        echo "❌ API 처리 실패: " . ($result['message'] ?? '알 수 없는 오류') . "\n";
    }
} else {
    echo "❌ 요청 실패 (HTTP {$httpCode}): {$error}\n";
}
```

---

### 4.4 Linux cURL / Shell Script (CSV 자동 백업)

서버 크론잡(Cron)이나 쉘 스크립트에서 단일 명령어로 CSV 파일을 즉시 내려받아 저장하는 방법입니다.

```bash
#!/bin/bash
TARGET_FILE="carrot_regions_$(date +%Y%m%d).csv"
API_URL="http://your-domain.com/api/export.php?type=carrot&format=csv&download=y"

echo "당근지역코드 CSV 다운로드 시작..."
curl -s -S -f -o "$TARGET_FILE" "$API_URL"

if [ $? -eq 0 ]; then
    echo "✅ 다운로드 완료: $TARGET_FILE ($(du -h "$TARGET_FILE" | cut -f1))"
else
    echo "❌ 다운로드 실패"
fi
```

---

### 4.5 C# (.NET Core / .NET 8)

C# 환경에서 `HttpClient`와 `System.Text.Json`을 사용하여 강력한 타입의 모델 객체로 역직렬화하는 예제입니다.

```csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

public class CarrotRegionItem
{
    [JsonPropertyName("id")] public int Id { get; set; }
    [JsonPropertyName("sido")] public string Sido { get; set; }
    [JsonPropertyName("sigungu")] public string Sigungu { get; set; }
    [JsonPropertyName("legal_dong_name")] public string LegalDongName { get; set; }
    [JsonPropertyName("complex_name")] public string ComplexName { get; set; }
    [JsonPropertyName("carrot_region_id")] public string CarrotRegionId { get; set; }
    [JsonPropertyName("target_name")] public string TargetName { get; set; }
    [JsonPropertyName("is_complex")] public string IsComplex { get; set; }
}

public class CarrotApiResponse
{
    [JsonPropertyName("status")] public int Status { get; set; }
    [JsonPropertyName("count")] public int Count { get; set; }
    [JsonPropertyName("data")] public List<CarrotRegionItem> Data { get; set; }
}

class Program
{
    static async Task Main()
    {
        using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
        string url = "http://your-domain.com/api/export.php?type=carrot&format=json&limit=100000";

        Console.WriteLine("당근지역코드 수신 중...");
        var response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode();

        string json = await response.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<CarrotApiResponse>(json);

        Console.WriteLine($"✅ 총 {result.Count:N0}건의 지역코드가 성공적으로 로드되었습니다.");
    }
}
```

---

## ⚡ 5. 대량 데이터 처리 및 운영 최적화 팁

1. **로컬 캐싱(Caching) 권장**
   - 당근 지역코드는 실시간으로 초 단위 변동이 발생하는 데이터가 아닙니다.
   - 외부 프로그램에서는 매 요청마다 API를 호출하기보다, **1일 1회 주기(새벽 배치 크론 등)로 전체 데이터를 수신하여 자체 DB나 인메모리(Redis 등)에 캐싱**해 두고 사용하는 것이 시스템 속도와 서버 부하 면에서 매우 유리합니다.

2. **특정 시/도 단위 분할 수신 (`q` 파라미터 활용)**
   - 전체 수신 대신 특정 지역만 필요하거나 분할하여 배치 처리하고자 할 경우 `q` 파라미터를 조합합니다.
   - 예: `GET /api/export.php?type=carrot&format=json&q=서울특별시`
   - 예: `GET /api/export.php?type=carrot&format=json&q=경기도`

3. **미매칭 데이터 선별 추출 (`missing_only=y`)**
   - 아직 당근 지역 ID가 할당되지 않은 지역만 조회하여 추가 수집 작업이 필요한지 검증할 때 유용합니다.
   - 예: `GET /api/export.php?type=carrot&missing_only=y&format=json`

4. **타임아웃(Timeout) 설정**
   - 수만 건의 전체 데이터를 일괄 직렬화하여 응답하므로, 통신 라이브러리의 `timeout` 값을 **최소 30초 ~ 60초 이상**으로 넉넉히 설정하는 것을 권장합니다.

---

## 🛡️ 6. 문제 해결 및 에러 코드

| HTTP 상태코드 | 응답 필드 (`status`) | 원인 및 조치 방법 |
| :---: | :---: | :--- |
| `200 OK` | `200` | 정상 응답. `data` 배열에 레코드가 반환됩니다. |
| `200 OK` | `500` | 데이터베이스 연결 지연 또는 쿼리 오류. 잠시 후 재시도하십시오. |
| `400 Bad Request` | `500` | 잘못된 파라미터 요청. `type=carrot`이 지정되었는지 확인하십시오. |
| `504 Gateway Timeout` | - | 데이터 전송량 초과로 프록시 타임아웃 발생 시 `limit`을 30,000 이하로 줄이거나 `q`로 시도별 분할 요청하십시오. |

