2026/04/28

오늘의 이야기

 


Jetpack Compose + Firebase 기반 게시판 앱 개발기


게시글 만들기



 


이 글은 Jetpack Compose, Firebase Realtime Database, Hilt, Compose Destinations를 기반으로 한 게시판 앱 개발 과정을 정리한 것입니다. 게시글은 최근순으로 표시되며, 댓글 기능도 포함합니다.


1. 프로젝트 구성



  • Kotlin

  • Jetpack Compose

  • Firebase Realtime Database

  • Hilt (DI)

  • Compose Destinations (Navigation)


2. 게시글 리스트 화면


Firebase에서 게시글 데이터를 읽고, 최근 등록순으로 보여줍니다.



LazyColumn(
reverseLayout = true // 최신 글이 위로
) {
items(posts) { post ->
PostItem(post)
}
}

FloatingActionButton을 이용해 글쓰기 화면으로 이동합니다.



Scaffold(
floatingActionButton = {
FloatingActionButton(onClick = {
navigator.navigate(NewPostScreenDestination())
}) {
Icon(Icons.Default.Add, contentDescription = "새 글 작성")
}
}
) {
// Content here
}

3. 글쓰기 화면



@Destination
@Composable
fun NewPostScreen(navigator: DestinationsNavigator) {
var title by remember { mutableStateOf("") }
var content by remember { mutableStateOf("") }

Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = title,
onValueChange = { title = it },
label = { Text("제목") },
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = Color.Black
)
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = content,
onValueChange = { content = it },
label = { Text("내용") }
)
Spacer(Modifier.height(16.dp))
Button(onClick = {
// Firebase에 저장
navigator.popBackStack()
}) {
Text("등록")
}
}
}

4. 게시글 수정


수정 시, postId를 인자로 전달해 해당 글을 로딩하고 수정합니다.



@Destination
@Composable
fun EditPostScreen(postId: String, navigator: DestinationsNavigator) {
val viewModel: PostViewModel = hiltViewModel()
val post = viewModel.getPost(postId)

// 수정 UI 구성
}

5. Compose Destinations 구성



@Composable
fun MainNavHost() {
val navController = rememberNavController()
DestinationsNavHost(
navController = navController,
navGraph = NavGraphs.root
)
}

Destinations에서 ViewModel 외부 주입이 필요한 경우



DestinationsNavHost(navController = navController, navGraph = NavGraphs.root) {
composable(EditPostScreenDestination) {
EditPostScreen(
postId = it.navArgs.postId,
navigator = it.destinationsNavigator,
mainViewModel = customViewModel
)
}
}

6. UI 요소 조정



  • TopBar 여백 제거: Scaffold에서 topBar 생략

  • FloatingActionButton 아래 배치: Scaffold 내에서 기본 위치에 둬도 하단 고정

  • ModalBottomSheet 테두리 색상 조정: 직접 설정은 어려움 → 커스텀 구현 필요


7. 다국어 번역 예시























용어 번역
Posts 한국어: 게시글, 중국어: 帖子, 대만어: 貼文, 일본어: 投稿, 베트남어: Bài viết, 태국어: โพสต์, 필리핀어: Mga post
도로명주소 영어: Road name address, 중국어: 道路名地址, 대만어: 路名地址, 일본어: 道路名住所, 베트남어: Địa chỉ theo tên đường, 태국어: ที่อยู่ตามชื่อถนน, 필리핀어: Address ng kalsada
Distance 중국어: 距离, 대만어: 距離, 일본어: 距離, 베트남어: Khoảng cách, 태국어: ระยะทาง, 필리핀어: Distansya

마무리


이와 같은 구조로 Jetpack Compose와 Firebase를 이용한 게시판 앱을 확장할 수 있습니다. 다음 포스트에서는 이미지 업로드, 사용자 인증, 또는 푸시 알림 추가도 소개할 수 있습니다.





오늘의 이야기


#스하리1000명프로젝트,
Parfois, il est difficile de parler avec des travailleurs étrangers, n'est-ce pas ?
J'ai créé une application simple qui aide ! Vous écrivez dans votre langue et les autres le voient dans la leur.
Il se traduit automatiquement en fonction des paramètres.
Super pratique pour des discussions faciles. Jetez-y un oeil quand vous en aurez l'occasion !
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

UI 초안



🏸 1 배드민턴 리그전





  • 오늘 참여 인원: 8명

  • 진행 현황: 총 4팀, 6경기 중 2경기 완료




📊 순위표










































순위 팀명 세트 득실
1 팀A 3 1 +4
2 팀B 2 2 +1
3 팀C 2 2 -2
4 팀D 1 3 -3



주요 기능



  • [🔄 리그 시작하기] : 오늘 리그전을 새로 시작합니다. (기존 진행 경기 결과는 저장 후 초기화됩니다.)

  • [👥 회원 관리] : 동호회 회원을 등록/수정/삭제할 수 있습니다.

  • [📅 이전 기록] : 지난 리그 결과 및 전체 누적 통계를 확인합니다.

  • [🗂 팀/대진표 보기] : 오늘의 팀 구성과 대진표를 확인할 수 있습니다.

  • [📝 경기 결과 입력] : 경기 점수 입력 및 경기 종료 판정을 할 수 있습니다.





오늘 리그전에 참여할 인원을 워치와 스마트폰 양쪽에서 모두 확인해 주세요.



2 팀 선택


참여할 팀을 하나 선택해 주세요.





  • [팀A] (남은 인원: 1명)

  • [팀B] (남은 인원: 2명)

  • [팀C] (남은 인원: 대기중)

  • [팀D] (남은 인원: 1명)





팀을 선택하면 선수 명단에 자동 등록됩니다.
이미 인원이 다 찬 팀은 선택 불가합니다.



[🔄 새로고침] [❌ 돌아가기]


3 경기 진행 중


[ 팀A vs 팀B ]



























세트 팀A 점수 팀B 점수
1 21 18
2 17 21
3 - -




  • [⏸ 일시정지] : 경기 일시정지 및 재개

  • [✔ 세트 승리 체크] : 각 세트가 끝날 때 승리한 팀을 체크합니다




⚠️ 심박수 주의


현재 심박수: 205bpm



🚨 심박수가 높게 감지되었습니다.
잠시 휴식을 취하세요.


계속 이상신호가 지속된다면 주최자에게 알리세요.



[알림 끄기] [의료 도움 요청]





워치 진동 및 팝업으로 즉시 안내됩니다.



4 경기 결과 입력


[ 팀A vs 팀B ]




세트별 점수를 입력해 주세요.



  • 세트 1 : 팀A [ 21 ] - 팀B [ 18 ]

  • 세트 2 : 팀A [ 17 ] - 팀B [ 21 ]

  • 세트 3 : 팀A [ ] - 팀B [ ] (승부가 나지 않으면 입력하지 않아도 됩니다.)

  • [➕ 세트 추가] [➖ 세트 삭제]





  • [✔️ 결과 저장] : 입력한 점수와 승패를 저장합니다.

  • [↩️ 이전 화면] : 결과 입력을 취소하고 돌아갑니다.





2세트를 먼저 이긴 팀이 최종 승리로 자동 처리됩니다.
입력을 마친 후 반드시 저장 버튼을 눌러 주십시오.
오류가 있는 경우 관리자에게 문의해 주세요.





기본 아이콘



 





오늘의 이야기

🌟 Welcome Korea – แอปไกด์อาหารท้องถิ่นของคุณ


앱 설명서



เวอร์ชันก่อนหน้านี้เป็นอย่างไร?
ก่อนหน้านี้ Welcome Jeju เป็นแอป Android ง่ายๆ ที่เน้นรวบรวมคำแนะนำเกี่ยวกับร้านอาหารและสถานที่ต่างๆ รอบเกาะเชจู โดยจะอัปเดตข้อมูล “สถานที่ยอดนิยม” รายวันจากการดึงข้อมูลบล็อกและโพสต์บนเว็บที่ติดแท็กว่า “맛집” (ร้านอาหารอร่อย)


ตอนนี้แอปได้พัฒนาให้มีจุดประสงค์ที่กว้างขึ้นแล้ว


ทำไมต้องปรับปรุงใหม่?
เมื่อไปเที่ยวเกาหลี แอปอย่าง Google Maps มักขาดข้อมูลท้องถิ่นที่ละเอียด โดยเฉพาะในย่านอย่างมยองดง แม้ว่าแอปอย่าง Naver Maps และ Kakao Maps จะเหมาะกับคนเกาหลี แต่กลับใช้งานไม่สะดวกสำหรับนักท่องเที่ยวต่างชาติ


ขณะทำงานในโครงการใกล้มยองดง ผมสังเกตเห็นนักท่องเที่ยวต่างชาติจำนวนมากยืนอยู่กับที่ ก้มมองมือถือ พยายามหาว่าตนเองอยู่ตรงไหนหรือจะไปที่ไหน ด้วยภาษาอังกฤษที่จำกัด ผมช่วยอะไรไม่ได้มาก จึงตัดสินใจปรับปรุงแอปให้ตอบโจทย์นักท่องเที่ยวต่างชาติมากขึ้น


ผมยังได้นำแนวคิดจากเครื่องมือ AI มาปรับปรุงประสบการณ์การใช้งานแอปด้วย


มีอะไรใหม่ในเวอร์ชันล่าสุด?
แอปเวอร์ชันใหม่เริ่มต้นด้วยหน้าต้อนรับ และรองรับหลายภาษา ได้แก่:



  • ภาษาเกาหลี

  • ภาษาอังกฤษ

  • ภาษาจีน (ตัวย่อ & ตัวเต็ม)

  • ภาษาฟิลิปปินส์

  • ภาษาเวียดนาม

  • ภาษาไทย


ผมเน้นภาษาที่นักท่องเที่ยวจากเอเชียตะวันออกเฉียงใต้มักใช้ เพราะนักท่องเที่ยวกลุ่มนี้เป็นส่วนใหญ่ของผู้มาเยือนเกาหลี


🧭 ฟีเจอร์หลัก



  1. รองรับหลายภาษา
    สามารถเลือกภาษาของแอปได้ตั้งแต่หน้าแรก และเปลี่ยนได้ภายหลังผ่านเมนู

  2. หน้าหลัก – สำรวจใกล้ตัว
    คุณสามารถค้นหาร้านอาหารหรือสถานที่ต่างๆ ได้จาก:



  • คำค้น (ในรัศมี 5 กิโลเมตร)

  • หมวดหมู่

  • สถานที่ยอดนิยมที่คัดสรรจากบล็อกเกอร์เกาหลี อัปเดตรายวัน



  1. ค้นหาด้วยเสียง
    สามารถค้นหาด้วยเสียงในภาษาของคุณเอง แอปจะ:



  • แปลงเสียงเป็นข้อความภาษาเกาหลี

  • ค้นหาในภาษาเกาหลี

  • แปลผลลัพธ์กลับเป็นภาษาของคุณ


🔐 หมายเหตุ: ต้องขอสิทธิ์การใช้งานไมโครโฟนเพื่อใช้ฟีเจอร์นี้



  1. ไอคอนผลการค้นหา
    📍 หมุดสถานที่: จากคำค้นของคุณ
    🚩 ธงแนะนำ: สถานที่ยอดนิยมที่รวบรวมรายวัน


📱 ดาวน์โหลดเลยตอนนี้
แอปพร้อมให้ดาวน์โหลดแล้วบน Play Store:


👉 Welcome Korea (맛집 리스트 모아보기)
“เรารวบรวมเคล็ดลับร้านอาหารบนเกาะเชจูจากเว็บต่างๆ – ตอนนี้ฉลาดขึ้น ใช้ได้หลายภาษา และเป็นมิตรกับนักท่องเที่ยว”


🔗 https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127



 


옵디강 (Welcome) - Google Play 앱


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


play.google.com




 





오늘의 이야기


#스하리1000명프로젝트,
कभी-कभी विदेशी कामगारों से बात करना कठिन होता है, है न?
मैंने एक सरल ऐप बनाया है जो मदद करता है! आप अपनी भाषा में लिखते हैं, और दूसरे इसे अपनी भाषा में देखते हैं।
यह सेटिंग्स के आधार पर स्वचालित अनुवाद करता है।
आसान चैट के लिए बहुत उपयोगी। जब मौका मिले तो देख लेना!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기


#스하리1000명프로젝트,
Bị lạc ở Hàn Quốc? Ngay cả khi bạn không nói được tiếng Hàn, ứng dụng này vẫn giúp bạn đi lại dễ dàng.
Chỉ cần nói ngôn ngữ của bạn—nó sẽ dịch, tìm kiếm và hiển thị kết quả bằng ngôn ngữ của bạn.
Tuyệt vời cho du khách! Hỗ trợ hơn 10 ngôn ngữ bao gồm tiếng Anh, tiếng Nhật, tiếng Trung, tiếng Việt, v.v.
Hãy thử nó ngay bây giờ!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




오늘의 이야기

① AI 관련 기사 🧠



  1. 삼성, Galaxy Unpacked 2025 출시 – Z Fold7·Flip7·Watch8 공개
    – 삼성은 7월 9일 행사에서 Z Fold7(두께 8.9 mm/8″ 디스플레이), Z Flip7(두께 13.7 mm, 31시간 영상 재생), Galaxy Watch8(항산화·혈관 로드 측정 기능) 등을 선보임 Business Insider+1TechRadar+1.
    리스크: 고가 전략 (Z Fold7 $1,999~), 스마트폰 가격대 강세. 긍정: AI 기능 강화(작성/검색/건강코칭)로 생태계 확장 및 경쟁력 상승.

  2. 삼성전자, Q2 반도체 실적 39%↓…AI 메모리 공급 지연
    – 2분기 영업이익 6.3조원(약 $4.62 B), 전년 대비 39% 감소 Reuters.
    리스크: HBM3E 인증 지연, 미국 수출제재 여파. 긍정: AMD 공급 시작, 메모리 포트폴리오 다변화 기대.

  3. 한국, 세계 최대 AI 데이터센터 구축 추진
    – SK·AWS, 울산에 7조원(약 $5.11 B) 규모 AI 센터 투자. 2025년 9월 착공, 2029년 100 MW 운영 → 향후 1 GW 확대 계획 ReutersRCR Wireless News+2Reuters+2artificialintelligence-news.com+2.
    리스크: 대규모 인프라 투자 리스크 및 지역 편중 문제. 긍정: 글로벌 AI 허브 도약, 관련주(KOSPI) 상승 촉진.

  4. 정부, 273억 원 규모 지역 기반 AI 칩·엣지 센터 지원
    – 과기정통부 주도로 비수도권에 컨테이너형 엣지 데이터센터 구축, AI 칩 국산화 및 중소병원·중소기업 지원 DIGITIMES Asia+15Reuters+15Reuters+15정책브리핑.
    리스크: 초기 국산 칩 품질·시장성 불확실. 긍정: 지역 균형 발전, 자급력 확보, 중소기업 디지털 도약.

  5. 기업 AI 에이전트, 2025 디지털 전환 핵심으로 부상
    – ActuIA 조사 결과 96% 기업이 향후 12개월 내 AI 에이전트 사용 확대 계획 ActuIA.
    리스크: 개인정보·책임 소재 규제, 기술 의존 리스크. 긍정: 업무 효율성과 생산성 대폭 향상, 경쟁력 강화.




② 부동산 관련 기사 🏠



  1. 2025 상반기 수도권 집값 1.0% 상승 전망
    – 한국건설산업연구소: 수도권 매매가 1분기 이후 +1.0%, 전고점 돌파 예상 미주중앙일보 - The Korea Daily+2Goover+2kdi.re.kr+2.
    리스크: 자산 격차 심화, 실수요자 부담 증가. 긍정: 주택시장 안정, 건설 투자 활성화 기대.

  2. 2025년 4분기 집값 하락 전환…바이어에 우호적
    – 미국 LA 중계, 수요·공급 증가로 3분기 정체 후 4분기 하락 전환 전망 YouTube+15미주중앙일보 - The Korea Daily+15Nate 뉴스+15.
    리스크: 사업자·시행사 수익성 악화. 긍정: 실수요자 구매 기회 확대, 전체 시장 안정화.

  3. 전국 미분양 7만 호↑…지방 거래 침체
    – 한국경제 4월: 지방은 미분양 70,000호 돌파, 수도권 상승세와 양극화 확대 한국경제.
    리스크: 시행사 부담, 지방경제 침체 지속. 긍정: 공급 재조정 기회, 정책 개입 명분 확보.

  4. 2025 부동산 10대 이슈 – KDI 분석
    – 경제 둔화 우려, 금리 변화, 공급 과잉·전세 월세 전환 등 다양한 변수 제시 Nate 뉴스+3KDI 경제정보센터+3한국경제+3.
    리스크: 고금리 지속 시 자금조달 부담. 긍정: 정보투명성 강화, 정책 대응 기회 포착.

  5. 2025 부동산 트렌드: 금리 인하 기대와 시장 반등
    – 한경 매거진: 미국·한국 기준금리 인하 기대, 금융비용 하락으로 자금 순환 가능성 증가 한경 매거진조선일보.
    리스크: 금리 변동성이 부동산 시장 예측을 어렵게 함. 긍정: 구매자·사업자 심리 회복, 투자 유인 강화.




③ 기업 관련 기사 🏢



  1. LG전자, 스마트물류센터 사업 확장 MOU 체결
    – 로지스밸리와 협업해 스마트팩토리→스마트물류 진출. 로지스밸리는 글로벌 물류센터 50개 운영 중 IT동아+1Fuel Cells Works+1.
    리스크: 물류 디지털 도입 실패 시 투자 회수 지연. 긍정: 신사업 기반 확보 → 수익 구조 고도화 기대.

  2. 증권사, AI·ESG·해외투자 확대 대응 필요
    – KCMI: 증권업계, 해외투자 및 AI 도입 확대, ESG 대응 등이 주요 과제 KCMI.
    리스크: AI 규제 및 글로벌 정책 불확실성. 긍정: 디지털 자문 확대, 운영 효율화 기대.

  3. 수출기업 48.6% ‘경영환경 유사’, 37.3% ‘악화 우려’
    – 무역협회 발표: 기업 48.6%는 2024년과 유사, 37.3%는 악화 전망 KDI 경제정보센터+15kita.net+15KDI 경제정보센터+15.
    리스크: 수출 부진 지속, 대미 규제·관세 위험. 긍정: 방어적 전략 마련 기회, 다변화 추진 동력.

  4. 삼성SDS, 2025 IT 투자 전망: 48%·25% 확대, 27% 축소
    – 기업 조사: 48% 유지, 25% 확장, 27% 축소할 계획 Samsung SDS.
    리스크: 투자 감소 기업의 경쟁력 약화 우려. 긍정: 확장 기업 중심 혁신 가속화.

  5. KDI: 수출 둔화·소비심리 악화…금리 인하로 하반기 회복 전망
    – 상반기 수출·소비 둔화, 하반기부터 금리 인하 효과로 회복 가능성 kdi.re.kr+3KCMI+3한경 매거진+3.
    리스크: 미국 통상 불확실성 → 수출 타격. 긍정: 금리 인하로 기업·소비자 자금부담 완화.




④ 해외 경제 관련 기사 🌍



  1. OECD “2025년 세계 경제 더 어려워질 것”
    – 교역량 둔화, 미국 철강·알루미늄 관세 25→50%, 한국 수·출 감소: 수출 -1.3%→572.7 억 달러, 수입 -5.3% 꿈의 무역센터.
    리스크: 글로벌 무역 환경 악화, 공급망 불안 증가. 긍정: 협력 강화를 통한 구조조정 추진 기회.

  2. 딜로이트 “2025 미국 GDP 2.6%, 2026년 2.1% 성장” 전망
    – 실질수출 +0.7%, 수입 +1.8%, 기준금리 75bp 인하 전망 Deloitte.
    리스크: 관세·물가 불확실성 잔존. 긍정: 온건 성장 지속→수출·수입 확대·무역 안정 가능.

  3. 영국, 1분기 GDP +0.7%, 인플레 3~3.5%, 하반기 금리 인하 기대
    – 기업세 및 관세 불확실성 있지만, 물가 하락 기대 russellinvestments.com.
    리스크: 지정학 리스크, 기업 투자 위축. 긍정: 금리 인하→소비·투자 회복 동력.

  4. 미국 2025년 연간 성장률 2.1~2.7%, 실업률 연말 4.2% 예상
    – 고용 냉각, 물가 안정 속 성장 지속 전망 kcif.or.kr.
    리스크: 정책 불확실성 증가 위험. 긍정: 고용 안정성 유지 → 소비지지 기대.

  5. KOTRA: 주요국 수출·수입 감소, 미국은 투자 견조
    – 수출 -1.3%, 수입 -5.3%인데 반해 미국은 생산·소비 둔화 속 투자 견조 꿈의 무역센터.
    리스크: 무역 글로벌 수요 둔화. 긍정: 투자 중심 경제전환 기회.








2026/04/27

오늘의 이야기

 



🎾 Kotlin으로 복식 경기 Round-Robin 매칭 구성하기


라운드 로빈 구현해 보기



 


이 글은 Kotlin과 Jetpack Compose를 사용하는 Android 앱에서 복식 경기 매칭을 어떻게 구성할 수 있는지 기록한 개발자 경험 공유입니다. 예제 코드는 초보자도 이해할 수 있도록 주석과 함께 설명합니다.


🧩 사용 시나리오


앱 사용자는 4명 이상의 참여자를 등록한 후, 복식 경기 방식으로 매칭을 자동 생성합니다.



  • 경기 방식은 Round-Robin 방식 (모든 가능한 조합을 구성)

  • 같은 팀 또는 상대가 중복되지 않도록 구성

  • 선수 수가 홀수인 경우 마지막 한 명을 제외

  • 참가자 수가 4명일 경우에도 다양한 팀 구성을 고려


🧠 핵심 데이터 구조



// 경기 참가자
data class Contestant(
val pk: String, // 고유 ID
val tokenId: String,
val enterTime: Long,
val exitTime: Long
)

// 경기 매칭
data class Match(
val pk: String, // "Round_0", "Round_1" 등
val startTime: Long,
val endTime: Long,
val teamA: List, // 2인 1조
val teamB: List,
val isDoubles: Boolean = true
)

⚙️ 매칭 로직 구현


복식 경기를 위한 모든 가능한 조합을 만들고, 이전 경기에 바로 참여한 선수는 다음 경기에서 쉬도록 구성합니다.



// 복식 팀 조합을 생성하는 함수
fun generateDoublesTeamPairs(players: List): List<List<Contestant>> {
val validPlayers = if (players.size % 2 != 0) players.dropLast(1) else players
val teamPairs = mutableListOf<List<Contestant>>()

// 모든 2명 조합 생성
for (i in validPlayers.indices) {
for (j in i + 1 until validPlayers.size) {
teamPairs.add(listOf(validPlayers[i], validPlayers[j]))
}
}

return teamPairs
}

이후 이 조합으로 가능한 모든 2팀 매칭을 구성합니다.



// 복식 경기를 위한 가능한 Match 조합 생성
fun generateDoublesMatches(players: List<Contestant>): List<Match> {
val validPlayers = if (players.size % 2 != 0) players.dropLast(1) else players
val teamPairs = generateDoublesTeamPairs(validPlayers)
val matchList = mutableListOf<Match>()
var idx = 0

val usedMatches = mutableSetOf<Set<String>>()

for (i in teamPairs.indices) {
for (j in i + 1 until teamPairs.size) {
val teamA = teamPairs[i]
val teamB = teamPairs[j]

// 팀원이 겹치지 않는지 확인
if (teamA.intersect(teamB.toSet()).isEmpty()) {
val allPlayers = (teamA + teamB).map { it.pk }.toSet()

// 동일한 구성의 경기인지 중복 확인
if (allPlayers !in usedMatches) {
usedMatches.add(allPlayers)

// 실제 Match 객체 생성
val match = Match(
pk = "Round_${idx++}",
startTime = System.currentTimeMillis(),
endTime = System.currentTimeMillis() + 10 * 60 * 1000,
teamA = teamA,
teamB = teamB,
isDoubles = true
)

matchList.add(match)
}
}
}
}

return matchList
}

✨ 위 함수는 4명이 참여했을 때도 3가지 가능한 조합 중 1경기로 끝나는 일이 없도록 팀 구성 중복 여부를 고려해 최소 2경기 이상이 생성될 수 있도록 보완하였습니다.

🔍 로그 출력 예시 (디버깅용)



Log.e("MatchGen", "Valid players (${validPlayers.size}): ${validPlayers.map { it.pk }}")
Log.e("MatchGen", "Generated ${teamPairs.size} teams")

for (match in matchList) {
Log.e("MatchGen", "Match: ${match.teamA.map { it.pk }} vs ${match.teamB.map { it.pk }}")
}

✅ 결과 예시 (4명일 때)



Match: A, B vs C, D
Match: A, C vs B, D

📌 마무리


이 방식은 경기 운영 자동화라운드로빈 방식의 배치 자동화에 유용하며, 참여자 수의 변화에 따라 유연하게 대응할 수 있는 점이 큰 장점입니다.


필요에 따라 매칭 조건(예: 직전 경기 제외, 최소 휴식 시간 보장 등)을 더 정교하게 설계해 나갈 수 있습니다.




문의나 개선 아이디어가 있다면 댓글로 남겨주세요! 😊


 


어디까지나... chatGPT 의 의견은 아직 100% 신뢰도를 보장 하지 않습니다. 





오늘의 이야기


#스하리1000명프로젝트

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

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

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





오늘의 이야기

 


📍 Android에서 Kakao 로컬 API로 주소/좌표 변환하기


주소검색



 


이 글에서는 안드로이드 앱에서 Kakao REST API를 사용하여 좌표 → 주소 변환, 주소 → 좌표 변환을 구현하는 방법을 설명합니다.
초보 개발자도 쉽게 따라 할 수 있도록 Retrofit2 + Coroutine + ViewModel 기반으로 단계별로 안내합니다.




✅ 1. 사전 준비


1.1. Kakao REST API 키 발급



  • 카카오 개발자 사이트에 로그인

  • 애플리케이션 등록 → 앱 키 → REST API 키 복사

  • Android 플랫폼 등록 → 패키지명 + 키 해시 입력 (중요!)


1.2. Android 권한 설정


AndroidManifest.xml에 다음 권한을 추가하세요.


<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

위치 권한은 런타임 요청이 필요합니다 (Activity나 Composable에서).


---


✅ 2. Gradle 설정


build.gradle (Module) 파일에 다음을 추가하세요.


dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
}

---


✅ 3. Retrofit 구성


3.1. 데이터 클래스


data class KakaoAddressResponse(
val documents: List<Document>,
val meta: Meta
)

data class Meta(
val is_end: Boolean,
val pageable_count: Int,
val total_count: Int
)

data class Document(
val address: Address,
val address_name: String,
val address_type: String,
val road_address: RoadAddress,
val x: String,
val y: String
)

data class Address(
val address_name: String,
val b_code: String,
val h_code: String,
val main_address_no: String,
val mountain_yn: String,
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_h_name: String,
val region_3depth_name: String,
val sub_address_no: String,
val x: String,
val y: String
)

data class RoadAddress(
val address_name: String,
val building_name: String,
val main_building_no: String,
val region_1depth_name: String,
val region_2depth_name: String,
val region_3depth_name: String,
val road_name: String,
val sub_building_no: String,
val underground_yn: String,
val x: String,
val y: String,
val zone_no: String
)

3.2. Retrofit 인터페이스


interface KakaoApiService {
@GET("v2/local/geo/coord2address.json")
suspend fun getAddressFromCoords(
@Header("Authorization") authorization: String,
@Query("x") longitude: Double,
@Query("y") latitude: Double
): KakaoAddressResponse

@GET("v2/local/search/address.json")
suspend fun getCoordsFromAddress(
@Header("Authorization") authorization: String,
@Query("query") query: String
): KakaoAddressResponse
}

3.3. Retrofit 클라이언트


object KakaoApiClient {
private const val BASE_URL = "https://dapi.kakao.com/"

private val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}

private val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()

val apiService: KakaoApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(KakaoApiService::class.java)
}
}

---


✅ 4. ViewModel + Coroutine 통합


Coroutine을 사용하여 API를 호출하고, StateFlow로 결과를 UI에 전달합니다.


class LocationViewModel : ViewModel() {

private val _address = MutableStateFlow<String?>(null)
val address: StateFlow<String?> = _address

private val apiKey = "KakaoAK YOUR_REST_API_KEY" // <-- 실제 키로 교체하세요

fun fetchAddress(lat: Double, lng: Double) {
viewModelScope.launch {
try {
val response = KakaoApiClient.apiService.getAddressFromCoords(
authorization = apiKey,
longitude = lng,
latitude = lat
)
val addressName = response.documents.firstOrNull()?.address?.address_name
_address.value = addressName ?: "주소 없음"
} catch (e: Exception) {
_address.value = "에러 발생: ${e.localizedMessage}"
}
}
}
}

---


✅ 5. Jetpack Compose에서 주소 출력


@Composable
fun AddressScreen(viewModel: LocationViewModel = viewModel()) {
val address by viewModel.address.collectAsState()

Column(modifier = Modifier.padding(16.dp)) {
Text(text = "현재 주소: ${address ?: "불러오는 중..."}")

Spacer(modifier = Modifier.height(8.dp))

Button(onClick = {
viewModel.fetchAddress(37.5665, 126.9780) // 서울시청 예시
}) {
Text("주소 가져오기")
}
}
}

---


🔚 마무리 정리



  • ✔️ Retrofit + Coroutine을 통해 네트워크 처리를 안전하고 깔끔하게 구성

  • ✔️ ViewModel과 StateFlow를 통해 UI와 데이터 연결

  • ✔️ 권한과 REST API 키 보안을 고려해 실제 배포 환경에서는 백엔드 연동 또는 키 보호 필요


이제 Kakao Local API를 통해 GPS 좌표와 주소를 자유롭게 변환할 수 있습니다!


 


결과 이미지는 다음과 같이 설정이 가능 합니다. 


위치 설정 결과 지표에 표시 하기



 




📘 도움이 되었나요? 댓글이나 공유로 응원해 주세요 😊





오늘의 이야기

 



Oracle의 PERCENTILE_CONT와 APPROX_PERCENTILE 활용


백분위



 


Oracle에서 백분위수를 계산할 때 사용할 수 있는 주요 함수와 활용 방법에 대해 정리해 보겠습니다.


PERCENTILE_CONT 함수


`PERCENTILE_CONT` 함수는 연속적인 값을 기반으로 특정 백분위수를 계산하는 함수입니다. 예제는 다음과 같습니다:


SELECT PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY column_name) FROM table_name;

하지만 `PERCENTILE_CONT`는 **상수 값**만을 매개변수로 받을 수 있어 동적으로 값을 변경하기 어렵습니다.


동적으로 백분위 값 설정하기


`PERCENTILE_CONT`의 매개변수를 동적으로 설정하는 방법은 제한적이지만 다음과 같은 방법을 고려할 수 있습니다:



  1. PL/SQL을 활용한 동적 SQL 실행

  2. WITH 절을 이용한 사전 백분위값 계산

  3. APPROX_PERCENTILE을 활용하여 근사값 반환


예를 들어, `WITH` 절을 활용하여 여러 백분위수를 계산할 수 있습니다:


WITH PercentileData AS (
SELECT column_name,
PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY column_name) AS percentile_25,
PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY column_name) AS percentile_50,
PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY column_name) AS percentile_75
FROM table_name
)
SELECT * FROM PercentileData;

APPROX_PERCENTILE 활용


Oracle 12c 이상에서는 `APPROX_PERCENTILE`을 활용하여 대량 데이터에서 빠르게 근사 백분위수를 계산할 수 있습니다.


SELECT APPROX_PERCENTILE(column_name, 0.75) FROM table_name;

이 함수는 빠른 성능을 제공하지만 `PERCENTILE_CONT`보다 정확성이 조금 낮을 수 있습니다.


결론


`PERCENTILE_CONT`를 동적으로 활용하는 것은 어렵지만, `WITH` 절을 이용한 미리 계산된 값 활용, `EXECUTE IMMEDIATE`를 통한 PL/SQL 실행, `APPROX_PERCENTILE`을 통한 근사값 추출 등의 방법을 사용할 수 있습니다.





오늘의 이야기

< 주변검색 주변찾기 앱 , 동작감지기 앱 :  이하 사용자앱으로 표시 >:는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보...