🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

JSON 파싱과 DataFrame 변환 완벽 가이드 - 슬라이드 1/8
A

AI Generated

2025. 12. 17. · 8 Views

JSON 파싱과 DataFrame 변환 완벽 가이드

API에서 받은 복잡한 JSON 데이터를 Pandas DataFrame으로 변환하는 실전 기술을 배웁니다. 중첩된 JSON 구조를 평탄화하고, 여러 API 결과를 하나로 합치는 방법까지 초급자도 쉽게 따라할 수 있도록 설명합니다.


목차

  1. 중첩 JSON 구조 이해
  2. json 모듈 사용법
  3. json_normalize() 활용
  4. 중첩 데이터 평탄화
  5. DataFrame으로 변환
  6. 여러 API 결과 합치기
  7. 데이터 저장하기

1. 중첩 JSON 구조 이해

신입 개발자 김데이터 씨는 오늘 처음으로 외부 API를 호출하는 업무를 맡았습니다. API 문서를 보니 응답이 JSON 형식이라고 합니다.

그런데 막상 데이터를 받아보니 { 안에 [ 가 있고, 그 안에 또 { 가 중첩되어 있습니다. 선배 개발자 박파이썬 씨는 "아, 중첩 JSON이네요"라고 말합니다.

도대체 이게 뭘까요?

중첩 JSON은 JSON 객체 안에 또 다른 객체나 배열이 들어있는 구조를 말합니다. 마치 러시아 인형처럼 겉을 열면 안에 또 인형이 있는 구조입니다.

실제 API에서는 사용자 정보 안에 주소 정보가, 주문 정보 안에 상품 목록이 중첩되어 있는 경우가 흔합니다. 이 구조를 이해해야 데이터를 제대로 파싱할 수 있습니다.

다음 코드를 살펴봅시다.

# 중첩 JSON 예시 - 사용자 주문 정보
sample_json = {
    "user_id": 1001,
    "name": "김개발",
    "address": {
        "city": "서울",
        "district": "강남구",
        "zipcode": "06000"
    },
    "orders": [
        {"product": "노트북", "price": 1500000},
        {"product": "마우스", "price": 30000}
    ]
}

# 중첩된 데이터 접근
print(sample_json["address"]["city"])  # 서울
print(sample_json["orders"][0]["product"])  # 노트북

김데이터 씨는 입사 1개월 차 주니어 개발자입니다. 오늘 팀장님께 처음으로 중요한 업무를 맡았습니다.

외부 쇼핑몰 API에서 주문 데이터를 가져와서 분석하는 일입니다. 설레는 마음으로 API를 호출했더니 화면에 길고 복잡한 텍스트가 나타났습니다.

중괄호와 대괄호가 뒤섞여 있고, 콤마와 콜론이 여기저기 흩어져 있습니다. "이게 도대체 뭐지?" 김데이터 씨는 당황했습니다.

옆자리의 박파이썬 씨가 모니터를 힐끗 보더니 웃으며 말합니다. "아, 전형적인 중첩 JSON이네요.

처음 보면 복잡해 보이지만 규칙만 알면 쉬워요." JSON이란 무엇일까요? JSON은 JavaScript Object Notation의 약자입니다.

쉽게 말하면 데이터를 주고받을 때 사용하는 텍스트 형식입니다. 마치 서로 약속한 편지 양식과 같습니다.

보내는 사람도 이 양식으로 쓰고, 받는 사람도 이 양식으로 읽습니다. JSON의 기본 구조는 간단합니다.

중괄호 안에 "키": "값" 쌍이 들어갑니다. 예를 들어 {"이름": "김개발", "나이": 25} 같은 형식입니다.

그런데 실제 세상의 데이터는 이렇게 단순하지 않습니다. 예를 들어 쇼핑몰 주문 정보를 생각해봅시다.

주문에는 주문자 정보가 있고, 배송 주소가 있고, 주문한 상품 목록이 있습니다. 배송 주소도 단순한 텍스트가 아니라 시, 구, 동, 우편번호로 나뉩니다.

주문 상품도 하나가 아니라 여러 개일 수 있습니다. 이런 복잡한 관계를 표현하려면 JSON 안에 또 JSON을 넣어야 합니다.

바로 이것이 중첩 JSON입니다. 위의 코드를 보면 세 가지 형태가 있습니다.

첫 번째는 단순한 값입니다. "user_id": 1001처럼 숫자나 문자열이 바로 오는 경우입니다.

이건 간단합니다. 두 번째는 객체 중첩입니다.

"address"의 값을 보면 또 다른 중괄호가 시작됩니다. 이 안에 "city", "district", "zipcode"가 들어있습니다.

마치 주소라는 큰 상자 안에 시, 구, 우편번호라는 작은 상자들이 들어있는 것과 같습니다. 세 번째는 배열 중첩입니다.

"orders"의 값은 대괄호로 시작합니다. 대괄호는 여러 개의 항목을 담는 리스트를 의미합니다.

이 안에 주문 상품들이 중괄호 형태로 여러 개 들어있습니다. 실제 API에서는 이런 중첩이 3단계, 4단계까지 깊어지기도 합니다.

예를 들어 소셜 미디어 API를 생각해봅시다. 게시글 하나를 가져오면 작성자 정보가 있고, 작성자 안에 프로필 사진 URL이 있고, 댓글 목록이 있고, 각 댓글마다 작성자 정보가 또 있습니다.

이렇게 복잡한 구조가 실무에서는 일반적입니다. 중첩된 데이터에 접근하려면 어떻게 해야 할까요?

파이썬에서는 대괄호를 연속으로 사용합니다. sample_json["address"]["city"]처럼 쓰면 됩니다.

첫 번째 대괄호로 address를 찾고, 그 안에서 두 번째 대괄호로 city를 찾습니다. 배열의 경우 숫자 인덱스를 사용합니다.

sample_json["orders"][0]은 첫 번째 주문을 의미합니다. 배열은 0부터 시작한다는 점을 기억해야 합니다.

박파이썬 씨가 김데이터 씨에게 말합니다. "처음에는 복잡해 보이지만 천천히 구조를 파악하면 돼요.

중괄호는 하나의 사물, 대괄호는 여러 개의 목록이라고 생각하면 쉬워요." 김데이터 씨는 고개를 끄덕입니다. 이제 JSON의 중첩 구조가 조금씩 눈에 들어오기 시작합니다.

실전 팁

💡 - JSON 구조를 한눈에 보려면 온라인 JSON Viewer를 사용하세요

  • 중첩 단계가 깊을수록 코드가 복잡해지므로 미리 구조를 파악하는 것이 중요합니다

2. json 모듈 사용법

김데이터 씨는 이제 JSON 구조는 이해했습니다. 그런데 API에서 받은 데이터는 문자열 형태입니다.

파이썬에서 이걸 어떻게 다뤄야 할까요? 박파이썬 씨가 "json 모듈을 사용하면 됩니다"라고 알려줍니다.

json 모듈은 파이썬 표준 라이브러리로, JSON 문자열을 파이썬 딕셔너리로 변환하거나 그 반대로 변환할 수 있습니다. json.loads() 함수는 JSON 문자열을 파싱하고, json.dumps() 함수는 파이썬 객체를 JSON 문자열로 만듭니다.

API 통신에서 필수적으로 사용되는 도구입니다.

다음 코드를 살펴봅시다.

import json

# API에서 받은 JSON 문자열 (실제로는 requests로 받습니다)
json_string = '''
{
    "name": "김개발",
    "skills": ["Python", "SQL", "Git"],
    "experience_years": 3
}
'''

# JSON 문자열을 파이썬 딕셔너리로 변환
data = json.loads(json_string)
print(type(data))  # <class 'dict'>
print(data["skills"])  # ['Python', 'SQL', 'Git']

# 파이썬 객체를 JSON 문자열로 변환
new_data = {"status": "success", "count": 100}
json_output = json.dumps(new_data, ensure_ascii=False, indent=2)
print(json_output)

김데이터 씨가 실제로 API를 호출해봤습니다. requests 라이브러리로 GET 요청을 보냈더니 뭔가 응답이 왔습니다.

그런데 type()으로 확인해보니 문자열입니다. "어?

이거 그냥 문자열이네요. 어떻게 데이터를 꺼내죠?" 김데이터 씨가 물었습니다.

박파이썬 씨가 설명합니다. "API는 네트워크를 통해 데이터를 보내니까 텍스트 형태로 보낼 수밖에 없어요.

받는 쪽에서 이걸 파이썬이 다룰 수 있는 형태로 바꿔야 합니다." 바로 이때 필요한 것이 json 모듈입니다. json 모듈은 파이썬을 설치하면 기본으로 따라오는 표준 라이브러리입니다.

별도로 설치할 필요 없이 import json만 하면 바로 사용할 수 있습니다. 가장 많이 사용하는 함수는 **json.loads()**입니다.

이름이 좀 이상하죠? loads는 load string의 약자입니다.

문자열을 읽어서 파이썬 객체로 만든다는 뜻입니다. 위의 코드를 보면 json_string이라는 변수에 JSON 형식의 문자열이 들어있습니다.

실제로는 삼중 따옴표로 감싸진 그냥 텍스트입니다. 이 상태에서는 data["name"]처럼 접근할 수 없습니다.

json.loads(json_string)을 실행하면 마법이 일어납니다. 문자열이 파이썬 딕셔너리로 변환됩니다.

이제 data["skills"]처럼 키로 값을 꺼낼 수 있습니다. for 문으로 순회할 수도 있고, if 문으로 조건을 확인할 수도 있습니다.

JSON의 데이터 타입은 파이썬의 타입으로 자동 변환됩니다. JSON 객체는 파이썬 딕셔너리가 되고, JSON 배열은 파이썬 리스트가 됩니다.

JSON 문자열은 파이썬 문자열로, JSON 숫자는 파이썬 int나 float로 변환됩니다. 반대 방향도 가능합니다.

파이썬에서 데이터를 만들어서 API로 보내야 할 때가 있습니다. 이때는 **json.dumps()**를 사용합니다.

dump string의 약자로, 파이썬 객체를 문자열로 덤프한다는 의미입니다. 예를 들어 new_data라는 딕셔너리를 만들었다고 해봅시다.

이걸 그대로 API로 보낼 수는 없습니다. 네트워크는 텍스트만 전송할 수 있으니까요.

json.dumps(new_data)를 실행하면 딕셔너리가 JSON 형식의 문자열로 변환됩니다. 이제 이걸 requests.post()의 body에 담아서 보낼 수 있습니다.

여기서 중요한 옵션 두 가지가 있습니다. 첫째는 ensure_ascii=False입니다.

기본값은 True인데, 이러면 한글이 \uXXXX 같은 이상한 코드로 변환됩니다. False로 설정하면 한글이 그대로 보입니다.

둘째는 indent입니다. JSON을 보기 좋게 들여쓰기해줍니다.

indent=2로 설정하면 2칸씩 들여쓰기되어 사람이 읽기 편한 형태로 출력됩니다. 로그를 찍을 때 유용합니다.

파일로 저장하거나 읽을 때는 어떻게 할까요? **json.dump()**와 **json.load()**를 사용합니다.

s가 없다는 게 차이점입니다. dump()는 파일 객체에 직접 쓰고, load()는 파일 객체에서 읽습니다.

박파이썬 씨가 덧붙입니다. "실무에서는 대부분 requests 라이브러리의 .json() 메서드를 쓰긴 해요.

그래도 json 모듈의 원리를 알아두면 커스텀 처리가 필요할 때 유용합니다." 김데이터 씨는 json.loads()와 json.dumps()를 몇 번 테스트해봤습니다. 이제 API 응답을 파이썬에서 자유롭게 다룰 수 있다는 자신감이 생겼습니다.

실전 팁

💡 - API 응답은 보통 response.json() 메서드로 바로 파싱할 수 있습니다

  • 디버깅할 때는 json.dumps(data, indent=2, ensure_ascii=False)로 예쁘게 출력하세요

3. json normalize() 활용

김데이터 씨는 이제 JSON을 파이썬 딕셔너리로 변환할 수 있게 됐습니다. 그런데 팀장님이 "이 데이터를 엑셀 같은 표 형태로 만들어주세요"라고 합니다.

딕셔너리를 어떻게 표로 만들까요? 박파이썬 씨가 "Pandas의 json_normalize()가 딱입니다"라고 말합니다.

**json_normalize()**는 Pandas 라이브러리의 함수로, 중첩된 JSON을 평탄한 DataFrame으로 변환합니다. 마치 복잡한 러시아 인형을 펼쳐서 한 줄로 나열하는 것과 같습니다.

중첩된 객체는 점(.)으로 연결된 컬럼명으로 변환되어 분석하기 쉬운 형태가 됩니다.

다음 코드를 살펴봅시다.

import pandas as pd
from pandas import json_normalize

# 중첩된 JSON 데이터
data = {
    "id": 1,
    "user": {
        "name": "김개발",
        "email": "kim@example.com"
    },
    "stats": {
        "views": 1000,
        "likes": 50
    }
}

# JSON을 DataFrame으로 변환
df = json_normalize(data)
print(df.columns)
# Index(['id', 'user.name', 'user.email', 'stats.views', 'stats.likes'])

print(df)
# 중첩 구조가 평탄화되어 표 형태로 변환됨

김데이터 씨는 팀장님께 보고할 자료를 준비하고 있습니다. API에서 사용자 통계 데이터를 가져왔는데, 팀장님은 "이걸 표로 만들어서 엑셀로 보내주세요"라고 하셨습니다.

문제는 데이터가 중첩된 JSON 형태라는 점입니다. 어떻게 이걸 표로 만들까요?

박파이썬 씨가 옆에서 말합니다. "Pandas의 json_normalize() 함수를 쓰면 한 방에 해결돼요." Pandas는 파이썬에서 데이터 분석을 할 때 가장 많이 사용하는 라이브러리입니다.

엑셀처럼 표 형태의 데이터를 다룰 수 있게 해줍니다. 이 라이브러리에서 제공하는 것이 json_normalize() 함수입니다.

이 함수의 핵심 아이디어는 간단합니다. 중첩된 구조를 펼쳐서 평탄하게 만드는 것입니다.

위의 코드를 보면 data라는 딕셔너리가 있습니다. "user" 안에 "name"과 "email"이 중첩되어 있고, "stats" 안에 "views"와 "likes"가 중첩되어 있습니다.

json_normalize(data)를 실행하면 어떻게 될까요? 중첩된 구조가 사라지고 모든 데이터가 한 줄로 펼쳐집니다.

대신 컬럼명이 "user.name", "user.email"처럼 점으로 연결됩니다. 이렇게 하면 원래 어디에 속했던 데이터인지 알 수 있습니다.

결과는 DataFrame이라는 형태가 됩니다. DataFrame은 Pandas의 핵심 자료구조로, 엑셀 시트처럼 행과 열로 이뤄진 표입니다.

각 열에는 이름이 있고, 각 행에는 인덱스가 있습니다. 이제 이 DataFrame을 가지고 무엇이든 할 수 있습니다.

df.to_excel("report.xlsx")로 엑셀 파일로 저장할 수 있습니다. df.to_csv("data.csv")로 CSV 파일로 내보낼 수도 있습니다.

혹은 df["user.name"]처럼 특정 컬럼만 뽑아서 분석할 수도 있습니다. 실무에서는 보통 API가 여러 개의 데이터를 배열로 반환합니다.

예를 들어 사용자 목록을 가져오면 [{"id": 1, ...}, {"id": 2, ...}] 형태가 됩니다. 이런 경우에도 json_normalize()가 완벽하게 작동합니다.

리스트를 넘겨주면 각 항목이 DataFrame의 한 행이 됩니다. 10개의 사용자가 있으면 10행짜리 표가 만들어집니다.

더 복잡한 경우도 있습니다. 예를 들어 사용자 데이터 안에 주문 목록이 배열로 들어있다면 어떻게 될까요?

한 사용자가 여러 주문을 했다면 말이죠. 이때는 record_path 파라미터를 사용합니다.

json_normalize(data, record_path="orders", meta=["user_id", "name"]) 같은 식으로 쓰면 주문 목록을 펼치면서도 사용자 정보를 함께 유지할 수 있습니다. 박파이썬 씨가 화면을 가리키며 설명합니다.

"보세요, 중첩된 JSON이 깔끔한 표로 바뀌었죠? 이제 이걸 엑셀로 저장하면 팀장님이 좋아하실 거예요." 김데이터 씨는 신기한 듯 화면을 봅니다.

복잡하게 얽혀있던 데이터가 한눈에 들어오는 표가 됐습니다. json_normalize()는 데이터 분석의 첫 단계에서 아주 유용합니다.

API에서 받은 복잡한 데이터를 분석 가능한 형태로 바꾸는 것, 이것이 json_normalize()의 핵심 역할입니다.

실전 팁

💡 - 중첩이 깊을 때는 max_level 파라미터로 평탄화 깊이를 조절할 수 있습니다

  • record_path와 meta를 활용하면 배열 안의 객체도 쉽게 펼칠 수 있습니다

4. 중첩 데이터 평탄화

김데이터 씨가 실제 API를 호출했더니 3단계, 4단계로 중첩된 복잡한 JSON이 왔습니다. json_normalize()로 변환했더니 컬럼명이 "user.address.detail.street"처럼 너무 깁니다.

박파이썬 씨가 "평탄화 전략을 세워야 합니다"라고 조언합니다.

평탄화는 중첩된 구조를 펼쳐서 일차원 구조로 만드는 과정입니다. 단순히 모든 중첩을 풀면 컬럼명이 너무 길어지고 관리가 어려워집니다.

필요한 깊이까지만 평탄화하거나, 특정 경로만 선택적으로 풀어내는 전략이 필요합니다. sep 파라미터로 구분자를 바꾸거나, 직접 재귀 함수로 커스텀 평탄화를 구현할 수도 있습니다.

다음 코드를 살펴봅시다.

from pandas import json_normalize

# 깊게 중첩된 JSON
deep_data = {
    "user": {
        "profile": {
            "name": "김개발",
            "address": {
                "city": "서울",
                "detail": "강남구"
            }
        },
        "stats": {"score": 95}
    }
}

# 기본 평탄화
df1 = json_normalize(deep_data)
print(df1.columns)  # user.profile.name, user.profile.address.city...

# 구분자 변경
df2 = json_normalize(deep_data, sep="_")
print(df2.columns)  # user_profile_name, user_profile_address_city...

# max_level로 깊이 제한
df3 = json_normalize(deep_data, max_level=1)
print(df3.columns)  # 1단계까지만 평탄화

김데이터 씨는 실전에 투입됐습니다. 진짜 운영 중인 API를 호출해서 데이터를 가져오는 업무입니다.

그런데 막상 데이터를 받아보니 상상 이상으로 복잡합니다. 사용자 정보 안에 프로필이 있고, 프로필 안에 주소가 있고, 주소 안에 또 세부 정보가 있습니다.

4단계, 5단계까지 중첩된 구조입니다. json_normalize()로 변환했더니 컬럼명이 엄청나게 깁니다.

"user.profile.settings.notification.email.enabled" 같은 식입니다. 도대체 이걸 어떻게 다뤄야 할까요?

박파이썬 씨가 설명합니다. "평탄화도 전략이 필요해요.

무조건 다 펼치면 오히려 불편해집니다." 평탄화의 목표는 데이터를 사용하기 쉽게 만드는 것입니다. 하지만 너무 과하면 역효과가 납니다.

첫 번째 전략은 구분자를 바꾸는 것입니다. 기본적으로 json_normalize()는 점(.)을 구분자로 사용합니다.

"user.name" 같은 식이죠. 그런데 점은 코드에서 속성 접근에 사용되는 기호라 헷갈릴 수 있습니다.

sep 파라미터를 사용하면 구분자를 바꿀 수 있습니다. sep="_"로 설정하면 "user_name"처럼 언더스코어로 연결됩니다.

이게 더 보기 편하다고 느끼는 사람도 많습니다. 어떤 팀에서는 sep="__"처럼 이중 언더스코어를 쓰기도 합니다.

이건 팀의 코딩 컨벤션에 따라 정하면 됩니다. 두 번째 전략은 깊이를 제한하는 것입니다.

max_level 파라미터를 사용하면 특정 깊이까지만 평탄화됩니다. max_level=1로 설정하면 1단계까지만 펼치고, 그 아래는 딕셔너리나 리스트 형태로 남겨둡니다.

예를 들어 사용자의 이름과 이메일만 필요하고, 주소의 세부 정보는 필요 없다면 max_level=1 정도면 충분합니다. 이렇게 하면 컬럼 수도 줄고 데이터도 깔끔해집니다.

세 번째 전략은 선택적 평탄화입니다. 모든 중첩을 다 풀 필요는 없습니다.

필요한 부분만 평탄화하고 나머지는 그대로 두는 것도 방법입니다. 예를 들어 json_normalize(data["user"]["profile"]) 같은 식으로 특정 경로만 추출해서 평탄화할 수 있습니다.

전체 데이터가 아니라 관심 있는 부분만 선택하는 것이죠. 네 번째 전략은 커스텀 평탄화입니다.

때로는 json_normalize()만으로는 부족할 때가 있습니다. 특정 필드는 평탄화하고 싶고, 특정 필드는 리스트로 남기고 싶을 때 말이죠.

이럴 때는 직접 재귀 함수를 작성하거나, DataFrame을 변환한 후에 추가 가공을 합니다. 예를 들어 df.rename(columns={...})로 컬럼명을 바꾸거나, df.drop(columns=[...])로 불필요한 컬럼을 제거할 수 있습니다.

실무에서는 어떻게 판단할까요? 일단 json_normalize()로 전체를 평탄화해봅니다.

그리고 df.columns를 출력해서 컬럼 목록을 확인합니다. 너무 길거나 복잡하면 전략을 수정합니다.

박파이썬 씨가 조언합니다. "처음부터 완벽하게 하려고 하지 마세요.

일단 평탄화하고, 결과를 보면서 조정하는 게 더 효율적입니다." 김데이터 씨는 여러 번 시도해봤습니다. max_level을 2로 했다가 1로 바꿔보고, sep도 "_"와 "."를 비교해봤습니다.

결국 자신의 업무에 딱 맞는 설정을 찾았습니다. 평탄화는 정답이 없습니다.

데이터의 구조와 분석 목적에 따라 최적의 전략이 달라집니다. 중요한 것은 결과 DataFrame이 사용하기 편한 형태여야 한다는 것입니다.

실전 팁

💡 - 먼저 df.columns와 df.head()로 결과를 확인한 후 전략을 조정하세요

  • 너무 깊은 중첩은 max_level로 제한하고, 필요시 나중에 추가 가공하는 게 낫습니다

5. DataFrame으로 변환

김데이터 씨는 이제 JSON을 평탄화할 수 있게 됐습니다. 그런데 실제 분석을 하려니 DataFrame에 대해 더 알아야 할 것 같습니다.

박파이썬 씨가 "DataFrame의 기본만 알면 충분합니다"라고 격려합니다.

DataFrame은 Pandas의 핵심 자료구조로, 행과 열로 이뤄진 2차원 테이블입니다. 마치 엑셀 시트와 같지만 훨씬 강력한 기능을 제공합니다.

JSON에서 변환된 DataFrame은 필터링, 정렬, 그룹화, 집계 등 다양한 분석이 가능합니다. df.head(), df.info(), df.describe() 같은 기본 메서드로 데이터를 빠르게 파악할 수 있습니다.

다음 코드를 살펴봅시다.

import pandas as pd
from pandas import json_normalize

# JSON 데이터 리스트
json_data = [
    {"name": "김개발", "age": 28, "city": "서울", "score": 85},
    {"name": "이코딩", "age": 32, "city": "부산", "score": 92},
    {"name": "박데이터", "age": 25, "city": "서울", "score": 78}
]

# DataFrame으로 변환
df = json_normalize(json_data)

# 기본 정보 확인
print(df.head())  # 처음 5행 확인
print(df.info())  # 컬럼 타입, 결측치 확인
print(df.describe())  # 숫자 컬럼 통계

# 필터링
seoul_df = df[df["city"] == "서울"]
high_score_df = df[df["score"] >= 80]

# 정렬
sorted_df = df.sort_values("score", ascending=False)

김데이터 씨는 JSON을 DataFrame으로 변환하는 데는 성공했습니다. 그런데 이제 이 데이터로 뭘 해야 할까요?

팀장님은 "서울에 사는 사람 중 점수가 80점 이상인 사람만 뽑아주세요"라고 하십니다. 박파이썬 씨가 말합니다.

"DataFrame의 기본 조작법을 알면 이런 건 식은 죽 먹기예요." DataFrame은 Pandas의 핵심입니다. 엑셀 시트를 상상하면 됩니다.

맨 위에는 열 제목이 있고, 왼쪽에는 행 번호가 있고, 각 셀에는 데이터가 들어있습니다. 하지만 엑셀보다 훨씬 강력합니다.

수백만 행의 데이터도 빠르게 처리하고, 복잡한 조건으로 필터링하고, 여러 데이터를 합치고, 그룹별로 집계하는 등 무엇이든 할 수 있습니다. JSON 리스트를 json_normalize()로 변환하면 각 JSON 객체가 한 행이 됩니다.

위의 코드에서 3개의 사용자 정보가 있으므로 3행짜리 DataFrame이 만들어집니다. "name", "age", "city", "score"가 컬럼명이 됩니다.

DataFrame을 처음 받으면 가장 먼저 할 일이 있습니다. 바로 데이터 파악입니다.

**df.head()**는 처음 5행을 보여줍니다. 데이터가 어떻게 생겼는지 빠르게 확인할 수 있습니다.

df.head(10)처럼 숫자를 주면 10행을 보여줍니다. **df.info()**는 DataFrame의 구조를 알려줍니다.

몇 행 몇 열인지, 각 컬럼의 데이터 타입이 무엇인지, 결측치는 없는지 확인할 수 있습니다. 데이터 분석의 첫 단계로 필수입니다.

**df.describe()**는 숫자 컬럼의 통계를 보여줍니다. 평균, 표준편차, 최솟값, 최댓값, 사분위수 등이 한눈에 나옵니다.

데이터의 분포를 빠르게 파악하는 데 유용합니다. 이제 본격적으로 데이터를 조작해봅시다.

필터링은 조건에 맞는 행만 추출하는 것입니다. df[df["city"] == "서울"]은 city 컬럼이 "서울"인 행만 뽑습니다.

대괄호 안에 조건을 쓰면 됩니다. 여러 조건을 조합할 수도 있습니다.

df[(df["city"] == "서울") & (df["score"] >= 80)]처럼 &(AND)나 |(OR)를 사용합니다. 단, 각 조건을 괄호로 감싸야 합니다.

정렬은 특정 컬럼 기준으로 행을 재배치하는 것입니다. df.sort_values("score")는 score 컬럼을 기준으로 오름차순 정렬합니다.

ascending=False를 주면 내림차순이 됩니다. 여러 컬럼으로 정렬할 수도 있습니다.

df.sort_values(["city", "score"])처럼 리스트로 주면 먼저 city로 정렬하고, 같은 city 안에서 score로 정렬합니다. 컬럼 선택도 자주 사용합니다.

df["name"]은 name 컬럼만 뽑아서 시리즈(Series)로 반환합니다. df[["name", "score"]]처럼 이중 대괄호를 쓰면 여러 컬럼을 DataFrame으로 뽑습니다.

필요 없는 컬럼은 df.drop(columns=["age"])로 제거할 수 있습니다. 이건 원본을 바꾸지 않고 새 DataFrame을 반환합니다.

원본을 바꾸려면 inplace=True를 추가합니다. 새 컬럼 추가도 간단합니다.

df["grade"] = "A"처럼 쓰면 모든 행에 "A"가 들어간 grade 컬럼이 생깁니다. 혹은 df["grade"] = df["score"].apply(lambda x: "A" if x >= 90 else "B")처럼 조건에 따라 값을 넣을 수도 있습니다.

박파이썬 씨가 실전 팁을 줍니다. "DataFrame 조작은 메서드 체이닝으로 연결하면 깔끔해요.

df.query('city == "서울"').sort_values("score").head(10) 같은 식으로요." 김데이터 씨는 여러 조작을 시도해봤습니다. 필터링하고, 정렬하고, 컬럼을 추가하고.

처음에는 에러가 났지만 몇 번 해보니 익숙해졌습니다. DataFrame은 데이터 분석의 기본 도구입니다.

JSON에서 변환한 데이터를 자유자재로 다룰 수 있어야 진짜 분석이 시작됩니다.

실전 팁

💡 - df.query() 메서드를 사용하면 SQL처럼 문자열로 조건을 쓸 수 있습니다

  • 대용량 데이터는 df.iterrows()보다 벡터 연산을 사용하는 게 훨씬 빠릅니다

6. 여러 API 결과 합치기

김데이터 씨는 한 API에서 데이터를 가져와 분석하는 데는 성공했습니다. 그런데 이번에는 여러 API를 호출해서 데이터를 합쳐야 한다고 합니다.

사용자 정보 API, 주문 정보 API, 상품 정보 API를 각각 호출해서 하나로 합쳐야 합니다. 박파이썬 씨가 "concat과 merge를 알아야 합니다"라고 말합니다.

여러 API에서 받은 데이터를 합치는 방법은 두 가지입니다. **pd.concat()**은 같은 구조의 DataFrame을 위아래로 붙이는 것으로, 마치 엑셀에서 시트를 복사해 붙이는 것과 같습니다.

**df.merge()**는 공통 키를 기준으로 좌우로 합치는 것으로, SQL의 JOIN과 같은 개념입니다. 상황에 따라 적절한 방법을 선택해야 합니다.

다음 코드를 살펴봅시다.

import pandas as pd
from pandas import json_normalize

# 첫 번째 API 응답 - 사용자 정보
users_json = [
    {"user_id": 1, "name": "김개발", "city": "서울"},
    {"user_id": 2, "name": "이코딩", "city": "부산"}
]

# 두 번째 API 응답 - 추가 사용자 정보 (같은 구조)
more_users_json = [
    {"user_id": 3, "name": "박데이터", "city": "대구"}
]

# 세 번째 API 응답 - 주문 정보
orders_json = [
    {"user_id": 1, "product": "노트북", "price": 1500000},
    {"user_id": 2, "product": "마우스", "price": 30000}
]

# DataFrame 변환
df_users1 = json_normalize(users_json)
df_users2 = json_normalize(more_users_json)
df_orders = json_normalize(orders_json)

# concat - 위아래로 합치기
df_all_users = pd.concat([df_users1, df_users2], ignore_index=True)
print(df_all_users)  # 3행으로 합쳐짐

# merge - 좌우로 합치기 (user_id 기준)
df_merged = df_users1.merge(df_orders, on="user_id", how="left")
print(df_merged)  # 사용자 정보 + 주문 정보

김데이터 씨에게 새로운 미션이 떨어졌습니다. 사용자 정보와 주문 정보를 합쳐서 "누가 무엇을 샀는지" 리포트를 만들어야 합니다.

문제는 사용자 정보와 주문 정보가 서로 다른 API에서 온다는 점입니다. 각각 호출해서 JSON을 받았는데, 이걸 어떻게 합칠까요?

박파이썬 씨가 화이트보드에 그림을 그리며 설명합니다. "합치는 방법은 크게 두 가지예요.

위아래로 붙이거나, 좌우로 붙이거나." 먼저 위아래로 붙이는 것부터 봅시다. 예를 들어 사용자 정보 API를 페이지별로 호출한다고 합시다.

1페이지에서 100명, 2페이지에서 100명, 이런 식으로 가져옵니다. 각 페이지의 데이터는 구조가 똑같습니다.

"user_id", "name", "city" 컬럼이 동일하게 있습니다. 이럴 때는 **pd.concat()**을 사용합니다.

concat은 concatenate의 약자로, 여러 DataFrame을 이어붙인다는 뜻입니다. pd.concat([df1, df2, df3])처럼 리스트로 묶어서 전달하면 됩니다.

결과는 df1의 행들 밑에 df2의 행들, 그 밑에 df3의 행들이 쌓인 형태입니다. 여기서 중요한 옵션이 ignore_index=True입니다.

각 DataFrame은 자신만의 인덱스를 가지고 있습니다. 보통 0, 1, 2...

같은 숫자죠. 그냥 concat하면 인덱스가 0, 1, 0, 1, 0, 1처럼 중복됩니다.

ignore_index=True를 주면 합친 후 0, 1, 2, 3, 4, 5로 새로 매깁니다. 다음은 좌우로 붙이는 것입니다.

사용자 정보에는 "user_id", "name", "city"가 있고, 주문 정보에는 "user_id", "product", "price"가 있습니다. 이 둘을 합쳐서 "user_id", "name", "city", "product", "price" 컬럼을 가진 DataFrame을 만들고 싶습니다.

바로 이럴 때 **merge()**를 사용합니다. SQL의 JOIN과 같은 개념입니다.

df1.merge(df2, on="user_id")처럼 쓰면 user_id가 같은 행끼리 합쳐집니다. 결과는 df1의 컬럼들과 df2의 컬럼들이 좌우로 나란히 붙은 형태입니다.

on 파라미터는 어떤 컬럼을 기준으로 합칠지 지정합니다. 보통 ID 같은 고유값을 사용합니다.

만약 컬럼명이 다르다면 어떻게 할까요? 예를 들어 df1에는 "user_id"인데 df2에는 "uid"라면 말이죠.

이럴 때는 left_on과 right_on을 씁니다. df1.merge(df2, left_on="user_id", right_on="uid")처럼 각각 지정하면 됩니다.

how 파라미터도 중요합니다. 기본값은 "inner"로, 양쪽에 모두 있는 것만 남깁니다.

user_id 1, 2가 양쪽에 있으면 이 둘만 결과에 나옵니다. "left"로 설정하면 왼쪽(df1)의 모든 행을 유지하고, 오른쪽(df2)에 없는 것은 NaN으로 채웁니다.

"right"는 반대로 오른쪽을 유지합니다. "outer"는 양쪽 모두 유지합니다.

실무에서는 어떤 상황에 어떤 방법을 쓸까요? concat은 같은 API를 여러 번 호출할 때, 혹은 날짜별로 나눠진 데이터를 합칠 때 씁니다.

예를 들어 1월 데이터, 2월 데이터, 3월 데이터를 합쳐서 분기별 분석을 하는 경우입니다. merge는 서로 다른 종류의 데이터를 연결할 때 씁니다.

사용자 정보와 주문 정보, 상품 정보와 카테고리 정보, 이런 식으로 ID로 연결되는 경우입니다. 박파이썬 씨가 실전 시나리오를 설명합니다.

"예를 들어 10개 페이지의 사용자 데이터를 concat으로 합치고, 그 결과를 주문 데이터와 merge하는 식으로 조합할 수 있어요." 김데이터 씨는 실제로 여러 API를 호출해서 데이터를 합쳐봤습니다. 처음에는 merge에서 how를 잘못 설정해서 데이터가 사라지기도 했습니다.

하지만 몇 번 시도하니 원하는 결과를 얻을 수 있었습니다. 여러 API 데이터를 합치는 것은 실무에서 매우 흔한 작업입니다.

concat과 merge를 자유자재로 다룰 수 있어야 복잡한 분석도 가능해집니다.

실전 팁

💡 - merge 전에 df.shape로 행 수를 확인하고, merge 후에도 확인해서 데이터가 의도대로 합쳐졌는지 검증하세요

  • 대용량 데이터는 merge 전에 필요한 컬럼만 선택하면 성능이 좋아집니다

7. 데이터 저장하기

김데이터 씨는 드디어 원하는 형태의 DataFrame을 만들었습니다. 이제 이걸 저장해서 팀원들과 공유하거나, 다른 프로그램에서 사용해야 합니다.

박파이썬 씨가 "저장 형식에 따라 메서드가 다릅니다"라고 알려줍니다.

DataFrame을 저장하는 방법은 용도에 따라 다릅니다. **to_csv()**는 엑셀에서 열 수 있는 범용 형식으로, 가장 많이 사용됩니다.

**to_excel()**은 엑셀 파일로 직접 저장하며, 서식까지 적용할 수 있습니다. **to_json()**은 다시 JSON으로 변환하여 API 응답을 만들 때 유용하고, **to_parquet()**은 대용량 데이터를 빠르고 효율적으로 저장할 때 씁니다.

다음 코드를 살펴봅시다.

import pandas as pd

# 분석 결과 DataFrame
df = pd.DataFrame({
    "name": ["김개발", "이코딩", "박데이터"],
    "score": [85, 92, 78],
    "city": ["서울", "부산", "서울"]
})

# CSV로 저장 - 가장 범용적
df.to_csv("result.csv", index=False, encoding="utf-8-sig")

# Excel로 저장 - 엑셀에서 바로 열기
df.to_excel("result.xlsx", index=False, sheet_name="분석결과")

# JSON으로 저장 - API 응답 형식
df.to_json("result.json", orient="records", force_ascii=False, indent=2)

# Parquet으로 저장 - 대용량 데이터 최적화
df.to_parquet("result.parquet", index=False)

# 데이터베이스에 저장 (SQLite 예시)
import sqlite3
conn = sqlite3.connect("mydata.db")
df.to_sql("users", conn, if_exists="replace", index=False)
conn.close()

김데이터 씨는 하루 종일 작업한 결과를 손에 쥐고 있습니다. API에서 데이터를 가져오고, 평탄화하고, 필터링하고, 합치고, 분석한 결과가 df라는 DataFrame에 담겨 있습니다.

이제 이걸 저장해야 합니다. 팀장님께 보고할 엑셀 파일도 만들어야 하고, 다른 팀에 전달할 CSV 파일도 만들어야 합니다.

어떻게 해야 할까요? 박파이썬 씨가 설명합니다.

"Pandas는 거의 모든 형식으로 저장할 수 있어요. 목적에 맞는 형식을 선택하면 됩니다." 가장 많이 사용하는 것은 CSV 형식입니다.

CSV는 Comma-Separated Values의 약자로, 쉼표로 구분된 텍스트 파일입니다. 엑셀, 구글 스프레드시트, 텍스트 에디터 등 어디서든 열 수 있어서 범용성이 높습니다.

**df.to_csv("파일명.csv")**로 저장합니다. 간단하죠?

여기서 중요한 옵션이 몇 가지 있습니다. index=False는 인덱스를 저장하지 않는다는 뜻입니다.

기본적으로 DataFrame의 인덱스(0, 1, 2...)가 첫 번째 컬럼으로 저장되는데, 보통은 필요 없으므로 False로 설정합니다. **encoding="utf-8-sig"**는 한글이 깨지지 않게 하는 설정입니다.

특히 엑셀에서 CSV를 열 때 한글이 깨지는 경우가 많은데, utf-8-sig로 저장하면 대부분 해결됩니다. 다음은 엑셀 형식입니다.

CSV는 텍스트라 서식이 없습니다. 셀 색깔, 굵은 글씨, 그래프 같은 건 저장할 수 없습니다.

진짜 엑셀 파일이 필요하다면 **to_excel()**을 사용합니다. df.to_excel("result.xlsx", index=False, sheet_name="분석결과")처럼 쓰면 .xlsx 파일이 만들어집니다.

sheet_name으로 시트 이름도 지정할 수 있습니다. 여러 시트를 만들고 싶다면 ExcelWriter를 사용합니다.

하나의 엑셀 파일에 여러 DataFrame을 각각 다른 시트로 저장할 수 있습니다. JSON 형식도 자주 사용됩니다.

DataFrame을 다시 JSON으로 변환해야 할 때가 있습니다. 예를 들어 백엔드에서 데이터를 가공한 후 프론트엔드로 보낼 때 말이죠.

**df.to_json()**을 사용하는데, 여기서 orient 파라미터가 중요합니다. orient="records"로 설정하면 [{"name": "김개발", ...}, {"name": "이코딩", ...}] 형태가 됩니다.

이게 가장 직관적이고 API 응답으로 많이 쓰는 형식입니다. orient="index"나 "columns"도 있지만 실무에서는 records를 주로 씁니다.

force_ascii=False는 한글을 그대로 저장하는 옵션입니다. True면 한글이 \uXXXX로 변환됩니다.

indent=2는 보기 좋게 들여쓰기하는 옵션입니다. Parquet 형식은 대용량 데이터에 최적화되어 있습니다.

CSV는 텍스트라 파일 크기가 크고 읽기가 느립니다. 수백만 행의 데이터를 CSV로 저장하면 기가바이트 단위가 됩니다.

Parquet은 컬럼 기반 바이너리 형식으로, 압축률이 높고 읽기가 빠릅니다. 같은 데이터를 저장해도 CSV의 1/10 크기로 줄어들기도 합니다.

**df.to_parquet("result.parquet")**로 저장하고, pd.read_parquet("result.parquet")로 읽습니다. 빅데이터 처리에서 표준처럼 사용됩니다.

데이터베이스에 저장하는 방법도 있습니다. SQLite, PostgreSQL, MySQL 같은 데이터베이스에 DataFrame을 테이블로 저장할 수 있습니다.

to_sql() 메서드를 사용합니다. if_exists="replace"는 테이블이 있으면 덮어쓴다는 뜻입니다.

"append"로 하면 기존 데이터 뒤에 추가합니다. "fail"은 테이블이 있으면 에러를 냅니다.

어떤 형식을 선택해야 할까요? 팀원과 공유하거나 엑셀로 볼 거면 CSVExcel을 씁니다.

간단하고 누구나 열 수 있습니다. API 응답이나 웹 애플리케이션에 사용할 거면 JSON을 씁니다.

프론트엔드에서 바로 파싱할 수 있습니다. 대용량 데이터를 저장하고 나중에 다시 분석할 거면 Parquet을 씁니다.

속도와 용량 면에서 최적입니다. 데이터를 서버에 영구 저장하고 SQL로 조회할 거면 데이터베이스를 씁니다.

박파이썬 씨가 마무리합니다. "저장 형식은 다르지만 코드는 비슷해요.

to_csv, to_excel, to_json, to_parquet 모두 사용법이 거의 같습니다." 김데이터 씨는 여러 형식으로 저장해봤습니다. 팀장님께는 엑셀 파일을 보내고, 개발팀에는 JSON을 전달하고, 자신의 로컬에는 Parquet으로 백업했습니다.

API에서 JSON을 받아서 DataFrame으로 변환하고, 분석하고, 다시 원하는 형식으로 저장하는 전체 흐름을 이제 완벽히 이해했습니다.

실전 팁

💡 - CSV는 범용성이 높지만 데이터 타입 정보가 손실됩니다. 다시 읽을 때 타입을 지정해야 할 수 있습니다

  • 대용량 데이터는 Parquet을 사용하면 저장 공간과 로딩 시간을 크게 줄일 수 있습니다

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#JSON#Pandas#DataFrame#API

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.