반응형

 

기존 에러로그가 자세하지 않고 크래시가 발생 할 경우 에러로그 추적이 조금 어려울때가 있습니다.

그럴때 아래와 같이 실행하면 디버깅할때 매우 도움이 되는 자세한 로그가 출력 됩니다.

 

flutter run --build -vv

 

오늘도 개발 화이팅~!!

(앱이 자꾸 죽어요 ㅠㅠ)

반응형
반응형

1. Google Maps API Key 얻는 방법

  1. Google Cloud Console 접속
  2. 프로젝트 생성 또는 선택
    • 기존 프로젝트 선택하거나 새 프로젝트 생성 (예: FlutterMapApp)
  3. Google Maps SDK for Android / iOS 활성화
    • 왼쪽 메뉴에서 API 및 서비스 > 라이브러리 선택
    • 검색창에 Maps SDK for Android, Maps SDK for iOS 입력하여 각각 활성화(Enable)
  4. API 키 생성
    • API 및 서비스 > 사용자 인증 정보(Credentials) 이동
    • 상단의 "+ 사용자 인증 정보 만들기" > API 키 선택
    • 생성된 키 복사해둠
  5. API 키 제한 설정 (선택 사항, 보안 목적)
    • 생성된 키 클릭
    • 앱이 호출하는 플랫폼(Android/iOS)을 지정하여 앱 패키지명 또는 번들 ID 기반으로 제한 가능

 

 

2. Flutter에 필요한 의존성 추가

# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.6.0  # 최신 버전 확인

 

# 의존성 설치
flutter pub get
 

✅ 3. Android 설정

🔹 1) android/app/src/main/AndroidManifest.xml 설정

<manifest>
  <application>
    <!-- 위치 권한 (필요시) -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <!-- Google Maps API 키 설정 -->
    <meta-data
      android:name="com.google.android.geo.API_KEY"
      android:value="YOUR_API_KEY_HERE"/>
  </application>
</manifest>
 

🔹 2) android/app/build.gradle 확인

defaultConfig {
    applicationId "com.example.your_app"
    minSdkVersion 20  // 최소 20 이상
}
 

✅ 4. iOS 설정

🔹 1) ios/Runner/AppDelegate.swift (Swift 프로젝트일 경우)

import UIKit
import Flutter
import GoogleMaps

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GMSServices.provideAPIKey("YOUR_API_KEY_HERE")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Objective-C일 경우에는 AppDelegate.m 파일에서 GMSServices provideAPIKey:@"YOUR_API_KEY" 추가

🔹 2) ios/Runner/Info.plist

<key>io.flutter.embedded_views_preview</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>지도 사용을 위해 위치 접근 권한이 필요합니다.</string>
 

✅ 5. Flutter 코드에서 구글 맵 사용 예시

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: GoogleMapScreen(),
    );
  }
}

class GoogleMapScreen extends StatefulWidget {
  @override
  _GoogleMapScreenState createState() => _GoogleMapScreenState();
}

class _GoogleMapScreenState extends State<GoogleMapScreen> {
  GoogleMapController? mapController;

  final LatLng _center = const LatLng(37.5665, 126.9780); // 서울 시청

  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('구글 맵')),
      body: GoogleMap(
        onMapCreated: _onMapCreated,
        initialCameraPosition: CameraPosition(
          target: _center,
          zoom: 11.0,
        ),
        myLocationEnabled: true, // 현재 위치 표시
      ),
    );
  }
}

✅ 6. 테스트 시 주의사항

  • 에뮬레이터에서는 지도가 동작하지 않을 수 있음 → 실제 기기에서 테스트 권장
  • API 키에 플랫폼 제한을 걸었으면, 올바른 SHA-1, 번들 ID가 설정되어 있는지 확인
반응형
반응형

Flutter 프로젝트에서 GoogleService-Info.plist (iOS)와 google-services.json (Android) 파일은 특정 폴더에 배치해야 합니다. 아래에 각 파일의 올바른 위치를 설명합니다.

1. GoogleService-Info.plist (iOS)

  • 위치: ios/Runner/
  • 설명:
    • iOS용 Firebase 설정 파일인 GoogleService-Info.plist는 Flutter 프로젝트의 ios/Runner/ 폴더에 넣습니다.
    • 이 파일은 Firebase와 같은 Google 서비스를 iOS 앱에서 사용하기 위해 필요합니다.

2. google-services.json (Android)

  • 위치: android/app/
  • 설명:
    • Android용 Firebase 설정 파일인 google-services.json은 Flutter 프로젝트의 android/app/ 폴더에 넣습니다.
    • 이 파일은 Android 앱에서 Google 서비스(예: Firebase)를 구성하는 데 사용됩니다.

 

기억용으로 기록해 놓습니다.

반응형
반응형

유니티의 UI 시스템인 Canvas 컴포넌트에는 다양한 Render Mode가 있습니다. 각 모드는 UI가 화면에 렌더링되는 방식에 영향을 줍니다. 자세히 살펴보겠습니다.

 

1. Screen Space - Overlay

이 모드는 UI가 항상 화면 최상단에 표시되며 게임 세계와 별개로 동작합니다.

특징:

  • UI가 화면 해상도에 따라 자동 조정됩니다
  • 카메라 설정과 무관하게 항상 보입니다
  • 게임 화면 위에 직접 렌더링됩니다

예시: 체력바, 점수 표시, 메뉴 버튼과 같이 항상 플레이어에게 보여야 하는 요소에 적합합니다.

 
Canvas myCanvas = GetComponent<Canvas>();
myCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
 

2. Screen Space - Camera

이 모드는 특정 카메라 앞에 UI를 배치합니다. UI는 카메라와의 거리에 따라 크기가 변경될 수 있습니다.

특징:

  • 지정된 카메라의 시야에 따라 렌더링됩니다
  • 카메라와의 거리를 조절할 수 있습니다
  • 게임 세계와 약간의 상호작용이 가능합니다

예시: 3D 게임에서 플레이어 주변에 떠다니는 상태 표시 UI나, 카메라 움직임에 따라 약간의 반응을 보여야 하는 요소에 적합합니다.

 
Canvas myCanvas = GetComponent<Canvas>();
myCanvas.renderMode = RenderMode.ScreenSpaceCamera;
myCanvas.worldCamera = Camera.main; // 메인 카메라 지정
myCanvas.planeDistance = 10f; // 카메라로부터의 거리

3. World Space

UI 요소가 완전히 게임 세계의 3D 공간 내에 존재하며 게임 오브젝트처럼 동작합니다.

특징:

  • 3D 공간에서 실제 오브젝트처럼 배치됩니다
  • 일반 3D 오브젝트와 상호작용할 수 있습니다
  • 다양한 카메라 각도에서 볼 수 있으며, 다른 오브젝트에 가려질 수 있습니다

예시:

  1. NPC 머리 위의 이름표나 대화창
  2. 게임 내 가상 모니터나 터치스크린
  3. VR/AR 게임의 상호작용 요소
 
Canvas myCanvas = GetComponent<Canvas>();
myCanvas.renderMode = RenderMode.WorldSpace;
// 이제 Transform을 통해 3D 공간에서 위치, 회전, 크기 조절 가능
myCanvas.transform.position = new Vector3(0, 2, 0);
myCanvas.transform.rotation = Quaternion.Euler(0, 30, 0);
myCanvas.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f);

실제 사용 예시

Screen Space - Overlay 예시

// 게임의 메인 UI 설정
public class MainUISetup : MonoBehaviour
{
    void Start()
    {
        Canvas canvas = GetComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceOverlay;
        
        // 해상도 조정을 위한 설정
        CanvasScaler scaler = GetComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(1920, 1080);
    }
}
 

Screen Space - Camera 예시

// 메인 카메라에 연결된 UI 설정
public class CameraUISetup : MonoBehaviour
{
    public Camera mainCamera;
    
    void Start()
    {
        Canvas canvas = GetComponent<Canvas>();
        canvas.renderMode = RenderMode.ScreenSpaceCamera;
        canvas.worldCamera = mainCamera;
        canvas.planeDistance = 5f;
        
        // 카메라가 줌인/줌아웃할 때도 UI가 적절하게 보이도록 설정
        CanvasScaler scaler = GetComponent<CanvasScaler>();
        scaler.dynamicPixelsPerUnit = 100f;
    }
}

World Space 예시

// NPC 위에 표시되는 정보 UI
public class NPCInfoDisplay : MonoBehaviour
{
    public Transform npcHead;
    
    void Start()
    {
        Canvas canvas = GetComponent<Canvas>();
        canvas.renderMode = RenderMode.WorldSpace;
        
        // NPC 머리 위에 배치
        RectTransform rectTransform = GetComponent<RectTransform>();
        rectTransform.position = npcHead.position + new Vector3(0, 0.5f, 0);
        
        // 항상 카메라를 향하도록 설정
        rectTransform.localScale = new Vector3(0.005f, 0.005f, 0.005f);
    }
    
    void Update()
    {
        // 항상 플레이어를 바라보도록 회전
        transform.LookAt(Camera.main.transform);
        transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y, 0);
    }
}
 
 

각 Render Mode는 고유한 장단점이 있으므로, 제작하려는 게임 유형과 UI 요소의 목적에 따라 적절한 모드를 선택하는 것이 중요합니다.

 
 
반응형
반응형

1. 기본 문법

const newArray = originalArray.filter(callback(element[, index[, array]])[, thisArg]);
  • originalArray: 원본 배열.
  • callback: 배열의 각 요소에 대해 실행할 함수.
    • element: 현재 처리 중인 요소.
    • index (선택): 현재 요소의 인덱스.
    • array (선택): filter()를 호출한 배열 자체.
  • thisArg (선택): callback 함수 내에서 사용될 this 값.

2. 콜백 함수의 역할

콜백 함수는 배열의 각 요소마다 호출되며, 각 호출 시 다음과 같이 전달됩니다:

  • element: 현재 요소.
  • index: 현재 요소의 인덱스.
  • array: 원본 배열 전체.

콜백 함수는 true 또는 false를 반환해야 합니다.

  • true를 반환하면, 해당 요소는 새 배열에 포함됩니다.
  • false를 반환하면, 해당 요소는 제외됩니다.

예를 들어:

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((num) => {
  return num % 2 === 0;  // 짝수일 경우에만 true 반환
});
console.log(evenNumbers); // [2, 4]
 

3. thisArg 매개변수

두 번째 인자로 전달하는 thisArg는 콜백 함수 내부에서 this로 사용됩니다.

예를 들어, 객체의 속성을 비교할 때 유용합니다:

 
const context = { threshold: 10 };
const numbers = [5, 12, 8, 130, 44];

const filtered = numbers.filter(function(num) {
  return num > this.threshold;
}, context);

console.log(filtered); // [12, 130, 44]

4. 다양한 사용 예제

4.1. 배열 내의 객체 필터링

객체 배열에서 특정 조건을 만족하는 객체만 골라낼 수 있습니다.

const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 35 }
];

const usersOver30 = users.filter(user => user.age > 30);
console.log(usersOver30); // [ { name: 'Charlie', age: 35 } ]

4.2. 중첩 배열에서 특정 요소 제거

특정 값이 포함된 배열을 제거하는 경우:

const fruits = ['apple', 'banana', 'orange', 'banana'];
const filteredFruits = fruits.filter(fruit => fruit !== 'banana');
console.log(filteredFruits); // ['apple', 'orange']

4.3. 조건에 따른 복합 필터링

여러 조건을 조합하여 필터링할 수도 있습니다.

const items = [
  { name: 'Book', price: 12, inStock: true },
  { name: 'Pen', price: 2, inStock: false },
  { name: 'Notebook', price: 5, inStock: true }
];

const affordableInStock = items.filter(item => item.inStock && item.price < 10);
console.log(affordableInStock); // [ { name: 'Notebook', price: 5, inStock: true } ]

5. filter()의 특성과 주의 사항

  • 불변성 유지: filter()는 원본 배열을 변경하지 않고, 조건에 맞는 요소들로 새 배열을 생성합니다.
  • 희소 배열(Sparse Array): 배열 내에 비어있는 요소(holes)가 있는 경우, 해당 빈 요소들은 callback 함수에서 호출되지 않습니다.
  • 반환 값: 조건에 맞는 요소가 하나도 없으면 빈 배열 ([]) 을 반환합니다.
  • 고차 함수(Higher-Order Function): filter()는 콜백 함수를 인자로 받으므로, 다른 함수와 조합하여 사용하기 좋습니다.
  • 실행 순서: 배열의 각 요소에 대해 순서대로 콜백 함수가 호출됩니다. 따라서 인덱스에 의존하는 조건문 작성 시 주의해야 합니다.

6. filter()와 다른 배열 메서드 비교

  • map(): 배열의 각 요소를 변환하여 새 배열을 만듭니다. (길이는 동일)
  • reduce(): 배열의 모든 요소를 누적하여 하나의 값으로 축소합니다.
  • find(): 조건을 만족하는 첫 번째 요소만 반환합니다.
  • forEach(): 배열의 각 요소에 대해 주어진 함수를 실행하지만, 반환 값을 저장하지 않습니다.

filter()는 배열에서 특정 조건에 맞는 요소들만 골라내는 데에 최적화되어 있으며, 반환된 배열은 원본 배열과 별개이므로 후속 조작에 안전하게 사용할 수 있습니다.


7. 결론

JavaScript의 filter() 메서드는 배열 데이터를 다룰 때 매우 강력하고 유연하게 사용할 수 있는 도구입니다. 다양한 상황에서 원하는 조건에 맞는 데이터를 선별하고, 원본 배열을 건드리지 않으면서 새로운 배열을 생성할 수 있기 때문에, 코드의 가독성과 유지보수성을 높이는 데 큰 도움을 줍니다.

이처럼 filter()의 기본 사용법부터 고급 사용법까지 이해하면, 복잡한 데이터 필터링 작업도 간단하고 직관적으로 해결할 수 있습니다.

반응형
반응형

유니티나 안드로이드 스튜디오등의 안드로이드 앱이나 게임 개발하고 업로드하는데는 업로드 키가 필요합니다.

 

업로드키를 분실하거나 키의 패스워드를 잊어버렸을때의 대처방법을 소개합니다.

 

새 업로드 키 생성

 

우선 새 업로드 키가 필요합니다. 만드는 방법은 기존 방법과 같습니다.

 

Project Setting > Player > Publishing Settings

 

 

 

간단하게 생성해줍니다.

 

새 업로드 인증서를 생성해서 구글 콘돌에 업로드

 

keytool -export -rfc -keystore 키의파일명 -alias Alias이름 -file upload_certificate.pem

새로운 키가 있는 장소로 이동하여 터미널을 열고 위와 같은 명령어를 입력해줍니다.

 

구글공식문서

 

해당앱의 테스트 및 출시 > 설정 -> 앱 서명 항목에서 업로드 키 재설정 요청을 선택합니다.
앞에서 생성한 새 업로드 인증서를 업로드 합니다. 

 

 

접수되면 메일로 재설정 요청이 왔고 언제부터 새로운 업로드 키로 업로드가 가능한지 안내받을 수 있습니다.

 

보통 2일정도 걸리는 듯 합니다.

 

해당 기간이 지나고 이제 다시 새로운 키로 업로드 하실 수 있습니다.

 

이상 슬기로운 개발생활 되세요.

반응형

+ Recent posts