2. 데이터 구조 다루기 (JSON, CSV)

header

프로그램은 데이터를 다루는 것이 핵심입니다. 특히 현대 애플리케이션에서는 다른 시스템과 데이터를 주고받기 위해 표준화된 형식을 사용하는데, 그중 가장 대표적인 것이 JSONCSV입니다.

  • JSON (JavaScript Object Notation): 키:값 쌍으로 이루어져 사람이 읽고 쓰기 쉬운 형식입니다. 파이썬의 딕셔너리 및 리스트와 구조가 거의 동일해 매우 널리 사용됩니다.
  • CSV (Comma-Separated Values): 쉼표(,)로 데이터를 구분하는 간단한 텍스트 형식으로, 표 형태의 데이터를 저장하고 교환하는 데 주로 사용됩니다.

이번 장에서는 파이썬의 내장 모듈을 사용하여 이 두 형식의 데이터를 다루는 방법부터 연습한 후, 이를 응용하여 외부 API와 통신하는 방법을 알아보겠습니다.

1. 파이썬 내장 모듈로 JSON 다루기

파이썬의 내장 json 모듈을 사용하면 JSON 데이터를 쉽게 다룰 수 있습니다.

  • json.loads(json_string): JSON 형식의 문자열을 파이썬 딕셔너리나 리스트로 변환합니다. (loads ← load from string)
  • json.dumps(python_object): 파이썬 딕셔너리나 리스트를 JSON 형식의 문자열로 변환합니다. (dumps ← dump to string)
import json

# 1. JSON 문자열을 파이썬 딕셔너리로 변환 (Parsing)
json_string = '{"name": "Alice", "age": 25, "city": "Seoul"}'
python_dict = json.loads(json_string)

print("--- json.loads() 결과 ---")
print(f"타입: {type(python_dict)}")
print(python_dict)
print(f"이름: {python_dict['name']}")

# 2. 파이썬 딕셔너리를 JSON 문자열로 변환 (Serialization)
python_dict_to_serialize = {
    'id': 101, 
    'item': '노트북', 
    'is_available': True
}
# indent=2는 예쁘게 출력하기 위한 옵션입니다.
# ensure_ascii=False는 한글이 깨지지 않도록 보장하는 중요한 옵션입니다.
json_string_from_dict = json.dumps(python_dict_to_serialize, indent=2, ensure_ascii=False)

print("\n--- json.dumps() 결과 ---")
print(f"타입: {type(json_string_from_dict)}")
print(json_string_from_dict)

JSON 연습문제

문제 1 (기초): 키(Key)로 값(Value) 찾기

아래 JSON 문자열에서 ‘name’ 값을 추출하여 “Welcome, [name]!” 형식으로 출력하세요.

{"id": "user1", "name": "Alice", "role": "admin"}
정답 보기

    import json
    json_data = '{"id": "user1", "name": "Alice", "role": "admin"}'
    data = json.loads(json_data)
    print(f"Welcome, {data['name']}!")
  

문제 2 (중급): 리스트 순회 및 조건부 접근

아래 JSON 데이터는 여러 과일의 정보를 담고 있습니다. for문을 사용하여 ‘color’가 ‘Red’인 모든 과일의 ‘name’을 출력하세요.

{
  "fruits": [
    {"name": "Apple", "color": "Red", "price": 1500},
    {"name": "Banana", "color": "Yellow", "price": 1000},
    {"name": "Cherry", "color": "Red", "price": 3000}
  ]
}
정답 보기

    import json
    json_data = '''
    {
      "fruits": [
        {"name": "Apple", "color": "Red", "price": 1500},
        {"name": "Banana", "color": "Yellow", "price": 1000},
        {"name": "Cherry", "color": "Red", "price": 3000}
      ]
    }
    '''
    data = json.loads(json_data)
    for fruit in data['fruits']:
        if fruit['color'] == 'Red':
            print(fruit['name'])
  

문제 3 (응용): 복잡한 데이터 탐색 및 파싱

아래는 프로야구 경기 결과 데이터입니다. for문과 if문을 사용하여 ‘teams’ 리스트에 ‘LG 트윈스’가 포함된 모든 경기를 찾으세요. 그 다음, 해당 경기의 ‘scores’ 딕셔너리에서 ‘LG’ 팀의 점수를 찾아 “경기 ID [match_id]: LG 점수 [score]” 형식으로 출력하세요.

{
  "game_results": [
    {
      "match_id": "K01",
      "teams": ["SSG 랜더스", "KT 위즈"],
      "scores": {"SSG": 3, "KT": 2}
    },
    {
      "match_id": "K02",
      "teams": ["LG 트윈스", "키움 히어로즈"],
      "scores": {"LG": 5, "키움": 1}
    },
    {
      "match_id": "K03",
      "teams": ["두산 베어스", "LG 트윈스"],
      "scores": {"두산": 4, "LG": 6}
    }
  ]
}
정답 보기

    import json
    json_data = '''
    {
      "game_results": [
        {
          "match_id": "K01",
          "teams": ["SSG 랜더스", "KT 위즈"],
          "scores": {"SSG": 3, "KT": 2}
        },
        {
          "match_id": "K02",
          "teams": ["LG 트윈스", "키움 히어로즈"],
          "scores": {"LG": 5, "키움": 1}
        },
        {
          "match_id": "K03",
          "teams": ["두산 베어스", "LG 트윈스"],
          "scores": {"두산": 4, "LG": 6}
        }
      ]
    }
    '''
    data = json.loads(json_data)
    for game in data['game_results']:
      if 'LG 트윈스' in game['teams']:
        match_id = game['match_id']
        lg_score = game['scores']['LG']
        print(f"경기 ID {match_id}: LG 점수 {lg_score}")
  

2. 파이썬 내장 모듈로 CSV 다루기

파이썬은 CSV 파일을 쉽게 다룰 수 있는 강력한 내장 모듈인 csv를 제공합니다.

CSV 파일 읽기 (csv.reader)

csv.reader를 사용하면 CSV 파일을 한 줄씩 읽어 각 줄을 리스트로 만들 수 있습니다.

예제 CSV 파일 (weather.csv)

city,date,temperature,humidity
Seoul,2023-10-27,15,60
Busan,2023-10-27,18,65
Incheon,2023-10-27,14,58
import csv

# 'weather.csv' 파일이 있다고 가정합니다.
# newline='' 옵션은 CSV 파일 처리 시 권장되는 설정입니다.
try:
    with open('weather.csv', 'r', newline='', encoding='utf-8') as file:
        reader = csv.reader(file)
        
        # 헤더(첫 줄) 건너뛰기
        header = next(reader)
        print(f"헤더: {header}")
        
        # 데이터 출력
        for row in reader:
            # row는 문자열 리스트입니다. 예: ['Seoul', '2023-10-27', '15', '60']
            city = row[0]
            temp = int(row[2]) # 숫자로 사용하려면 형 변환 필요
            print(f"{city}의 온도는 {temp}도 입니다.")

except FileNotFoundError:
    print("weather.csv 파일을 찾을 수 없습니다.")

CSV 파일 쓰기 (csv.writer)

csv.writer를 사용하면 리스트 형태의 데이터를 CSV 파일로 쉽게 저장할 수 있습니다.

import csv

# 저장할 데이터 (리스트의 리스트)
data_to_write = [
    ['name', 'department', 'employee_id'],
    ['Alice', 'Engineering', 101],
    ['Bob', 'Marketing', 102],
    ['Charlie', 'Sales', 103]
]

# 'employees.csv' 파일로 저장
with open('employees.csv', 'w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerows(data_to_write) # 여러 줄을 한 번에 쓰기

print("employees.csv 파일이 생성되었습니다.")

딕셔너리로 CSV 다루기 (DictReaderDictWriter)

데이터를 리스트가 아닌 딕셔너리 형태로 다루면 열 인덱스 대신 헤더 이름을 키로 사용할 수 있어 코드가 더 명확해집니다.

import csv

# DictReader로 읽기
try:
    with open('weather.csv', 'r', newline='', encoding='utf-8') as file:
        reader = csv.DictReader(file)
        print("\n--- DictReader로 읽기 ---")
        for row in reader:
            # row는 딕셔너리입니다. 예: {'city': 'Seoul', 'date': '2023-10-27', ...}
            print(f"{row['city']}의 습도는 {row['humidity']}% 입니다.")
except FileNotFoundError:
    print("weather.csv 파일을 찾을 수 없습니다.")

# DictWriter로 쓰기
data_to_write_dict = [
    {'name': 'Dave', 'department': 'HR', 'employee_id': 104},
    {'name': 'Eve', 'department': 'Engineering', 'employee_id': 105}
]

# 'employees_dict.csv' 파일로 저장
with open('employees_dict.csv', 'w', newline='', encoding='utf-8') as file:
    # 필드 이름(헤더)을 지정해야 합니다.
    fieldnames = ['name', 'department', 'employee_id']
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    
    writer.writeheader() # 헤더 쓰기
    writer.writerows(data_to_write_dict) # 데이터 쓰기

print("employees_dict.csv 파일이 생성되었습니다.")

3. API로 JSON 데이터 활용하기

로컬 파일을 다루는 방법을 익혔으니, 이제 네트워크를 통해 외부 서버로부터 실시간 데이터를 받아오는 방법을 알아보겠습니다. requests 라이브러리를 사용하면 이 과정을 간단하게 처리할 수 있습니다.

예제: 실시간 미세먼지 농도 확인 프로그램

이 프로그램은 requests 라이브러리를 사용하여 특정 지역의 미세먼지 농도를 가져와 출력합니다.

💡 시작하기 전에!
이 코드를 실행하려면 requests 라이브러리가 필요합니다. 터미널에서 아래 명령어로 설치해주세요.

pip install requests

⚠️ API 키 발급받기
공공데이터포털 API를 사용하려면 먼저 회원가입 후 대기오염정보 조회 서비스 페이지에서 ‘활용신청’을 통해 개인 인증키(Service Key)를 발급받아야 합니다. 발급받은 키를 아래 코드의 YOUR_API_KEY 부분에 붙여넣으세요.

import requests
import json

# 공공데이터포털에서 발급받은 본인의 API 키를 입력하세요.
# 주의: Decoding 된 키를 사용해야 합니다. 포털에서 '일반 인증키(Decoding)'을 복사하세요.
API_KEY = "YOUR_API_KEY" 

# 데이터를 조회할 측정소 이름
STATION_NAME = "종로구"

# API 요청을 보낼 URL
# dataType을 'json'으로 설정하여 JSON 형식으로 데이터를 받습니다.
URL = (
    "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty"
    f"?serviceKey={API_KEY}"
    f"&returnType=json"
    f"&numOfRows=1"
    f"&pageNo=1"
    f"&stationName={STATION_NAME}"
    f"&dataTerm=DAILY"
    f"&ver=1.0"
)

# requests 라이브러리를 사용해 URL에 GET 요청을 보냅니다.
response = requests.get(URL)

# 응답이 성공적인지 확인 (HTTP 상태 코드 200)
if response.status_code == 200:
    # 응답받은 JSON 문자열을 파이썬 딕셔너리로 변환합니다.
    data = json.loads(response.text)
    
    # 데이터가 정상적으로 수신되었는지 확인합니다.
    # API 응답 구조에 따라 키를 확인해야 합니다.
    if data['response']['header']['resultCode'] == '00':
        # 필요한 정보 추출
        item = data['response']['body']['items'][0]
        
        measure_time = item['dataTime']
        pm10_value = item['pm10Value']
        pm25_value = item['pm25Value']
        
        print(f"--- {STATION_NAME} 대기오염정보 ---")
        print(f"측정 시간: {measure_time}")
        print(f"미세먼지(PM10) 농도: {pm10_value}µg/m³")
        print(f"초미세먼지(PM2.5) 농도: {pm25_value}µg/m³")
    else:
        # API 자체에서 보낸 에러 메시지 출력
        error_msg = data['response']['header']['resultMsg']
        print(f"API 오류가 발생했습니다: {error_msg}")
else:
    # HTTP 요청 실패 시
    print(f"데이터를 가져오는 데 실패했습니다. 상태 코드: {response.status_code}")

코드 설명

  1. import requests, json: HTTP 요청을 보내기 위한 requests와 JSON 데이터를 다루기 위한 json 라이브러리를 가져옵니다.
  2. API_KEY, STATION_NAME, URL: API 요청에 필요한 기본 정보들을 변수에 저장합니다. 특히 URL에는 f-string을 사용하여 변수들을 동적으로 포함시켰습니다. returnType=json 파라미터를 통해 JSON 형식의 응답을 요청하는 것이 핵심입니다.
  3. requests.get(URL): requests 라이브러리의 get 함수를 이용해 해당 URL로 HTTP GET 요청을 보냅니다. 서버로부터의 모든 응답이 response 객체에 담깁니다.
  4. response.status_code == 200: HTTP 상태 코드를 확인하여 요청이 성공했는지 검사합니다. 200은 ‘성공’을 의미합니다.
  5. json.loads(response.text): response.text에는 서버가 보낸 순수한 JSON 문자열이 담겨 있습니다. json.loads() 함수는 이 문자열을 파이썬 딕셔너리와 리스트로 변환해줍니다. 이제 파이썬에서 다루기 쉬운 형태로 데이터가 가공되었습니다.
  6. data['response']['body']['items'][0]: 변환된 딕셔너리에서 우리가 원하는 실제 데이터가 있는 곳까지 키를 이용해 한 단계씩 접근합니다. API 문서를 보며 데이터 구조를 파악하는 것이 중요합니다.
  7. 정보 출력: 필요한 데이터(측정소 이름, 시간, 미세먼지 농도 등)를 추출하여 print 함수로 보기 좋게 출력합니다.