Server Action이 간헐적으로 실행되지 않는 문제

🌐

문제

서버 사이드에서 아래와 같은 Mixpanel 트래킹 코드를 실행했을 때, 이벤트가 간헐적으로만 기록되는 현상이 발생했다.

mixpanel.track(event, { distinct_id: userId, properties }, err => {
  if (err) {
    reject(err);
  } else {
    resolve("success");
  }
});

로컬 환경에서는 모든 이벤트가 정상적으로 기록되었지만, Vercel 배포 환경에서는 일부 이벤트만 기록되는 문제가 있었다.

원인

원인을 살펴보기전 Vercel Function에 대해 알아보자. Vercel에서는 Vercel에 배포된 환경에 대해 Vercel Function이라는 기능을 제공한다.

Vercel Function에 대해 간략하게 설명하면, Vercel에 배포된 모든 서버 사이드에서의 코드 실행은 버셀의 인프라 환경 아래에서 실행된다는 것이며. 즉, FaaS가 적용 된다는 뜻이다.

Fass 란?

FaaS는 Funtion as a Service의 약자로, 서버리스 컴퓨팅을 구현하는 방식 중 하나이다. 여기서 Function은 우리가 개발시 사용하는 그 Function이며 이러한 함수들을 클라우드 서비스에 올려 필요할 때만 호출하는 개념이다.
즉, FaaS를 사용하면 서버를 개발할 필요없이 함수만을 작성하여 실행할 수 있게 해준다.
Vercel의 이러한 기능 덕에, 우리가 서버설정을 딱히 하지 않아도 API Routes나, SSR등을 이용할 수 있는 것이다.

✏️ Good to Know
Vercel의 Serverless architecture는 AWS의 Lambda를 통해 구동된다.

문제가 발생했던 코드도 서버 액션(Server Action) 으로 설정되어 서버에서 실행되었고, Vercel이 서버리스를 기반으로 동작하기 때문에 코드가 서버리스 함수로 등록되었다.

그러나 서버리스 함수는 실행 도중 프로세스가 종료될 가능성이 있으며, 이 경우 비동기 작업이 완료되기 전에 함수가 종료될 수 있었다. 즉, callback함수가 실행되지 않아 이벤트가 정상적으로 전송되지 않았던 것이다.

해결 방법

따라서 코드를 아래와 같이 Promise로 감싸주었고, 모든 이벤트가 정상적으로 들어왔다.

return new Promise((resolve, reject) => {
    mixpanel.track(event, { distinct_id: userId, properties }, (err) => {
      if (err) {
        reject(err)
      } else {
        resolve('success')
      }
    })
  })

Tracking Code를 서버 사이드에서 실행한 이유

그렇다면 왜 mixpanel.track과 같은 트래킹 코드를 클라이언트 사이드에서 실행 시키지 않았는가?

  • 데이터 무결성: 클라이언트에서 이벤트를 직접 전송할 경우, 사용자가 브라우저 설정(예: 광고 차단기)이나 네트워크 문제로 인해 이벤트가 누락될 수 있다. 서버사이드에서 실행하면 이러한 외부 요인을 줄이고 더 신뢰할 수 있는 데이터 수집이 가능하다.
  • 비즈니스 로직과의 통합: 트래킹 이벤트가 서버사이드 로직(예: 결제 처리, 사용자 인증 등)과 밀접하게 연관될 때, 서버에서 직접 처리하는 것이 더 자연스럽고 효율적이다. 예를 들어, 결제 완료 후 이벤트를 기록하려면 서버에서 결제 성공 여부를 확인한 뒤 트래킹하는 것이 적합하다.
  • 성능 최적화: 클라이언트에서 트래킹 요청을 처리하면 추가적인 네트워크 호출이 발생해 페이지 로드 시간이 늘어날 수 있다. 서버사이드에서 처리하면 클라이언트 부담을 줄이고 사용자 경험을 개선할 수 있다.

이러한 이유들 때문에, 가능하면 클라이언트가 아닌 서버 사이드에서 실행하는게 더 정확하고 안정적인 지표를 얻을 수 있다.

🔎 참조