현재 위치 기반 미세먼지·측정소·날씨 정보를 제공하는 생활형 앱 만들기
이번 글에서는 미세먼지어때 앱을 개발하면서 적용한 주요 기능과 개발 방법을 정리해보려고 합니다.
미세먼지어때는 사용자의 현재 위치를 기준으로 미세먼지 정보를 조회하고, 가까운 측정소를 확인하며, 오늘날씨와 기상청 일기예보까지 함께 볼 수 있도록 만든 생활 편의 앱입니다.
앱의 기본 방향은 단순합니다.
현재 위치 확인
→ 가까운 측정소 조회
→ 미세먼지 정보 표시
→ 측정소 지도 확인
→ 오늘날씨 확인
→ 기상청 일기예보 확인
하나의 앱에서 대기질과 날씨 정보를 함께 확인할 수 있도록 구성한 것이 핵심입니다.
1. 앱 개발 목적
미세먼지는 일상생활과 밀접한 정보입니다.
외출 전이나 출근 전, 산책이나 운동 전에는 미세먼지 상태와 날씨를 함께 확인하는 경우가 많습니다.
기존에는 미세먼지 앱, 날씨 앱, 지도 앱을 따로 확인해야 하는 경우가 많았습니다. 그래서 미세먼지어때는 다음과 같은 목적을 가지고 개발했습니다.
현재 위치 기준 미세먼지 정보 확인
가까운 측정소 정보 확인
측정소 위치 지도 확인
오늘 날씨 확인
기상청 일기예보 확인
즉, 미세먼지와 날씨 정보를 한 앱에서 쉽게 확인할 수 있도록 하는 것이 목표였습니다.
2. 개발 환경
미세먼지어때 앱은 C# .NET MAUI 기반으로 개발했습니다.
.NET MAUI는 하나의 코드베이스로 Android, iOS, Windows 등 여러 플랫폼 앱을 만들 수 있는 프레임워크입니다. 이번 앱은 Android 앱을 중심으로 개발했습니다.
개발 환경은 다음과 같습니다.
개발 언어: C#
프레임워크: .NET MAUI
대상 플랫폼: Android
개발 도구: Visual Studio
지도 표시: WebView + VWorld 지도
날씨 정보: 기상청 단기예보 API, 기상청 날씨누리 WebView
미세먼지 정보: 공공데이터 기반 대기질 정보
UI 구성: XAML
앱 화면은 XAML로 구성하고, 기능 로직은 C# 코드비하인드에서 처리했습니다.
3. 전체 화면 구성
이번 개편에서는 하단 탭 구조를 적용했습니다.
하나의 화면에 모든 기능을 넣으면 화면이 복잡해지고 사용자가 원하는 정보를 찾기 어렵기 때문에, 기능별로 탭을 분리했습니다.
구성은 다음과 같습니다.
홈
측정소
오늘날씨
일기예보
각 탭의 역할은 다음과 같습니다.
| 홈 | 현재 위치 기준 미세먼지 정보 표시 |
| 측정소 | 가까운 측정소를 지도에 표시 |
| 오늘날씨 | 오늘 날씨와 시간대별 날씨 표시 |
| 일기예보 | 기상청 날씨누리 WebView 표시 |
하단 탭을 사용하면 사용자가 원하는 기능으로 바로 이동할 수 있어 앱 사용성이 좋아집니다.
4. 홈 화면 개발
홈 화면은 미세먼지어때의 기본 화면입니다.
현재 위치를 기준으로 미세먼지 정보를 조회하고, 가까운 측정소의 대기질 정보를 보여줍니다.
홈 화면에서 표시하는 주요 정보는 다음과 같습니다.
현재 위치
측정소명
미세먼지 PM10
초미세먼지 PM2.5
대기질 등급
측정 시간
미세먼지 정보는 숫자만 표시하면 사용자가 직관적으로 이해하기 어렵기 때문에, 등급과 색상을 함께 표시하는 방식이 좋습니다.
예를 들면 다음과 같습니다.
좋음
보통
나쁨
매우나쁨
이런 등급 표현을 통해 사용자는 숫자를 자세히 몰라도 현재 대기질 상태를 쉽게 이해할 수 있습니다.
5. 현재 위치 조회 기능
현재 위치 기반 앱에서 가장 중요한 기능은 GPS 위치 조회입니다.
.NET MAUI에서는 Geolocation 기능을 사용해 현재 위치를 가져올 수 있습니다.
기본 흐름은 다음과 같습니다.
위치 권한 확인
→ GPS 위치 요청
→ 위도/경도 수신
→ 해당 좌표 기준 데이터 조회
예시 구조는 다음과 같습니다.
var request = new GeolocationRequest(
GeolocationAccuracy.Medium,
TimeSpan.FromSeconds(10));
var location = await Geolocation.Default.GetLocationAsync(request);
if (location != null)
{
double lat = location.Latitude;
double lon = location.Longitude;
}
위치 조회가 실패할 수도 있기 때문에 기본 위치를 하나 정해두는 것이 좋습니다.
예를 들어 창원 기준 기본 좌표를 설정해두면 GPS가 실패해도 앱이 완전히 멈추지 않습니다.
private double _lat = 35.227;
private double _lon = 128.681;
이렇게 하면 위치 권한이 없거나 에뮬레이터 GPS가 이상한 경우에도 기본 위치 기준으로 데이터를 표시할 수 있습니다.
6. 가까운 측정소 조회
미세먼지 정보는 보통 측정소 기준으로 제공됩니다.
따라서 사용자의 현재 위치에서 가까운 측정소를 찾는 기능이 필요합니다.
기본 흐름은 다음과 같습니다.
전체 측정소 목록 확보
→ 사용자 현재 위도/경도 확인
→ 각 측정소와의 거리 계산
→ 가까운 순서로 정렬
→ 상위 N개 표시
초기에는 측정소를 20개까지 표시했지만, 지도 화면이 복잡해질 수 있어 10개, 이후 5개로 조정했습니다.
현재 구조에서는 가까운 측정소를 5개만 표시하는 방식이 적절합니다.
가까운 측정소 5개 표시
현재 위치 표시
측정소 위치 지도 표시
측정소가 너무 많이 표시되면 지도 가독성이 떨어지고, 사용자가 어떤 측정소를 선택해야 할지 헷갈릴 수 있습니다.
7. 거리 계산 방식
현재 위치와 측정소 간 거리는 위도/경도 기반으로 계산합니다.
일반적으로 Haversine 공식을 사용합니다.
개념은 다음과 같습니다.
현재 위치 위도/경도
측정소 위도/경도
→ 두 지점 사이 거리 계산
→ km 단위 거리 산출
거리 계산 결과를 기준으로 가까운 측정소를 정렬합니다.
앱에서는 사용자에게 꼭 km 단위를 표시하지 않아도 됩니다. 화면이 작을 경우 숫자 중심으로 간결하게 표시하는 것이 더 보기 좋을 수 있습니다.
8. 측정소 지도 화면 개발
측정소 탭에서는 지도에 현재 위치와 측정소 위치를 표시합니다.
지도는 WebView를 활용해 구현했습니다.
구성은 다음과 같습니다.
상단: 지역 입력 또는 현재 위치 조회
중앙: 지도 WebView
지도 위: 현재 위치 마커
지도 위: 측정소 마커
측정소 마커를 클릭하면 해당 측정소 기준으로 홈 화면에 미세먼지 정보를 표시하도록 구성할 수 있습니다.
동작 흐름은 다음과 같습니다.
측정소 탭 이동
→ 현재 위치 조회
→ 가까운 측정소 5개 조회
→ 지도에 현재 위치와 측정소 표시
→ 측정소 클릭
→ 선택 측정소 기준 홈 화면 표시
지도에 표시되는 항목이 너무 많으면 복잡해 보이기 때문에 가까운 측정소 5개 정도가 적당합니다.
9. VWorld 지도 활용
측정소 지도는 VWorld 지도 기반으로 구성할 수 있습니다.
VWorld를 사용하면 국내 위치 기반 서비스에 적합한 지도를 앱 안에서 표시할 수 있습니다.
MAUI 앱에서는 WebView에 HTML과 JavaScript를 로드하여 지도를 표시하는 방식이 많이 사용됩니다.
구성 방식은 다음과 같습니다.
C#에서 측정소 데이터 준비
→ HTML 문자열 생성
→ JavaScript에 좌표 전달
→ WebView에서 지도 표시
→ 마커 클릭 이벤트 처리
주의할 점은 C# 문자열 안에 JavaScript를 넣을 때 { }, $, 백틱 문자가 충돌할 수 있다는 점입니다.
처음에는 C# 원시 문자열을 사용하다가 JavaScript 중괄호 때문에 오류가 발생할 수 있습니다. 이 경우 StringBuilder 방식으로 HTML을 생성하는 것이 더 안정적입니다.
10. 오늘날씨 화면 개발
오늘날씨 화면은 기상청 단기예보 API를 기준으로 구성했습니다.
현재는 오늘 날씨만 표시하도록 정리했습니다.
화면 구성은 다음과 같습니다.
상단: 오늘 날씨 요약
하단: 시간대별 날씨
상단 오늘 날씨 요약에는 다음 정보를 표시합니다.
날짜
오전 날씨
오후 날씨
최고 / 최저 온도
강수확률
예시는 다음과 같습니다.
5/20(수)
오전 비
오후 비
20 / 19 ℃
오전 60% · 1mm 미만 / 오후 60% · 1.0mm
하단 시간대별 날씨는 현재 시간 이후 오늘 데이터만 표시합니다.
09:00
10:00
11:00
12:00
...
처음에는 오늘/내일/모레까지 표시했지만, 오늘날씨 화면에서는 오늘 정보에 집중하는 것이 더 자연스러워 오늘만 표시하도록 변경했습니다.
11. 기상청 단기예보 API 활용
오늘날씨는 기상청 단기예보 API를 사용합니다.
사용하는 API는 다음과 같습니다.
VilageFcstInfoService_2.0
getVilageFcst
기상청 단기예보는 위도/경도를 직접 받지 않고, 기상청 격자 좌표인 nx, ny를 사용합니다.
따라서 앱에서는 다음 변환 과정이 필요합니다.
현재 위치 위도/경도
→ 기상청 격자 좌표 nx, ny 변환
→ getVilageFcst 호출
→ 예보 데이터 파싱
주요 예보 항목은 다음과 같습니다.
| TMP | 1시간 기온 |
| TMX | 일 최고기온 |
| TMN | 일 최저기온 |
| SKY | 하늘상태 |
| PTY | 강수형태 |
| POP | 강수확률 |
| PCP | 강수량 |
| REH | 습도 |
| WSD | 풍속 |
이 데이터를 조합해 오늘 날씨와 시간대별 날씨를 구성합니다.
12. 위도/경도 → 기상청 격자 변환
기상청 API는 일반적인 위도/경도가 아니라 격자 좌표를 사용합니다.
따라서 변환 함수가 필요합니다.
흐름은 다음과 같습니다.
위도 lat
경도 lon
→ DFS 격자 변환
→ nx, ny 산출
이 변환이 제대로 되지 않으면 다른 지역의 날씨가 표시될 수 있습니다.
특히 GPS가 에뮬레이터에서 미국 위치로 잡히는 경우가 있으므로, 테스트할 때는 위치를 한국 좌표로 설정하거나 기본 좌표를 지정하는 것이 좋습니다.
13. 시간 기준 처리
날씨 앱에서 중요한 부분 중 하나는 시간 기준입니다.
Android 에뮬레이터가 미국 시간대로 동작하면 오늘 날짜가 어제로 표시될 수 있습니다.
이를 방지하기 위해 앱에서는 한국시간 KST 기준을 적용했습니다.
DateTime.Now 직접 사용
→ 에뮬레이터 시간대 영향 가능
UTC 기준 + 9시간
또는 Asia/Seoul TimeZone 적용
→ 한국 날짜 기준 유지
기본 구조는 다음과 같습니다.
private static DateTime GetKoreaNow()
{
try
{
var tz = TimeZoneInfo.FindSystemTimeZoneById("Asia/Seoul");
return TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tz).DateTime;
}
catch
{
return DateTime.UtcNow.AddHours(9);
}
}
이렇게 하면 에뮬레이터나 기기 시간대가 달라도 앱 내부에서는 한국시간 기준으로 오늘 날짜를 계산할 수 있습니다.
14. 일기예보 탭 개발
일기예보 탭은 자체적으로 중기예보 데이터를 조합하는 방식 대신, 기상청 날씨누리를 WebView로 표시하는 방식으로 변경했습니다.
기상청 공식 사이트는 다음과 같습니다.
https://www.weather.go.kr/
앱에서는 WebView를 통해 해당 사이트를 표시합니다.
구성 기능은 다음과 같습니다.
기상청 날씨누리 표시
뒤로가기
새로고침
홈 이동
외부 브라우저 열기
로딩 표시
WebView 방식의 장점은 기상청 공식 화면을 그대로 볼 수 있다는 점입니다.
기상청의 예보 정보는 지역, 단기예보, 중기예보, 특보 등 다양한 정보를 포함하고 있기 때문에, 앱에서 모든 정보를 직접 재구성하는 것보다 공식 사이트를 연결하는 방식이 더 안정적일 수 있습니다.
15. WebView 화면 구현
일기예보 화면은 다음 구조로 구성할 수 있습니다.
<WebView x:Name="webForecast" />
C# 코드에서는 다음처럼 주소를 로드합니다.
private const string WeatherUrl = "https://www.weather.go.kr/";
private void LoadWeatherPage()
{
webForecast.Source = WeatherUrl;
}
외부 브라우저 열기도 함께 제공하면 좋습니다.
await Launcher.OpenAsync("https://www.weather.go.kr/");
일부 사이트는 WebView에서 새 창, 팝업, 메뉴 동작이 제한될 수 있기 때문에 외부 브라우저 열기 버튼을 넣어두는 것이 사용자 입장에서 편리합니다.
16. 하단 탭 아이콘 문제 해결
하단 탭에는 아이콘을 적용했습니다.
초기에는 PNG 아이콘을 사용했는데, 투명 배경이 제대로 처리되지 않아 네모 박스처럼 보이는 문제가 있었습니다.
원인은 MAUI Shell의 아이콘 처리 방식입니다.
PNG 이미지에 배경 픽셀이 조금이라도 남아 있으면, 탭 아이콘에서 그 영역이 통째로 칠해질 수 있습니다.
이를 해결하기 위해 SVG 아이콘으로 변경했습니다.
tab_home.png
→ eair_tab_home.svg
SVG 파일명도 기존 PNG와 겹치지 않도록 다르게 지정해야 합니다.
eair_tab_home.svg
eair_tab_station.svg
eair_tab_today.svg
eair_tab_forecast.svg
MAUI에서는 확장자가 달라도 출력 이름이 같으면 중복 오류가 발생할 수 있습니다.
예를 들어 다음 두 파일은 문제가 될 수 있습니다.
tab_home.png
tab_home.svg
그래서 이름 자체를 다르게 지정하는 것이 안전합니다.
17. 화면 여백과 글자 잘림 개선
모바일 앱에서는 사용자가 글자 크기를 크게 설정하거나, 화면 확대 기능을 켜는 경우가 많습니다.
이 경우 고정 높이 화면에서는 글자가 잘리거나 겹칠 수 있습니다.
이번 앱에서는 여러 화면에서 다음 요소를 조정했습니다.
RowSpacing
HeightRequest
Padding
Margin
ColumnDefinitions
FontSize
LineBreakMode
예를 들어 오늘날씨 화면에서 첫 번째 블록과 시간대별 블록 사이가 너무 넓게 보이면 다음 값을 줄일 수 있습니다.
<Grid RowDefinitions="Auto,*"
Padding="14"
RowSpacing="0">
상단 카드 높이는 다음처럼 조정할 수 있습니다.
<CollectionView
x:Name="listToday"
HeightRequest="105">
이런 방식으로 화면을 실제 기기에서 확인하며 계속 조정하는 것이 중요합니다.
18. 오늘날씨 화면 레이아웃 조정
오늘날씨 화면은 여러 번 조정했습니다.
처음에는 오늘/내일/모레를 표시했지만, 화면이 복잡해지고 정보가 많아져 최종적으로 오늘 정보만 표시하도록 정리했습니다.
현재 구조는 다음과 같습니다.
오늘 날씨 요약
시간대별 날씨
시간대별 날씨는 오늘 시간만 표시합니다.
09:00
10:00
11:00
12:00
상단 날짜는 다음처럼 표시합니다.
5/20(수)
시간대별 시간은 날짜를 붙이지 않고 시간만 표시합니다.
10:00
화면을 단순하게 만들수록 사용자가 빠르게 정보를 확인할 수 있습니다.
19. 탭 선택 시 재조회
오늘날씨 화면은 탭을 선택할 때마다 현재 위치 기준으로 다시 조회하도록 했습니다.
MAUI에서는 OnAppearing()을 활용할 수 있습니다.
protected override async void OnAppearing()
{
base.OnAppearing();
await LoadByCurrentLocationAsync();
}
이렇게 하면 사용자가 다른 탭으로 이동했다가 다시 오늘날씨 탭으로 돌아올 때 현재 위치 기준으로 데이터를 다시 조회합니다.
이동 중 사용하는 앱에서는 이런 자동 갱신 구조가 유용합니다.
20. Android 권한 설정
위치 정보와 인터넷을 사용하기 위해 Android 권한 설정이 필요합니다.
AndroidManifest.xml에는 다음 권한이 필요합니다.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
WebView나 공공데이터 API 호출을 위해 인터넷 권한은 필수입니다.
21. API 키 관리
공공데이터 API와 VWorld 지도 API를 사용하려면 인증키가 필요합니다.
앱에서는 AppConfig.cs 같은 파일에 키를 모아 관리하는 방식이 편합니다.
예시는 다음과 같습니다.
public static class AppConfig
{
public const string KmaServiceKey = "기상청_API_키";
public const string VWorldApiKey = "VWorld_API_키";
}
운영 앱에서는 API 키 노출을 최소화하는 구조가 필요합니다.
다만 개인용 또는 테스트 앱에서는 설정 파일 방식으로 관리하면 개발이 편합니다.
22. 앱 빌드와 배포
Android 앱으로 배포하려면 Release 빌드와 서명이 필요합니다.
일반적인 MAUI Android 빌드 명령은 다음과 같습니다.
dotnet publish -f net9.0-android -c Release
Google Play에 올리려면 보통 AAB 형식으로 빌드합니다.
dotnet publish -f net9.0-android -c Release ^
-p:AndroidPackageFormats=aab
서명 키를 적용하려면 다음과 같은 옵션을 사용합니다.
-p:AndroidKeyStore=true
-p:AndroidSigningKeyStore=파일명.keystore
-p:AndroidSigningStorePass=비밀번호
-p:AndroidSigningKeyAlias=별칭
-p:AndroidSigningKeyPass=비밀번호
Google Play Console에 새 버전을 올릴 때는 ApplicationVersion 값을 이전보다 크게 올려야 합니다.
23. 개발하면서 주의할 점
미세먼지어때 같은 위치 기반 정보 앱을 개발할 때는 다음 부분을 주의해야 합니다.
GPS 위치 권한 처리
에뮬레이터 위치 오류
한국시간 기준 처리
API 응답 실패 처리
측정소 데이터 누락 처리
화면 글자 잘림
WebView 사이트 동작 제한
API 키 관리
Google Play 버전 코드 관리
특히 에뮬레이터에서는 GPS가 미국으로 잡히는 경우가 있으므로, 실제 테스트는 한국 위치로 설정하거나 실제 기기에서 확인하는 것이 좋습니다.
24. 최종 앱 구조
최종적으로 미세먼지어때 앱은 다음 구조로 정리할 수 있습니다.
AppShell
└─ 하단 탭
├─ 홈
│ └─ 미세먼지 정보
├─ 측정소
│ └─ 측정소 지도
├─ 오늘날씨
│ └─ 오늘 요약 + 시간대별 날씨
└─ 일기예보
└─ 기상청 날씨누리 WebView
기능은 단순하지만 사용자가 자주 확인하는 정보를 탭으로 분리해 접근성을 높였습니다.
마무리
미세먼지어때 앱은 현재 위치를 기준으로 미세먼지와 날씨 정보를 쉽게 확인하기 위해 개발한 생활형 앱입니다.
이번 개발에서는 단순히 미세먼지 수치만 보여주는 것이 아니라, 측정소 지도와 오늘날씨, 기상청 일기예보까지 연결해 실생활에서 더 유용하게 사용할 수 있도록 구성했습니다.
특히 하단 탭 구조를 적용해 화면을 기능별로 분리했고, 오늘날씨 화면은 현재 시간 이후의 오늘 날씨만 간결하게 표시하도록 조정했습니다.
앞으로는 관심 지역 저장, 미세먼지 나쁨 알림, 측정소 즐겨찾기, 지도 기반 대기질 시각화 같은 기능을 추가하면 더 완성도 높은 생활 정보 앱으로 발전시킬 수 있을 것입니다.
'조그만 기술로 세상을 이롭게 > 미세먼지어때' 카테고리의 다른 글
| 미세먼지어때 앱 개편 - SQLite 제거 후 CSV 방식으로 (0) | 2026.05.23 |
|---|---|
| 기상청 API를 이용한 일기예보 앱 개발 방법 (3) | 2026.05.21 |
| 미세먼지어때 앱 개편 안내 (0) | 2026.05.20 |
| 미세먼지란? (0) | 2026.05.19 |
| 기상청 단기예보 요소 간단 설명 (0) | 2026.05.15 |