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

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

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

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

사용자 추천의 끈기와 동적 링크의 지속성을 결합하여 새로운 사용자를 앱 콘텐츠로 직접 끌어들이거나 추천인과 추천받은 사람에게 상호 이익이 되는 프로모션을 제공함으로써 사용자 간 공유 및 추천 기능을 만들 수 있습니다. .

주요 혜택

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

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

새 Firebase 프로젝트를 설정 하고 Dynamic Links SDK를 앱에 설치합니다.

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

이제 사용자가 친구에게 보낼 수 있는 링크를 설정할 차례입니다. 사용자의 친구가 아직 앱을 설치하지 않은 경우에도 걱정하지 마십시오. Dynamic Links가 이를 처리해 드립니다.

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

동적 링크를 생성할 때 공유 중인 콘텐츠를 식별하는 데 사용할 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이 앱에 전달됩니다.

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

iOS

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;
}

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();
}

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
}

동적 링크가 있으면 표준 플랫폼 공유 흐름을 시작하는 공유 버튼을 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];
}

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"));
}

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"))
}

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

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

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

iOS

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

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) {
        // ...
    }

}

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
)

여기에 필요한 데이터는 URL뿐이며, 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

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) {
        // ...
    }

}

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) {
        // ...
    }
}

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

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

장점:

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

단점:

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

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

앱에서 연결된 콘텐츠 열기

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

iOS

iOS에서는 application:continueUserActivity:restorationHandler: 메서드를 구현하여 동적 링크를 수신합니다. 복원 핸들러에서 handleUniversalLink:completion: 을 호출하여 Dynamic Link를 얻을 수 있습니다. 동적 링크가 앱에 전달된 경우 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 라우팅 라이브러리가 이 작업에 도움이 될 수 있습니다.

특정 수신자를 위한 링크를 수신하는 경우 사용자별 논리를 실행하기 전에 Dynamic Link의 일치 신뢰도가 strong 지 확인하십시오.

기계적 인조 인간

Android에서는 getDynamicLink() 메서드를 사용하여 Dynamic Link에서 데이터를 가져옵니다.

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
                    // ...
                }
            }
        });

Kotlin+KTX

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

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

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