Nuxt3로 제비뽑기 웹 서비스를 만든 이유와 랜덤 추첨 기능 구현 방법

Nuxt3를 활용해 제비뽑기 웹 서비스를 개발한 과정을 정리했습니다. 랜덤 추첨 알고리즘 구현, 상태 관리, SEO 적용, 배포 과정까지 실제 개발 경험을 기반으로 설명합니다.

Nuxt3로 제비뽑기 웹 서비스를 만든 이유와 랜덤 추첨 기능 구현 방법

최근 간단한 이벤트나 모임에서 사용할 수 있는 웹 기반 제비뽑기 서비스를 만들게 되었습니다. 단순히 랜덤 숫자를 출력하는 수준이 아니라, 누구나 모바일에서 쉽게 사용할 수 있고 검색 유입까지 고려한 서비스가 필요했습니다. 그래서 선택한 기술이 바로 Nuxt3입니다.

이번 글에서는 실제로 제작한 제비뽑기 서비스인 '제비뽑기' 기능을 예시로, 왜 Nuxt를 선택했는지와 랜덤 추첨 기능을 어떻게 구현했는지 개발자 관점에서 정리해보겠습니다.

https://tool.daplus.co.kr/tools/lottery

 

제비뽑기 추첨 - 다플 도구모음

이름이나 항목을 입력하고 공정하게 무작위 추첨하세요.

tool.daplus.co.kr

 


왜 Nuxt로 만들었을까

처음에는 Vue SPA로만 구현할 생각이었습니다. 하지만 툴 서비스 특성상 검색 유입이 중요했습니다.

예를 들어 사용자는 다음과 같은 키워드로 검색합니다.

  • 제비뽑기 사이트
  • 랜덤 추첨
  • 온라인 사다리타기
  • 이름 추첨기
  • 추첨 프로그램

일반적인 SPA는 검색 엔진이 페이지 내용을 수집하는 데 한계가 있을 수 있습니다. 반면 Nuxt는 SSR과 SSG를 지원하기 때문에 SEO 측면에서 훨씬 유리합니다.

특히 툴 사이트는 검색 유입이 곧 트래픽으로 연결되기 때문에 처음부터 SEO를 고려하는 것이 중요했습니다.


프로젝트 구조

pages/
 └── tools/
      └── lottery.vue

components/
 ├── LotteryForm.vue
 ├── LotteryResult.vue
 └── LotteryAnimation.vue

composables/
 └── useLottery.ts

페이지는 최대한 단순하게 유지하고, 랜덤 로직은 composable로 분리했습니다.


제비뽑기 기능 요구사항

생각보다 요구사항은 많았습니다.

입력

  • 참가자 이름 입력
  • 여러 명 추가 가능
  • 빈 값 제거
  • 중복 이름 허용 여부 결정

추첨

  • 랜덤 선택
  • 중복 당첨 방지
  • 재추첨 가능

결과

  • 애니메이션 출력
  • 당첨자 강조
  • 모바일 대응

랜덤 추첨 알고리즘 구현

가장 단순한 방법은 Math.random()입니다.

const index = Math.floor(Math.random() * users.length)
const winner = users[index]

하지만 여러 명을 동시에 뽑아야 하는 경우에는 문제가 발생합니다.

예를 들어 3명을 추첨해야 하는데 같은 사람이 여러 번 선택될 수 있습니다.

그래서 배열을 섞은 뒤 필요한 개수만큼 가져오는 방식으로 구현했습니다.

export const shuffle = (array) => {
  const copied = [...array]

  for (let i = copied.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1))

    ;[copied[i], copied[j]] = [copied[j], copied[i]]
  }

  return copied
}

사용 방법입니다.

const winners = shuffle(users).slice(0, count)

이 방식은 중복 당첨을 방지하면서 비교적 균등한 랜덤 결과를 얻을 수 있습니다.


Composable로 추첨 로직 분리

Nuxt에서는 재사용 가능한 로직을 composable로 분리하는 것이 좋습니다.

export const useLottery = () => {
  const winners = ref([])

  const draw = (users, count) => {
    winners.value = shuffle(users).slice(0, count)
  }

  return {
    winners,
    draw
  }
}

페이지에서는 매우 간단하게 사용할 수 있습니다.

const { winners, draw } = useLottery()

draw(users.value, 1)

애니메이션 구현

사용자가 버튼을 누르자마자 결과가 나오면 재미가 없습니다.

그래서 짧은 로딩 애니메이션을 추가했습니다.

const loading = ref(false)

const startLottery = async () => {
  loading.value = true

  await wait(2000)

  draw(users.value, count.value)

  loading.value = false
}

2초 정도의 대기 시간을 넣어주면 실제 제비를 뽑는 듯한 느낌을 줄 수 있습니다.


SEO를 고려한 Meta 설정

Nuxt의 장점 중 하나는 페이지 단위 메타 태그 설정입니다.

useSeoMeta({
  title: '온라인 제비뽑기',
  description: '무료 랜덤 추첨 및 제비뽑기 서비스',
  ogTitle: '온라인 제비뽑기',
  ogDescription: '이름 추첨, 랜덤 추첨 기능 제공'
})

툴 사이트는 검색 유입이 중요하기 때문에 페이지별 메타 설정을 반드시 해주는 것이 좋습니다.


모바일 대응

실제 사용자의 대부분은 모바일에서 접속합니다.

그래서 다음 사항을 고려했습니다.

항목적용

반응형 레이아웃 O
모바일 버튼 크기 O
터치 영역 확대 O
키보드 입력 대응 O

Nuxt와 TailwindCSS를 함께 사용하면 모바일 대응이 상당히 편해집니다.


성능 최적화

툴 사이트는 기능보다 속도가 더 중요할 때가 많습니다.

적용한 최적화는 다음과 같습니다.

컴포넌트 분리

<LotteryForm />
<LotteryAnimation />
<LotteryResult />

Lazy Loading

defineAsyncComponent(() => import('./LotteryAnimation.vue'))

Hydration 최소화

필요 없는 상태 관리를 제거하고 클라이언트 렌더링 비용을 줄였습니다.


배포 후 느낀 점

생각보다 "제비뽑기" 키워드의 검색량이 꾸준했습니다.

특히 다음과 같은 상황에서 사용됩니다.

  • 회사 점심 메뉴 정하기
  • 이벤트 당첨자 추첨
  • 회식 자리 게임
  • 학생 발표 순서 정하기
  • 랜덤 팀 구성

작은 기능 하나라도 검색 의도가 명확하면 꾸준한 유입이 발생할 수 있다는 점을 다시 느끼게 되었습니다.


마무리

이번 프로젝트는 복잡한 서비스는 아니었지만 Nuxt3의 장점을 다시 한번 체감할 수 있는 프로젝트였습니다.

SSR과 SEO, 간단한 상태 관리, 빠른 개발 속도까지 고려하면 작은 웹 툴 서비스를 만들 때 Nuxt는 상당히 좋은 선택이라고 생각합니다.

단순한 제비뽑기 기능이라도 사용자가 실제로 필요로 하는 문제를 해결해 주는 서비스라면 충분히 의미 있는 프로젝트가 될 수 있습니다.

앞으로도 이런 작은 웹 도구들을 계속 만들어 보면서 Nuxt 기반의 서비스 운영 경험을 정리해볼 예정입니다.

서비스 체험:
https://tool.daplus.co.kr/tools/lottery

 

제비뽑기 추첨 - 다플 도구모음

이름이나 항목을 입력하고 공정하게 무작위 추첨하세요.

tool.daplus.co.kr