1. 개요 및 현황

현직 딸, 아들, 딸 세 아이의 아빠 입니다. 첫 째, 둘 째가 모두 초등학교에 들어가게 되었고, 둘 째는 온 동네를 돌아다니며 자전거 타는 것에

너무 홀릭이 되버려서 두 아이에게 구형이긴 하지만 스마트폰을 쥐어줄 수밖에 없게 되었고, 쓸만한 위치추적 어플을 찾아 설치해야 하는 상황이 되었습니다.

위치추적으로 검색해보니 너무 많은 앱들이 있어서 일단 매출액 및 검색 순위 등을 기준으로 앱을 설치하고 동작시켜보았습니다.

실제로 테스트 해본 앱들은 더 많은데, 기능이나 결제 정책들이 아래 소개될 내용들과 대동소이해서 매출액 순위를 기준으로 아래에 나열된 앱에 대해서만 리뷰해보도록 하겠습니다.

 

내용이 많다 싶으신 분들은 맨 맽에 '4. 총평 및 결론' 부분만 확인하시면 될 것 같습니다.

 

구글 플레이에 등록된 안드로이드 앱 기준으로 글이 작성되었습니다.

위치추적 어플 들은 보통 라이프스타일, 출산/육아, 커뮤니케이션 등의 카테고리에 등록되어 있습니다.

구글 플레이 인기차트에서 각 카테고리 별로 최고 매출 기준의 순위에 따라 살펴보면

 

커뮤니케이션 카테고리에서는 아이쉐어링 앱이

출산/육아 카테고리에서는 Be Closer, 파인드 마이 키즈 앱이

라이프스타일 카테고리에서는 mLite, Looka, Life360 앱이 10위권내에 있습니다.

 

이 앱들은 기본적으로 자녀 등으로 등록된 다른 사용자의 위치를 추적하는 기능을 기본적으로 제공하고, 그 외에 안심존 설정 및 안심존 이탈여부 알림 기능, 간단한 채팅 기능, 긴급알림 등이 기능을 제공하고 파인드 마이 키즈 앱의 경우는 '주변 소리 듣기' 라는 기능을 제공하고 있습니다.

 

이제 위에서 말씀 드린 6개의 앱과 최종적으로 현재 제가 설치해서 사용중인 앱에 대해 설명하도록 하겠습니다. 최근에 이슈가 되고 있는 주변 음성 들리는 위치추적 기능여부까지 비교해 보도록 하겠습니다.

 

 

2. 앱 소개

2.1 Life360

2.1.1 개요

미국 샌프란시스코에 위치한 Life360 Inc. 에서 2010년 4월 7일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 1억회 이상 다운로드됨.

 

2.1.2 특장점

앱에서 내새우고자 하는 특장점은 대부분 Google Play 의 앱 정보 및 스크린샷을 통해 대부분은 알 수 있습니다.

앱 정보 란에 설명된 특장점과 스크린샷은 아래와 같습니다.

• 전용 지도에서 친구와 가족의 실시간 위치를 편리하게 확인합니다.

• 소중한 사람이 집, 직장 또는 학교에 도착하거나 떠날 때 알림을 받습니다.

• 안전한 비공개 채팅 기능으로 사진과 문자 메시지를 공유합니다.

• 서클 구성원의 과거 위치 기록을 추적합니다.

• 운전할 때마다 정보를 제공하여 안전한 운전을 가능케 합니다.

 

후술할 앱들과 비교해 위치추적 어플 본연의 목적과 기능과는 무관해 보이는 안전운전관련기능, 채팅기능 등이 있습니다.

위치추적 어플들이 대부분 유료앱인경우가 많은데 이 앱도 모든 기능을 원활하게 사용하려면 이용료를 내야 합니다.

 

Life360 프리미엄이라는 이름으로 유료기능을 제공하고 있고,

월 5,500원, 년 55,000원의 비용을 내면 운행보고서, 30일간의 위치 내역, 무제한 장소 알림, 우선 고객 지원 등의 기능을 제공한다고 설명되어 있습니다.

 

01234
Life360 Google Play ScreenShot

 

2.2 Looka

2.2.1 개요

터키에 위치한 SMART RIVER 소프트웨어 서비스 유한 회사 에서 2021년 5월 3일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 100만회 이상 다운로드됨.

 

2.2.2 특장점

이 앱도 위치추적 어플 본연의 목적과 기능과는 무관해 보이는 채팅기능이 있습니다. 이 앱은 앱을 실행하자 마자 정기결제를 강요하는 화면이 뜹니다. 정기결제를 하지 않으면 다음 화면과 기능을 사용할 수 없는 구조 입니다. Google Play 리뷰글을 보면 대부분 '동의 없이 결제가 된다' '앱 삭제 이후에도 결제가 된다.' '정기 결재 취소가 없다' 등의 내용과 항의들이 많습니다.

이용요금도 주 9,900원(월로 환산시 월 39,600원) 으로 굉장히 비싼 편입니다. 매출 순위가 높아서 리뷰 항목에 올렸지만, 매출 순위가 높은 이유가 이런 것 때문이었나 싶습니다. 이런 이유로 Looka 는 설치 및 이용을 안 하는 것이 좋을 것 같습니다.

대표사진 삭제

Looka 정기 결제 유도 화면

 

참고로 Google Play 앱 정보 및 스크린샷에 표기된 내용은 아래와 같습니다.

• 전화 번호부를 통해 아이를 추적하십시오

• 실시간으로지도에서 자녀의 위치를 확인하십시오.

• 최고 수준의 보안 유지 : 언제든지 친구를 막을 수 있습니다.

• 한 번의 탭으로 자녀 위치로 방향을 얻으십시오.

• 도난 당하거나 잃어버린 전화의 위치를 추적하십시오.

 
 

2.3 mLite

2.3.1 개요

Ipl Group PTY 에서 2016년 6월 23일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 500만회 이상 다운로드 됨.

 

2.3.2 특장점

이 앱도 Looka 앱 처럼 앱을 실행하자마자 정기 결제를 강요하는 화면이 뜹니다. 정기결제를 하지 않으면 다음 화면과 기능을 사용할 수 없는 구조 입니다. 이용요금도 3개월 60,000원(월로 환산시 월 20,000원) 으로 비싼 편입니다. Google Play 리뷰글을 보면 결제와 관련하여 항의성 글들이 정말 많고, 사기앱이니 카드사에 전화하고 구글 통해서까지 조취를 취했다는 등의 내용이 많이 보입니다. 기능의 유용함 확인 이전에 이런 앱들은 이용시에 결제와 관련한 스트레스가 매우 높을 확률이 높습니다. 이런 이유로 mLite 는 설치를 하지 않는 것이 좋을 것 같습니다.

mLite 정기 결제 유도 화면 및 상단 알람 노출
 

 

또 이 앱은 결제할 때까지 이 스크린샷의 상단에 50% 할인 어떠세요 라는 알림이 뜨면서 지속적으로 결제를 유도합니다. 이 50% 할인은 속칭 마트식 할인(이전에 실제 판매되는 가격에서 50% 할인이 된 것이 아니라, 50% 할인된 가격이 그냥 평소 판매되는 가격)으로 후술할 파인드 마이 키즈 앱에서도 같은식의 마트식 할인을 적용한 가격으로 지속적으로 결제를 유도합니다.

 

참고로 Google Play 앱 정보 및 스크린샷에 표기된 내용은 아래와 같습니다.

1. 자녀가 특정 장소를 떠나거나 도착할 때 알림을받습니다. mLite를 사용하면지도에 가상 위치 안전 구역 (지오 펜싱)을 만들 수 있으며 구역에 들어 오거나 나갈 때 알림을 받습니다.

2. 추적 휴대폰의 마이크를 사용하여 자녀 주변의 소리를 들어보세요. 마이크 레코더는 앱에 모든 트랙을 저장합니다.

3. 자녀의 위치 기록을 파악하고 자녀에게 더 가까이 다가갈 수 있습니다.

4. 긴급 상황을 위해 자녀의 휴대폰에 알람 버튼을 추가하세요. 자녀의 휴대폰에서 버튼을 한 번만 누르면 즉각적인 주의가 필요한 상황에 대한 알림을 받을 수 있습니다.

5. 연락처 목록 모니터링은 부모가 자녀가 누구와 소통하는지 파악하고 개인적으로 모르는 사람에게 더 세심한 주의를 기울일 수 있도록 돕기 위해 개발된 부모용 기능입니다.

6. 추적 대상 휴대폰에 설치된 앱 목록 보기*. 자녀가 플레이하는 게임과 사용하는 애플리케이션을 확인하세요. 위험하거나 금지된 콘텐츠가 없는지 확인하세요.

* 추적 대상 휴대폰이 iPhone인 경우 *로 표시된 기능은 사용할 수 없습니다.

0123

mLite Google Play ScreenShot

 

 

2.4 Be Closer

2.4.1 개요

Cyprus(키프로스) 에 위치한 Applabel LTD 에서 2019년 2월 5일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 1000만회 이상 다운로드 됨.

 

2.4.2 특장점

이 앱도 Looka, mLite 앱 처럼 앱을 실행하자마자 정기 결제를 강요하는 화면이 뜹니다. 정기결제를 하지 않으면 다음 화면과 기능을 사용할 수 없는 구조 입니다. 이용요금도 주 7,000원(월로 환산시 월 28,000원) 으로 비싼 편입니다. 이 앱도 Google Play 리뷰글을 보면 Looka, mLite 와 같이 결제와 관련하여 항의성 글들이 정말 많습니다. 기능의 유용함 확인 이전에 이런 앱들은 이용시에 결제와 관련한 스트레스가 매우 높을 확률이 높습니다. 이런 이유로 Be Closer 도 설치를 하지 않는 것이 좋을 것 같습니다.

 

참고로 Google Play 앱 정보 및 스크린샷에 표기된 내용은 아래와 같습니다.

- 몇 시간마다 문자를 보내지 않음으로써 자녀가 안전한지 확인하십시오. GPS 가족 추적기가 당신을 도울 것입니다!

- 엄마나 아빠의 위치와 찾을 수 있는 위치 파악

0123
Be Closer Google Play ScreenShot

 

2.5 파인드 마이 키즈

2.5.1 개요

GEO TRACK TECHNOLOGIES INC 에서 2015년 6월 17일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 1000만회 이상 다운로드 됨.

 

2.5.2 특장점

자녀 및 감시대상에게는 Pingo by Findmykids 라는 앱을 설치해야 합니다. 얼마전에는 그냥 Pingo 였습니다. 위치추적 어플 본연의 목적과 기능과 무관해 보이는 자녀 폰의 앱 관리 등의 기능이 있습니다. 위치 추적기능은 무난한 편이고, 다른 위치 추적 앱에 비해서는 배터리 소모가 크지는 않지만 이 앱의 설치에 따른 배터 사용량의 변화가 조금 느껴질 정도 입니다. 아이가 전화등을 받지 않는 경우에' 아이 주변 소리 듣기' 라는 기능을 통해 말 그대로 주변의 소리를 들을 수 있습니다. 위치 추적과 관련된 이용요금은 월 4,700원, 연 37,600원, 아이주변소리듣기와 관련된 이용요금은 1,300원/30분, 7,500원/180분, 28,000원/무제한(1개월) 입니다. 결제와 관련된 민원이 Looka, mLite, Be Closer 와 같이 많지는 않습니다.

 

Google Play 앱 정보 및 스크린샷에 표기된 내용은 아래와 같습니다.

• GPS 위치추적 -지도에서 자녀의 위치와 하루 동안의 이동 기록을 확인해 보세요. 아이가 가야 할 곳에 있는지, 또 위험한 장소를 가지는 않았는지 실시간으로 체크할 수 있습니다.

• 주변 소리 자녀와 연락이 되지 않을 때 주변 소리를 들어서 혹시 해로운 환경에 있지 않은지 확인해 보세요.

• 보안 관리 - 자녀가 학교 잘 도착했는지 확인하세요. 학교에 도착했을 때, 집에 돌아 왔을 때, 또 설정만 하면 추가적인 활동들에 대한 알림을 받아 안심할 수 있습니다.

• 큰 알림소리 - 자녀가 당신의 전화 소리를 못 듣고 있다면 큰 알림음을 보내 보세요.

• 앱 관리 - 자녀가 어떤 앱을 사용했는지, 무슨 앱이 자녀를 방해하는지 확인해 보세요.

• 배터리 체크 - 자녀의 배터리를 체크하고 혹시 배터리가 부족할 때 충전하도록 알려 주세요.

• 가족 채팅 - 아이들에게 스티커를 보내서 칭찬해준다면 떨어져 있어도 아이가 행복해 할 거예요!

 

• 자녀에게 스마트폰이 있는 경우:

자녀의 스마트폰에 GPS 위치추적기 기능을 탑재한 Pingo! 을 설치하세요. Pingo! 앱을 통해 자녀가 당신과 대화할 수 있고, 위험한 상황이 찾아오면 비상 버튼을 누를 수 있습니다. 자녀가 비상 버튼을 누르는 즉시 당신의 스마트폰에 큰 알림이 울립니다.

 

• 자녀에게 GPS 워치가 있는 경우:

본 앱은 Smart Baby Watch, Smart Age Watch, Smart Pet Tracker, Smart Tracker 등 모든 종류의 워치에 적합합니다.

만약 어린이 GPS 워치 구입에 관심이 있으시다면, 우선 Q8, W10, Q360, DS18, G10, I8과 같은 기본 모델을 추천드립니다.

0123
FindMyKids Google Play ScreenShot

 

 

2.6 아이쉐어링

2.6.1 개요

iSharingSoft, inc. 에서 2012년 6월 16일에 출시한 앱. 2023년 8월 17일 현재 Google Play 제공 정보 기준으로 1000만회 이상 다운로드 됨.

 

 

2.6.2 특장점

매출 상위에 있는 위치추적 앱들이 대부분 외국에서 만든 앱인데, 미국에 법인을 만들고 하긴 했지만, 이 앱은 한국인 대표가 만든 앱으로 보입니다. 위치추적과 관련된 디테일한 기능들이 있습니다만 실용성 및 활용성은 조금 떨어지는 기능들입니다. 이용요금은 월 9,900원, 연간 86,000원 입니다. 몇 년 전에는 년간 99,000원이었었는데, 가격이 조금 내려가긴 했습니다만 꽤 비싼 수준입니다. 아이쉐어링 에는 주변소리듣기 기능이 없습니다.

 

Google Play 앱 정보 및 스크린샷은 아래와 같습니다.

 

앱 정보

 

1. 실시간 전세계 위치찾기 어플

■ 최신 GPS 엔진,기지국, Wifi를 기반으로 구글 지도 사용

■ 아이폰, 아이패드, 갤럭시 노트 등 다양한 핸드폰 찾기 가능!

 

2. SOS 긴급 호출 서비스 (안심귀가 서비스)

■ 스마트폰을 위아래로 3회 흔들면 가족들에게 알람 메시지 전송

위험에 처했을때, 신고해서 위치를 언제 알려주나요? 위급시 바로 내 위치와 긴급호출을 자동으로 전송

언제 어플을 실행해서 버튼을 누르나요!! 아이쉐어링은 흔들기만 해도 자동으로 긴급호출 전송!!

 

3. 장소 / 거리 알람

■ 집 또는 학교 도착 알람 통보

■ 근처에 들어오거나 나가는 사항 in/out 알람 통보

■ 미리 정해놓은 장소를 벗어날 경우 통보 알람

 

4. 무전기 스타일의 무료 음성통화

■ WiFi에서 완전무료, 음성 저장기능

 

5. 사생활 보호 설정

■ 3가지 이상의 모드로 개인별 설정 가능

 

6. 위치히스토리 (이동경로에 따른 이력. 일자별, 이동별 확인 가능)

 

7. 배터리 / 전원 세이브 기능

■ 배터리 절약 모드 선택가능

 

8. 핸드폰 분실 대비 위치찾기 (아이폰, 아이패드 지원)

■ 핸드폰 분실 대비 미리 설치해둔 아이쉐어링으로 정확한 위치찾기가 가능합니다.. 또한 근처에 오면 알람이 울리는 기능을 활용하여, 장소에서 벗어낫는지 확인하여 실시간으로 위치찾기 할 수 있으며 유심없이도 사용가능.

 

가족의 안전을 위해 아이쉐어링을 사용하세요.

01234
iSharing Google Play ScreenShot

 

 

 

2.7 내아이찾기

2.7.1 개요

ESENMOBILE 이라는 곳에서 출시한 앱입니다. 출시한지 얼마되지 않아 다운로드 건수는 얼마되지 않아 보입니다.

 

2.7.2 특장점

앱의 기능이나 내새우는 특장점이 명확해 보입니다. 위치추적 기능과 아이 주변 소리 듣기 기능. 두 핵심 기능을 Google Play 앱정보의 설명처럼 착한가격으로 제공해 주고 있습니다.

 

위치추적과 관련된 기능을 먼저 살펴보면 앱 실행하자마자 첫 화면에 내 자녀 및 친구로 등록된 이용자들의 위치 정보를 한 눈에 파악할 수 있습니다. 해당 정보를 클릭하면 가장 최근의 위치 정보 9개를 볼 수 있고, 날짜 검색 등을 통해 최대 1달 이내의 위치정보 목록을 확인할 수 있습니다.

 
01
위치추적 관련 기능 스크린샷
 

 

아이주변소리듣기 기능은 mLite, FindMyKids 등에도 있는 있는 기능이고, 그 앱들과 비슷한 수준으로 동작합니다. 초등학교 저학년의 아이들 같은 경우는 전화를 받는 행동 자체가 익숙하지 않아 스마트폰을 줘도 전화통화가 잘 안 되는 경우가 많습니다. 전화를 해도 전화를 받지 않을 때 이 이 기능을 쓰면 자동으로 수신처리가 되어 아이주변소리를 들을 수 있게 됩니다. 몇 번 써보니 굉장히 유용한 기능이고, 놀랍다는 생각이 드네요.

 

01
아이 주변 소리 듣기 관련 기능 스크린샷

 

 

이 앱도 유료인데 타 앱 대비 굉장히 저렴한 수준입니다. 위치추적기능과 관련된 금액은 2,990원/월, 19,900원/년 으로 리뷰한 앱들 중에 제일 저렴합니다.

아이주변소리듣기 기능과 관련된 금액은 990원/30분, 5,990원/180분 입니다. 일반 전화 통화와 똑같은 수준의 음성 통화가 되는 셈인데, 통신사들의 통화 요금에 비하면 꽤 저렴하단 생각이 듭니다.

비용이 타 앱 대비 굉장히 저렴한 수준이고 특히 Looka 앱과 비교하면 최대 92%나 저렴합니다. 타 앱은 무료이용기간이 보통 3일 ~ 7일인데 비해 이 앱은 14일로 꽤 긴편이고, 자동 결제나 결제와 관련하여 알림이나 압박 같은 건 따로 없어서 그런 부담감 없이 편하게 사용할 수 있어서 좋았던 것 같습니다.

 

Google Play 앱 정보 및 스크린샷은 아래와 같습니다.

01234

 

 

4. 총평 및 결론

4.1 총평

구분
Life360
Looka
mLite
Be Closer
Find My Kids
iSharing
내아이찾기
기능
위치추적
간단한 채팅
알림존 설정 및 알람
위치추적
간단한 채팅
위치추적
주변소리청취
긴급알림
알림존 설정 및 알람
앱 모니터링
위치추적
위치추적
주변소리듣기
알림존 설정 및 알람
간단한 채팅
앱 모니터링
위치추적
알림존 설정 및 알람
간단한 채팅
위치추적
아이주변소리듣기
주변소리듣기
없음
없음
있음
없음
있음
없음
있음
주변소리듣기요금
기능 없음
기능 없음
44,000/월
(이 요금제를 사용해야 기능 사용 가능)
기능 없음
1,300원/30분
7,500원/180분
기능 없음
990원/30분
5,990원/180분
무료
7일
3일
3일
3일
?
7일
14일
위치
추적
요금
5,500원/월
55,000원/년
39,600원/월
년간요금없음
20,000원/월
년간요금없음
28,000원/월
년간요금없음
4,700원/월
37,600원/년
9,900원/월
86,000원/년
 


많은 위치추적 어플들을 써 본 결과 대부분의 위치추적 어플들이 꽤 비쌌고, 배터리 소모도 많았고, 이 기능이 위치추적 어플에서 꼭 필요한 건가 싶은 생각이 드는 기능들도 많았습니다.

 

실제 설치 및 사용시 효용이 크게 다가오는 부분, 배터리 소모 부분을 많이 따져보았고 저 같은 경우는 자녀가 3명이다 보니 아무래도 비용적인 부분을 제일 유념하면서 최종 결정하게 되었습니다만 이런 가격적인 부분은 자녀가 1명이더라도 다들 중요하게 생각할 부분일 것 같습니다.

 

그 결과 저는 현재 최종적으로 아이들 폰 설정을 하면서 위치추적 본연의 기능과 꽤 유용하게 사용되었던 아이 주변소리듣기 기능이 있던 '내 아이 찾기' 앱을 설치해서 현재 사용중이고 불편함 없이 잘 사용하고 있습니다. 다른 앱들 처럼 자동 결제도 아니고, 결제하라는 압박도 따로 없고, 무료 이용기간도 타 앱 대비 꽤 넉넉해서 다른 앱들처럼 비용이나 나 모르게 비용이 결제될지도 모른다는 것에 대한 심리적 압박감이나 부담감 없이 편안한 마음으로 사용할 수 있어서 좋았던 것 같습니다.

 

특히 위치추적 기능과 함께 최근 위치추적 어플에서 중요하게 여겨지는 기능인 주변소리듣기 기능 까지 있는 앱 중에는 편의성과 경제성이 제일 좋은 것 같습니다.

 

4.2 결론

내 아이 찾기

 

 

 

* 설치 주소

앱 설치하기(내 아이 찾기, Google Play)

 

 

 

#위치추적앱 #위치추적 #위치추적어플 #위치추적어플무료 #위치추적기 #위치추적어플상대방위치추적

#위치추적앱#위치추적#위치추적어플#위치추적어플무료#위치추적기#위치추적어플상대방위치추적#위치추적앱추천#주변소리듣기#주변음성들리는위치추적#아이쉐어링소리듣기#위치추적주변소리듣기#위치추적 어플#위치추적어플

g 프로젝트를 진행할 때 다른 사람의 프로젝트 import 시 가끔 아래의 에러가 뜨는 경우가 있다.

내 경우에는 아래와 같은 방법으로 해결하였다.

Description Resource Path Location Type

cvc-id.3: A field of identity constraint 'web-app-filter-name-uniqueness' matched element 'web-app', but this element does not have a simple type. web.xml /test_project/src/main/webapp/WEB-INF line 41 Language Servers

Description Resource Path Location Type

cvc-id.3: A field of identity constraint 'web-app-servlet-name-uniqueness' matched element 'web-app', but this element does not have a simple type. web.xml /test_project/src/main/webapp/WEB-INF line 26 Language Servers

Description Resource Path Location Type

There are '37' errors in 'jsp_2_1.xsd'. web.xml /test_project/src/main/webapp/WEB-INF line 2 Language Servers

web.xml 파일의

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

metadata-complete="true">

부분을

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://JAVA.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

metadata-complete="true">

이렇게 변경해주면 해결 된다.

변경 전

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

변경 후

xsi:schemaLocation="http://JAVA.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

Spring 으로 구축된 서비스에 네이버 클라우드 플랫폼의 SMS API 를 이용해서 SMS 를 보내는 방법입니다. 

정식 서비스 명칭은 Simple & Easy Notification Service 이고 줄여서 SENS 라고 부릅니다. 

네이버 클라우드 플랫폼 메인 페이지 > 서비스 > Application Service > Simple & Easy Notification Service 에서 확인 가능합니다. ​

 

개인적으로 테스트 하고 싶으시면 네이버 클라우드 플랫폼에 가입하신 후

네이버 클라우드 플랫폼 메인 페이지 > 서비스 > Application Service > Simple & Eash Notification Service

에서 이용 신청 하신 후 프로젝트 생성 하셔야 이용 가능합니다. ​

 

개인 계정으로 가입해서 테스트 시에는 발송 전화번호를 기입해야 하고, 아래 코드의 sendSMS() 에서 발신번호 항목에 이용 신청한 전화번호를 넣어야 오류 없이 SMS 가 전송됩니다.

법인 계정으로 가입 및 SMS 발송 서비스 이용시에는 별도의 법인 인증 절차 등이 필요합니다. ​

 

제 소스코드에서 사용한 import 내용들입니다. 참고하시기 바랍니다.

signature 를 만들 때 Access Key, Secret Key, Service ID 가 필요한데, 아래 SMS API설명서에는 어디에 있는 내용들이라는 설명 따위는 없는 것 같습니다. 몇 번 찾아 봤는데 그런 설명 못 찾겟네요. ㅋ;;; ​

 

Access Key, Secret Key, Service ID 는 아래 경로에서 확인 가능합니다.

// Access Key : https://www.ncloud.com/mypage/manage/info > 인증키 관리 > Access Key ID 

// Secret Key : https://www.ncloud.com/mypage/manage/info > 인증키 관리 > Secret Key 

// Service ID : https://console.ncloud.com/sens/project > Simple & Easy Notification Service > Project > 서비스 ID ​ 

 

아래 코드에 있는 값들은 실제 제가 사용하는 값들에서 몇 몇 문자들을 다른 문자로 임의로 변경한 것입니다. 해당 Key 값의 길이나 형태 등 참고하시라고 비슷한 형태로 놔뒀습니다.

 

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

 

// https://api.ncloud-docs.com/docs/common-ncpapi
	private String makeSignature(String url, String timestamp, String method, String accessKey, String secretKey) throws NoSuchAlgorithmException, InvalidKeyException {
	    String space = " ";                    // one space
	    String newLine = "\n";                 // new line
	    

	    String message = new StringBuilder()
	        .append(method)
	        .append(space)
	        .append(url)
	        .append(newLine)
	        .append(timestamp)
	        .append(newLine)
	        .append(accessKey)
	        .toString();

	    SecretKeySpec signingKey;
	    String encodeBase64String;
		try {
			
			signingKey = new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA256");
			Mac mac = Mac.getInstance("HmacSHA256");
			mac.init(signingKey);
			byte[] rawHmac = mac.doFinal(message.getBytes("UTF-8"));
		    encodeBase64String = Base64.getEncoder().encodeToString(rawHmac);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			encodeBase64String = e.toString();
		}
	    

	  return encodeBase64String;
	}

 

	/*
	 * https://api.ncloud-docs.com/docs/ko/ai-application-service-sens-smsv2
		{
		    "type":"(SMS | LMS | MMS)",
		    "contentType":"(COMM | AD)",
		    "countryCode":"string",
		    "from":"string",
		    "subject":"string",
		    "content":"string",
		    "messages":[
		        {
		            "to":"string",
		            "subject":"string",
		            "content":"string"
		        }
		    ],
		    "files":[
		        {
		            "name":"string",
		            "body":"string"
		        }
		    ],
		    "reserveTime": "yyyy-MM-dd HH:mm",
		    "reserveTimeZone": "string",
		    "scheduleCode": "string"
		}
	 */
	private void sendSMS() {
		String hostNameUrl = "https://sens.apigw.ntruss.com";     		// 호스트 URL
		String requestUrl= "/sms/v2/services/";                   		// 요청 URL
		String requestUrlType = "/messages";                      		// 요청 URL
		String accessKey = "QWALu3XgiCxABC2aynAf";                     	// 네이버 클라우드 플랫폼 회원에게 발급되는 개인 인증키			// Access Key : https://www.ncloud.com/mypage/manage/info > 인증키 관리 > Access Key ID
		String secretKey = "bXGAcyQw1FG9Zjq6f1U8SD5CHMFVsvumivXoP194";  // 2차 인증을 위해 서비스마다 할당되는 service secret key	// Service Key : https://www.ncloud.com/mypage/manage/info > 인증키 관리 > Access Key ID	
		String serviceId = "ncp:sms:kr:178053617394:projectname";       // 프로젝트에 할당된 SMS 서비스 ID							// service ID : https://console.ncloud.com/sens/project > Simple & ... > Project > 서비스 ID
		String method = "POST";											// 요청 method
		String timestamp = Long.toString(System.currentTimeMillis()); 	// current timestamp (epoch)
		requestUrl += serviceId + requestUrlType;
		String apiUrl = hostNameUrl + requestUrl;
		
		// JSON 을 활용한 body data 생성
		JSONObject bodyJson = new JSONObject();
		JSONObject toJson = new JSONObject();
	    JSONArray  toArr = new JSONArray();

	    //toJson.put("subject","");							// Optional, messages.subject	개별 메시지 제목, LMS, MMS에서만 사용 가능
	    //toJson.put("content","sms test in spring 111");	// Optional, messages.content	개별 메시지 내용, SMS: 최대 80byte, LMS, MMS: 최대 2000byte
	    toJson.put("to","01012345678");						// Mandatory(필수), messages.to	수신번호, -를 제외한 숫자만 입력 가능
	    toArr.put(toJson);
	    
	    bodyJson.put("type","SMS");							// Madantory, 메시지 Type (SMS | LMS | MMS), (소문자 가능)
	    //bodyJson.put("contentType","");					// Optional, 메시지 내용 Type (AD | COMM) * AD: 광고용, COMM: 일반용 (default: COMM) * 광고용 메시지 발송 시 불법 스팸 방지를 위한 정보통신망법 (제 50조)가 적용됩니다.
	    //bodyJson.put("countryCode","82");					// Optional, 국가 전화번호, (default: 82)
	    bodyJson.put("from","01012345678");					// Mandatory, 발신번호, 사전 등록된 발신번호만 사용 가능		
	    //bodyJson.put("subject","");						// Optional, 기본 메시지 제목, LMS, MMS에서만 사용 가능
	    bodyJson.put("content","sms test in spring 222");	// Mandatory(필수), 기본 메시지 내용, SMS: 최대 80byte, LMS, MMS: 최대 2000byte
	    bodyJson.put("messages", toArr);					// Mandatory(필수), 아래 항목들 참조 (messages.XXX), 최대 1,000개
	    
	    //String body = bodyJson.toJSONString();
	    String body = bodyJson.toString();
	    
	    System.out.println(body);
	    
        try {
            URL url = new URL(apiUrl);

            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setUseCaches(false);
            con.setDoOutput(true);
            con.setDoInput(true);
            con.setRequestProperty("content-type", "application/json");
            con.setRequestProperty("x-ncp-apigw-timestamp", timestamp);
            con.setRequestProperty("x-ncp-iam-access-key", accessKey);
            con.setRequestProperty("x-ncp-apigw-signature-v2", makeSignature(requestUrl, timestamp, method, accessKey, secretKey));
            con.setRequestMethod(method);
            con.setDoOutput(true);
            DataOutputStream wr = new DataOutputStream(con.getOutputStream());
            
            wr.write(body.getBytes());
            wr.flush();
            wr.close();

            int responseCode = con.getResponseCode();
            BufferedReader br;
            System.out.println("responseCode" +" " + responseCode);
            if(responseCode == 202) { // 정상 호출
                br = new BufferedReader(new InputStreamReader(con.getInputStream()));
            } else { // 에러 발생
                br = new BufferedReader(new InputStreamReader(con.getErrorStream()));
            }

            String inputLine;
            StringBuffer response = new StringBuffer();
            while ((inputLine = br.readLine()) != null) {
                response.append(inputLine);
            }
            br.close();
            
            System.out.println(response.toString());

        } catch (Exception e) {
            System.out.println(e);
        }
	}

 

* Reference

-. 서비스 안내 : https://www.ncloud.com/product/applicationService/sens

-. 사용 가이드 : https://guide.ncloud-docs.com/docs/ko/sens-sens-1-1

-. SMS API 사용 설명서 : https://api.ncloud-docs.com/docs/ko/ai-application-service-sens

-. 인증키 생성 예제 : https://api.ncloud-docs.com/docs/common-ncpapi

-. SMS 요청 내용(Json) : https://api.ncloud-docs.com/docs/ko/ai-application-service-sens-smsv2

2021년 8월 2일부터 모든 신규 앱은 결제 라이브러리 버전 3 이상을 사용해야 합니다. 2021년 11월 1일까지 기존 앱의 모든 업데이트는 결제 라이브러리 버전 3 이상을 사용해야 합니다.

bemind.tistory.com/24 의 내용과 기본적인 흐름은 같습니다.

위 링크에서 작성된 내용을 기반으로 결제 라이브러리 버전 3의 내용을 적용하였습니다.

기존에 작성된 내용에서 int responseCode 는 BillingResult billingResult 으로 변경되었고,

그에 따라 구매 결과 확인을 billingResult.getResponseCode() 를 이용해 확인합니다.

구매 내역 처리시

ConsumeParams consumeParams =

ConsumeParams.newBuilder()

.setPurchaseToken(purchase.getPurchaseToken())

.build();

부분이 추가 되었습니다.

Reference : https://developer.android.com/google/play/billing/integrate#java

 

1. app 수준 build.gradle 에 dependencies 추가

dependencies {
    ...
    def billing_version = "3.0.0"
    implementation "com.android.billingclient:billing:$billing_version"
}

 

2. 소스코드
결제기능을 구현하고자 하는 화면에 아래 구문 추가.

public class BuyPointActivity extends AppCompatActivity implements PurchasesUpdatedListener {
    Context mContext;

    // create new Person
    private BillingClient mBillingClient;

    SkuDetails skuDetails700, skuDetails2100;
    String skuID700 = "point_700", skuID2100 = "point_2100";  //제품 ID
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_buy_point);
        mContext = BuyPointActivity.this;
        ...
        mBillingClient = BillingClient.newBuilder(mContext).setListener(this).build();
        mBillingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(BillingResult billingResult) {
                if (billingResult.getResponseCode() == BillingResponse.OK) {
                    // The billing client is ready. You can query purchases here.
                    List<String> skuList = new ArrayList<> ();
                    skuList.add(skuID700);
                    skuList.add(skuID2100);
                    SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
                    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
                    mBillingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
                        @Override
                        public void onSkuDetailsResponse(BillingResult billingResult, List<SkuDetails> skuDetailsList) {
                            // Process the result.
                            if (billingResult.getResponseCode() == BillingClient.BillingResponse.OK && skuDetailsList != null) {
                                for (SkuDetails skuDetails : skuDetailsList) {
                                    String sku = skuDetails.getSku();
                                    String price = skuDetails.getPrice();

                                    if(skuID700.equals(sku)) {
                                        skuDetails700 = skuDetails;
                                    } else if(skuID2100.equals(sku)) {
                                        skuDetails2100 = skuDetails;
                                    } 
                                }
                            }
                        }});
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
            }
        });
    }

    private void doBillingFlow(SkuDetails skuDetails) {
        BillingFlowParams flowParams;
        int responseCode;

        // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
        flowParams = BillingFlowParams.newBuilder().setSkuDetails(skuDetails).build();
        responseCode = mBillingClient.launchBillingFlow(BuyPointActivity.this, flowParams);
    }

    private void handlePurchase(Purchase purchase) {
        String purchaseToken;
        ConsumeParams consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build();
        mBillingClient.consumeAsync(consumeParams, consumeListener);
    }

    ConsumeResponseListener consumeListener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingClient.BillingResponse.OK) {
                // Handle the success of the consume operation.
                // For example, increase the number of coins inside the user's basket.
            }
        }
    };

    /**
     * Handle a callback that purchases were updated from the Billing library
     */
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        if (billingResult.getResponseCode() == BillingClient.BillingResponse.OK
                && purchases != null) {
            for (Purchase purchase : purchases) {
                handlePurchase(purchase);
            }
        } else if (billingResult.getResponseCode() == BillingClient.BillingResponse.USER_CANCELED) {
            // Handle an error caused by a user cancelling the purchase flow.
        } else {
            // Handle any other error codes.
        }
    }

    Button.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int id = v.getId();

            switch (id) {
                case R.id.btn1000:
                    doBillingFlow(skuDetails700);
                    break;
                ...
            }
        }
    };
    ...
}

 

 

비소비성 구매를 확인하려면 결제 라이브러리의 BillingClient.acknowledgePurchase() 또는 Google Play Developer API의 Product.Purchases.Acknowledge를 사용합니다. 구매를 확인하기 전에 Google Play 결제 라이브러리의 isAcknowledged() 메서드 또는 Google Developer API의 acknowledgementState 필드를 사용하여 앱에서 이미 구매를 확인했는지 검토해야 합니다.

 

다음 예는 Google Play 결제 라이브러리를 사용하여 구매를 확인하는 방법을 보여줍니다.

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

 

 

Google Play 결제 라이브러리 통합 테스트  |  Google Play 결제 시스템  |  Android Developers

알림: 2021년 11월 1일부터는 기존 앱의 모든 업데이트에도 결제 라이브러리 버전 3 이상이 요구됩니다. 자세히 알아보기 Google Play 결제 라이브러리 통합 테스트 개발 과정 전체에서 통합을 테스트

developer.android.com

 

 

등반 코스 : 성판악 탐방로 시작 > 백록담 > 관음사 탐방로 하산


등반 날짜 : 2021년 3월 23일 화요일


필수 준비물 : 
등산 스틱, 등산화, 아이젠, 옷(보온을 위한)
휴지, 물티슈, 물(냉수, 온수(보온병)), 김밥, 컵라면, 젖가락(김밥용 1개, 컵라면용 1개), 
초콜릿, 쓰레기 담아올 비닐, 컵라면 남은 국물 등을 담아올 페트 병


팁 :

- 성판악, 관음사 코스 모두 탐방로 입구 부터 백록담까지 매점이 없습니다. 물과 음식은 반드시 본인이 준비하여야 하고 도시락류는 반입 불가 입니다.
- 화장실은 있지만, 씻을 물은 없습니다. 물티슈 준비하시는게 좋습니다.
- 마실 물은 500ml 페트병으로 2통 준비하여 다 마신 후 빈 페트병에 컵라면 남은 국물 등을 잘 부어서 담아 오시면 편합니다.
- 성판악 코스에서 등산하실 때 정상이 가까워 오면(1900m 표지석이 보이면) 막판 스퍼트로 뛰어 가셔서 백록담 표지석 사진 찍는 줄을 서는게 좋을 것 같네요. 여기서 줄 서는데만 1시간 걸린...
- 성판악 입구 매점에서 김밥을 제외한 나머지 식사는 카드 결제 가능합니다.
김밥도 저는 3줄 사서 현금 다 드리고 왔는데, 옆분 계산할 때 하는 소리를 들으니 '5,000원은 카드 결제 하셔도 됩니다.' 하시네요. 5,000원만 카드 결제하고, 나머지는 현금결제 해달라는 말인지, 5,000원 이상 구매시에는 카드결제 가능하다는 말인진 모르겠지만, 5,000원 이상은 카드 결제 가능합니다. 그 이하는 카드 결제 안 해 주는 것 같아 보였습니다.


숙박 : 라마다 제주 시티홀


이동 : 대중교통
- 등산 : 6시 30분경 제주지방법원(아라방면) 에서 281번 버스 승차후 
7시경 성판악 입구에서 하차
- 하산 : 오후 6시경 관음사 탐방로 입구 앞에서 택시 승차하고 제주의료원 길 건너에서 하차 후 제주의료원(신천단 한국폴리텍 방면) 에서 281번 버스 승차후 
제주지방법원(광양방면) 에서 하차

 

 

2021년 3월 23일 화요일 성판악 탐방로에서 7시 20분 등산 시작하여 11시 50분 백록담 도착하고 오후 6시에 관음사 탐방로 입구로 하산 완료 하였습니다. 장모님을 모시고, 천천히 무리하지 않고, 등반 하였습니다. 

정상에 가까워질수록 성판악 코스, 관음사 코스 모두 눈과 얼음이 아직 녹지 않고 쌓여 있는 곳이 많습니다. 아이젠 챙겨 가는 것이 정신 건강, 육체 건강에 좋습니다. 제가 등산한 날은 날씨가 굉장히 좋고, 바람이 거의 불지 않은 날씨 였지만 역시 산이라 그런지 쉬고 있는데 바람이 불 때나, 정상에서는 꽤 춥습니다. 추위를 타시는 분이라면 보온에 신경을 써서 옷을 준비하는 것이 정신 건강, 육체 건강에 좋을 것 같습니다. 

저도 170cm 50kg 의 마른 체격에 추위를 많이 타는 편인데, 바지는 여름용 긴 등산 바지, 상의는 긴팔 등산복, 1 layer 바람막이류, 얇은 조끼 패딩 준비하니 벗었다 입었다 딱 적당하고 좋았습니다. 

성판악 코스, 관음사 코스 모두 거의 다 돌과 계단 길 인데, 관음사 코스가 돌과 계단이 훨씬 더 많아서 등산, 하산 모두 다 힘듭니다. 

성판악 코스, 관음사 코스 어느 곳을 선택하던지, 등산 초보이던지 고수이던지 3월 한라산 백록담 등산에는 등산화, 스틱, 아이젠 이 필수일 것 같습니다. 관음사 코스로 하산하는 길에 무릎 망가지신 분 여럿 보았습니다. 눈이 꽤 오래 있을 거 같은 느낌이 드는데, 눈이 녹으면 아이젠은 필요 없겠지만, 등산화, 등산 스틱은 한라산 등반시 무조건 꼭 챙기시는 것이 무릎에 좋습니다. 돌과 계단이 너~~~~ 무 많아요. 

관음사 코스는 탐방로가 능선을 따라 가다가 중간에 계곡으로 내려갔다가 다시 올라오는 길이 있는데, 계단이 꽤 가파릅니다. 

관음사 코스는 V자 계곡(계단)이 1회, 물이 약간 있는 개울물이 2회, 다리가 2회 있습니다. 백록담 직전의 계단은 그 경사가 매우 가파르고, 꽤 깁니다. 체력 안배 잘 하셔야 할 것 같습니다.

 

관음사 탐방로 백록담 직전의 계단입니다. 꽤 가파르고 깁니다. 성판악 코스는 여기에 비하면 아주 완만하네요.
이런 눈 길이 관음사 탐방로 정상 부근에 꽤 많이 있습니다. 성판악 코스에도 간간히 있긴 한데, 관음사 코스에 더 많습니다.
다리 입니다. 약간 출렁거립니다. 이 것 말고, 다리가 하나 더 있습니다. 관음사 탐방로에 총 2개 있습니다.
다리입니다. 관음사 탐방로에 총 2개 있습니다. 성판악 코스에는 다리가 없습니다.
관음사 탐방로에 V 자 계곡을 오르 내리는 계단길.
관음사 탐방로에 개울물을 지나가야 합니다. 총 2군데 있습니다. 성판악 코스에는 이런 길은 없습니다.
관음사 탐방로에 개울물을 지나가야 합니다. 총 2군데 있습니다. 성판악 코스에는 이런 길은 없습니다.

 


이번에 백록담을 찍었고, 백록담에 물이 있는 것도 보았으니 당분간 한라산을 등반할 생각이 없는데, 다시 한라산 백록담을 가게 된다면, 성판악으로 시작해서 성판악으로 내려올 생각입니다. 관음사 코스는 등산길이 전부 다 계단과 돌이라 할 수 있을 정도라 하산시 무릎에 무리도 많이 가고, 대중교통도 불편합니다. 

지금 시기는 백록담 가는 탐방로는 성판악 탐방로, 관음사 탐방로 모두 5시 30분 부터 입산 가능합니다. 차를 렌트하였지만 대중교통을 이용하였습니다. 라마다 호텔 바로 앞에서 281번, 181번 버스를 타면 성판악 탐방로 입구 바로 앞에서 하차할 수 있습니다. 281번 버스의 첫차가 181번 보다  조금 빠릅니다. 

 

 


저는 라마다 제주 시티홀 호텔 앞 제주지방법원(아라방면) 에서 281번 버스를 타고 성판악 탐방로로 이동하였습니다. 배차 간격이 12분 정도 되는 것 같고, 성판악 까지 소요 시간은 30분 내외로 걸리는 것 같습니다. 281번 버스 시간표가 해당 정류소에 붙어 있습니다. 사진 찍는다는게 깜박했네요. ㅜㅜㅜ

성판악 탐방로에서 시작해서 관음사 탐방로로 하산 하였는데, 관음사 탐방로는 버스 배차 간격이 꽤 길어서 제주의료원 앞 까지 택시를 타고 이동한 후 제주 의료원 앞에서 281번 버스를 타고 호텔로 돌아왔습니다. 제주 의료원 앞 도로가 차들이 많고, 내리막이라 길이 꽤 위험한데 신호등이 없어서 길 건너기가 위험하고, 힘듭니다. 택시 하차시 제주의료원 입구쪽에 세워 달라고 하는게 좋을 것 같습니다. 



성판악 탐방로 입구의 매점 입니다. 김밥 속재료 3개 밖에 없지만, 배고파서 그런지 맛있게 잘 먹었네욤.
저 표시된 시간이 저희 속도랑은 대충 맞더군요.
진달래밭 대피소서 정상으로 가는 길!!!
2시 전에 하산하라고 방송 계속 나옵니다. 백록담 정상에선 와이파이도 되긴 하는데, 사람 많아서 잘 터지지는 않는 것 같아요. 되다 안 되다 그래요.
백록담에서 성판악 관음사 로 가는 갈림길 안내 표시판
관음사 탐방로 소요 시간 안내
화장실에 라면 국물 절대 버리시면 안 됩니다!!!

+ Recent posts