반응형
1. 목표
Instagram 링크를 앱에 공유했을 때, 해당 게시글의 대표 이미지(thumbnail)와 간단한 텍스트 요약(설명 또는 제목)을 flutter 앱 내 표시하기 위한 기능 구현
2. 사전 준비
(0) IOS share extension 설정
(1) Meta 개발자 계정 생성
- https://developers.facebook.com/ 접속
- Facebook 계정으로 로그인
- 앱 만들기 → Consumer / Business 등 유형 선택
(2) Instagram oEmbed 제품 활성화
- 만든 앱 → 왼쪽 메뉴 “제품 추가”
- “Instagram oEmbed” → “Set Up” 클릭
(3) App 정보 확인
- App ID 와 App 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에 카드 형식으로 출력 | 대표 이미지 + 설명 요약 표시 |
반응형
'프로그래밍 > flutter' 카테고리의 다른 글
VS Code에서 안드로이드폰으로 Flutter 프로젝트 테스트 방법 (0) | 2024.02.26 |
---|---|
[flutter] android앱 출시 방법 (0) | 2024.01.29 |