기술 심층 · 필러 콘텐츠

Instagram 데이터 ZIP 구조 심층 해부
— 개발자가 분석한 JSON의 진실

인스타그램이 주는 "내 정보 내보내기" ZIP을 처음 열어본 사람은 대부분 당황합니다. 폴더가 수십 개, JSON은 비슷비슷한 이름, 그리고 어딘가에서 데이터가 잘려 있습니다. 이 글은 그 ZIP 파일을 실제로 파싱해 CheckMate를 만든 입장에서, 파일별 역할과 누구도 설명하지 않는 365일 절단의 이유까지 기술적으로 풀어냅니다.

작성자: 이명진 (thingineeer) · 업데이트: 2026-04-12 · 읽는 시간: 약 12분

이 글이 답하는 질문 6가지
  1. ZIP 파일 안의 디렉터리 트리는 정확히 어떻게 생겼는가
  2. followers_1.json / following.json / followers_unfollowed.json은 무엇이 다른가
  3. 왜 following은 전체인데 followers는 ~365일로 잘리는가
  4. close_friends.json,pending_follow_requests.json 같은 덜 알려진 파일에는 무엇이 있는가
  5. HTML 형식과 JSON 형식은 실제로 무엇이 다른가
  6. TypeScript로 직접 파싱하려면 어디서부터 시작해야 하는가

1. ZIP을 열어보면 이렇게 생겼습니다

Instagram 앱 → 계정 센터 → 내 정보 및 권한 → 내 정보 내보내기에서 "전체 기간 + JSON"으로 요청하면 수 분에서 최대 48시간 후 ZIP 파일을 받게 됩니다. 파일명은 보통 instagram-유저명-YYYY-MM-DD-해시.zip 형태이고, 해제하면 다음 구조가 나옵니다(발췌 — 체크한 항목에 따라 일부 폴더는 없을 수 있습니다).

instagram-myeongjin-2026-04-12-xxxxx/ ├─ connections/ │ └─ followers_and_following/ │ ├─ followers_1.json ← 팔로워 (여러 개로 분할될 수 있음: followers_2.json ...) │ ├─ following.json ← 팔로잉 전체 │ ├─ pending_follow_requests.json ← 내가 보낸 팔로우 요청 중 대기 중 │ ├─ follow_requests_you've_received.json │ ├─ recent_follow_requests.json │ ├─ recently_unfollowed_profiles.json ← 내가 최근 언팔한 계정 │ ├─ removed_suggestions.json │ ├─ hide_story_from.json │ ├─ blocked_profiles.json │ ├─ close_friends.json │ └─ restricted_profiles.json ├─ personal_information/ │ └─ personal_information/personal_information.json ├─ your_instagram_activity/ │ ├─ content/ │ │ ├─ posts_1.json │ │ ├─ stories.json │ │ └─ reels.json │ ├─ likes/liked_posts.json │ ├─ comments/post_comments_1.json │ └─ saved/saved_posts.json ├─ messages/ │ └─ inbox/<상대방별 폴더>/message_1.json ├─ logged_information/ │ └─ login_activity.json └─ media/ (이미지/영상 파일)

맞팔/언팔 확인에 필요한 건 이 중 거의 전부가 아니라 딱 하나의 폴더, connections/followers_and_following/ 뿐입니다. CheckMate도 이 경로만 읽습니다. 나머지는 인사이트 대시보드(좋아요·댓글 상위 인물 등)를 만들 때 선택적으로 사용합니다.

2. 핵심 3파일: followers_1.json · following.json · followers_unfollowed.json

이름이 비슷해서 헷갈리는 세 파일의 역할을 먼저 표로 정리합니다. 실제 ZIP에 존재하는 이름을 기준으로 했으며, 일부 파일명은 파이썬 쪽 분석 툴에서 흔히 쓰는 별명(unfollowed.json 등)과 다를 수 있습니다.

파일명의미기간 제한CheckMate에서 쓰는가
followers_1.json
(+ followers_2, 3 … 팔로워 많을 때)
나를 팔로우하는 사람약 365일 (뒤에서 상세 설명)필수
following.json내가 팔로우하는 사람전체필수
recently_unfollowed_profiles.json내가 최근 언팔한 사람최근 몇 달 (비공식)보조 (히스토리 분석)
pending_follow_requests.json내가 보낸 팔로우 요청 중 아직 승인 안 된 것현재 상태 스냅샷보조 (인사이트 옵션)
close_friends.json친한 친구 리스트현재 상태 스냅샷선택
blocked_profiles.json차단한 계정누적 전체선택

followers_1.json 실제 구조 (발췌)

{
  "relationships_followers": [
    {
      "title": "",
      "media_list_data": [],
      "string_list_data": [
        {
          "href": "https://www.instagram.com/_u/some_user",
          "value": "some_user",
          "timestamp": 1701456789
        }
      ]
    },
    { "...다음 팔로워..." }
  ]
}

주의할 포인트 네 가지입니다.

following.json과의 차이

구조 자체는 매우 유사하지만 최상위 키가 relationships_following이고, 이 파일에는 절단이 없습니다. 즉 몇 년 전 팔로우한 계정도 그대로 들어 있습니다. 비대칭성이 분석 정확도에 직접 영향을 주기 때문에 다음 섹션에서 별도로 다룹니다.

3. 왜 following은 전체이고 followers는 ~365일로 잘리는가

경쟁 도구들이 거의 설명하지 않는 지점입니다. 사용자 입장에서는 "숫자가 안 맞네?" 한 마디지만, ZIP을 직접 파싱해 보면 규칙이 보입니다. Instagram이 데이터 내보내기를 설계한 방식은 다음과 같습니다.

데이터주체제공 범위근거(해석)
following내가 수행한 팔로우 액션전체내 행동 기록 → 주체가 "나"이므로 GDPR/개인정보 주권 기준 전체 제공
followers타인이 나를 팔로우한 액션대략 1년(약 365일)타인의 행동 기록 → 제3자 데이터 → 제공 범위가 좁고, 최근성 기준으로만 내보냄
요약 — 한 문장

"내가 한 팔로우(following)"는 내 데이터라 전체가 주어지지만, "남이 나를 팔로우한 것(followers)"은 상대방의 데이터 성격이 강하므로 최근 활동 기준으로만 반환된다. 그래서 현재 UI에 표시되는 팔로워 수와 ZIP 속 followers 개수가 다른 게 정상입니다.

정확한 365일이라는 숫자는 Instagram의 공식 문서에 명시돼 있지 않고, 시기에 따라 6개월·1년 사이에서 움직이는 것으로 관측됩니다. CheckMate는 보수적으로 "약 1년 이내에 너를 팔로우한 사람만 포함된다"고 안내하며, 그 이전에 팔로우했는데 최근 아무 행동이 없는 계정은 ZIP에서 빠질 수 있다는 점을 전제로 분석합니다.

이 비대칭이 만드는 실제 문제 3가지

  1. "맞팔 X"로 잘못 잡히는 오래된 팔로워. 내가 3년 전 팔로우한 친구가 지금도 나를 팔로우 중인데 ZIP에는 followers 쪽에 안 들어 있어서, 단순 차집합 계산에서 "내가 팔로우 중인데 상대는 안 함"으로 잡힙니다. CheckMate는 이 케이스를 경고 표시로 알려 줍니다.
  2. 팔로워 수 미스매치. Instagram 앱에 표시되는 팔로워 수는 실시간·누적이고, ZIP의 followers 개수는 최근 1년 범위라 차이가 날 수밖에 없습니다.
  3. 비활성/삭제 계정 처리. following에는 이미 사라진 계정도 남아 있을 수 있습니다.timestamp 값이 매우 오래됐고 username이 "instagramuser"처럼 일반화된 경우가 그 신호입니다.

2025~2026년 데이터 정책 변경 전후의 차이는 별도 글 — Instagram 데이터 정책 변경 해설에서 다룹니다.

"시간 창(time window)" 관점에서 한 번 더 정리

혼동이 잦은 포인트라 다른 각도로도 설명합니다. 데이터 내보내기는 "현재 상태"가 아니라 "기간 내 이벤트 로그"에 가깝습니다. following은 내가 일으킨 이벤트라 계정이 살아 있는 한 그대로 남지만, followers는 타인이 나를 대상으로 일으킨 이벤트이고 Instagram이 제3자 행동 이력을 "최근성" 기준으로만 되돌려 줍니다. 그래서 "한 번 맞팔이었지만 최근 1년간 아무 상호작용이 없던 계정"은 팔로우 관계가 유지돼 있어도 ZIP의 followers에서 누락될 수 있습니다.

이 때문에 CheckMate는 세 가지 보정 로직을 운용합니다. (1) timestamp가 오래된 following 항목에 "장기" 태그를 붙여 사용자가 차집합 결과를 무작정 믿지 않도록 안내하고, (2) "맞팔 아님"으로 분류된 결과에는 "상대가 오래된 팔로워일 가능성" 주석을 보여주며, (3) 같은 계정을 한 달 간격으로 두 번 분석했을 때의 차이를 비교해 1년 절단 창이 움직이는 양을 시각화합니다. 이 세 장치가 "ZIP 데이터의 한계"를 UI 레벨에서 보완하는 방식입니다.

4. 잘 안 알려진 파일들 — close_friends, pending_follow_requests, blocked_profiles

맞팔 확인에는 필수가 아니지만, 인사이트 대시보드를 만드는 데 쓸 수 있는 파일들입니다.

close_friends.json

친한 친구 기능에 추가된 계정 리스트입니다. 일방적 선택이므로 상대는 모릅니다. 구조는 followers와 동일 — relationships_close_friends 배열 안에 string_list_data 가 있습니다.

pending_follow_requests.json

비공개 계정에 팔로우를 보냈는데 승인 대기 중인 목록. "왜 내가 팔로잉은 많은데 상대는 반응이 없는가?" 같은 질문에 답이 됩니다. 오래된 pending이 쌓이면 팔로우 요청 도달률이 떨어지니 정리 대상 후보입니다.

recently_unfollowed_profiles.json

내가 최근 언팔한 계정 리스트입니다. "팔로우 해제 이력"이 앱에는 UI로 노출되지 않기 때문에 ZIP만이 유일한 공식 기록입니다. 보통 3~6개월치가 남습니다.

blocked_profiles.json · restricted_profiles.json

차단/제한한 계정. 이 둘은 followers/following 로직에 영향을 줍니다. 차단한 계정은 followers에서 자동으로 빠지므로, "분명히 맞팔이었는데 사라졌다"면 내가 그 사람을 차단했을 가능성도 확인해 볼 수 있습니다.

5. HTML로 받은 경우 vs JSON으로 받은 경우

Instagram은 내보내기 요청 시 HTML 또는 JSON 형식을 선택할 수 있습니다. 처음 쓰는 사람이 자주 하는 실수가 HTML을 그대로 두는 것인데, 분석 용도로는 거의 쓸 수 없습니다.

특성HTML 형식JSON 형식
목적사람이 브라우저로 눈으로 보기기계 파싱·분석
구조화 정도<a href> 태그 + 텍스트만스키마 있는 배열/객체
timestamp 보존부분적 (텍스트로만)완전 (Unix epoch)
파일 크기큰 편 (스타일 포함)작음
CheckMate 지원비권장 — 정확도 낮음권장

HTML을 받았다면 다시 내보내기 요청을 JSON으로 걸어 주세요. 과거 ZIP과 새 ZIP 간 기간 설정이 같다면 결과는 거의 동일합니다. 자세한 재요청 절차는 Instagram 데이터 다운로드 방법 가이드에서 다룹니다.

6. TypeScript로 직접 파싱해 보기 (최소 예제)

"도구를 쓰지 않고 직접 언팔을 뽑을 수 있을까?" — 네, 가능합니다. 다음 코드는 followers와 following의 차집합을 계산하는 최소 예제입니다. CheckMate 내부 로직도 본질은 이와 같고, 다중 followers_N.json 병합, 타임존 처리, 닉변 감지만 더해진 형태입니다.

// deno/node 어느 쪽에서도 동작. zip 해제는 유틸로 먼저 수행.
import { readFile } from 'node:fs/promises';

type Entry = {
  title: string;
  media_list_data: unknown[];
  string_list_data: Array<{ href: string; value: string; timestamp: number }>;
};
type FollowersFile = { relationships_followers: Entry[] };
type FollowingFile = { relationships_following: Entry[] };

const parseUsername = (e: Entry) => e.string_list_data[0]?.value ?? '';

async function analyze(followersPath: string, followingPath: string) {
  const [fRaw, gRaw] = await Promise.all([
    readFile(followersPath, 'utf8'),
    readFile(followingPath, 'utf8'),
  ]);
  const followersFile = JSON.parse(fRaw) as FollowersFile;
  const followingFile = JSON.parse(gRaw) as FollowingFile;

  const followers = new Set(followersFile.relationships_followers.map(parseUsername));
  const following = new Set(followingFile.relationships_following.map(parseUsername));

  // 내가 팔로우 중인데 상대는 나를 팔로우 안 함
  const notFollowingBack = [...following].filter((u) => !followers.has(u));
  // 나를 팔로우 중인데 나는 팔로우 안 함
  const fans = [...followers].filter((u) => !following.has(u));
  // 맞팔
  const mutual = [...following].filter((u) => followers.has(u));

  return { notFollowingBack, fans, mutual };
}

// 사용 예
analyze('followers_1.json', 'following.json').then((r) => {
  console.log('맞팔 아님:', r.notFollowingBack.length);
  console.log('팬(논맞팔):', r.fans.length);
  console.log('맞팔:', r.mutual.length);
});

엣지 케이스 4가지

7. CheckMate가 이 구조를 어떻게 쓰는가

CheckMate는 이 ZIP을 서버에 올리지 않고 브라우저에서 직접 해제·파싱합니다. 핵심 파일 경로는 connections/followers_and_following/ 하위이며, 업로드된 ZIP의 분할된 followers 파일을 전부 합친 뒤 username을 소문자 정규화하고, 위 예제처럼 차집합을 계산합니다. 그 외의 파일(likes, comments, saved 등)은 사용자가 허용한 경우에만 인사이트 대시보드로 분석합니다.

로그인·자동화·서버 저장 없이 로컬에서 처리하는 이유는 두 가지입니다. 하나는 프라이버시, 또 하나는 — 뒤에서 다룰 — Instagram의 쉐도우밴 리스크를 회피하기 위해서입니다. 로그인 자격증명을 받는 순간 자동화 트리거가 발생할 수 있기 때문에, 이 툴은 의도적으로 그 선을 넘지 않습니다. 관련 내용은 Shadowban 회피와 안전한 언팔 패턴 글에서 자세히 다룹니다.

8. 자주 나오는 질문

Q. ZIP에 비밀번호가 걸려 있나요?

아니요. Instagram이 보내는 다운로드 링크 자체가 본인 계정에서만 접근 가능한 개인 URL이라, ZIP 파일에 별도 암호는 걸려 있지 않습니다. 단, 다운로드한 파일을 타인에게 보내면 곧바로 읽을 수 있으니 보관에 주의하세요.

Q. 여러 번 내보내도 결과가 같나요?

같은 기간에서 요청하면 거의 동일합니다. 단, followers의 ~365일 절단은 "요청 시점 기준"이라 어제 받은 ZIP과 오늘 받은 ZIP의 followers 리스트가 1~2명 차이날 수 있습니다.

Q. 파일 일부만 보내도 분석되나요?

CheckMate 기준으로는 followers_*.json 한 개 이상 + following.json 이 있으면 됩니다. ZIP을 통째로 올려도 되고, 압축을 풀어 JSON만 골라 올려도 됩니다.

Q. 내 데이터가 서버에 저장되나요?

저장되지 않습니다. 브라우저에서 파싱해 결과만 보여주고, 탭을 닫으면 휘발됩니다. 자세한 처리 원칙은 FAQ 에 정리해 두었습니다.

ZIP 구조는 이해했으니 이제 실제로 분석해 볼 차례입니다

ZIP 파일을 그대로 올리면 위 로직을 브라우저에서 바로 실행합니다.

CheckMate에서 분석하기 →

관련 글

인스타그램 데이터 다운로드 방법

올바른 ZIP을 받기 위한 설정과 오류 대응 가이드

CheckMate에서 확인할 수 있는 5가지

언팔로워, 맞팔, 팬, 닉변 감지, 인사이트 대시보드

Shadowban 회피 — 언팔 한도와 안전 패턴

자동화 툴 리스크와 수동 처리의 근거

CheckMate 운영자 소개

누가 이 글과 툴을 만들었는지