SCRUM-11021 | 커밋 수: 81개 | 기간: 2025.02 ~ 2025.03


문제상황

공유하기 기능이 여러 컴포넌트에 분산되어 있어 일관성이 부족했습니다. 리워드링크 기능을 신규로 도입해야 하는 요구사항이 있었고, 웹/앱/더보기 지원 여부에 따른 UI 분기가 복잡한 상황이었습니다.

주요 개선사항

SharePopup 컴포넌트를 통합하여 공유하기 로직을 중앙화했습니다. 리워드링크 안내, 적립금 조회, 설정 페이지를 구현했고, 단축 URL 적용 및 클립보드 복사 기능을 추가했습니다. Lottie 애니메이션을 적용하여 사용자 경험을 향상시켰습니다.

정량적 성과

공유하기 관련 컴포넌트를 3개에서 1개로 통합했습니다. 리워드링크 관련 신규 페이지를 4개 구현했으며, SharePopup 관련 테스트 코드를 추가했습니다.

통합 성과
항목비교값 (개)
컴포넌트(Before)33 개
컴포넌트(After)11 개
신규 페이지44 개
커밋 수8181 개

비즈니스 임팩트

사용자의 공유 경험에서 일관성을 확보했습니다. 리워드링크를 통한 신규 마케팅 채널을 확보했으며, SNS 공유 시 단축 URL을 적용하여 사용자 경험을 개선했습니다.

🔗

공유 통합

3개 컴포넌트 → 1개 SharePopup

🎁

리워드링크

신규 마케팅 채널 4페이지

✂️

단축 URL

정규식 검증으로 불필요한 API 호출 방지


기술적 구현 상세

AS-IS (분산 공유)

장점

  • 각 페이지 독립 동작

고려할 점

  • 공유 로직 중복
  • 단축 URL 불일치
  • 리워드링크 추가 시 전체 수정 필요

TO-BE (SharePopup 통합)

장점

  • 단일 컴포넌트로 공유 일원화
  • 리워드링크 토글 내장
  • 단축 URL 자동 처리

고려할 점

  • SharePopup 복잡도 증가

AS-IS

기존에는 공유하기 기능이 여러 컴포넌트에 분산되어 있었습니다.

// 기존 구조 (더미 예시)
// 각 페이지마다 공유 로직이 중복
const shareToKakao = () => {
  // 카카오 공유 로직
};

const shareToSms = () => {
  // SMS 공유 로직
};

const copyToClipboard = () => {
  // 클립보드 복사 로직
};

문제점:

공유 로직이 여러 컴포넌트에 중복되어 있었습니다. 단축 URL 생성 로직이 컴포넌트마다 불일치했고, 리워드링크 기능을 추가하려면 모든 컴포넌트를 수정해야 하는 구조였습니다.

TO-BE

SharePopup 컴포넌트로 통합하고, 리워드링크 섹션을 추가했습니다.

// 통합된 공유 인터페이스
interface ShareInfo {
  title: string;
  description: string;
  imageUrl: string;
  shareUrl: string;
  shortUrl?: string;
  rewardLinkEnabled?: boolean;
}

  1. 공유 트리거
  2. SharePopup 열기
  3. 리워드링크 토글
  4. 단축URL 생성
  5. 채널별 공유

핵심 구현

1. SharePopup 컴포넌트 통합

<!-- components/popup/SharePopup.vue -->
<script setup lang="ts">
import { useShare } from '@/hooks/common';

const props = defineProps<{
  shareInfo: ShareInfo;
}>();

const { shareToKakao, shareToSms, copyUrl, getShortUrl } = useShare();

const handleShare = async (type: ShareType) => {
  // 단축 URL 생성
  const url = await getShortUrl(props.shareInfo.shareUrl);

  switch (type) {
    case 'kakao':
      shareToKakao({ ...props.shareInfo, shareUrl: url });
      break;
    case 'sms':
      shareToSms(url);
      break;
    case 'copy':
      copyUrl(url);
      break;
  }
};
</script>

<template>
  <div class="share-popup">
    <RewardLinkSection v-if="shareInfo.rewardLinkEnabled" />
    <div class="share-buttons">
      <button @click="handleShare('kakao')">카카오톡</button>
      <button @click="handleShare('sms')">문자</button>
      <button @click="handleShare('copy')">URL 복사</button>
    </div>
  </div>
</template>

2. 리워드링크 섹션 구현

<!-- components/popup/RewardLinkSection.vue -->
<script setup lang="ts">
import { useBenefitStore } from '@/stores/modules/benefit';

const benefitStore = useBenefitStore();
const { rewardLinkInfo, isRewardLinkEnabled } = storeToRefs(benefitStore);

const toggleRewardLink = async (enabled: boolean) => {
  await benefitStore.setRewardLinkEnabled(enabled);
};
</script>

<template>
  <div class="reward-link-section">
    <div class="info">
      <p>리워드링크로 공유하면 적립금을 받을 수 있어요!</p>
    </div>
    <ToggleButton
      :value="isRewardLinkEnabled"
      @change="toggleRewardLink"
    />
  </div>
</template>

3. 단축 URL 정규표현식 검증

// hooks/common.ts
export const useShare = () => {
  const isShortUrl = (url: string): boolean => {
    // 단축 URL 패턴 검증
    const shortUrlPattern = /^https?:\/\/[a-z]+\.link\/[a-zA-Z0-9]+$/;
    return shortUrlPattern.test(url);
  };

  const getShortUrl = async (url: string): Promise<string> => {
    if (isShortUrl(url)) {
      return url;
    }
    // 단축 URL API 호출
    const response = await commonApi.createShortUrl(url);
    return response.shortUrl;
  };

  return { getShortUrl, isShortUrl };
};

4. 리워드링크 적립금 조회 페이지

<!-- views/promotion/RewardLink.vue -->
<script setup lang="ts">
import { useBenefitStore } from '@/stores/modules/benefit';
import { LottieAnimation } from '@/components/common';

const benefitStore = useBenefitStore();
const { rewardHistory, totalReward } = storeToRefs(benefitStore);

// 고래 이미지 위치 계산 (최대 포인트 기준)
const whalePosition = computed(() => calculatePosition(totalReward.value));
</script>

<template>
  <div class="reward-link-page">
    <LottieAnimation :position="whalePosition" />
    <!-- 총 적립금 표시 -->
    <!-- 월별 요약 내역 -->
  </div>
</template>


회고

잘한 점

공유하기 로직을 Composable로 추출하여 재사용성을 확보했습니다. 단축 URL 검증 로직을 정규표현식으로 구현하여 이미 단축된 URL에 대한 불필요한 API 호출을 방지했습니다. Lottie 애니메이션을 적용하여 리워드링크 페이지의 사용자 경험을 향상시켰습니다.

개선할 점

초기 설계 시 리워드링크 기능을 고려했다면 더 깔끔한 구조가 가능했을 것입니다. 또한 토글 버튼의 유효성 검사 로직에 대한 개선이 필요합니다.

배운 점

공유하기 기능의 플랫폼별 차이점(웹/앱/네이티브)을 체감했습니다. 단축 URL 서비스 연동 방법과 Lottie 애니메이션을 Vue 컴포넌트에 통합하는 방법을 익혔습니다.