2026/04/27

오늘의 이야기

 


 


💡 DevExpress dxChart & dxDataGrid 활용 가이드


열심히 일하는 중.



 


이 블로그에서는 dxChart 및 dxDataGrid를 사용하는 방법에 대해 설명합니다. 각 질문별로 요점을 강조하고 예제를 포함하여 쉽게 이해할 수 있도록 구성하였습니다.


📊 dxChart 관련 질문 & 해결 방법


✅ 하나의 차트에서 Bar와 Line을 동시에 표시할 수 있나요?

가능합니다! dxChart에서는 여러 개의 시리즈를 활용할 수 있습니다.



$("#chartContainer").dxChart({
dataSource: myData,
series: [
{ type: "bar", valueField: "barValue", name: "Bar Chart" },
{ type: "line", valueField: "lineValue", name: "Line Chart" }
]
});


✅ 서로 다른 값의 범위를 가진 데이터를 동시에 표현하려면?

각 시리즈를 서로 다른 축(valueAxis)에 바인딩하면 됩니다.



$("#chartContainer").dxChart({
dataSource: myData,
series: [
{ type: "bar", valueField: "largeValue", name: "Large Data", axis: "largeAxis" },
{ type: "line", valueField: "smallValue", name: "Small Data", axis: "smallAxis" }
],
valueAxis: [
{ name: "largeAxis", position: "left", min: 0, max: 100000 },
{ name: "smallAxis", position: "right", min: -100, max: 100 }
]
});


✅ 소수점 2자리까지 표시하려면?

label.format 속성을 설정하여 해결할 수 있습니다.



$("#chartContainer").dxChart({
dataSource: myData,
series: [
{
valueField: "sales",
name: "Sales",
label: {
visible: true,
format: {
type: "fixedPoint",
precision: 2
}
}
}
]
});


📋 dxDataGrid 관련 질문 & 해결 방법


✅ 셀 값이 NaN으로 표시되는 경우, 공백으로 변경하는 방법?

`cellTemplate` 또는 `calculateCellValue`를 활용하여 NaN을 공백으로 변환할 수 있습니다.



$("#gridContainer").dxDataGrid({
dataSource: myData,
columns: [
{
dataField: "price",
cellTemplate: function(container, options) {
let value = options.value;
container.text(isNaN(value) ? "" : value.toFixed(2));
}
}
]
});


✅ dxChart에서 X축 데이터의 표시 순서를 역정렬하려면?

`argumentAxis.inverted` 옵션을 활용하면 **X축 순서를 반대로 변경**할 수 있습니다.



$("#chartContainer").dxChart({
dataSource: myData,
argumentAxis: { inverted: true },
series: [{ valueField: "value", argumentField: "category", type: "bar" }]
});


🎯 마무리


이 블로그에서는 dxChart 및 dxDataGrid의 핵심 기능을 정리했습니다. 더 많은 활용 방법을 알고 싶다면 DevExpress 공식 문서를 참고하세요!





2026/04/26

오늘의 이야기

 


1. 전체 기능 설계도(텍스트 플로우)


필요기능



[회원 관리]
|
v
[참여자 인식 (Watch)]
|
v
[팀 배정/리그 대진 생성]
|
v
[경기 진행]
| \
v \
[경기 결과 입력] [심박수 감지 및 알림 (Watch)]
| |
v |
[결과 저장 및 요약] <------/
|
v
[순위/통계/공유]



2. 주요 기능별 상세


A. 회원 관리



  • 회원 등록 및 목록 조회

  • 동호회 정보 관리


B. 참여자 인식 및 팀 배정



  • Wear OS Watch와의 연동

    • 워치 착용자 자동 인식

    • 인원 파악 및 명단 동기화



  • 팀 자동/수동 배정

    • 복식팀 조합 알고리즘

    • 임의/무작위 배정 선택




C. 리그 대진표 생성



  • 현재 팀 구성에 따른 리그 생성

  • 경기 일정표 및 매치업 자동 생성


D. 경기 진행



  • 경기 시작/종료 기록

  • 세트/포인트 입력(스마트폰/워치)

  • 실시간 경기 상황 표시

  • Watch에서 심박수 실시간 측정

    • 200bpm 이상 감지 시 알림

    • 알림에 따라 휴식 권고




E. 결과 처리/저장



  • 세트별 승패 기록

  • 2승 기준 자동 종료 판정/기록 저장

  • 각 팀/개인별 경기 통계


F. 리그 결과 및 공유



  • 순위 집계 및 시각화(리그/전체)

  • 통계(승률, 점수 등) 제공

  • 결과 내보내기(공유, 백업 등)




3. 모듈 간 관계 요약(도식 예시)



  • Watch 앱

    • [심박수 측정] → [알림 팝업]

    • [참여 인식] → [팀 선택] → [경기 결과 입력]

    • ↔ 데이터 실시간 동기화 (Firebase, RESTful API)



  • 스마트폰 앱

    • [회원 관리] → [참여 인원 동기화]

    • [팀/리그 설정] → [대진표 생성]

    • [경기 상황 모니터링] ← [Watch 결과 수신]

    • [리그 결과 및 통계] → [사용자 화면 표시/공유]






4. 기능 설계도 (Markdown 표 예시)























































주요기능 세부설명 연동방식 주요화면 예시
회원 관리 회원 등록, 수정, 목록 스마트폰 회원 목록/등록 화면
참여자 인식 Watch 착용자 자동 인식, 인원 동기화 Watch/스마트폰 참여자 리스트
팀 배정/리그 생성 팀 자동/수동 편성, 리그 대진 자동 생성 스마트폰 팀 구성/대진표 화면
경기 진행 세트/포인트 입력, 경기 진행 상태 표시 스마트폰/Watch 경기 진행/입력 화면
심박수 체크 및 알림 200bpm 이상 시 알림, 휴식 권고 Watch 알림(팝업)
결과 저장/통계 경기 결과 저장, 통계 집계 스마트폰/서버 결과, 통계 화면
리그 결과 공유 최종 순위, 통계 공유/내보내기 스마트폰 결과 요약/공유 화면

 





오늘의 이야기

🌟 Welcome Korea – 당신의 현지 음식 가이드 앱!


앱 섦영서



 


이전에 Welcome Jeju라는 앱을 아시나요? 제주도 내 맛집과 장소 추천을 모으는 데 집중했던 간단한 안드로이드 앱이었죠. 매일 "맛집" 태그가 달린 인기 블로그나 웹 게시물을 스크래핑하여 "핫플레이스" 정보를 수집했었어요.


이제 저희 앱이 더 넓은 목적을 가지고 진화했습니다!




왜 리뉴얼이 필요했을까요?


한국을 방문할 때 구글 지도는 특히 명동과 같은 지역에서 자세한 현지 정보가 부족한 경우가 많습니다. 네이버 지도나 카카오 맵 같은 앱들은 한국어 사용자에게 최적화되어 있지만, 외국인 관광객들에게는 불편한 경향이 있죠.


명동 근처에서 프로젝트를 진행하면서, 많은 외국인 관광객들이 휴대폰에 몰두한 채 어디로 가야 할지 헤매는 모습을 보았습니다. 저의 서툰 영어로는 큰 도움을 드릴 수 없었기에, 외국인 여행자들에게 더 나은 서비스를 제공하기 위해 앱을 업그레이드하기로 결심했습니다.


또한, 앱 경험을 개선하기 위해 다양한 AI 도구들에게 조언을 구하며 얻은 통찰력도 이번 업데이트에 반영했습니다.




업데이트된 버전의 새로운 기능은 무엇인가요?


새 버전은 환영 화면으로 시작하며, 이제 다음 언어들을 지원합니다:



  • 한국어

  • 영어

  • 중국어 (간체 & 번체)

  • 필리핀어

  • 베트남어

  • 태국어


저는 한국을 방문하는 관광객의 큰 부분을 차지하는 동남아시아 방문객들이 주로 사용하는 언어에 초점을 맞췄습니다.




🧭 주요 기능


1. 다국어 지원


인터페이스 언어는 시작할 때 선택할 수 있으며, 나중에 메뉴를 통해 변경할 수 있습니다.


2. 메인 화면 – 주변 탐색


다음 기준으로 식당이나 장소를 검색할 수 있습니다:



  • 키워드 (반경 5km 이내)

  • 카테고리

  • 한국 블로거들이 매일 선별한 데일리 핫플레이스


3. 음성 검색


키워드만 사용하여 음성으로 검색할 수 있습니다. 모국어로 말하면 앱이:



  • 음성 입력을 한국어로 변환하고,

  • 한국어로 검색한 다음,

  • 결과를 다시 사용자의 언어로 번역해줍니다.


🔐 참고: 이 기능을 사용하려면 음성 인식 권한이 필요합니다.


4. 검색 결과 아이콘



  • 📍 위치 마커: 검색 키워드에 기반한 위치

  • 🚩 깃발 마커: 매일 수집된 "핫플레이스" 강조 표시




📱 지금 다운로드하세요!


앱은 현재 플레이 스토어에서 다운로드할 수 있습니다:


👉 Welcome Korea (맛집 리스트 모아보기)


“웹에 흩어져 있는 모든 제주 음식 팁을 한곳에 모았습니다 – 이제 더 똑똑하고, 다국어를 지원하며, 관광객 친화적입니다.”


앱 다운로드 링크





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, ¡una aplicación imprescindible para los clubes de bádminton!
👉 Match Play: registra puntuaciones y encuentra oponentes 🎉
¡Perfecto para cualquier lugar, solo, con amigos o en un club! 🤝
Si te gusta el bádminton, definitivamente pruébalo.

Ir a la aplicación 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

기본설계



 


🔮 M - Mystery & Need


"복식 경기, 오늘 몇 명 올까? 누구랑 팀 짜야 하지?"



호기심 자극 메인 카피: "워치만 차면 자동 팀 구성된다고?"




  • 매 경기, 팀 짜기 너무 번거롭지 않으셨나요?

  • 경기 결과는 늘 기억 속에만 남으셨나요?

  • 심박수 200 넘는 친구가 있는데, 아무도 몰랐다면?


이 앱 하나면, 모든 문제 해결됩니다.




❤️ A - Appeal & Affirm



  • "모임 시작 전 워치만 차면 자동으로 참여 등록 끝"

  • "알고리즘이 실시간으로 복식 팀 구성!"

  • "경기 결과는 자동 저장, 다음 팀도 자동 배정"

  • "심박수 200 이상? 워치가 쉬는 시간 알림까지!"



우리 브랜드의 철학은 스마트하게 운동을 즐기게 하자는 것.





💎 V - Value & Gap
































기능 사용 전 사용 후
팀 편성 수기 작성, 시간 지연 자동 구성, 대기 없음
기록 저장 누락, 분실 Firebase 자동 저장
안전 관리 없음 심박수 감지 → 알림
경기 흐름 중단 많음 리그 자동 운영


단순 앱이 아닙니다. 오늘 경기의 매니저가 되어드립니다.





🔍 E - Evidence & Interest



  • Google 인증 API, Firebase 공식 기술 사용

  • 베타 유저 94%가 “운영이 훨씬 수월해졌다” 응답

  • "우리 클럽에서는 매주 이 앱으로 경기 운영해요" – 사용자 후기

  • 캠핑/체육관/사내 동호회까지 확장 가능




🟢 N - Nudge (Reassure & CTA)



  • 교환/환불: 앱은 설치형, Pro 결제 시 3일 내 환불 가능

  • 심박수 센서 민감도 조절 가능

  • "지금 설치하면 프리미엄 1개월 무료!"

  • "워치 연동은 한정 기간 전용 기능!"



👉 앱 배포가 시작 되면 다시 ...



 


 


** 실제 이글의 연재는 2025.06.24 에 시작 합니다.





오늘의 이야기

🌟 Welcome Korea – あなたのローカルグルメガイドアプリ


 




以前のバージョンはどんな感じだったのか?


以前のWelcome Jejuは、シンプルなAndroidアプリで、済州島周辺の食事処や観光スポットのおすすめ情報を集めていました。人気ブログやウェブ記事から「맛집」(美味しい店)タグをつけた情報を毎日スクレイピングしていました。


なぜリニューアルが必要だったのか?


韓国を訪れる際、特に明洞のような観光地ではGoogleマップに詳しい地元情報が不足しています。NaverマップやKakaoマップは韓国語に最適化されていますが、外国人観光客にとっては使いにくい面があります。


明洞付近で外国人観光客がスマホの画面をじっと見て立ち止まっている姿をよく見かけました。彼らの多くは、今どこにいるのか、どこに行けばよいのか分からず戸惑っているようでした。私の英語力ではあまり助けられなかったため、もっと外国人旅行者に役立つアプリにしたいと思い、改良を決意しました。


さらに、様々なAIツールを活用してユーザー体験の向上を図りました。


新しいバージョンの特徴は?


最新バージョンはウェルカム画面から始まり、以下の言語に対応しています。



  • 韓国語

  • 英語

  • 中国語(簡体字・繁体字)

  • フィリピン語

  • ベトナム語

  • タイ語


特に東南アジアからの訪問者が多いため、彼らに使いやすい言語を重点的にサポートしています。


🧭 主な機能



  1. 多言語対応
    起動時に言語選択ができ、後からメニューで変更も可能です。

  2. メイン画面 – 近くを探索
    キーワードやカテゴリで検索でき、
    半径5km以内のスポットが対象。
    韓国のブロガーが毎日更新する「ホットプレイス」情報も見られます。

  3. 音声検索
    母国語でキーワードを話すだけで、
    アプリが音声を韓国語に変換し検索。
    検索結果は元の言語に翻訳して表示されます。
    ※ 音声認識の許可が必要です。

  4. 検索結果アイコン
    📍 位置マーカー:検索キーワードに基づく場所
    🚩 フラグマーカー:毎日収集される「ホットプレイス」


 









📱 ダウンロードはこちら


Google Playストアで配信中:
👉 Welcome (맛집 리스트 모아보기)


「ウェブ上に散らばる済州島のグルメ情報をまとめました。今はもっと賢く、多言語対応で旅行者に優しいアプリです。」


https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127&pcampaignid=web_share



 


옵디강 (Welcome) - Google Play 앱


인터넷에 널린(?) 전국 맛집 정보 제공


play.google.com




 





오늘의 이야기


#스하리1000명프로젝트

오늘 내가 만든앱 하나 알려주고 싶어, 이 앱은 알림수집기 라고 이름을 붙였는 데,
내 폰에 표시 되는 알림을 읽어서 내가 지정한 단어가 들어 있고, 지출기록을 남겨야 하는 알림이
있으면 수집하고, 카카오톡으로 친구에게 전달해 주는 기능을 구현해 줄꺼야. 📲

이번 패치에서는 하루 한번 지정한 시간에 나에게 알림(노티) 하도록 기능을 추가 했어. 🙏
한번 써보고 불편한 거 있으면 말해줘.

앱 바로가기
👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao





오늘의 이야기

 


 


🧵 Python으로 Threads API 자동화하기


 


 


threads 게시 예제



 


목표: 이미지와 텍스트를 Threads API를 통해 자동으로 게시하고, Access Token을 주기적으로 갱신하여 안정적인 운영 시스템을 구축합니다.




1. 프로젝트 개요



  • Threads API를 활용한 이미지 업로드 및 게시

  • SQLite로 Access Token 저장 및 발급일 관리

  • 자동 토큰 갱신 (60일 유효 기간 감지)


2. 초기 Access Token 저장 (최초 1회)


import sqlite3
from datetime import datetime

DB_PATH = "token_store.db"
INIT_TOKEN = "여기에 초기 access_token 입력"
ISSUE_DATE = datetime(2025, 6, 17)

def initialize_token():
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS tokens (
id INTEGER PRIMARY KEY,
access_token TEXT NOT NULL,
issued_at TEXT NOT NULL
)
''')
cursor.execute("DELETE FROM tokens")
cursor.execute("INSERT INTO tokens (access_token, issued_at) VALUES (?, ?)",
(INIT_TOKEN, ISSUE_DATE.isoformat()))
conn.commit()
conn.close()

initialize_token()

3. Access Token 자동 갱신 함수


import requests
from datetime import datetime, timedelta

PAGE_ID = "페이지 ID 입력"

def get_token():
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("SELECT access_token, issued_at FROM tokens ORDER BY id DESC LIMIT 1")
row = cursor.fetchone()
conn.close()
return row if row else (None, None)

def save_token(new_token):
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("INSERT INTO tokens (access_token, issued_at) VALUES (?, ?)",
(new_token, datetime.utcnow().isoformat()))
conn.commit()
conn.close()

def refresh_token_if_needed():
token, issued_at = get_token()
if not token:
raise Exception("토큰이 없습니다.")
issued_time = datetime.fromisoformat(issued_at)
if datetime.utcnow() - issued_time > timedelta(days=55):
print("🔄 갱신 시도 중...")
response = requests.get(
f"https://graph.threads.net/v1.0/{PAGE_ID}/refresh_access_token",
params={"access_token": token}
)
if response.status_code == 200:
new_token = response.json().get("access_token")
save_token(new_token)
print("✅ 토큰 갱신 완료")
return new_token
else:
raise Exception("❌ 갱신 실패")
return token

4. 게시글 업로드 & 발행 함수


def upload_and_publish(image_url, text, page_id, access_token):
create_url = f"https://graph.threads.net/v1.0/{page_id}/threads"
payload = {
"media_type": "IMAGE",
"image_url": image_url,
"text": text,
"access_token": access_token
}
create_response = requests.post(create_url, data=payload)
if create_response.status_code == 200:
creation_id = create_response.json().get("id")
print(f"✅ 업로드 성공. creation_id: {creation_id}")
publish_url = f"https://graph.threads.net/v1.0/{page_id}/threads_publish"
publish_response = requests.post(publish_url, params={
"creation_id": creation_id,
"access_token": access_token
})
if publish_response.status_code == 200:
print("🎉 게시 완료")
else:
print("❌ 게시 실패:", publish_response.text)
else:
print("❌ 업로드 실패:", create_response.text)

5. 전체 실행 흐름


if __name__ == "__main__":
from datetime import datetime
DB_PATH = "token_store.db"
PAGE_ID = "page id 입력"
IMAGE_URL = "http://이미지서버.myddns.me:8059/image_sender1.png"
TEXT = "#python #앱 https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127&pcampaignid=web_share"

try:
token = refresh_token_if_needed()
upload_and_publish(IMAGE_URL, TEXT, PAGE_ID, token)
except Exception as e:
print("🚨 오류 발생:", e)

📝 마무리


이렇게 Python과 SQLite를 이용해 Threads API 자동화를 구현했습니다. 토큰 갱신과 게시 흐름이 깔끔하게 처리되기 때문에, 향후 자동 배포나 백그라운드 업로드도 손쉽게 확장할 수 있어요.


 


** 테스트을 위해서는 테스터 등록을 해야 합니다. 또한 등록된 테스터의 계정에서 승인 해야 하는 데, 내가 관리 하는 계정의 경우


아래 그림과 같이 등록된 테스터 목록에 나오는 "웹사이트 권한" 링크를 타고 들어가면 바로 테스트 초대 을 찾을 수 있으니 해당 메뉴에서 승인을 해 주면 바로 적용 됩니다.







오늘의 이야기


#스하리1000명프로젝트,
迷失在韓國?即使您不會說韓語,這個應用程式也可以幫助您輕鬆出行。
只需說出您的語言即可 - 它會翻譯、搜尋並以您的語言顯示結果。
非常適合旅行者!支援英語、日語、中文、越南語等10多種語言。
現在就試試吧!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




2026/04/25

오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 “혹시나~” 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 “로또 예상번호 by Gemini” 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기

 


 


📍 Jetpack Compose + ARCore + Google Maps로 위치 기반 AR 구현하기


가이드앱 초안 이미지



 


ARCore의 Geospatial API는 GPS 좌표와 같은 실제 세계의 위치를 기준으로 가상 객체를 배치할 수 있는 기능을 제공합니다. 본 게시물에서는 Jetpack ComposeGoogle Maps Compose를 활용해 지도와 AR 콘텐츠를 동시에 표시하는 방법을 소개합니다.


💡 이 예제는 관광지 안내, 실외 AR 내비게이션, 위치 기반 게임 등에 활용할 수 있습니다.



1️⃣ 프로젝트 구성 및 의존성 추가


build.gradle.kts에 다음 라이브러리들을 추가합니다.


dependencies {
implementation("com.google.maps.android:maps-compose:4.1.1")
implementation("com.google.android.gms:play-services-maps:18.2.0")
implementation("com.google.ar:core:1.43.0")
}

추가적으로 ARCore 사용을 위한 권한 및 카메라 기능도 AndroidManifest에 설정해야 합니다.




2️⃣ ARCore 렌더러 구현 (ArCoreRenderer)


ARCore 프레임을 받아서 SurfaceView 위에 AR 객체를 렌더링하는 ArCoreRenderer 클래스를 작성합니다. Geospatial API를 통해 현재 위치의 위도, 경도, heading 값을 받아 지도 위치와 연동할 수 있습니다.


if (earth?.trackingState == TrackingState.TRACKING) {
val pose = earth.cameraGeospatialPose
Log.d("Geo", "Lat: ${pose.latitude}, Lng: ${pose.longitude}, Heading: ${pose.heading}")
}

지도의 좌표를 클릭했을 때 해당 위치에 Anchor를 생성해 AR 콘텐츠를 띄울 수 있습니다.


fun onMapClick(latLng: LatLng) {
val earth = session?.earth ?: return
if (earth.trackingState != TrackingState.TRACKING) return

earthAnchor?.detach()
earthAnchor = earth.createAnchor(
latLng.latitude, latLng.longitude, 0.0,
0f, 0f, 0f, 1f // 회전값 (Quaternion)
)
}



3️⃣ AR SurfaceView를 Compose에 포함시키기


Jetpack Compose에서는 AndroidView를 통해 SurfaceView를 삽입합니다.
렌더링 준비가 완료되면 SampleRender를 실행해 OpenGL 렌더링 루프를 시작합니다.


@Composable
fun ArSurfaceView(
modifier: Modifier = Modifier,
onSurfaceReady: (SurfaceView) -> Unit
) {
AndroidView(
factory = { context ->
val renderer = HelloArRenderer(
context = context,
sessionProvider = sessionProvider,
latLng = currentLocation?.let {
LatLng(
it.latitude,
it.longitude
)
} ?: LatLng(37.5665, 126.9780),
tapQueue = tapQueue // 🔥 renderer로 전달
) // 예시
sessionProvider.setOnRendererEventCallback { event ->
rendererEvent = event
}
sessionProvider.setOnTrackingMessageCallBack { message ->
trackingMessage = message
}
SampleRender(glSurfaceView, renderer, context.assets)
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
glSurfaceView
},
modifier = Modifier.fillMaxSize()
.pointerInteropFilter { motionEvent ->
if (motionEvent.action == MotionEvent.ACTION_UP) {
tapQueue.offer(motionEvent) // 🔥 터치 이벤트 큐에 전달
}
true
}
)
}



4️⃣ 지도와 AR 통합 Compose 화면 만들기


GoogleMap과 AR SurfaceView를 하나의 Compose 화면에 겹쳐서 배치합니다. 지도를 클릭하면 해당 좌표에 Anchor를 생성하고 AR로 렌더링합니다.


@Composable
fun ArWithMapScreen(activity: MainComposeActivity) {
val lifecycleOwner = LocalLifecycleOwner.current
val arRenderer = remember { ArCoreRenderer(activity) }

DisposableEffect(Unit) {
lifecycleOwner.lifecycle.addObserver(arRenderer)
onDispose { lifecycleOwner.lifecycle.removeObserver(arRenderer) }
}

Box(modifier = Modifier.fillMaxSize()) {
var clickedLatLng by remember { mutableStateOf<LatLng?>(null) }

GoogleMap(
modifier = Modifier.fillMaxSize(),
onMapClick = { latLng ->
clickedLatLng = latLng
arRenderer.onMapClick(latLng)
}
)

ArSurfaceView(
modifier = Modifier
.fillMaxSize()
.zIndex(1f),
onSurfaceReady = { surfaceView ->
SampleRender(surfaceView, arRenderer, activity.assets)
}
)
}
}



5️⃣ MainActivity 설정


Jetpack Compose와 ARCore를 통합한 MainActivity입니다. ArCoreSessionHelper는 세션 초기화 및 생명주기 처리를 돕습니다.


@AndroidEntryPoint
class MainComposeActivity : ComponentActivity() {
lateinit var arCoreSessionHelper: ArCoreSessionHelper

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arCoreSessionHelper = ArCoreSessionHelper(this)
arCoreSessionHelper.initialize()

setContent {
ArWithMapScreen(this)
}
}

override fun onResume() {
super.onResume()
arCoreSessionHelper.session?.resume()
}

override fun onPause() {
super.onPause()
arCoreSessionHelper.session?.pause()
}
}



✅ 마무리


Jetpack Compose로 Google Map과 ARCore를 동시에 사용할 수 있다는 점은 매우 큰 장점입니다. UI를 선언적으로 작성하면서도 실시간 위치 기반 AR 콘텐츠를 렌더링할 수 있어, 차세대 사용자 경험을 만드는 데 적합한 아키텍처입니다.



  • 지도 기반 AR 관광 가이드

  • 위치 기반 보물 찾기 게임

  • 실외 AR 내비게이션


🚀 다음으로는 AR 오브젝트가 지도 위에 정확하게 위치하고, 거리/방향을 시각화하는 기능도 확장해보세요!




오늘의 이야기

    💡 DevExpress dxChart & dxDataGrid 활용 가이드 열심히 일하는 중.   이 블로그에서는 dxChart 및 dxDataGrid 를 사용하는 방법에 대해 설명합...