접속한 사용자 위치 알아내기

이짜젠·2024년 11월 21일
0

해외에 있는 사용자들에 한해서 팝업을 보여주고 싶었다.
사용자가 해외인지 아닌지를 판별하기 위해 진행한 작업을 간단하게 정리해본다.

2가지 과정으로 이뤄진다.

  1. IP 주소를 얻어내기
  2. IP 대역을 참고하여 위치를 알아내기

모든 페이지에서 체크를 하기위해 위 과정을 _app.tsx 레이어에서 수행헀다.

// apps/web/src/pages/_app.tsx

App.getInitialProps = async ({
  ctx,
}: AppContext): Promise => {

	// 1. IP 주소 얻어내기
  const clientIp = getClientIP(ctx.req.headers['x-forwarded-for'] as string);
  
  // 2. IP 주소를 통해 위치 알아내기
  const geoData = await getGeoData(clientIp);

  return {
    geoData,
  };
};

1. IP 주소 얻어내기

  • x-forwarded-for header 를 참조하면 된다.

    x-forwarded-for
    정식 표준헤더는 아니지만 업계 표준으로 사용되며, 요청이 Proxy 서버를 지나갈때 생성되는 헤더
    client (1.1.1.1) ——> proxy1 (2.2.2.2) ——> proxy2 (3.3.3.3) ——> server
    X-Forwarded-For: ::ffff:1.1.1.1,2.2.2.2,3.3.3.3

// apps/web/src/utils/auth.ts

export const getClientIP = (xffHeader: string | null) => {
  const ip = xffHeader ? xffHeader.split(',')[0] : null;

  if (ip) {
    return ip.replace(/^::ffff:/, '');
  }

  return ip;
};

2. IP 대역을 참고하여 위치를 알아내기

방법은 크게 2가지가 있다.

외부 API 활용

다양한 무료 서비스가 있으나, 무료버전은 제한이 있어 상용서비스에는 어울리지 않는다.

IP-API (Recommended for testing)

- ✅ Free
- ✅ No API key required
- ✅ 45 requests/minute limit
- ❌ HTTPS only in paid version
- Response includes: country, city, region, lat/long, timezone

IPStack

- ✅ Free tier (10,000 requests/month)
- ✅ HTTPS included
- ✅ More detailed data
- ❌ Requires API key
- Response includes: country, city, region, lat/long, timezone, currency

Abstract API

- ✅ Free tier (20,000 requests/month)
- ✅ HTTPS included
- ✅ Very detailed data
- ❌ Requires API key
- Response includes: country, city, region, lat/long, timezone, currency, ISP info

패키지를 활용.

패키지를 활용하는 방법으로써, geoip-lite 를 이용했다.

// apps/web/src/utils/auth.ts

export const getGeoData = async (clientIp: string | null) => {
  let result = null;

  if (clientIp && typeof window === 'undefined') {
    const geoip = (await import('geoip-lite')).default;
    result = geoip.lookup(clientIp);
  }

  return result;
};

3. geoip-lite 를 동적 import 한 이유

geoip-lite는 node 전용 패키로, node runtime에서만 동작한다.

그러나 _app.tsx 는 서버 사이드, 클라이언트 사이드 모두에서 동작한다.
따라서 서버사이드일 경우에만 import 할 수 있도록 동적으로 처리해야한다.

클라이언트 사이드에서 사용할 수 없는 패키지를 선언, import 하는 구문 자체만로도 에러를 발생시킨다.

  if (clientIp && typeof window === 'undefined') {
    const geoip = (await import('geoip-lite')).default;
    result = geoip.lookup(clientIp);
  }

위치 체크를 _app.tsx가 아닌 middeware.ts 레이어에서는 할 수 없었나?
middleware 의 runtime은 node가 아니다. edge 라고하는 또 다른 runtime 이다.
node runtime 의 경량화 버전이라고 생각하면 되며, 일부기능만 지원한다.
edge runtime 에서는 fs 패키지사용이 불가하다.

Reference

profile
오늘 먹은 음식도 기억이 안납니다. 그래서 모든걸 기록합니다.

0개의 댓글