이론적인 부분들을 공부하면서 본격적으로 안드로이드를 이용하여 앱을 만들고 있다. 앱을 만들다 보면 기기의 위치가 필요한 경우가 많을것 같아서 위치를 기반으로 상세 주소를 가져오는 방법을 정리해 보려고 한다.
우선 permission 을 추가해 주어야 한다. 두 권한 모두 위치 권한을 요청하기 위한 권한으로, ACCESS_COARSE_LOCATION은 대략적인 위치 , ACCESS_FINE_LOCATION은 더 자세한 위치를 요청하는 권한이다.
권한을 추가 했다면 다음은 라이브러리를 추가해야 한다. Google Play 서비스의 위치 관련 API를 사용하기 위한 라이브러리이다.
implementation ("com.google.android.gms:play-services-location:21.0.1")
우선 전체적인 소스 코드로, 아래에서 기능별로 조금더 자세히 정리해보겠다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationTextView: TextView
private lateinit var getLocationButton: Button
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val fineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true
val coarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true
if (fineLocationGranted || coarseLocationGranted) {
checkLocationSettings()
} else {
locationTextView.text = "위치 권한이 필요합니다."
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
locationTextView = binding.locationTextView
getLocationButton = binding.getLocationButton
getLocationButton.setOnClickListener {
if (hasLocationPermissions()) {
checkLocationSettings()
} else {
requestPermissionLauncher.launch(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
)
}
}
}
private fun hasLocationPermissions(): Boolean {
val fineLocationPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
val coarseLocationPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
return fineLocationPermission && coarseLocationPermission
}
private fun checkLocationSettings() {
val locationMode = Settings.Secure.getInt(
contentResolver,
Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_OFF
)
if (locationMode == Settings.Secure.LOCATION_MODE_OFF) {
locationTextView.text = "위치 서비스가 비활성화되어 있습니다. 위치 서비스를 활성화해주세요."
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
} else {
getCurrentLocation()
}
}
private fun getCurrentLocation() {
if (!hasLocationPermissions()) {
locationTextView.text = "위치 권한이 없습니다."
return
}
val locationRequest = LocationRequest.create().apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
val location = locationResult.lastLocation
if (location != null) {
val latitude = location.latitude
val longitude = location.longitude
locationTextView.text = "위도: $latitude, 경도: $longitude"
// Convert coordinates to address
convertCoordinatesToAddress(latitude, longitude)
} else {
locationTextView.text = "위치를 가져올 수 없습니다."
}
}
}
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, mainLooper)
.addOnFailureListener { e ->
locationTextView.text = "위치 요청 실패: ${e.message}"
}
}
private fun convertCoordinatesToAddress(latitude: Double, longitude: Double) {
val geocoder = Geocoder(this)
try {
val addresses = geocoder.getFromLocation(latitude, longitude, 1)
if (addresses != null && addresses.isNotEmpty()) {
val address = addresses[0]
val addressText = buildString {
append("주소: ${address.getAddressLine(0)}\n")
}
locationTextView.text = addressText
} else {
locationTextView.text = "주소를 가져올 수 없습니다."
}
} catch (e: Exception) {
Log.e("GeocoderError", "주소 변환 실패: ${e.message}")
locationTextView.text = "주소 변환 실패"
}
}
}
앱의 권한을 처리하는 함수로 앱의 위치 서비스가 비활성화 되어 있다면 설정화면으로 이동하여 위치 서비스를 활성화 하는 함수 이다.
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val fineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true
val coarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true
if (fineLocationGranted || coarseLocationGranted) {
checkLocationSettings()
} else {
locationTextView.text = "위치 권한이 필요합니다."
}
}
private fun hasLocationPermissions(): Boolean {
val fineLocationPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
val coarseLocationPermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
return fineLocationPermission && coarseLocationPermission
}
private fun checkLocationSettings() {
val locationMode = Settings.Secure.getInt(
contentResolver,
Settings.Secure.LOCATION_MODE,
Settings.Secure.LOCATION_MODE_OFF
)
if (locationMode == Settings.Secure.LOCATION_MODE_OFF) {
locationTextView.text = "위치 서비스가 비활성화되어 있습니다. 위치 서비스를 활성화해주세요."
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
} else {
getCurrentLocation()
}
}
이제 본격적으로 앱의 위도,경도를 구하는 함수이다. 위치서비스가 활성화 되어 있다면, Google Play 서비스의 FusedLocationProviderClient를 위치 요청을 수행하여 현재 위치에 데이터를 수신하는 콜백함수이다.
private fun getCurrentLocation() {
if (!hasLocationPermissions()) {
locationTextView.text = "위치 권한이 없습니다."
return
}
val locationRequest = LocationRequest.create().apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
locationResult ?: return
val location = locationResult.lastLocation
if (location != null) {
val latitude = location.latitude
val longitude = location.longitude
locationTextView.text = "위도: $latitude, 경도: $longitude"
// Convert coordinates to address
convertCoordinatesToAddress(latitude, longitude)
} else {
locationTextView.text = "위치를 가져올 수 없습니다."
}
}
}
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
return
}
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, mainLooper)
.addOnFailureListener { e ->
locationTextView.text = "위치 요청 실패: ${e.message}"
}
}
위치에 대한 정보(위도,경도)를 기반으로 Geocoder 클래스를 사용하여 실제 주소로 변환하여 화면에 보여주는 함수 이다.
private fun convertCoordinatesToAddress(latitude: Double, longitude: Double) {
val geocoder = Geocoder(this)
try {
val addresses = geocoder.getFromLocation(latitude, longitude, 1)
if (addresses != null && addresses.isNotEmpty()) {
val address = addresses[0]
val addressText = buildString {
append("주소: ${address.getAddressLine(0)}\n")
}
locationTextView.text = addressText
} else {
locationTextView.text = "주소를 가져올 수 없습니다."
}
} catch (e: Exception) {
Log.e("GeocoderError", "주소 변환 실패: ${e.message}")
locationTextView.text = "주소 변환 실패"
}
}
실행 화면
이렇게 현재 내기기의 위치를 받아오는 과정에 대해 알아보았다. 끝 !