본문 바로가기

프로그래밍/flutter

Flutter에서 Instagram URI로 데이터 가져오는 방법(썸네일 + 설명)

반응형

 

1. 목표

 

Instagram 링크를 앱에 공유했을 때, 해당 게시글의 대표 이미지(thumbnail)와 간단한 텍스트 요약(설명 또는 제목)을 flutter 앱 내 표시하기 위한 기능 구현

 

 

2.  사전 준비

(0) IOS share extension 설정

(1) Meta 개발자 계정 생성

 

(2) Instagram oEmbed 제품 활성화

  • 만든 앱 → 왼쪽 메뉴 “제품 추가”
  • “Instagram oEmbed” → “Set Up” 클릭

(3) App 정보 확인

  • App IDApp Secret 복사
  • 앱 설정 > 앱 도메인instagram.com 추가 (화이트리스트)
  • 앱 설정 -> 고급 설정-> 클라이언트 토큰 복사

3.  토큰 구성

Instagram oEmbed는 Graph API 경로에서만 동작하며 access_token 필수입니다.

▸ App Access Token 구성 (예시)

 

INSTAGRAM_OEMBED_TOKEN=123456789012345|abcdef1234567890abcdef1234567890
#{APPID}|{clientToken}
.env 파일에 저장하고, Flutter에 flutter_dotenv로 주입

 


4. pubspec.yaml 의존성 추가

dependencies:
  http: ^0.13.6
  html: ^0.15.4
  flutter_dotenv: ^5.1.0

 


5.  Flutter 앱 초기화 코드(main.dart)

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: '.env');
  runApp(MyApp());
}

 


6.  핵심 기능 구현: 

fetchLinkSummary(String url)

 

위치(예시): /lib/link_summary.dart

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:html/parser.dart' show parse;
import 'package:flutter_dotenv/flutter_dotenv.dart';

final String _instagramAccessToken = (() {
  final token = dotenv.env['INSTAGRAM_OEMBED_TOKEN'];
  if (token != null && token.isNotEmpty) return token;

  final appId = dotenv.env['INSTAGRAM_APP_ID'];
  final secret = dotenv.env['INSTAGRAM_APP_SECRET'];
  if (appId != null && secret != null && appId.isNotEmpty && secret.isNotEmpty) {
    return '$appId|$secret';
  }
  return '';
})();

const String _instagramOEmbedEndpoint = 'https://graph.facebook.com/v18.0/instagram_oembed';

class LinkSummary {
  final String? imageUrl;
  final String text;
  LinkSummary({required this.text, this.imageUrl});
}

Future fetchLinkSummary(String url) async {
  try {
    if (url.contains('instagram.com')) {
      print('Fetching Instagram oEmbed data for: $url');

      final embedUrl =
          '$_instagramOEmbedEndpoint?url=$url&access_token=$_instagramAccessToken';
      final embedRes = await http.get(Uri.parse(embedUrl));
      print('Instagram oEmbed response status: ${embedRes.statusCode}');

      if (embedRes.statusCode == 200) {
        final data = jsonDecode(embedRes.body);
        return LinkSummary(
          text: data['title'] ?? 'Instagram 게시물',
          imageUrl: data['thumbnail_url'],
        );
      }

      // Fallback: Open Graph 스크래핑
      final res = await http.get(
        Uri.parse(url),
        headers: {
          'User-Agent': 'Mozilla/5.0 (LinkSummaryBot)',
        },
      );
      if (res.statusCode == 200) {
        final doc = parse(res.body);
        final image = doc
            .querySelector('meta[property="og:image"]')
            ?.attributes['content'];
        final description = doc
            .querySelector('meta[property="og:description"]')
            ?.attributes['content'];
        return LinkSummary(
          text: description ?? 'Instagram 게시물',
          imageUrl: image,
        );
      }

      return LinkSummary(text: 'Instagram 링크입니다. 내용을 확인하려면 앱에서 여세요.');
    }

    // 기타 URL: 기본 OG 스크래핑
    final res = await http.get(Uri.parse(url));
    if (res.statusCode != 200) return LinkSummary(text: '요약을 불러올 수 없습니다.');

    final document = parse(res.body);
    final image = document
        .querySelector('meta[property="og:image"]')
        ?.attributes['content'];
    final description = document
        .querySelector('meta[property="og:description"]')
        ?.attributes['content'];
    final title = document
            .querySelector('meta[property="og:title"]')
            ?.attributes['content'] ??
        document.querySelector('title')?.text;

    final textSummary = [title, description]
        .where((t) => t != null && t.trim().isNotEmpty)
        .join(' - ');

    return LinkSummary(text: textSummary, imageUrl: image);
  } catch (e) {
    print('Error: $e');
    return LinkSummary(text: '요약을 불러올 수 없습니다.');
  }
}

 


7. 🧪 사용 예시

final summary = await fetchLinkSummary('https://www.instagram.com/p/XXXXXXXXXXX');
print(summary.text);      // 게시글 설명 또는 제목
print(summary.imageUrl);  // 대표 썸네일

 


8. ✅ 보완사항 및 주의

항목설명

.env 보안 절대 Git에 커밋하지 않도록 .gitignore 포함
oEmbed 호출 실패 access_token 잘못되거나 rate limit 걸리면 fallback으로 OG 스크래핑 시도
캐시 전략 권장 동일 URL에 대한 반복 호출 방지용 local cache 도입 추천 (예: Isar, Hive 등)

 

정리

단계설명

1️⃣ Facebook 개발자 앱 생성 App ID / App Secret 확보
2️⃣ oEmbed 제품 활성화 토큰 인증 기반으로 oEmbed API 호출 가능
3️⃣ Flutter .env 구성 앱 ID/클라이언트 토큰 입력
4️⃣ oEmbed API 호출 또는 OG 대체 스크래핑 게시글 썸네일 및 설명 파싱
5️⃣ UI에 카드 형식으로 출력 대표 이미지 + 설명 요약 표시

 

반응형