프로젝트/오늘의 운세

오늘의 운세 웹앱 개발 일지 (2026.05)

ksc-dev 2026. 5. 25. 17:26

최근 간단한 웹 프로젝트를 만들면서 입력 → 결과 → 저장 → 배포 흐름을 직접 구성해 봤다.
처음에는 AI 연동까지 고려했지만, 현재는 기능 범위를 줄여 총운 중심의 운세 웹앱으로 정리했다.

프로젝트명도 정리하면서 배포와 운영 관점까지 같이 적용했다.


프로젝트 개요

이름과 생년월일을 입력하면 오늘의 총운을 보여주는 간단한 웹앱.

현재 구현 범위:

  • 이름 입력
  • 생년월일 입력
  • 총운 결과 표시
  • 입력값 저장 옵션
  • 결과 저장
  • 반응형 대응
  • 개인정보 안내
  • About 페이지
  • 배포 및 분석 도구 연결

기술 스택

Frontend

  • Vue 3
  • TypeScript
  • Vite
  • Tailwind CSS
  • Vue Router

Day 1 — 입력 폼과 타입 설계

운세 입력 화면부터 구성했다.

입력 항목:

  • 이름
  • 생년월일

입력 완료 시 부모 컴포넌트로 전달하는 구조로 구현했다.

예시:

 
const emit = defineEmits<{
  submit: [name: string, birthdate: string]
}>()

emit('submit', name.value, birthdate.value)
 

배운 점:

  • defineEmits 반환값을 받아야 이벤트 호출 가능
  • 자식 → 부모 이벤트 흐름 이해
  • TypeScript 타입 설계 연습

Day 2 — 상태 전환과 결과 화면 구성

라우터 없이 단일 흐름으로 화면 상태를 관리했다.

상태:

 
type AppView =
  | 'form'
  | 'loading'
  | 'result'
 

App 상태:

 
const currentView =
ref<AppView>('form')
 

흐름:

입력

↓

로딩

↓

결과
 

현재는 총운만 표시하도록 범위를 줄였다.

배운 점:

  • 상태 분리하면 조건문 복잡도가 줄어든다
  • 화면 전환 구조를 먼저 만들면 이후 확장이 쉬움

Day 3 — 입력값 저장 기능

다시 방문했을 때 입력을 유지하도록 저장 기능 추가.

설계 변경:

처음

name
birthdate
checkbox
 

최종

fortune_saved_input
 

저장 구조:

 
localStorage.setItem(
  SAVE_KEY,
  JSON.stringify({
    name,
    birthdate
  })
)
 

복원:

 
const saved =
localStorage.getItem(
  SAVE_KEY
)

if (saved) {
  const parsed =
  JSON.parse(saved)
}
 

배운 점:

  • 관련 데이터는 JSON 하나로 묶는 게 관리가 쉬움
  • localStorage는 문자열만 저장 가능

Day 4 — 반응형과 페이지 분리

페이지 구조를 정리했다.

구조:

views/
├── HomeView.vue
├── PrivacyView.vue
└── AboutView.vue
 

반응형 적용:

p-8

↓

p-6 sm:p-8
 
max-w-sm

↓

max-w-sm sm:max-w-md
 

추가:

  • Privacy 페이지
  • About 페이지
  • 문의하기 버튼
  • Footer 구성

배운 점:

  • 모바일 퍼스트 설계가 유지보수 편함
  • 페이지 분리가 되면 역할이 명확해짐

Day 5 — 배포와 운영 준비

프로젝트 이름 정리:

ai-fortune-web

↓

fortune-web
 

수정 대상:

  • package.json
  • package-lock.json
  • GitHub Repository

배포:

  • Vercel

Vue Router 새로고침 문제 해결:

 
{
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/"
    }
  ]
}
 

추가 설정:

  • Google Analytics 연결
  • Google AdSense 연결
  • ads.txt 등록
  • 환경변수 설정

예시:

VITE_GA_ID
VITE_ADSENSE_ID
 

배운 점:

  • 배포는 코드보다 환경 설정이 더 오래 걸릴 수 있다
  • SPA는 라우팅 설정까지 고려해야 한다

현재 상태

완료:

✅ 입력 폼
✅ 총운 결과
✅ 입력 저장
✅ 결과 저장
✅ 반응형
✅ Privacy
✅ About
✅ 문의하기
✅ 배포
✅ GA
✅ AdSense

보류:

  • 외부 API 연동
  • 추가 운세 카테고리
  • 회원 기능

느낀 점

처음에는 기능을 크게 잡았는데, 실제로 만들면서 총운 하나만 먼저 완성하는 방향으로 줄였다.

결과적으로 범위를 줄이니까 구현·배포·운영까지 한 번 경험할 수 있었다.

다음에는 실제 사용자 반응을 보고 필요한 기능만 추가해 볼 예정이다.

 

 

※ 기능 추가 - 오늘의 운세 링크 공유 기능 추가 (2026.05.25)

작업 배경

기존 오늘의 운세 앱은 결과를 확인한 뒤 본인 화면에서만 볼 수 있는 구조였다.

친구에게 결과를 보여주려면:

  • 스크린샷을 보내거나
  • 직접 내용을 전달해야 했다.

그래서 링크 하나만 전달하면 같은 결과를 바로 확인할 수 있는 공유 기능을 추가했다.


핵심 설계 — 서버 없이 URL만으로 공유

이번 기능은 운세 생성 로직이 결정론적(deterministic) 이라는 점을 활용했다.

 
const seed = hashString(name + birthdate + today)
 

같은 이름 + 생년월일 + 날짜라면 항상 같은 운세가 생성된다.

이 구조라면 결과를 DB에 저장하지 않아도 된다.

공유 URL에 필요한 정보만 담아서 상대방도 동일한 결과를 생성하도록 구현했다.

예시:

https://fortune-web.vercel.app/?name=홍길동&birth=19900101
 

장점:

  • 서버 저장 불필요
  • 별도 API 없음
  • 유지 비용 없음
  • URL만으로 동일 결과 재현 가능

구현 — 결과 화면에서 공유 링크 생성

FortuneResult.vue

결과 화면에 공유 버튼을 추가하고 현재 결과 기준 URL을 생성해 클립보드로 복사하도록 구현했다.

 
async function shareResult() {
  const params = new URLSearchParams({
    name: props.result.name,
    birth: props.birthdate
  })

  const url =
    `${window.location.origin}/?${params}`

  await navigator.clipboard.writeText(url)

  copied.value = true

  setTimeout(() => {
    copied.value = false
  }, 2000)
}
 

URLSearchParams 를 사용해서 한글 인코딩도 자동 처리했다.

복사 후 버튼 문구를 잠시 "링크 복사됨! ✅" 으로 변경해 사용자에게 피드백을 제공했다.

추가로 birthdate 는 기존 FortuneResult 타입에 없어서 별도 prop으로 전달했다.


구현 - 공유 링크 자동 진입 처리

HomeView.vue

공유 링크로 접속하면 URL 파라미터를 감지해서 자동으로 결과를 생성하도록 처리했다.

 
const route = useRoute()
const currentBirthdate = ref('')

onMounted(() => {
  clearIfNewDay()

  if (route.query.name &&
      route.query.birth) {
    handleSubmit(
      route.query.name as string,
      route.query.birth as string
    )
  }
})
 

route.query 타입이 string | string[] | null 이라서 필요한 구간에서 타입 캐스팅을 적용했다.

공유 링크 접속 시 별도 입력 없이 로딩 후 바로 운세 결과 화면으로 진입한다.


마무리

처음에는 공유 기능을 만들려면 DB나 서버 저장이 필요할 것이라고 생각했다.

하지만 운세 결과가 같은 입력이면 항상 같은 결과가 나온다는 구조를 활용하면서 URL만으로 기능을 구현할 수 있었다.

작은 기능이지만 데이터를 저장하지 않고 전달하는 방식과 사용자 경험 개선을 함께 고민해볼 수 있었던 작업이었다.

🚀 서비스 링크

직접 확인해볼 수 있습니다.

 

오늘의 운세

 

fortune-web-yr43.vercel.app