본문 바로가기
조그만 기술로 세상을 이롭게/미세먼지어때

기상청 API를 이용한 일기예보 앱 개발 방법

by eplus 2026. 5. 21.

현재 위치 기반 오늘날씨와 10일 예보 구현하기

스마트폰 앱에서 날씨 정보를 제공하려면 단순히 “날씨 API 하나를 호출한다”는 수준으로는 부족합니다. 특히 우리나라 기상청 API는 예보 종류에 따라 조회 기준이 다릅니다.

예를 들어 오늘날씨나 시간대별 예보는 현재 위치의 위도/경도를 기상청 격자 좌표인 nx, ny로 변환해서 조회해야 합니다. 반면 5일 이후의 중기예보는 위도/경도가 아니라 예보구역코드 regId 기준으로 조회해야 합니다.

이번 글에서는 C# MAUI 앱에서 기상청 API를 이용해 다음 기능을 구현하는 방법을 정리합니다.

1. 현재 위치 기반 오늘날씨 조회
2. 시간대별 단기예보 조회
3. 10일 일기예보 화면 구성
4. 단기예보 + 중기예보 병합
5. SQLite를 이용한 지역/예보코드 관리
6. 큰 글씨 설정 폰에서도 보기 좋은 UI 구성
 

1. 기상청 API 구성 이해하기

기상청에서 제공하는 예보 API는 크게 다음과 같이 나눌 수 있습니다.

1) 초단기실황

현재에 가까운 기상 상태를 조회하는 API입니다.

주요 정보는 다음과 같습니다.

기온
습도
풍향
풍속
강수형태
1시간 강수량
 

앱에서 “오늘날씨” 화면을 만들 때 가장 기본이 되는 API입니다.

예를 들어 현재 기온, 습도, 바람, 강수 상태를 보여줄 때 사용합니다.


2) 초단기예보

현재 시점부터 몇 시간 이내의 짧은 예보를 제공합니다.

주로 오늘 몇 시간 뒤의 날씨 흐름을 간단히 보여줄 때 사용할 수 있습니다.

기온
하늘상태
강수형태
습도
풍속
낙뢰
 

3) 단기예보

일기예보 앱에서 가장 많이 사용하는 API입니다.

단기예보는 다음 정보를 제공합니다.

TMP : 1시간 기온
TMN : 일 최저기온
TMX : 일 최고기온
SKY : 하늘상태
PTY : 강수형태
POP : 강수확률
PCP : 1시간 강수량
REH : 습도
WSD : 풍속
VEC : 풍향
 

단기예보는 현재 위치의 위도/경도를 바로 넣는 방식이 아닙니다.

반드시 위도/경도를 기상청 격자 좌표로 변환해야 합니다.

위도/경도
   ↓
기상청 격자 변환
   ↓
nx, ny
   ↓
단기예보 API 호출
 

4) 중기예보

중기예보는 4일 또는 5일 이후부터 10일 정도까지의 예보를 제공합니다.

중기예보는 단기예보와 다르게 nx, ny를 사용하지 않습니다.

대신 다음과 같은 예보구역코드를 사용합니다.

중기육상예보 regId
중기기온예보 regId
 

예를 들어 창원 기준으로는 다음과 같은 구성이 가능합니다.

중기육상예보 : 부산/울산/경남 권역 코드
중기기온예보 : 창원 지역 코드
 

즉 10일 예보를 만들려면 단기예보와 중기예보를 함께 사용해야 합니다.


2. 전체 개발 구조

기상청 API를 이용한 일기예보 앱은 다음 구조가 가장 안정적입니다.

현재 위치 확인
   ↓
위도/경도 획득
   ↓
단기예보용 nx, ny 산출
   ↓
중기예보용 regId 조회
   ↓
단기예보 API 호출
   ↓
중기육상예보 API 호출
   ↓
중기기온예보 API 호출
   ↓
날짜별 데이터 병합
   ↓
화면 표시
 

앱 내부 구조는 다음과 같이 나눌 수 있습니다.

Models
 ├─ Weather10DayItem.cs
 ├─ KmaShortGridEntity.cs
 └─ KmaMidRegionEntity.cs

Services
 ├─ KmaWeatherService.cs
 ├─ KmaTenDayForecastService.cs
 ├─ KmaGridConverter.cs
 └─ WeatherLocationHelper.cs

Database
 └─ KmaForecastDbService.cs

Pages
 ├─ TodayWeatherPage.xaml
 └─ ForecastPage.xaml
 

3. 현재 위치 가져오기

C# MAUI에서는 Geolocation을 이용해 현재 위치를 가져올 수 있습니다.

 
Location? location = await Geolocation.Default.GetLocationAsync(
    new GeolocationRequest
    {
        DesiredAccuracy = GeolocationAccuracy.Medium,
        Timeout = TimeSpan.FromSeconds(10)
    });
 

다만 실제 앱에서는 GPS가 꺼져 있거나 위치 권한이 거부될 수 있습니다.

따라서 다음 처리가 필요합니다.

1. 위치 권한 확인
2. 위치 권한 요청
3. GPS 활성 여부 확인
4. 위치 조회 실패 시 기본 위치 사용
 

예를 들어 창원시청 부근을 기본 위치로 둘 수 있습니다.

 
private double _lat = 35.227;
private double _lon = 128.681;
 

위치 조회에 실패해도 앱이 멈추지 않고 기본 지역의 날씨를 보여주는 방식이 사용자 경험 측면에서 좋습니다.


4. 위도/경도를 기상청 격자로 변환하기

단기예보는 위도/경도를 직접 받지 않습니다.

요청 파라미터는 다음과 같습니다.

base_date
base_time
nx
ny
 

따라서 현재 위치의 위도/경도를 기상청 격자인 nx, ny로 변환해야 합니다.

구현 방식은 두 가지입니다.

방법 1. 기상청 격자 변환 공식 사용

앱 내부에 KmaGridConverter 클래스를 만들어 위도/경도를 직접 변환합니다.

 
var grid = KmaGridConverter.ToGrid(lat, lon);

int nx = grid.nx;
int ny = grid.ny;
 

이 방식은 별도 DB가 없어도 되므로 단순합니다.

방법 2. SQLite에 격자 정보를 저장

전국 읍면동 또는 주요 지역의 격자 정보를 SQLite에 저장해두고, 현재 위치와 가장 가까운 격자를 찾는 방식입니다.

현재 위치
   ↓
SQLite 격자 테이블 조회
   ↓
가장 가까운 nx, ny 선택
 

예시 테이블은 다음과 같습니다.

 
CREATE TABLE KMA_SHORT_GRID (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    Sido TEXT,
    Sigungu TEXT,
    Dong TEXT,
    Lat REAL,
    Lon REAL,
    Nx INTEGER,
    Ny INTEGER
);
 

앱 초기 실행 시 기본 지역 데이터를 넣어둘 수 있습니다.

 
await _db.CreateTableAsync<KmaShortGridEntity>();
await SeedAsync();
 

5. 단기예보 API 호출하기

단기예보 API URL은 다음 형태입니다.

https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst
 

요청 파라미터는 다음과 같습니다.

serviceKey : 공공데이터포털 인증키
numOfRows  : 조회 건수
pageNo     : 페이지 번호
dataType   : JSON 또는 XML
base_date  : 발표일자
base_time  : 발표시각
nx         : 기상청 X 격자
ny         : 기상청 Y 격자
 

C# 호출 예시는 다음과 같습니다.

 
string url =
    $"{ShortUrl}?serviceKey={key}" +
    "&numOfRows=2000&pageNo=1&dataType=JSON" +
    $"&base_date={baseDate}&base_time={baseTime}" +
    $"&nx={nx}&ny={ny}";

string json = await _http.GetStringAsync(url);
 

단기예보는 하루 여러 번 발표됩니다.

주요 발표시각은 다음과 같습니다.

0200
0500
0800
1100
1400
1700
2000
2300
 

API는 발표시각 직후 바로 안정적으로 조회되지 않을 수 있으므로, 앱에서는 현재 시각에서 약간 여유를 두고 최근 발표시각을 선택하는 것이 좋습니다.

 
private static (string date, string time) GetVilageBaseDateTimeKst()
{
    DateTime now = GetKoreaNow().AddMinutes(-15);

    int[] baseHours =
    {
        2, 5, 8, 11, 14, 17, 20, 23
    };

    int hour = baseHours.LastOrDefault(h => h <= now.Hour);

    DateTime baseTime = hour == 0
        ? new DateTime(now.Year, now.Month, now.Day, 23, 0, 0).AddDays(-1)
        : new DateTime(now.Year, now.Month, now.Day, hour, 0, 0);

    return (
        baseTime.ToString("yyyyMMdd"),
        baseTime.ToString("HHmm")
    );
}
 

6. 단기예보 데이터 해석하기

단기예보 응답은 항목별로 여러 행이 내려옵니다.

예를 들어 같은 날짜, 같은 시간에 대해 다음 항목들이 각각 따로 내려옵니다.

TMP
SKY
PTY
POP
PCP
REH
WSD
 

따라서 앱에서는 fcstDate, fcstTime, category를 기준으로 데이터를 묶어야 합니다.

 
private sealed class KmaForecastValue
{
    public string Category { get; set; } = "";
    public string FcstDate { get; set; } = "";
    public string FcstTime { get; set; } = "";
    public string FcstValue { get; set; } = "";
}
 

하늘상태는 다음처럼 변환합니다.

 
private static string ToSkyText(string value)
{
    return value switch
    {
        "1" => "맑음",
        "3" => "구름많음",
        "4" => "흐림",
        _ => "날씨정보"
    };
}
 

강수형태는 다음처럼 변환합니다.

 
private static string ToPtyText(string value)
{
    return value switch
    {
        "0" => "없음",
        "1" => "비",
        "2" => "비/눈",
        "3" => "눈",
        "4" => "소나기",
        _ => "없음"
    };
}
 

최종 날씨 문구는 강수형태를 우선 적용하는 것이 좋습니다.

 
private static string ToWeatherText(string? sky, string? pty)
{
    return pty switch
    {
        "1" => "비",
        "2" => "비/눈",
        "3" => "눈",
        "4" => "소나기",
        _ => sky switch
        {
            "1" => "맑음",
            "3" => "구름많음",
            "4" => "흐림",
            _ => "날씨정보"
        }
    };
}
 

7. 오늘날씨 화면 만들기

오늘날씨 화면은 크게 두 영역으로 구성하면 좋습니다.

1. 오늘 요약
2. 시간대별 날씨
 

오늘 요약에는 다음 정보를 보여줍니다.

오전 날씨
오후 날씨
최고/최저기온
강수확률
강수량
 

시간대별 날씨에는 다음 정보를 보여줍니다.

시간
날씨
기온
강수확률
습도
바람
 

화면 구성 예시는 다음과 같습니다.

 
<CollectionView x:Name="listHourly"
                SelectionMode="None">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Border BackgroundColor="White"
                    Stroke="#E6EEF8"
                    StrokeThickness="1"
                    StrokeShape="RoundRectangle 16"
                    Padding="11"
                    Margin="0,0,0,8">

                <Grid ColumnDefinitions="58,*"
                      ColumnSpacing="8">

                    <Label Text="{Binding TimeText}"
                           FontSize="13"
                           FontAttributes="Bold" />

                    <VerticalStackLayout Grid.Column="1"
                                         Spacing="5">

                        <Label Text="{Binding WeatherText}"
                               FontSize="15"
                               FontAttributes="Bold" />

                        <Label Text="{Binding TemperatureText}"
                               FontSize="15"
                               FontAttributes="Bold"
                               TextColor="#DC2626" />

                        <Label Text="{Binding RainText}"
                               FontSize="12"
                               TextColor="#64748B" />

                        <Label Text="{Binding HumidityText}"
                               FontSize="12"
                               TextColor="#64748B" />

                        <Label Text="{Binding WindText}"
                               FontSize="12"
                               TextColor="#64748B" />
                    </VerticalStackLayout>
                </Grid>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
 

큰 글씨 설정을 고려하면 가로로 너무 많은 정보를 배치하는 것보다 세로형 구성이 안전합니다.


8. 중기예보 API 구성

10일 예보를 만들려면 중기예보를 함께 사용해야 합니다.

중기예보에서 주로 사용하는 API는 두 가지입니다.

getMidLandFcst : 중기육상예보
getMidTa       : 중기기온예보
 

중기육상예보는 날씨와 강수확률을 제공합니다.

wf5Am
wf5Pm
rnSt5Am
rnSt5Pm
wf6Am
wf6Pm
rnSt6Am
rnSt6Pm
...
wf10
rnSt10
 

중기기온예보는 최저/최고기온을 제공합니다.

taMin5
taMax5
taMin6
taMax6
...
taMin10
taMax10
 

중기예보 호출 예시는 다음과 같습니다.

 
string landUrl =
    $"{MidLandUrl}?serviceKey={key}" +
    "&numOfRows=10&pageNo=1&dataType=JSON" +
    $"&regId={landRegId}&tmFc={tmFc}";

string tempUrl =
    $"{MidTempUrl}?serviceKey={key}" +
    "&numOfRows=10&pageNo=1&dataType=JSON" +
    $"&regId={tempRegId}&tmFc={tmFc}";
 

9. 중기예보 발표시각 처리

중기예보는 보통 하루 2회 발표 기준으로 처리합니다.

0600
1800
 

앱에서는 현재 시각 기준으로 최근 발표시각을 자동 선택해야 합니다.

 
private static string GetMidTmFcKst()
{
    DateTime now = GetKoreaNow().AddMinutes(-30);
    DateTime tm;

    if (now.Hour >= 18)
        tm = new DateTime(now.Year, now.Month, now.Day, 18, 0, 0);
    else if (now.Hour >= 6)
        tm = new DateTime(now.Year, now.Month, now.Day, 6, 0, 0);
    else
        tm = new DateTime(now.Year, now.Month, now.Day, 18, 0, 0).AddDays(-1);

    return tm.ToString("yyyyMMddHHmm");
}
 

여기서 AddMinutes(-30)을 적용한 이유는 발표 직후 API 반영 지연을 고려하기 위해서입니다.


10. 단기예보와 중기예보 병합 기준

10일 예보를 만들 때 가장 중요한 부분은 병합 기준입니다.

처음에는 단기예보를 5일까지 사용하는 방식도 가능하지만, 실제로는 마지막 날짜의 기온 정보가 부족하거나 최저/최고기온이 동일하게 보이는 경우가 발생할 수 있습니다.

따라서 앱에서는 다음 기준을 추천합니다.

오늘 포함 4일차까지 : 단기예보 사용
5일차부터 10일차까지 : 중기예보 사용
 

예를 들어 오늘이 5월 21일이라면 다음과 같습니다.

5/21 : 단기예보
5/22 : 단기예보
5/23 : 단기예보
5/24 : 단기예보
5/25 : 중기예보
5/26 : 중기예보
...
 

병합 코드는 다음과 같이 구성할 수 있습니다.

 
private static List<Weather10DayItem> MergeForecast(
    List<Weather10DayItem> shortItems,
    List<Weather10DayItem> midItems)
{
    DateTime today = GetKoreaNow().Date;
    var result = new List<Weather10DayItem>();

    for (int offset = 0; offset < 10; offset++)
    {
        DateTime date = today.AddDays(offset);

        var s = shortItems.FirstOrDefault(x => x.Date.Date == date);
        var m = midItems.FirstOrDefault(x => x.Date.Date == date);

        if (offset <= 3 && s != null)
        {
            result.Add(s);
        }
        else if (m != null)
        {
            result.Add(m);
        }
        else if (s != null)
        {
            result.Add(s);
        }
    }

    return result.OrderBy(x => x.Date).ToList();
}
 

11. SQLite로 예보 지역코드 관리하기

기상청 중기예보는 위도/경도 기준이 아니라 예보구역코드 기준입니다.

따라서 앱 내부에 지역별 예보코드를 관리하는 구조가 필요합니다.

예시 테이블은 다음과 같습니다.

 
CREATE TABLE KMA_MID_REGION (
    Id INTEGER PRIMARY KEY AUTOINCREMENT,
    Sido TEXT,
    Sigungu TEXT,
    Lat REAL,
    Lon REAL,
    LandRegId TEXT,
    TempRegId TEXT
);
 

C# 모델은 다음과 같이 만들 수 있습니다.

 
public class KmaMidRegionEntity
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }

    public string Sido { get; set; } = "";
    public string Sigungu { get; set; } = "";

    public double Lat { get; set; }
    public double Lon { get; set; }

    public string LandRegId { get; set; } = "";
    public string TempRegId { get; set; } = "";
}
 

앱에서는 현재 위치와 가장 가까운 지역코드를 찾습니다.

 
var mid = await _db.GetNearestMidRegionAsync(lat, lon);
 

이렇게 하면 GPS 위치를 기준으로 적절한 중기예보 코드를 자동으로 선택할 수 있습니다.


12. SQLite 초기화 주의사항

모바일 앱에서 SQLite를 사용할 때 흔히 발생하는 오류가 있습니다.

no such table
 

이 오류는 대부분 다음 원인에서 발생합니다.

1. 테이블 생성 전에 조회함
2. 이전 버전 DB가 폰에 남아 있음
3. 앱 업데이트 후 DB 구조가 바뀌었지만 재생성하지 않음
 

초기화 코드는 반드시 테이블 생성 후 데이터를 넣도록 구성해야 합니다.

 
public async Task InitializeAsync()
{
    if (_initialized)
        return;

    string dbPath = Path.Combine(
        FileSystem.AppDataDirectory,
        "kma_forecast.db3");

    _db = new SQLiteAsyncConnection(dbPath);

    await _db.CreateTableAsync<KmaShortGridEntity>();
    await _db.CreateTableAsync<KmaMidRegionEntity>();

    await SeedAsync();

    _initialized = true;
}
 

그리고 no such table 오류가 발생하면 DB를 삭제 후 재생성하는 복구 로직을 넣는 것이 좋습니다.

 
private async Task ResetDatabaseAsync()
{
    string dbPath = Path.Combine(
        FileSystem.AppDataDirectory,
        "kma_forecast.db3");

    if (_db != null)
        await _db.CloseAsync();

    if (File.Exists(dbPath))
        File.Delete(dbPath);

    _initialized = false;
    await InitializeAsync();
}
 

13. 10일 예보 화면 구성

10일 예보 화면은 너무 많은 정보를 한 줄에 넣으면 가독성이 떨어집니다.

특히 Android에서 글자 크기를 크게 설정한 경우, 고정 높이 카드나 가로 배치가 쉽게 깨집니다.

추천 구성은 다음과 같습니다.

날짜
날씨 아이콘
대표 날씨
최고/최저기온
강수확률
오전 날씨
오후 날씨
 

XAML 예시는 다음과 같습니다.

 
<CollectionView x:Name="listForecast"
                SelectionMode="None">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Border BackgroundColor="White"
                    Stroke="#E5EAF0"
                    StrokeThickness="1"
                    StrokeShape="RoundRectangle 18"
                    Padding="11"
                    Margin="0,0,0,8">

                <Grid ColumnDefinitions="58,*"
                      ColumnSpacing="9">

                    <VerticalStackLayout Spacing="5">
                        <Label Text="{Binding DateText}"
                               FontSize="13"
                               FontAttributes="Bold"
                               LineBreakMode="WordWrap"
                               MaxLines="2" />

                        <Image Source="{Binding WeatherIcon}"
                               WidthRequest="34"
                               HeightRequest="34" />
                    </VerticalStackLayout>

                    <VerticalStackLayout Grid.Column="1"
                                         Spacing="6">

                        <Label Text="{Binding WeatherText}"
                               FontSize="16"
                               FontAttributes="Bold"
                               LineBreakMode="WordWrap"
                               MaxLines="2" />

                        <Label Text="{Binding TemperatureText}"
                               FontSize="13"
                               FontAttributes="Bold"
                               TextColor="#DC2626"
                               LineBreakMode="WordWrap" />

                        <Label Text="{Binding RainText}"
                               FontSize="12"
                               TextColor="#475569"
                               LineBreakMode="WordWrap"
                               MaxLines="3" />

                        <Label Text="{Binding WeatherTextAm, StringFormat='오전 {0}'}"
                               FontSize="12"
                               TextColor="#64748B"
                               LineBreakMode="WordWrap" />

                        <Label Text="{Binding WeatherTextPm, StringFormat='오후 {0}'}"
                               FontSize="12"
                               TextColor="#64748B"
                               LineBreakMode="WordWrap" />
                    </VerticalStackLayout>
                </Grid>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
 

14. 재조회 버튼 구성

날씨 앱에서는 사용자가 직접 새로고침할 수 있어야 합니다.

다만 화면을 아래로 당겨서 새로고침하는 방식은 사용자가 알기 어렵거나, 스크롤 동작과 충돌할 수 있습니다.

그래서 타이틀 오른쪽에 재조회 버튼을 두는 방식이 더 직관적입니다.

 
<Shell.TitleView>
    <Grid ColumnDefinitions="*,Auto">
        <Label Text="일기예보"
               FontSize="17"
               FontAttributes="Bold" />

        <Button x:Name="btnTitleRefresh"
                Grid.Column="1"
                Text="↻"
                WidthRequest="38"
                HeightRequest="38"
                CornerRadius="19"
                BackgroundColor="#2563EB"
                TextColor="White"
                Clicked="btnRefresh_Clicked" />
    </Grid>
</Shell.TitleView>
 

코드비하인드에서는 중복 조회를 막아야 합니다.

 
private bool _isRefreshing;

private async void btnRefresh_Clicked(object sender, EventArgs e)
{
    if (_isRefreshing)
        return;

    try
    {
        _isRefreshing = true;
        btnTitleRefresh.IsEnabled = false;
        btnTitleRefresh.Text = "…";

        await LoadForecastAsync();
    }
    finally
    {
        btnTitleRefresh.Text = "↻";
        btnTitleRefresh.IsEnabled = true;
        _isRefreshing = false;
    }
}
 

15. API 인증키 처리 주의사항

기상청 API를 사용할 때 가장 자주 발생하는 오류는 인증키 문제입니다.

대표적인 오류는 다음과 같습니다.

403 Forbidden
인증키 오류
활용신청 미승인
서비스별 승인 누락
Encoding/Decoding 키 혼용
 

공공데이터포털에서는 일반적으로 다음 두 가지 키를 제공합니다.

Encoding 인증키
Decoding 인증키
 

C# 코드에서 이미 Encoding된 인증키를 다시 인코딩하면 % 문자가 %25로 바뀌어 인증 오류가 발생할 수 있습니다.

따라서 다음 둘 중 하나로 통일하는 것이 좋습니다.

 
// Encoding 키를 그대로 사용할 때
string key = AppConfig.KmaServiceKey;
 

또는

 
// Decoding 키를 사용할 때
string key = Uri.EscapeDataString(AppConfig.KmaServiceKey);
 

중요한 것은 중복 인코딩을 피하는 것입니다.


16. Android 설정

기상청 API를 http://로 호출하는 경우 Android에서 cleartext 통신이 차단될 수 있습니다.

가능하면 https:// 주소를 사용하는 것이 좋습니다.

그래도 HTTP를 사용해야 한다면 AndroidManifest.xml에 다음 설정이 필요합니다.

 
<application
    android:allowBackup="true"
    android:supportsRtl="true"
    android:usesCleartextTraffic="true" />
 

또한 위치 기반 앱이므로 다음 권한이 필요합니다.

 
<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" />
 

17. 큰 글씨 설정 폰 대응

실제 앱을 폰에 설치해보면 개발 PC 화면과 다르게 보이는 경우가 많습니다.

특히 Android에서 글자 크기를 크게 설정하면 다음 문제가 생깁니다.

버튼 글자 잘림
카드 높이 부족
가로 배치 깨짐
온도/강수확률 잘림
목록 항목 일부 미표시
 

이를 피하려면 다음 기준을 적용하는 것이 좋습니다.

1. 고정 높이를 최소화한다.
2. 긴 문구는 WordWrap을 허용한다.
3. 한 줄에 너무 많은 정보를 넣지 않는다.
4. 버튼은 최소 높이 38~44 이상 확보한다.
5. CollectionView 항목은 세로형으로 구성한다.
6. FontAutoScalingEnabled를 필요한 곳에만 제한한다.
 

예를 들어 버튼은 다음처럼 구성합니다.

 
<Button Text="↻"
        WidthRequest="38"
        HeightRequest="38"
        MinimumWidthRequest="38"
        MinimumHeightRequest="38"
        FontAutoScalingEnabled="False" />
 

정보 표시 Label은 다음처럼 줄바꿈을 허용합니다.

 
<Label Text="{Binding RainText}"
       LineBreakMode="WordWrap"
       MaxLines="3" />
 

18. 앱 완성 구조

최종적으로 앱은 다음과 같은 구조가 됩니다.

오늘날씨
 ├─ 현재 위치 기준 초단기실황
 ├─ 기온/습도/풍향/풍속/강수 표시
 └─ 시간대별 날씨 표시

일기예보
 ├─ 단기예보 4일
 ├─ 중기예보 5~10일
 ├─ SQLite 지역코드 관리
 └─ 10일 예보 카드 표시

공통
 ├─ 타이틀 재조회 버튼
 ├─ 위치 권한 처리
 ├─ API 오류 처리
 ├─ 큰 글씨 화면 대응
 └─ 앱 업데이트 후 SQLite 자동 복구
 

19. 개발하면서 느낀 점

기상청 API는 공공데이터이기 때문에 비용 부담 없이 사용할 수 있다는 장점이 있습니다. 하지만 API 종류가 여러 개이고, 단기예보와 중기예보의 조회 기준이 다르기 때문에 처음에는 구조를 잡는 데 시간이 걸립니다.

특히 다음 부분이 중요합니다.

단기예보는 nx, ny 격자 기준
중기예보는 regId 기준
단기와 중기를 날짜별로 병합해야 함
발표시각을 정확히 계산해야 함
인증키 인코딩 문제를 주의해야 함
 

이 구조만 잘 잡으면 현재 위치 기반 오늘날씨, 시간대별 예보, 10일 예보까지 안정적으로 구현할 수 있습니다.


마무리

기상청 API를 이용한 일기예보 앱은 단순한 API 호출 앱이 아니라, 위치 정보, 격자 변환, 예보구역코드, 발표시각, 데이터 병합, 모바일 UI까지 함께 고려해야 하는 앱입니다.

이번 개발 방식의 핵심은 다음과 같습니다.

현재 위치 기반 조회
단기예보와 중기예보 병합
SQLite 기반 지역코드 관리
큰 글씨 설정 대응 UI
타이틀 재조회 방식 적용
 

이 구조를 활용하면 미세먼지 앱, 교통 앱, 지도 앱, 생활정보 앱 등 다양한 위치 기반 서비스에 날씨 기능을 자연스럽게 추가할 수 있습니다.

728x90
반응형