# SNS 통합 가이드 이 문서는 Zellyy 앱에서 다양한 소셜 미디어 플랫폼과의 통합 방법을 안내합니다. ## 개요 Zellyy 앱의 핵심 기능 중 하나는 사용자가 작성한 카드를 다양한 소셜 미디어 플랫폼에 공유하는 기능입니다. 이 문서는 다음 플랫폼과의 통합 방법을 설명합니다: - Facebook - Instagram - Twitter (X) - LinkedIn - Pinterest ## 사전 요구사항 - 각 소셜 미디어 플랫폼의 개발자 계정 - 각 플랫폼에 등록된 앱 - React Native 개발 환경 - Supabase 설정 완료 ## 1. 소셜 미디어 앱 등록 ### 1.1 Facebook 앱 등록 1. [Facebook 개발자 포털](https://developers.facebook.com/)에 접속 2. "내 앱" > "앱 만들기" 클릭 3. "소비자" 유형 선택 4. 앱 이름 입력 (예: "Zellyy") 5. 앱 생성 후 다음 제품 추가: - Facebook 로그인 - Instagram API - 공유 API 설정 완료 후 다음 정보를 기록: - 앱 ID - 앱 시크릿 ### 1.2 Twitter 앱 등록 1. [Twitter 개발자 포털](https://developer.twitter.com/)에 접속 2. "Projects & Apps" > "Overview" > "Create App" 클릭 3. 앱 이름, 설명 입력 4. 앱 권한 설정: "Read and Write" 5. 리디렉션 URL 설정: `zellyy://auth/twitter` 설정 완료 후 다음 정보를 기록: - API 키 - API 시크릿 키 - 액세스 토큰 - 액세스 토큰 시크릿 ### 1.3 LinkedIn 앱 등록 1. [LinkedIn 개발자 포털](https://www.linkedin.com/developers/)에 접속 2. "Create App" 클릭 3. 앱 이름, 설명, 로고 등 입력 4. 제품 추가: "Share on LinkedIn" 5. OAuth 2.0 설정: - 리디렉션 URL: `zellyy://auth/linkedin` - 권한: `r_liteprofile`, `w_member_social` 설정 완료 후 다음 정보를 기록: - 클라이언트 ID - 클라이언트 시크릿 ### 1.4 Pinterest 앱 등록 1. [Pinterest 개발자 포털](https://developers.pinterest.com/)에 접속 2. "Apps" > "Create app" 클릭 3. 앱 이름, 설명 입력 4. 리디렉션 URL 설정: `zellyy://auth/pinterest` 5. 권한 설정: `read_public`, `write_public` 설정 완료 후 다음 정보를 기록: - 앱 ID - 앱 시크릿 ## 2. 환경 변수 설정 앱의 `.env` 파일에 소셜 미디어 API 키를 추가합니다: ``` # Facebook FACEBOOK_APP_ID=your_facebook_app_id FACEBOOK_APP_SECRET=your_facebook_app_secret # Twitter TWITTER_CONSUMER_KEY=your_twitter_api_key TWITTER_CONSUMER_SECRET=your_twitter_api_secret # LinkedIn LINKEDIN_CLIENT_ID=your_linkedin_client_id LINKEDIN_CLIENT_SECRET=your_linkedin_client_secret # Pinterest PINTEREST_APP_ID=your_pinterest_app_id PINTEREST_APP_SECRET=your_pinterest_app_secret ``` ## 3. 필요한 패키지 설치 ```bash # 소셜 로그인 및 공유 라이브러리 npm install react-native-fbsdk-next npm install @react-native-twitter-signin/twitter-signin npm install react-native-linkedin npm install react-native-pinterest # 일반 공유 기능 npm install react-native-share # 딥링크 처리 npm install react-native-app-auth ``` ## 4. iOS 설정 ### 4.1 Info.plist 설정 `ios/YourApp/Info.plist` 파일에 다음 내용을 추가합니다: ```xml CFBundleURLTypes CFBundleURLSchemes fb{FACEBOOK_APP_ID} CFBundleURLSchemes twitterkit-{TWITTER_CONSUMER_KEY} CFBundleURLSchemes zellyy CFBundleURLSchemes pdk{PINTEREST_APP_ID} FacebookAppID {FACEBOOK_APP_ID} FacebookDisplayName Zellyy LSApplicationQueriesSchemes fbapi fb-messenger-share-api fbauth2 fbshareextension twitter twitterauth linkedin linkedin-sdk2 pinterestsdk ``` ### 4.2 AppDelegate.m 수정 `ios/YourApp/AppDelegate.m` 파일에 다음 내용을 추가합니다: ```objective-c #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... // Facebook SDK 초기화 [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; // Twitter SDK 초기화 [[Twitter sharedInstance] startWithConsumerKey:@"TWITTER_CONSUMER_KEY" consumerSecret:@"TWITTER_CONSUMER_SECRET"]; return YES; } // URL 스킴 처리 - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { // Facebook URL 처리 BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:app openURL:url options:options]; // Twitter URL 처리 if (!handled) { handled = [[Twitter sharedInstance] application:app openURL:url options:options]; } return handled; } @end ``` ## 5. Android 설정 ### 5.1 AndroidManifest.xml 수정 `android/app/src/main/AndroidManifest.xml` 파일에 다음 내용을 추가합니다: ```xml ``` ### 5.2 strings.xml 설정 `android/app/src/main/res/values/strings.xml` 파일에 다음 내용을 추가합니다: ```xml Zellyy FACEBOOK_APP_ID fbFACEBOOK_APP_ID FACEBOOK_CLIENT_TOKEN ``` ### 5.3 build.gradle 설정 `android/app/build.gradle` 파일에 다음 내용을 추가합니다: ```gradle dependencies { // ... implementation 'com.facebook.android:facebook-android-sdk:latest.release' implementation 'com.twitter.sdk.android:twitter-core:3.3.0' implementation 'com.twitter.sdk.android:tweet-composer:3.3.0' // ... } ``` ## 6. 소셜 로그인 구현 ### 6.1 Facebook 로그인 ```javascript import { LoginManager, AccessToken, GraphRequest, GraphRequestManager } from 'react-native-fbsdk-next'; import supabase from '../services/supabase'; const loginWithFacebook = async () => { try { // 로그인 권한 요청 const result = await LoginManager.logInWithPermissions(['public_profile', 'email']); if (result.isCancelled) { throw new Error('User cancelled login'); } // 액세스 토큰 가져오기 const data = await AccessToken.getCurrentAccessToken(); if (!data) { throw new Error('Failed to get access token'); } // 사용자 정보 가져오기 const profileRequest = new GraphRequest( '/me', { accessToken: data.accessToken, parameters: { fields: { string: 'id,name,email,picture.type(large)', }, }, }, async (error, result) => { if (error) { console.error('Error fetching profile:', error); return; } // Supabase에 소셜 계정 연결 const { data: socialAccount, error: socialError } = await supabase .from('zellyy.social_accounts') .upsert({ user_id: supabase.auth.user().id, platform: 'facebook', platform_user_id: result.id, access_token: data.accessToken, token_expires_at: new Date(data.expirationTime), }) .select() .single(); if (socialError) { console.error('Error connecting Facebook account:', socialError); } } ); new GraphRequestManager().addRequest(profileRequest).start(); } catch (error) { console.error('Facebook login error:', error); throw error; } }; ``` ### 6.2 Twitter 로그인 ```javascript import { TwitterSignIn } from '@react-native-twitter-signin/twitter-signin'; import supabase from '../services/supabase'; const loginWithTwitter = async () => { try { // Twitter SDK 초기화 TwitterSignIn.init( 'TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET' ); // 로그인 요청 const { authToken, authTokenSecret, userName, userID } = await TwitterSignIn.logIn(); // Supabase에 소셜 계정 연결 const { data: socialAccount, error: socialError } = await supabase .from('zellyy.social_accounts') .upsert({ user_id: supabase.auth.user().id, platform: 'twitter', platform_user_id: userID, access_token: authToken, refresh_token: authTokenSecret, }) .select() .single(); if (socialError) { console.error('Error connecting Twitter account:', socialError); } } catch (error) { console.error('Twitter login error:', error); throw error; } }; ``` ## 7. 소셜 공유 구현 ### 7.1 일반 공유 기능 ```javascript import Share from 'react-native-share'; const shareToDefault = async (card) => { try { // 카드 이미지 생성 (별도 함수로 구현) const imageUrl = await generateCardImage(card); const options = { title: 'Share via', message: card.content, url: imageUrl, }; const result = await Share.open(options); console.log('Share result:', result); return result; } catch (error) { console.error('Error sharing:', error); throw error; } }; ``` ### 7.2 Facebook 공유 ```javascript import { ShareDialog } from 'react-native-fbsdk-next'; import supabase from '../services/supabase'; const shareToFacebook = async (card) => { try { // 카드 이미지 생성 (별도 함수로 구현) const imageUrl = await generateCardImage(card); const shareContent = { contentType: 'link', contentUrl: imageUrl, contentDescription: card.content, }; // 공유 다이얼로그 표시 const result = await ShareDialog.show(shareContent); if (result.isCancelled) { throw new Error('User cancelled sharing'); } // 공유 기록 저장 const { data: share, error: shareError } = await supabase .from('zellyy.social_shares') .insert({ card_id: card.id, user_id: supabase.auth.user().id, platform: 'facebook', status: 'success', response_data: result, }) .select() .single(); if (shareError) { console.error('Error recording share:', shareError); } return result; } catch (error) { console.error('Error sharing to Facebook:', error); throw error; } }; ``` ### 7.3 Twitter 공유 ```javascript import { TwitterSignIn } from '@react-native-twitter-signin/twitter-signin'; import supabase from '../services/supabase'; const shareToTwitter = async (card) => { try { // 소셜 계정 정보 가져오기 const { data: socialAccount, error: socialError } = await supabase .from('zellyy.social_accounts') .select('*') .eq('user_id', supabase.auth.user().id) .eq('platform', 'twitter') .single(); if (socialError || !socialAccount) { throw new Error('Twitter account not connected'); } // 카드 이미지 생성 (별도 함수로 구현) const imageUrl = await generateCardImage(card); // Edge Function 호출하여 트윗 게시 const response = await fetch('https://a11.ism.kr/functions/v1/social-share', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${supabase.auth.session().access_token}`, }, body: JSON.stringify({ cardId: card.id, platform: 'twitter', message: card.content, }), }); const result = await response.json(); if (!result.success) { throw new Error(result.error || 'Failed to share to Twitter'); } return result; } catch (error) { console.error('Error sharing to Twitter:', error); throw error; } }; ``` ## 8. 소셜 계정 관리 ### 8.1 연결된 계정 목록 조회 ```javascript import { useEffect, useState } from 'react'; import supabase from '../services/supabase'; const SocialAccountsScreen = () => { const [accounts, setAccounts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchAccounts(); }, []); const fetchAccounts = async () => { try { setLoading(true); const { data, error } = await supabase .from('zellyy.social_accounts') .select('*') .eq('user_id', supabase.auth.user().id); if (error) { throw error; } setAccounts(data || []); } catch (error) { console.error('Error fetching social accounts:', error); alert('Failed to load social accounts'); } finally { setLoading(false); } }; // 컴포넌트 렌더링 코드 }; ``` ### 8.2 계정 연결 해제 ```javascript const disconnectAccount = async (platform) => { try { // 소셜 계정 삭제 const { error } = await supabase .from('zellyy.social_accounts') .delete() .match({ user_id: supabase.auth.user().id, platform, }); if (error) { throw error; } // 플랫폼별 로그아웃 처리 if (platform === 'facebook') { LoginManager.logOut(); } else if (platform === 'twitter') { TwitterSignIn.logOut(); } // 계정 목록 새로고침 fetchAccounts(); alert(`${platform} account disconnected`); } catch (error) { console.error(`Error disconnecting ${platform} account:`, error); alert(`Failed to disconnect ${platform} account`); } }; ``` ## 9. 공유 기록 관리 ### 9.1 공유 기록 조회 ```javascript import { useEffect, useState } from 'react'; import supabase from '../services/supabase'; const ShareHistoryScreen = () => { const [shares, setShares] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchShareHistory(); }, []); const fetchShareHistory = async () => { try { setLoading(true); const { data, error } = await supabase .from('zellyy.social_shares') .select(` id, platform, share_url, shared_at, status, cards:card_id ( id, content, background_color, text_color ) `) .eq('user_id', supabase.auth.user().id) .order('shared_at', { ascending: false }); if (error) { throw error; } setShares(data || []); } catch (error) { console.error('Error fetching share history:', error); alert('Failed to load share history'); } finally { setLoading(false); } }; // 컴포넌트 렌더링 코드 }; ``` ## 10. 카드 이미지 생성 카드를 이미지로 변환하여 소셜 미디어에 공유하기 위한 함수입니다. ```javascript import ViewShot from 'react-native-view-shot'; import { useRef } from 'react'; import supabase from '../services/supabase'; // 카드 컴포넌트 const Card = ({ card, viewShotRef }) => { return ( {card.content} ); }; // 이미지 생성 및 업로드 함수 const generateCardImage = async (card) => { try { // ViewShot 참조 const viewShotRef = useRef(); // 카드 이미지 캡처 const uri = await viewShotRef.current.capture(); // 파일 이름 생성 const fileName = `${supabase.auth.user().id}/${card.id}_${Date.now()}.jpg`; // 이미지 파일 생성 const formData = new FormData(); formData.append('file', { uri, name: fileName, type: 'image/jpeg', }); // Supabase Storage에 업로드 const { data, error } = await supabase .storage .from('card-images') .upload(fileName, formData); if (error) { throw error; } // 공개 URL 생성 const { publicURL, error: urlError } = supabase .storage .from('card-images') .getPublicUrl(fileName); if (urlError) { throw urlError; } return publicURL; } catch (error) { console.error('Error generating card image:', error); throw error; } }; ``` ## 11. 문제 해결 ### 11.1 일반적인 문제 및 해결 방법 1. **소셜 로그인 실패**: - 앱 ID와 시크릿이 올바른지 확인 - 리디렉션 URL이 올바르게 설정되었는지 확인 - 앱 권한이 올바르게 설정되었는지 확인 2. **공유 기능 실패**: - 소셜 미디어 앱이 기기에 설치되어 있는지 확인 - 액세스 토큰이 유효한지 확인 - 필요한 권한이 부여되었는지 확인 3. **이미지 업로드 실패**: - 스토리지 버킷 권한 설정 확인 - 파일 크기 및 형식 확인 ### 11.2 플랫폼별 문제 해결 #### Facebook - 앱 검토 상태 확인 - 개발 모드에서는 테스트 사용자만 앱에 접근 가능 #### Twitter - API 키와 시크릿이 올바른지 확인 - 개발자 계정 상태 확인 #### Instagram - Facebook 개발자 계정과 연결되어 있는지 확인 - Instagram Graph API 권한 확인 ## 12. 보안 고려사항 1. **토큰 보안**: - 액세스 토큰과 리프레시 토큰은 안전하게 저장 - 서버 측에서만 API 시크릿 사용 2. **사용자 데이터 보호**: - 필요한 최소한의 권한만 요청 - 사용자 동의 없이 데이터를 공유하지 않음 3. **API 키 관리**: - 환경 변수로 관리하고 소스 코드에 하드코딩하지 않음 - 프로덕션과 개발 환경에 별도의 API 키 사용 ## 결론 이 가이드는 Zellyy 앱에서 다양한 소셜 미디어 플랫폼과의 통합 방법을 제공합니다. 각 플랫폼의 API는 지속적으로 변경될 수 있으므로, 최신 문서를 참조하여 구현을 업데이트하는 것이 중요합니다. 소셜 미디어 통합은 사용자 경험을 향상시키고 앱의 확산을 촉진하는 중요한 기능입니다.