초기 커밋
This commit is contained in:
802
ZELLYY/zellyy note/02_기술_문서/SNS_통합_가이드.md
Normal file
802
ZELLYY/zellyy note/02_기술_문서/SNS_통합_가이드.md
Normal file
@@ -0,0 +1,802 @@
|
||||
# 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
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<!-- Facebook -->
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>fb{FACEBOOK_APP_ID}</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Twitter -->
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>twitterkit-{TWITTER_CONSUMER_KEY}</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- LinkedIn -->
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>zellyy</string>
|
||||
</array>
|
||||
</dict>
|
||||
<!-- Pinterest -->
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>pdk{PINTEREST_APP_ID}</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<!-- Facebook 설정 -->
|
||||
<key>FacebookAppID</key>
|
||||
<string>{FACEBOOK_APP_ID}</string>
|
||||
<key>FacebookDisplayName</key>
|
||||
<string>Zellyy</string>
|
||||
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>fbapi</string>
|
||||
<string>fb-messenger-share-api</string>
|
||||
<string>fbauth2</string>
|
||||
<string>fbshareextension</string>
|
||||
<string>twitter</string>
|
||||
<string>twitterauth</string>
|
||||
<string>linkedin</string>
|
||||
<string>linkedin-sdk2</string>
|
||||
<string>pinterestsdk</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
### 4.2 AppDelegate.m 수정
|
||||
|
||||
`ios/YourApp/AppDelegate.m` 파일에 다음 내용을 추가합니다:
|
||||
|
||||
```objective-c
|
||||
#import <FBSDKCoreKit/FBSDKCoreKit-swift.h>
|
||||
#import <TwitterKit/TwitterKit.h>
|
||||
|
||||
@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<UIApplicationOpenURLOptionsKey,id> *)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
|
||||
<manifest ...>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application ...>
|
||||
<!-- Facebook 설정 -->
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ApplicationId"
|
||||
android:value="@string/facebook_app_id" />
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ClientToken"
|
||||
android:value="@string/facebook_client_token" />
|
||||
|
||||
<activity
|
||||
android:name="com.facebook.FacebookActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
|
||||
android:label="@string/app_name" />
|
||||
<activity
|
||||
android:name="com.facebook.CustomTabActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="@string/fb_login_protocol_scheme" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Twitter 설정 -->
|
||||
<activity
|
||||
android:name="com.twitter.sdk.android.core.identity.OAuthActivity"
|
||||
android:exported="true">
|
||||
</activity>
|
||||
|
||||
<!-- 딥링크 처리 -->
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="zellyy" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
```
|
||||
|
||||
### 5.2 strings.xml 설정
|
||||
|
||||
`android/app/src/main/res/values/strings.xml` 파일에 다음 내용을 추가합니다:
|
||||
|
||||
```xml
|
||||
<resources>
|
||||
<string name="app_name">Zellyy</string>
|
||||
<string name="facebook_app_id">FACEBOOK_APP_ID</string>
|
||||
<string name="fb_login_protocol_scheme">fbFACEBOOK_APP_ID</string>
|
||||
<string name="facebook_client_token">FACEBOOK_CLIENT_TOKEN</string>
|
||||
</resources>
|
||||
```
|
||||
|
||||
### 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 (
|
||||
<ViewShot ref={viewShotRef} options={{ format: 'jpg', quality: 0.9 }}>
|
||||
<View
|
||||
style={{
|
||||
width: 300,
|
||||
height: 300,
|
||||
backgroundColor: card.background_color,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
color: card.text_color,
|
||||
fontFamily: card.font_family,
|
||||
fontSize: card.font_size,
|
||||
textAlign: card.text_align,
|
||||
}}
|
||||
>
|
||||
{card.content}
|
||||
</Text>
|
||||
</View>
|
||||
</ViewShot>
|
||||
);
|
||||
};
|
||||
|
||||
// 이미지 생성 및 업로드 함수
|
||||
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는 지속적으로 변경될 수 있으므로, 최신 문서를 참조하여 구현을 업데이트하는 것이 중요합니다. 소셜 미디어 통합은 사용자 경험을 향상시키고 앱의 확산을 촉진하는 중요한 기능입니다.
|
||||
Reference in New Issue
Block a user