Flutter에서 Deep Links: 초보자를 위한 완벽 가이드 (타사 패키지 없이) (파트 1)

발행: (2026년 4월 5일 AM 03:59 GMT+9)
10 분 소요
원문: Dev.to

Source: Dev.to

Imagine isso: seu usuário recebe um link de desconto, clica nele — e BOOM! Ele não só abre seu app, mas já está na tela de checkout com o cupom aplicado.
Isso é mágica? Não — são Deep Links!

Objetivo da série

  • Implementar deep links em Flutter sem utilizar pacotes prontos.
  • Aprender como tudo funciona por baixo dos panos.

이 기사에서 배울 내용

  • 딥 링크가 무엇인지.
  • App LinksCustom Schemes의 차이점.
  • Flutter에서 기본 구조를 설정하는 방법.

기본 개념

Deep links는 사용자를 브라우저가 아니라 앱의 특정 화면으로 바로 이동시키는 URL입니다.

실제 예시

전통적인 흐름딥 링크
1️⃣ 브라우저 열기 →
2️⃣ 사용자가 앱 설치 →
3️⃣ 앱 열기 →
4️⃣ 원하는 화면으로 이동
1️⃣ 앱이 아직 설치되지 않아도 Deferred Deep Linking을 통해 올바른 화면으로 바로 열기.

Source:

지연 딥링크 (Deferred Deep Linking)

아래 다이어그램은 지연 딥링크 라고 알려진 라우팅을 보여줍니다. 사용자가 아직 앱을 설치하지 않았더라도, 링크의 컨텍스트가 앱 스토어를 거친 후에도 보존됩니다.

전체 흐름:
1. 사용자가 링크를 클릭 → 
2. 앱이 설치되지 않은 경우 스토어로 리디렉션 → 
3. 사용자가 앱을 설치 → 
4. 앱을 열면 원래 링크를 받아 올바른 화면으로 이동합니다.

링크 예시

https://fitconnect.app/signup?referralCode=TRAINER12345
부분
Schemehttps
Hostfitconnect.app
Path/signup
QueryreferralCode=TRAINER12345

각 부분은 네비게이션에서 특정 역할을 합니다: scheme 은 프로토콜(또는 커스텀 스킴일 경우 앱)을 식별하고, hostpath 는 라우트를 결정하며, query parameters 는 추가 데이터를 전달합니다(예: 추천 코드).

딥 링크 종류

1️⃣ Custom Scheme

fitconnect://fitconnect.app/signup?referralCode=TRAINER12345
  • ✅ 구현이 빠름.
  • ✅ 로컬 테스트에 적합.
  • ⚠️ 보안이 낮음 — 어떤 앱이든 같은 스킴을 등록할 수 있음.
  • ⚠️ 앱이 설치되지 않은 경우, 시스템이 눈에 거슬리는 오류를 표시함.
https://fitconnect.app/signup?referralCode=TRAINER12345
  • ✅ 안전함 — 앱과 서버 간 양방향 검증이 이루어짐.
  • ✅ 앱이 설치되지 않은 경우, 브라우저에서 정상적으로 열림.
  • ✅ 프로덕션에 권장됨.
  • ⚠️ 자체 도메인이 필요함.
  • ⚠️ 설정이 복잡함.

양방향 검증이란?

운영체제는 양쪽 모두가 서로를 인식할 때만 앱을 엽니다:

  • 이 처리할 도메인을 선언합니다.

  • 서버가 해당 도메인에 호스팅된 파일을 통해 어떤 앱이 접근 권한을 갖는지 확인합니다:

    • Android → assetlinks.json
    • iOS → apple-app-site-association

이 파일 중 하나라도 없거나 일치하지 않으면, 링크는 폴백으로 브라우저에서 열리며 악성 앱이 링크를 가로채는 것을 방지합니다.

팁: 개발 단계에서는 커스텀 스킴을 사용하고, 프로덕션에서는 HTTPS (App Links / Universal Links) 로 전환하세요.

사용 사례: FitConnect

시나리오

  • Maria, 퍼스널 트레이너가 자신의 추천 링크를 공유합니다.
  • 학생이 해당 링크를 통해 회원가입을 하면 Maria는 보너스를 받고, 학생은 할인을 받습니다.

시리즈 주요 딥 링크

https://fitconnect.app/signup?referralCode=TRAINER12345678901234

클릭하면 앱이 바로 TRAINER12345678901234 코드가 입력된 회원가입 화면으로 열려야 합니다.

항목
도메인fitconnect.app
Custom schemefitconnect://
Package (Android)com.fitconnect.app

Source:

구조 준비하기

네이티브 코드를 작성하기 전에, 앱의 모든 레이어에서 공유될 상수, enum, 모델을 정의합니다. 문자열(예: 채널 이름)을 중앙화하면 추적하기 어려운 오타를 방지할 수 있습니다.

상수

// lib/shared/const/deep_link_const.dart
class DeepLinkConst {
  static const String methodChannel = 'com.fitconnect.app/deeplink';
  static const String eventChannel = 'com.fitconnect.app/deeplink_stream';

  static const String customScheme = 'fitconnect';
  static const String httpsScheme = 'https';
  static const String appHost = 'fitconnect.app';

  static const String signupPath = '/signup';
  static const String referralCodeParam = 'referralCode';
  static const int referralCodeLength = 20;
}
// lib/shared/enums/deep_link_type.dart
enum DeepLinkType {
  customScheme,
  appLink,       // Android HTTPS
  universalLink, // iOS HTTPS
  unknown;

  bool get isSecure => this == appLink || this == universalLink;
}

isSecure getter는 코드를 더 표현력 있게 만들어 줍니다 — 문자열을 비교하거나 scheme을 직접 확인하는 대신 data.type.isSecure만 사용하면 됩니다.

데이터 모델 (Freezed 사용)

// lib/shared/models/deep_link_data.dart
import 'package:freezed_annotation/freezed_annotation.dart';
import 'deep_link_type.dart';

part 'deep_link_data.freezed.dart';
part 'deep_link_data.g.dart';

@freezed
class DeepLinkData with _$DeepLinkData {
  const factory DeepLinkData({
    required String url,
    required DeepLinkType type,
    required String scheme,
    String? host,
    String? path,
    @Default({}) Map<String, dynamic> queryParameters,
    String? referralCode,
  }) = _DeepLinkData;

  factory DeepLinkData.fromJson(Map<String, dynamic> json) =>
      _$DeepLinkDataFromJson(json);
}

Freezed를 사용하는 이유

  • 불변성을 보장합니다.
  • copyWith, ==, hashCode 및 JSON (de)serialization 코드를 자동으로 생성합니다.
  • 중요한 파라미터(예: 추천 코드)를 담는 객체의 유지보수를 용이하게 합니다.

Source:

다음 단계

  1. 네이티브 레이어 구현 (Android & iOS) – 링크를 가로채기 위해.
  2. Flutter에서 DeepLinkData를 소비하는 네비게이션 흐름 만들기.
  3. App Links / Universal Links 설정 (assetlinks.jsonapple-app-site-association 파일).
  4. 디버그 모드(커스텀 스킴)와 프로덕션 모드(HTTPS) 모두에서 테스트하기.

다음 글에서는 각 항목을 자세히 다루며, 양방향 검증 파일 설정(포스트 5)과 Flutter와의 완전한 통합을 소개합니다.


다음에 또 만나요! 🚀

eferralCode,
required DateTime receivedAt,
}) = _DeepLinkData;
}

이 단계가 끝나면 다음을 갖추게 됩니다:

  • 딥 링크가 어떻게 동작하는지에 대한 명확한 이해.
  • 다양한 종류의 링크 간 차이점 파악.
  • 구현을 시작할 수 있는 탄탄한 Flutter 기반.

이 기반을 바탕으로 다음 단계에서 네이티브 코드를 통합하고 엔드‑투‑엔드 흐름을 완성합니다.


다음 단계

다음 단계에서는 Android 네이티브 구현으로 넘어갑니다 – AndroidManifest.xmlMainActivity.kt 전체 예시 포함.

전체 코드는 GitHub의 FitConnect 저장소에서 확인할 수 있습니다.


시리즈 소개

이 글은 9편 시리즈첫 번째 포스트이며, Flutter에서 딥 링크를 다룹니다. 다음 글에서는 다음 주제를 다룹니다:

  • Android와 iOS용 네이티브 코드
  • 연기된 링크(Deferred links)
  • 리다이렉트 페이지
  • 테스트

모두 별도 패키지 없이 구현합니다.

궁금한 점이나 제안, 혹은 딥 링크와 관련된 경험이 있다면 댓글로 알려 주세요! 실제 프로젝트에 어떻게 적용했는지 듣고 싶습니다.

다음 포스트를 놓치고 싶지 않다면 Medium에서 저를 팔로우해 주세요.


피드백

이 내용이 도움이 되었다면 DEV.to에 ❤️ 혹은 🔖를 남겨 주세요 – 더 많은 개발자에게 포스트가 전달되는 데 도움이 됩니다.


커뮤니티 질문

여러분은 앱에 딥 링크를 구현해 본 적이 있나요?
가장 큰 도전 과제는 무엇이었나요? 다음 포스트에 실제 사례로 활용하고 싶습니다 👇


태그

flutter android ios deeplinks mobile programming

0 조회
Back to Blog

관련 글

더 보기 »