오랜만에 firebase 연결을 이용해서 개발 하려고 공식 문서 세팅 하던 중 아래와 같은 에러가 발생 했다.

Error: Failed to list Firebase projects. See firebase-debug.log for more info.

이것 저것 해보다가 

firebase login --reauth

로 해결 했다.

'플러터' 카테고리의 다른 글

[flutter] admob 적용 - 2  (0) 2023.12.01
[flutter] admob 적용 - 1  (0) 2023.12.01
[flutter] Debounce, Throttle를 쉽게 적용하는 방법  (0) 2023.11.30

앱스토어에 어플을 올리려고 들어가 보니 'EU 디지털 서비스법 규정(DSA)'에 관련된 정보 제공을 하라고 알람이 와있었다.

요구 사항 완료하기를 누르면 아래와 같은 팝업이 뜬다.

거래자 선택 팝업

거래자, 비거래자를 선택하고 진행하면 된다.

- 거래자    : 개발자 계정내에 유로앱, 인앱 결제 등 수익이 발생 되거나 은행 계좌, 세금 양식 계약서가 있을 경우 선택

- 비거래자 : 거래자 항목에 해당되는것이 없고 무료앱만 운영 중이라면 선택

 

비거래자를 선택 했을 경우는 더이상 해줄 것은 없고, 거래자 선택시 이후 추가 정보 확인 프로세스를 진행 해주시면 됩니다.

안녕하세요. 원원원입니다.

최근 회사의 엄청난 업무량에 휩쓸리면서 눈에 생기를 잃어 가던 나에게 해외의 성공적인 인디메이커들과 1인으로 창업한 사례들은 큰 자극이 되었어요. 나도 한번 저렇게 인생을 내가 끌고 나가보고싶다고 생각했습니다.

(지금 생각해보니 번아웃이 쌔게 왔나봐요. 번아웃 극복은 더 큰 자극으로!)

 

가장 자극을 많이 받았던 개발자 tony dinh

 

그래서 오래 생각하지 않고 바로 시작해 보려고 합니다.

 

어떻게 하면 꾸준히 해나갈까 생각 해보니 '내가 인디메이커로서 성장해가는 과정을 기록 하면 좋겠다'라고 생각 했고 그래서 지금 글을 쓰고 있습니다.

 

그래서 앞으로 어떻게 해나갈 것이냐 힌트는 티스토리 이름에 있어요.

oneoneone = 'one man, one month, one product' = "한명, 한달, 하나의 프로덕트"라는 프로젝트를 해보려고 합니다.

경험도 많이 부족하고 이것저것 생각하느라 행동이 느린 저에게는 정말 딱 맞는거 같아요.

 

한달에 하나씩 회사를 런칭하고 성공한 헌터해먼드

 

사실 가장 좋은 공부가 경험이라는건 살면서 많이 깨닫지만 시작도 전에 걱정부터 하는 겁쟁이 성향은 안바뀌더라고요.

그래서 할 수 밖에 없는 환경에 저를 던지려고 해요. 그런 환경을 만드는 데에는 많은 사람들 앞에서 내가 만든 결과물을 발표하는 것이 좋다고 생각 합니다.

 

매주 하나 이상씩 글을 작성하고(주간 보고), 매달 하나씩 제 제품과 느꼈던 점들을 상세히 공유하고 피드백을 받으려고 해요.

그 이유는 회사에서 새롭게 개발한 기능이나 제품을 고객들에게 설명하는 자리가 데드라인으로 굉장한 동기부여가 되고 피드백을 통해 많이 아프지만 빠르게 잘못된 점을 고칠 수 있다는 장점이 있다고 생각해서 입니다.

 

그래서 이글을 본 모든 분들이 저의 고객이 되는겁니다. 이 글의 조회수 만큼 부담감이 늘겠내요.

 

그럼 이제 제품을 통해 인사드리도록 하겠습니다. 감사합니다.

5줄요약
1. admob 계정 생성 (https://apps.admob.com/)
2. admob에서 앱 추가, 광고단위 만들기
3.google_mobile_ads라이브러리 추가 (https://pub.dev/packages/google_mobile_ads/install)
4. ios :ios/Runner/Info.plistios/Runner/Info.plist, android :android/app/src/main/AndroidManifest.xml광고 단위 ID 추가
5. google_mobile_ads를 활용해서 소스 구현

이전 글 보기(요약 1, 2번) : https://onemanonemonthoneproduct.tistory.com/4

3. google_mobile_ads 라이브러리 추가 

pub.dev 페이지에서 google_mobile_ads 검색 후 플러터 프로젝트에서 라이브러리 인스톨

 

4. 프로젝트에 ApplicationID(단위 ID) 추가

✔︎ 개발 중에는 테스트ID로 활용 (참고: https://developers.google.com/admob/android/test-ads?hl=ko)

 

- Android : android/app/src/main/AndroidManifest.xml 

   - 아래의 meta-data에 application id(단위 ID)추가

<application ...>
...
   <meta-data
     android:name="com.google.android.gms.ads.APPLICATION_ID"
     android:value="[YOUR APP ID]"
   />
...
</application>

 

- ios : ios/Runner/Info.plist 

   - 아래의 meta-data에 application id(단위 ID)추가

<dict>
...
    <key>GADApplicationIdentifier</key>
     <string>[YOUR APP ID]</string>
...
</dict>

 

 

5. 소스 구현

- main.dart 에 라이브러리 init

Future<void> main() async {
...
    //라이브러리 init
    await MobileAds.instance.initialize();
...
    runApp(
        const ProviderScope(child: TestApp()),
    );
}

5-0. 개요

    - 구현 하고자 하는 광고 단위를 검색해서 적용하세요.

    - 호출 구현 플로우는 load ➜ show ➜ dispose 순서로 구현합니다.

    - <YOUR_AD_UNIT_ID>에 발급받은 application id(단위 ID)로 변경합니다.

5-1. 배너 광고 (Banner Ads)

// Load
BannerAd _bannerAd = BannerAd(
  adUnitId: '<YOUR_AD_UNIT_ID>',
  size: AdSize.banner,
  request: AdRequest(),
  listener: BannerAdListener(),
);
_bannerAd.load();

// Show
if (_isBannerAdReady) {
  AdWidget(ad: _bannerAd);
}

// Dispose
_bannerAd.dispose();

5-2. 전면 광고 (Interstitial Ads)

// Load
InterstitialAd.load(
  adUnitId: '<YOUR_AD_UNIT_ID>',
  request: AdRequest(),
  adLoadCallback: InterstitialAdLoadCallback(
    onAdLoaded: (InterstitialAd ad) {
      _interstitialAd = ad;
    },
    onAdFailedToLoad: (LoadAdError error) {
      print('Interstitial ad failed to load: $error');
    },
  ),
);

// Show
_interstitialAd?.show();

// Dispose
_interstitialAd?.dispose();

5-3. 보상형 광고 (Rewarded Ads)

// Load
RewardedAd.load(
  adUnitId: '<YOUR_AD_UNIT_ID>',
  request: AdRequest(),
  rewardedAdLoadCallback: RewardedAdLoadCallback(
    onAdLoaded: (RewardedAd ad) {
      _rewardedAd = ad;
    },
    onAdFailedToLoad: (LoadAdError error) {
      print('Rewarded ad failed to load: $error');
    },
  ),
);

// Show
_rewardedAd?.show(onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
  print('Reward earned: ${reward.amount}');
});

// Dispose
_rewardedAd?.dispose();

 

예제를 참고해서 앱에 AD mob을 추가하고 수익을 발생시켜보세요.

5줄요약
1. admob 계정 생성 (https://apps.admob.com/)
2. admob에서 앱 추가, 광고단위 만들기
3. google_mobile_ads 라이브러리 추가 (https://pub.dev/packages/google_mobile_ads/install)
4. ios : ios/Runner/Info.plist, android : android/app/src/main/AndroidManifest.xml 광고 단위 ID 추가
5. google_mobile_ads를 활용해서 소스 구현

 1. admob 계정 생성

구글 애드몹 홈페이지(https://apps.admob.com)에서 회원가입을 진행합니다. 일반적인 회원가입과 다를게 없으니 자세한 설명은 생략합니다.

2. admob에서 앱 추가, 광고단위 만들기

- 'GET STARTED'를 눌러서 앱추가 시작

 

 

- 개발 중인 앱의 플랫폼을 선택하고

- 보통의 경우 개발중인 프로젝트는 '아니오'를 선택, 앱이 이미 등록되어 있는 경우 '예 지원되는 앱 스토어에 앱이 등록되어 있습니다.' 를 선택 

 

 

- 앱 이름 등록(앱 이름을 앱 스토어 등록정보와 일치시키는 것이 좋습니다.)

- firebase 사용 시 사용자 측정항목 설정

    - 사용시 Google analytics 사용 가능

 

 

- 필요한 광고 단위 추가

    - 광고 단위 이름 추가 후 라벨에 따라 추가하면 되기 때문에 자세한 설명은 생략 하겠습니다.

 

 - 배너 광고 (Banner Ads): 화면의 상단이나 하단에 고정되어 표시되는 표준 크기의 광고입니다.
 - 전면 광고 (Interstitial Ads): 화면 전체를 차지하는 광고로, 사용자의 앱 사용 흐름 사이에 표시됩니다.
 - 보상형 광고 (Rewarded Ads): 사용자가 광고를 시청한 후 보상을 받을 수 있는 광고 형태입니다.
 - 네이티브 광고 (Native Ads): 앱의 컨텐츠와 비슷한 형태로 디자인되어 사용자 경험을 방해하지 않는 광고입니다.

 

이어보기 : https://onemanonemonthoneproduct.tistory.com/5

debounce, throttle 비교 요약

Debounce, Throttle 비교

 

구현 예제

물론, Timer를 이용하여 직접 구현할 수 있겠지만, 아주 잘만들어진 외부 라이브러리가 있습니다.

라이브러리(그림을 누르면 해당 라이브러리로 이동)

라이센스는 MIT로 편하게 사용하시면 돼요

 

예제를 보면 (아래에 전체 코드가 있습니다.)

debounce, throttle 구현 예제

 

끝입니다. 정말 쉽죠?

 

물론, 직접 구현 할때와 마찬 가지로 라이브러리 내부에 static 변수에 할당 되기 때문에 widget이 dispose할 때 부여 되었던 변수를 cancel할 필요가 있습니다.

 

전체 예제 소스

import 'package:easy_debounce/easy_debounce.dart';

void myMethod(String message) {
print(message);
}

void main() async {
// 예제 1: 약 200ms 후에 myMethod()를 단 한 번 호출
print('\nExample 1');
for (int i = 0; i < 5; i++) {
EasyDebounce.debounce('debouncer1', Duration(milliseconds: 200),
() => myMethod('Executing debouncer1! (i: $i)'));
}

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));

// 예제 2: 5번의 myMethod() 호출. 각 반복 사이에 300ms 기다림
print('\nExample 2');
for (int i = 0; i < 5; i++) {
EasyDebounce.debounce('debouncer2', Duration(milliseconds: 200),
() => myMethod('Executing debouncer2! (i: $i)'));
await Future.delayed(Duration(milliseconds: 300));
}

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));

// 예제 3: 약 200ms 후에 myMethod()를 두 번 호출. 서로 다른 태그 사용
print('\nExample 3');
for (int i = 0; i < 5; i++) {
EasyDebounce.debounce('debouncer3', Duration(milliseconds: 200),
() => myMethod('Executing debouncer3! (i: $i)'));
EasyDebounce.debounce('debouncer4', Duration(milliseconds: 200),
() => myMethod('Executing debouncer4! (i: $i)'));
}

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));

// 예제 4: 지속 시간이 0일 때, onExecute()는 동기적으로 호출
print('\nExample 4');
int x = 0;
EasyDebounce.debounce('debouncer5', Duration.zero, () {
++x;
myMethod('Executing debouncer5! (x: $x)');
});
print('After debouncer5: x: $x');

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));

// 예제 5: 지속 시간이 0이 아닐 때, onExecute()는 비동기적으로 호출
print('\nExample 5');
int y = 0;
EasyDebounce.debounce('debouncer6', Duration(milliseconds: 10), () {
++y;
myMethod('Executing debouncer6! (y: $y)');
});
print('After debouncer6: y: $y');

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));

// 예제 6: fire()를 호출하면 콜백이 즉시 실행됨. 타이머는 제거되지 않음
print('\nExample 6');
int z = 0;
EasyDebounce.debounce('debouncer7', Duration(milliseconds: 10), () {
++z;
myMethod('Executing debouncer7! (z: $z)');
});
EasyDebounce.fire('debouncer7');
print('After debouncer7: z: $z');

// 위 예제가 끝날 때까지 기다리기
await Future.delayed(Duration(milliseconds: 400));
}

+ Recent posts