본문 바로가기

MAUI Shell Navigation 파라미터 전달 실패 문제 해결, 구조적 원인 분석과 실전 대응 전략

@veedeeo 2025. 12. 12. 16:01

1. 서론 — Shell Navigation은 편하지만 파라미터 문제는 가장 많이 터진다

.NET MAUI Shell은 복잡한 페이지 구조를 단순하게 구성하고, URL 기반 탐색을 지원하며, Native Navigation과 통합되는 강력한 프레임워크다. 그러나 많은 개발자가 Shell Navigation을 사용하는 순간 겪게 되는 중요한 문제가 있다. 바로 Navigation 파라미터가 정상적으로 전달되지 않는 현상이다.

예를 들어 다음 코드를 사용하면:

await Shell.Current.GoToAsync(nameof(DetailPage), new Dictionary<string, object>
{
    { "Item", selectedItem }
});
 
 

DetailPage에서 다음과 같이 받으려고 하지만:

[QueryProperty(nameof(Item), "Item")]
public partial class DetailPage : ContentPage
{
    public MyItem Item { get; set; }
}
 
 

Item이 null로 전달되거나, 페이지 로드가 끝난 뒤에 값이 들어오거나, BindingContext가 갱신되지 않아 UI가 업데이트되지 않는 문제가 발생한다.

이 글에서는 Shell Navigation 파라미터가 실패하는 구조적 원인을 깊이 있게 분석하고, 실전 프로젝트에서 안정적으로 동작하도록 만드는 패턴을 안내한다.

 

MAUI Shell Navigation Diagram


2. 파라미터 전달 실패가 발생하는 핵심 원인 분석

원인 1: QueryProperty가 Navigation 시점보다 늦게 반영됨

Shell Navigation은 다음 순서로 동작한다.

  1. 페이지 인스턴스 생성
  2. BindingContext 연결
  3. Navigation 파라미터 전달
  4. QueryProperty 반영
  5. UI 렌더링

이때 개발자가 흔히 저지르는 실수는 생성자에서 이미 BindingContext를 사용하려고 하는 것이다.

예:

public DetailPage()
{
    InitializeComponent();
    TitleLabel.Text = Item.Title; // ❌ Item은 아직 null
}

 

 

QueryProperty는 생성자 실행 이후에 반영되므로
이 시점에서는 Item이 존재하지 않는다.


원인 2: 바인딩 타이밍 문제 — BindingContext는 이미 래핑된 값만 본다

많은 개발자가 DetailPage.xaml에 다음과 같은 바인딩을 만든다.

<Label Text="{Binding Item.Title}" />
 
 

하지만 DetailPage 생성 시점에는 Item이 null이다.
따라서 BindingContext는 null 상태를 기억하고 UI 갱신이 일어나지 않는다.

특히 INotifyPropertyChanged가 구현되지 않은 모델이라면
Item이 할당된 이후에도 UI가 갱신되지 않는다.


원인 3: Navigation 파라미터 이름 불일치

개발자가 가장 많이 놓치는 부분:

  • QueryProperty(nameof(Item), "Item")
  • Navigation Dictionary: { "item", selectedItem }

이런 작은 오탈자 차이 때문에 값이 전달되지 않는다.

Shell은 대소문자를 구분한다.
즉, "Item"과 "item(소문자)"는 전혀 다르다.


원인 4: 복잡한 객체 전달 시 Serialization 문제

다음과 같은 객체를 전달하면 정상이다.

  • int
  • string
  • Guid
  • 모델 클래스(MyItem)

하지만 다음은 문제가 발생한다.

  • ObservableCollection
  • Dictionary
  • 이벤트나 델리게이트 포함 객체
  • NavigationParameter 속성 내부에 또 다른 복합객체가 있는 경우

특히 JSON 직렬화가 필요한 구조에서는 Shell이 객체를 복사하지 못하고 null을 전달하는 문제까지 발생한다.


원인 5: Shell Navigation이 뒤에서 페이지 인스턴스를 재사용하는 경우

Shell은 기본적으로 “페이지 캐싱” 전략을 사용한다.
따라서 DetailPage가 이미 NavigationStack에 존재하면
새 인스턴스를 만들지 않고 기존 인스턴스를 재사용할 수 있다.

결과:

  • 이전에 전달된 파라미터가 남아 있음
  • 새 파라미터가 적용되지 않음
  • UI 업데이트가 일어나지 않음

이 문제가 실제로 매우 흔하다.


3. 실전에서 가장 자주 발생하는 문제 코드 패턴 3가지

❌ 문제 코드 1 — 생성자에서 Item 사용

public DetailPage() { InitializeComponent(); TitleLabel.Text = Item.Title; // ❌ 아직 null }
 

❌ 문제 코드 2 — QueryProperty 이름 불일치

[QueryProperty(nameof(Item), "item")] // 소문자
 
 
await Shell.Current.GoToAsync(nameof(DetailPage), new Dictionary<string, object> { { "Item", model } // 대문자 });
 

❌ 문제 코드 3 — BindingContext 교체 타이밍 엉킴

BindingContext = this; // QueryProperty 이후에도 UI 갱신 안됨
 
 
Shell Navigation Conflict Diagram

4. Shell Navigation 파라미터를 안정적으로 전달하는 정석 패턴

✔ 정석 1: QueryProperty는 Page 속성 + INotifyPropertyChanged 조합 사용

[QueryProperty(nameof(Item), "Item")]
public partial class DetailPage : ContentPage, INotifyPropertyChanged
{
    private MyItem _item;

    public MyItem Item
    {
        get => _item;
        set
        {
            _item = value;
            OnPropertyChanged(); // UI 갱신
        }
    }
}
 
 

많은 개발자가 놓치는 부분이 이것이다.

QueryProperty가 속성 setter를 호출하는 시점은 생성자 이후이므로
UI 업데이트를 위해 반드시 OnPropertyChanged가 필요하다.


✔ 정석 2: BindingContext = this 사용은 괜찮지만 타이밍을 조정해야 한다

DetailPage 생성자에서는 다음 코드만 둔다.

BindingContext = this;
 
 

그리고 Item이 설정되면 자동으로 UI가 갱신된다.


✔ 정석 3: Navigation 파라미터 이름은 반드시 동일해야 한다

[QueryProperty(nameof(Item), "Item")]
 
 

await Shell.Current.GoToAsync(nameof(DetailPage), new Dictionary<string, object>
{
    { "Item", selectedItem }
});

 


대소문자 포함 100% 일치해야 한다.


✔ 정석 4: Navigation 파라미터 전달 안정화 패턴

항목 전체를 넘기는 것이 가장 안전하다.

await Shell.Current.GoToAsync($"{nameof(DetailPage)}",
    new Dictionary<string, object>
    {
        ["Item"] = selectedItem
    });
 

✔ 정석 5: 페이지 캐싱 방지

Shell은 다음 페이지로 이동할 때 기존 페이지 인스턴스를 재사용할 수 있다.

재사용을 방지하려면
Navigation 전에 UI 값을 초기화해야 한다.

protected override void OnAppearing() { base.OnAppearing(); Item = null; // 새로 채워질 준비 }
 
 

또는 페이지 재사용을 아예 차단할 수 있다.


5. 마무리 — Navigation 구조를 이해하면 복잡한 UI 흐름도 안정적으로 설계할 수 있다

Shell Navigation 파라미터 전달 실패 문제는
단순 오탈자나 잘못된 바인딩 때문이 아니라
MAUI Shell의 내부 동작 순서와 MVVM 구조 특성 때문에 발생한다.

핵심 요점은 다음과 같다.

  • 생성자에서 파라미터는 항상 null이다
  • BindingContext는 QueryProperty 이후 값 변화를 감지해야 한다
  • INotifyPropertyChanged가 필수이다
  • Navigation Dictionary와 QueryProperty 이름은 반드시 일치해야 한다
  • Shell은 페이지를 재사용할 수 있으므로 캐싱 문제를 고려해야 한다

이 구조를 이해하는 순간
파라미터 전달 문제뿐 아니라
복잡한 Navigation 구조를 가진 앱에서도
UI 흐름이 안정적으로 설계된다.

veedeeo
@veedeeo :: .net MAUI·Netlify·SEO·웹 최적화 기술 블로그

.net MAUI·Netlify·SEO·웹 최적화 기술 블로그

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차