Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

사용자가 앱 콘텐츠를 공유할 수 있도록 하여 앱의 신규 사용자 확보

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

신규 사용자가 앱을 설치하도록 하는 가장 효과적인 방법 중 하나는 사용자가 친구와 앱의 콘텐츠를 공유할 수 있도록 하는 것입니다. 동적 링크를 사용하면 훌륭한 사용자 간 공유 경험을 만들 수 있습니다. 친구로부터 콘텐츠 추천을 받은 사용자는 앱으로 이동해야 하는 경우에도 링크를 클릭하고 앱의 공유 콘텐츠로 바로 이동할 수 있습니다. 스토어 또는 Google Play 스토어에서 앱을 먼저 설치합니다.

사용자 추천의 끈기와 동적 링크의 지속성을 결합하여 사용자 간 공유 및 추천 기능을 생성하여 신규 사용자를 앱 콘텐츠로 직접 유도하거나 추천자와 추천자 모두에게 이익이 되는 프로모션을 제공함으로써 새로운 사용자를 유치할 수 있습니다. .

주요 혜택

  • 처음으로 앱을 여는 신규 사용자는 친구가 공유하고 싶었던 내용을 기반으로 맥락화된 맞춤형 첫 실행 경험을 얻습니다. 예를 들어 공유된 콘텐츠를 표시하거나 초대한 친구와 자동으로 연결할 수 있습니다.
  • 친구가 앱을 설치했는지 여부에 관계없이 사용자가 플랫폼에서 친구와 콘텐츠를 쉽게 공유할 수 있습니다.

시작하는 방법은 다음과 같습니다.

새 Firebase 프로젝트를 설정 하고 동적 링크 SDK를 앱에 설치합니다.

동적 링크 SDK를 설치하면 사용자가 앱을 설치한 후를 포함하여 Firebase에서 동적 링크에 대한 데이터를 앱에 전달할 수 있습니다.

이제 사용자가 친구에게 보낼 수 있는 링크를 설정할 차례입니다. 사용자의 친구가 아직 앱을 설치하지 않았더라도 걱정하지 마십시오. 동적 링크가 이를 처리할 수 있습니다.

공유할 콘텐츠의 각 요소에 대해 동적 링크를 만듭니다 .

동적 링크를 만들 때 공유하는 콘텐츠를 식별하는 데 사용할 link 매개변수로 HTTP 또는 HTTPS URL을 제공해야 합니다. 동등한 콘텐츠가 포함된 웹사이트가 있는 경우 웹사이트의 URL을 사용해야 합니다. 이렇게 하면 데스크톱 브라우저와 같이 동적 링크를 지원하지 않는 플랫폼에서 이러한 링크가 올바르게 렌더링됩니다. 예:

https://example.page.link/?link=https://www.example.com/content?item%3D1234&apn=com.example.android&ibi=com.example.ios&isi=12345

URL 인코딩 매개변수를 추가하여 데이터 페이로드에 추가 정보를 추가할 수도 있습니다. 예를 들어 게임 초대와 같이 링크가 특정 사용자를 위한 것임을 나타냅니다.

https://example.page.link/?link=https://www.example.com/invitation?gameid%3D1234%26referrer%3D555&apn=com.example.android&ibi=com.example.ios&isi=12345

이러한 링크를 공유하기 전에 Firebase Dynamic Links URL 단축 API 를 사용하여 친근해 보이는 URL을 생성할 수 있습니다. 짧은 동적 링크는 다음 예와 같습니다.

https://example.page.link/WXYZ

어떤 링크를 사용하든 사용자가 기기에서 동적 링크를 열면 apn 매개변수(Android) 또는 ibiisi 매개변수(iOS)로 지정된 앱이 앱을 설치하기 위해 사용자를 Play 스토어 또는 App Store로 안내합니다. 아직 설치되어 있지 않은 경우. 그런 다음 앱을 설치하고 열면 'link' 매개변수에 지정된 URL이 앱으로 전달됩니다.

먼저 사람들을 채팅방에 초대하는 링크를 생성하는 행아웃과 같은 채팅방 기반 채팅 앱의 간단한 예를 살펴보세요.

아이폰 OS

chat app screenshotchat app screenshot with share sheet

기계적 인조 인간

chat app screenshotchat app screenshot with share sheet

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
func generateContentLink() -> URL {
  let baseURL = URL(string: "https://your-custom-name.page.link")!
  let domain = "https://your-app.page.link"
  let linkBuilder = DynamicLinkComponents(link: baseURL, domainURIPrefix: domain)
  linkBuilder?.iOSParameters = DynamicLinkIOSParameters(bundleID: "com.your.bundleID")
  linkBuilder?.androidParameters =
      DynamicLinkAndroidParameters(packageName: "com.your.packageName")


  // Fall back to the base url if we can't generate a dynamic link.
  return linkBuilder?.link ?? baseURL
}

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
- (NSURL *)generateContentLink {
  NSURL *baseURL = [NSURL URLWithString:@"https://your-custom-name.page.link"];
  NSString *domain = @"https://your-app.page.link";
  FIRDynamicLinkComponents *builder = [[FIRDynamicLinkComponents alloc] initWithLink:baseURL domainURIPrefix:domain];
  builder.iOSParameters = [FIRDynamicLinkIOSParameters parametersWithBundleID:@"com.your.bundleID"];
  builder.androidParameters = [FIRDynamicLinkAndroidParameters parametersWithPackageName:@"com.your.packageName"];

  // Fall back to the base url if we can't generate a dynamic link.
  return builder.link ?: baseURL;
}

Kotlin+KTX

fun generateContentLink(): Uri {
    val baseUrl = Uri.parse("https://your-custom-name.page.link")
    val domain = "https://your-app.page.link"

    val link = FirebaseDynamicLinks.getInstance()
            .createDynamicLink()
            .setLink(baseUrl)
            .setDomainUriPrefix(domain)
            .setIosParameters(DynamicLink.IosParameters.Builder("com.your.bundleid").build())
            .setAndroidParameters(DynamicLink.AndroidParameters.Builder("com.your.packageName").build())
            .buildDynamicLink()

    return link.uri
}

Java

public static Uri generateContentLink() {
    Uri baseUrl = Uri.parse("https://your-custom-name.page.link");
    String domain = "https://your-app.page.link";

    DynamicLink link = FirebaseDynamicLinks.getInstance()
            .createDynamicLink()
            .setLink(baseUrl)
            .setDomainUriPrefix(domain)
            .setIosParameters(new DynamicLink.IosParameters.Builder("com.your.bundleid").build())
            .setAndroidParameters(new DynamicLink.AndroidParameters.Builder("com.your.packageName").build())
            .buildDynamicLink();

    return link.getUri();
}

동적 링크가 있으면 표준 플랫폼 공유 흐름을 시작하는 공유 버튼을 UI에 추가할 수 있습니다.

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
lazy private var shareController: UIActivityViewController = {
  let activities: [Any] = [
    "Learn how to share content via Firebase",
    URL(string: "https://firebase.google.com")!
  ]
  let controller = UIActivityViewController(activityItems: activities,
                                            applicationActivities: nil)
  return controller
}()

@IBAction func shareButtonPressed(_ sender: Any) {
  let inviteController = UIStoryboard(name: "Main", bundle: nil)
    .instantiateViewController(withIdentifier: "InviteViewController")
  self.navigationController?.pushViewController(inviteController, animated: true)
}

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
- (UIActivityViewController *)shareController {
  if (_shareController == nil) {
    NSArray *activities = @[
      @"Learn how to share content via Firebase",
      [NSURL URLWithString:@"https://firebase.google.com"]
    ];
    UIActivityViewController *controller = [[UIActivityViewController alloc] initWithActivityItems:activities applicationActivities:nil];
    _shareController = controller;
  }
  return _shareController;
}

- (IBAction)shareLinkButtonPressed:(UIView *)sender {
  if (![sender isKindOfClass:[UIView class]]) {
    return;
  }

  self.shareController.popoverPresentationController.sourceView = sender;
  [self presentViewController:self.shareController animated:YES completion:nil];
}

Kotlin+KTX

private fun onShareClicked() {
    val link = DynamicLinksUtil.generateContentLink()

    val intent = Intent(Intent.ACTION_SEND)
    intent.type = "text/plain"
    intent.putExtra(Intent.EXTRA_TEXT, link.toString())

    startActivity(Intent.createChooser(intent, "Share Link"))
}

Java

private void onShareClicked() {
    Uri link = DynamicLinksUtil.generateContentLink();

    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    intent.putExtra(Intent.EXTRA_TEXT, link.toString());

    startActivity(Intent.createChooser(intent, "Share Link"));
}

이 예에서 기본 공유 UI는 링크를 공유하기 위한 앱 목록을 자동으로 표시하므로 몇 줄의 코드만으로 자신의 앱에서 설정할 수 있습니다.

사용자가 연락처를 선택하고 앱에서 메시지를 작성하도록 하는 대신 이러한 작업은 공유 대화 상자에서 선택한 앱에 위임됩니다. 또한 다른 앱에 공유를 위임하면 사용자에게 연락처 권한을 요청할 필요가 없으며 사용자가 선택한 앱 내에서 확장된 연락처 목록에서 선택할 수 있습니다. 소셜 공유를 보다 용이하게 하기 위해 주요 소셜 채널의 링크와 함께 표시될 동적 링크 에 소셜 미디어 미리보기 메타데이터를 추가 할 수 있습니다.

그러나 때로는 텍스트 없이 링크만 보내는 것만으로는 설득력 있는 추천을 받기에 충분하지 않습니다. 링크를 짧은 메시지와 함께 제공하고 가능한 경우 더 풍부한 프레젠테이션을 통해 사용자는 추천을 받았을 때 추천의 가치 제안을 이해할 수 있습니다.

아이폰 OS

rewarded referral screenshotrewarded referral screenshot with share sheet

기계적 인조 인간

rewarded referral screenshotrewarded referral screenshot with share sheet

이것은 마지막 예제보다 더 복잡하지만 접근 방식은 거의 동일합니다. 이 화면에는 초대의 가치 제안과 주요 소셜 채널에 공유하기 위한 버튼이 있는 큰 그래픽이 있습니다. 이 UI 흐름에는 약간의 중복이 있습니다. 일부 공유 채널은 이메일 초대에 제목 줄을 추가하는 것과 같이 더 많은 채널별 메시지 사용자 지정을 허용하기 위해 개별적으로 표시됩니다. 이 초대 메뉴에서 다음을 수행합니다.

  • 이메일, 문자 메시지 및 복사 링크 공유 버튼을 표시하고 메시지를 적절하게 사용자 지정합니다. 이메일에는 제목이 포함되며 줄 바꿈, 이미지 및 공백이 있는 더 긴 본문을 포함할 수 있습니다. 텍스트에는 줄 바꿈이 있는 더 짧은 본문이 포함되어야 하지만 공백이 거의 없고 이미지가 없어야 합니다. 링크 복사는 링크만 복사해야 합니다.
  • 링크와 함께 제공되는 짧은 초대 메시지를 포함하여 다른 모든 것에 대해 시스템 공유 UI를 사용하십시오.
  • URL 체계를 통한 딥 링크 또는 앱의 초대를 처리하기 위한 특수 로직이 있는 다른 앱에 대한 범용 링크입니다. 이는 조직과 다른 앱 간의 파트너십 외부에서는 작동하지 않으며 소규모 조직에는 옵션이 아닐 수 있습니다. 즉, 일부 앱은 범용/딥 링크 동작을 공개적으로 문서화할 수 있습니다. 샘플에서 이것의 더미 버전을 구현할 것입니다.

먼저 초대의 정보만 캡슐화하고 기능은 포함하지 않는 초대 콘텐츠 유형을 정의합니다. 이런 식으로 데이터 유형으로 시작하고 해당 데이터를 함께 조각내는 방법 측면에서 코드에 대해 생각할 수 있습니다.

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
/// The content within an invite, with optional fields to accommodate all presenters.
/// This type could be modified to also include an image, for sending invites over email.
struct InviteContent {

  /// The subject of the message. Not used for invites without subjects, like text message invites.
  var subject: String?

  /// The body of the message. Indispensable content should go here.
  var body: String?

  /// The URL containing the invite. In link-copy cases, only this field will be used.
  var link: URL

}

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
/// The content within an invite, with optional fields to accommodate all presenters.
/// This type could be modified to also include an image, for sending invites over email.
@interface InviteContent : NSObject <NSCopying>

/// The subject of the message. Not used for invites without subjects, like text message invites.
@property (nonatomic, readonly, nullable) NSString *subject;

/// The body of the message. Indispensable content should go here.
@property (nonatomic, readonly, nullable) NSString *body;

/// The URL containing the invite. In link-copy cases, only this field will be used.
@property (nonatomic, readonly) NSURL *link;

- (instancetype)initWithSubject:(nullable NSString *)subject
                           body:(nullable NSString *)body
                           link:(NSURL *)link NS_DESIGNATED_INITIALIZER;

- (instancetype)init NS_UNAVAILABLE;

@end

Kotlin+KTX

/**
 * The content of an invitation, with optional fields to accommodate all presenters.
 * This type could be modified to also include an image, for sending invites over email.
 */
data class InviteContent(
    /** The subject of the message. Not used for invites without subjects, like SMS.  */
    val subject: String?,
    /** The body of the message. Indispensable content should go here.  */
    val body: String?,
    /** The URL containing the link to invite. In link-copy cases, only this field will be used.  */
    val link: Uri
)

Java

/**
 * The content of an invitation, with optional fields to accommodate all presenters.
 * This type could be modified to also include an image, for sending invites over email.
 */
public class InviteContent {

    /**
     * The subject of the message. Not used for invites without subjects, like SMS.
     **/
    @Nullable
    public final String subject;

    /**
     * The body of the message. Indispensable content should go here.
     **/
    @Nullable
    public final String body;

    /**
     * The URL containing the link to invite. In link-copy cases, only this field will be used.
     **/
    @NonNull
    public final Uri link;

    public InviteContent(@Nullable String subject, @Nullable String body, @NonNull Uri link) {
        // ...
    }

}

여기서 유일하게 필요한 데이터는 URL이며 이것이 없으면 앱에 사용자를 초대할 수 없습니다. 다른 데이터 조각은 이메일 전송을 위해 명확하게 구성되어 있기 때문에 다른 경우에는 약간 어색합니다. 텍스트로 초대장을 보낼 때 링크와 함께 제공되는 설명은 이메일 제목과 유사하게 읽을 수 있지만 소셜 미디어에 공유할 때는 텍스트와 함께 제공되는 링크는 이메일 본문과 비슷할 수 있습니다. 앱에 가장 적합한 균형을 찾으려면 이를 직접 실험해야 합니다. 확실하지 않은 경우 언제든지 원격 구성 과 같은 서비스를 사용하여 앱 실행 후 텍스트 값을 변경할 수 있습니다.

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
/// A type responsible for presenting an invite given using a specific method
/// given the content of the invite.
protocol InvitePresenter {

  /// The name of the presenter. User-visible.
  var name: String { get }

  /// An icon representing the invite method. User-visible.
  var icon: UIImage? { get }

  /// Whether or not the presenter's method is available. iOS devices that aren't phones
  /// may not be able to send texts, for example.
  var isAvailable: Bool { get }

  /// The content of the invite. Some of the content type's fields may be unused.
  var content: InviteContent { get }

  /// Designated initializer.
  init(content: InviteContent, presentingController: UIViewController)

  /// This method should cause the presenter to present the invite and then handle any actions
  /// required to complete the invite flow.
  func sendInvite()

}

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
/// A type responsible for presenting an invite given using a specific method
/// given the content of the invite.
@protocol InvitePresenter <NSObject>

/// The name of the presenter. User-visible.
@property (nonatomic, readonly) NSString *name;

/// An icon representing the invite method. User-visible.
@property (nonatomic, readonly, nullable) UIImage *icon;

/// Whether or not the presenter's method is available. iOS devices that aren't phones
/// may not be able to send texts, for example.
@property (nonatomic, readonly) BOOL isAvailable;

/// The content of the invite. Some of the content type's fields may be unused.
@property (nonatomic, readonly) InviteContent *content;

/// Designated initializer.
- (instancetype)initWithContent:(InviteContent *)content presentingViewController:(UIViewController *)controller;

/// This method should cause the presenter to present the invite and then handle any actions
/// required to complete the invite flow.
- (void)sendInvite;

@end

Kotlin+KTX

/**
 * Presents the invite using a specific method, such as email or social.
 */
open class InvitePresenter(
    /** The user-visible name of the invite method, like 'Email' or 'SMS'  */
    val name: String,
    /** An icon representing the invite method.  */
    @param:DrawableRes @field:DrawableRes
    val icon: Int,
    /** Whether or not the method is available on this device. For example, SMS is phone only.  */
    val isAvailable: Boolean,
    /** The Content of the invitation  */
    val content: InviteContent
) {
    /**
     * Send the invitation using the specified method.
     */
    open fun sendInvite(context: Context) {
        // ...
    }
}

Java

/**
 * Presents the invite using a specific method, such as email or social.
 */
public class InvitePresenter {

    /**
     * The user-visible name of the invite method, like 'Email' or 'SMS'
     **/
    public final String name;

    /**
     * An icon representing the invite method.
     **/
    @DrawableRes
    public final int icon;

    /**
     * Whether or not the method is available on this device. For example, SMS is phone only.
     **/
    public final boolean isAvailable;

    /**
     * The Content of the invitation
     **/
    public final InviteContent content;

    public InvitePresenter(String name, @DrawableRes int icon, boolean isAvailable, InviteContent content) {
        // ...
    }

    /**
     * Send the invitation using the specified method.
     */
    public void sendInvite(Context context) {
        // ...
    }

}

이제 남은 것은 이것을 선택한 UI 구성 요소에 연결하는 것입니다. 이 초대 흐름의 전체 구현은 iOSAndroid 용 GitHub의 샘플을 참조하세요.

이것은 사용자가 친구에게 초대장을 보낼 수 있도록 하는 가장 가벼운 초대 솔루션인 모든 방법입니다. 또한 많은 인기 앱은 자체 백엔드를 통해 이메일을 전송하여 초대장을 전달합니다. 이를 위해서는 메일 전송 서비스를 통합해야 하지만 몇 가지 사소한 단점만 제외하고는 사용할 수 없는 많은 이점을 제공합니다.

장점:

  • 보내기 전에 사용자가 수정할 수 없는 복잡한 마크업이 있는 이메일을 활성화합니다.
  • 보다 세분화된 추적/분석을 활성화합니다(예: 백엔드에서 성공 및 실패 전송).

단점:

  • 스팸으로 표시될 가능성이 높은 이메일
  • 이메일 전송 서비스와의 통합 필요
  • 인앱 연락처 권한이 필요합니다.

일반적으로 자신의 이메일 배달 서비스를 통해 초대장을 보내면 다용성을 희생하는 대신 더 일관되고 잠재적으로 더 풍부한 초대장 경험을 제공합니다.

앱에서 연결된 콘텐츠 열기

마지막으로 링크된 콘텐츠를 수신자에게 표시할 수 있도록 앱에 전달된 링크를 받아야 합니다. 동적 링크 SDK를 사용하면 쉽습니다.

아이폰 OS

iOS에서는 application:continueUserActivity:restorationHandler: 메서드를 구현하여 동적 링크를 수신합니다. 복원 처리기에서 handleUniversalLink:completion: 을 호출하여 동적 링크를 가져올 수 있습니다. 동적 링크가 앱에 전달된 경우 FIRDynamicLinkurl 속성에서 가져올 수 있습니다. 예를 들어:

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
[[FIRDynamicLinks dynamicLinks]
    handleUniversalLink:userActivity.webpageURL
             completion:^(FIRDynamicLink * _Nullable dynamicLink,
                          NSError * _Nullable error) {
      NSString *link = dynamicLink.url;
      BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
      // ...
    }];

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
FIRDynamicLinks.dynamicLinks()?.handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
    let link = dynamicLink.url
    let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
    // ...
}

또한 application:openURL:options: 메서드에서 dynamicLinkFromCustomSchemeURL: 을 호출하여 앱에 사용자 지정 스키마 URL로 전달된 동적 링크를 수신해야 합니다. 예를 들어:

목표-C

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink) {
  NSString *link = dynamicLink.url;
  BOOL strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong;
  // ...
  return YES;
}

빠른

참고: 이 Firebase 제품은 macOS, Mac Catalyst, tvOS 또는 watchOS 대상에서 사용할 수 없습니다.
let dynamicLink = FIRDynamicLinks.dynamicLinks()?.dynamicLinkFromCustomSchemeURL(url)
if let dynamicLink = dynamicLink {
  let link = dynamicLink.url
  let strongMatch = dynamicLink.matchConfidence == FIRDynamicLinkMatchConfidenceStrong
  // ...
  return true
}

link 매개변수의 값이 있으므로 수신자에게 링크된 콘텐츠를 표시하거나 매개변수에 지정된 데이터를 다른 방식으로 처리할 수 있습니다. JLRoutes 와 같은 URL 라우팅 라이브러리가 이 작업에 도움이 될 수 있습니다.

특정 수신자를 위한 링크를 수신하는 경우 사용자별 로직을 실행하기 전에 동적 링크의 일치 신뢰도가 strong 확인하세요.

기계적 인조 인간

Android에서는 getDynamicLink() 메서드를 사용하여 동적 링크에서 데이터를 가져옵니다.

Kotlin+KTX

Firebase.dynamicLinks
        .getDynamicLink(intent)
        .addOnCompleteListener { task ->
            if (!task.isSuccessful) {
                // Handle error
                // ...
            }

            val invite = FirebaseAppInvite.getInvitation(task.result)
            if (invite != null) {
                // Handle invite
                // ...
            }
        }

Java

FirebaseDynamicLinks.getInstance()
        .getDynamicLink(getIntent())
        .addOnCompleteListener(new OnCompleteListener<PendingDynamicLinkData>() {
            @Override
            public void onComplete(@NonNull Task<PendingDynamicLinkData> task) {
                if (!task.isSuccessful()) {
                    // Handle error
                    // ...
                }

                FirebaseAppInvite invite = FirebaseAppInvite.getInvitation(task.getResult());
                if (invite != null) {
                    // Handle invite
                    // ...
                }
            }
        });

link 매개변수의 값이 있으므로 수신자에게 링크된 콘텐츠를 표시하거나 매개변수에 지정된 데이터를 다른 방식으로 처리할 수 있습니다. URL 라우팅 라이브러리가 이 작업에 도움이 될 수 있습니다.