본 포스트는 휴대폰의 위치 기능을 사용해서 현재 나와 가까운 곳, 근처에 있는 로또 1등 판매점을 구하는 방법에 대한 설명이며, 개발환경은 Node.js, NestJs, MySQL, React 입니다.

 

후배와 사이드 프로젝트로 로또 앱을 하나 만들면서 언젠가부터 로또에 진심이 되어 버렸습니다. 43년을 살면서 복권 한 장 사보지 않았었지만 이제 매주 빼먹지 않고 로또를 구매하고 있습니다. 이 블로그에서 설명하는 결과물은 아래 앱에서 보실 수 있습니다. 

 

구글 플레이스토어, 애플  앱 스토어에서 "로또킷"으로 검색하시면 되고, 링크는 아래와 같습니다.

 

[구글 플레이스토어]

https://play.google.com/store/apps/details?id=com.nextact.lottoapp 

 

로또킷 - 로또 선물함 - Google Play 앱

로또 행운번호 생성, 주변 로또판매점 바로가기, 로또 번호 선물하기 등 다양한 로또 정보를 제공하는 로또킷(lottoKit)입니다.

play.google.com

 

[애플 앱 스토어]

http://itunes.apple.com/app/id1658994153

 

‎로또킷

‎로또킷(lottoKit) 제공 기능을 소개 합니다 - 로또킷 A.I 분석 및 랜덤 조건으로 로또 행운번호를 자동생성 해보세요 - 로또킷은 내 주변 1등 로또 판매점을 안내해주고 바로 찾아갈 수 있도록 길

apps.apple.com

 

다시 본론으로 넘어가서 위 앱에서 내 위치와 가장 가까운 곳에 있는 로또 1등 판매점을 구하는 로직을 설명하겠습니다.

 

1. 1등 판매점 DB 생성

사용자의 위치 기반으로 연산을 해야 하기 때문에 1등 판매점 정보를 DB로 가지고 있어야 합니다. 개발할 당시에는 API도 없거니와 API가 있다고 해도 매번 트래픽을 발생시켜서 구하기 위해서는 사용자가 상당한 시간을 기다려야 합니다.

 

일단 간단히 아래와 같이 테이블을 만들어서 데이터를 적재합니다. (이 부분은 여러 방법이 있으므로 사용할 테이블 구조에 대해서만 설명하겠습니다.)

CREATE TABLE TB_TOP_STORE (
    TIMES INT NOT NULL,
    CODE VARCHAR(20) NOT NULL,
    NAME VARCHAR(40),
    ADDR VARCHAR(120),
    LAT VARCHAR(14),
    LON VARCHAR(14)
)

 

컬럼 순서대로 당첨회차, 판매점 코드, 판매점 명, 판매점 주소, 위도, 경도 입니다. 

 

2. 1등 판매점 기준 정하기

글을 작성하는 현재 기준에도 이미 1048회를 판매하고 있습니다. 1회에 당첨되거나 한 곳은 크게 의미가 없을 것 같아서 저 같은 경우는 최근 10회차 이내에 1등을 배출한 지점을 1등 판매점이라고 정의했습니다.

 

위 테이블 구조에서 최근 10회차 이내의 1등 당첨 판매점을 구하는 Query는 아래와 같이 작성합니다. (한 회차, 한 지점에서 여러 건의 1등 당첨이 있을 수 있으므로 DISTINCT 로 SELECT 합니다.)

SELECT  DISTINCT TIMES
     ,  CODE
     ,  NAME
     ,  ADDR
     ,  LAT
     ,  LON
  FROM  TB_TOP_STORE
 WHERE  TIMES > (SELECT  MAX(TIMES)
                   FROM  TB_TOP_STORE) - 10

 

3. 서버 API 만들기

위 Query를 결과에서 사용자의 위치와 가장 가까운 판매점을 구하는 서버 로직은 대강 아래와 같습니다. 사용자의 위치는 클라이언트 애플리케이션에서 현재 위치의 위도와 경도를 전달 받아야 합니다.

 

위 테이블의 데이터 구조를 받는 데이터 타입은 아래와 같이 정의합니다.

interface IStoreInfo {
    times: number;
    code: string;
    name: string;
    addr: string;
    lat: string;
    lon: string;
    dist: number;
}

 

테이블 구조와 동일하게 선언하되 현재 위치와의 거리를 연산하기 위해 dist라는 항목만 추가합니다.

 

Repository에서 데이터를 SELECT 합니다.

async selectTopStore(): Promise<IStoreInfo[]> {
    const topStore: IStoreInfo[] = await getConnection().query(this.queries.selectTopStore) as IStoreInfo[];
    return topStore;
}

 

여기까지 진행하면 10회차 이내의 1등 판매점이 모두 추출됩니다. 거리 오름차순으로 3건만 추출해야 하므로 먼저 아래 로직을 사용해서 사용자와 판매점 간의 거리를 구합니다.

/**
 * 두 지점 사이의 거리를 구한다.
 * @param slat 출발지 위도
 * @param slon 출발지 경도
 * @param dlat 도착지 위도
 * @param dlon 도착지 경도
 */
getDist(slat: number, slon: number, dlat: number, dlon: number): number {
    if (slat === dlat && slon === dlon) return 0;

    const radSlat: number = Math.PI * slat / 180;
    const radDlat: number = Math.PI * dlat / 180;
        
    const theta: number = slon - dlon;
    const radTheta: number = Math.PI * theta / 180;

    let dist: number = Math.sin(radSlat) * Math.sin(radDlat)
                     + Math.cos(radSlat) * Math.cos(radDlat) * Math.cos(radTheta);
    if (dist > 1) dist = 1;
        
    dist = Math.acos(dist);
    dist = dist * 180 / Math.PI;
    dist = dist * 60 * 1.1515 * 1.609344 * 1000;

    dist = dist < 100 ? Math.round(dist / 10) * 10 : Math.round(dist / 100) * 100;
    return dist;
}

 

위 코드를 이용해 지점 간의 거리를 구한 후 오름차순 정렬하여 가장 가까운 3건을 추출합니다.

async selectTopStore(lat: string, lon: string): Promise<IStoreInfo[]> {
    const topStore: IStoreInfo[] = await this.storeRepository.selectTopStore();

    const sorted: IStoreInfo[] = topStore.map((store: IStoreInfo) => {
        const dist: number = this.getDist(Number(lat), Number(lon), Number(store.lat), Number(store.lon));
        store.dist = dist;
        return store;
    }).sort((a: IStoreInfo, b: IStoreInfo) => a.dist - b.dist);

    return sorted.filter((store: IStoreInfo, inx: number) => inx < 3); 
}

 

4. 클라이언트에서 현재 위치 구하기

클라이언트에서는 아래 코드를 이용하여 현재 좌표를 구하여 서버로 전달하면 됩니다.

navigator.geolocation.getCurrentPosition((pos: GeolocationPosition) => {
    const slat: number = pos.coords.latitude;
    const slng: number = pos.coords.longitude;
});

 

위 코드의 결과물은 아래와 같습니다.

300x250

블로그에 정리한 내용들 중 React와 Express로 애플리케이션을 구성하여 이전 회차들의 당첨번호를 수집하여, 해당 데이터들로 통계를 분석하여 로또 추천(예측/예상) 번호를 생성해주는 애플리케이션을 사이드 프로젝트로 만들어 보았습니다.

 

아래 URL에서 다운로드 하실 수 있습니다.

https://play.google.com/store/apps/details?id=com.nextact.lottoapp

 

로또킷(lottoKit) - Google Play 앱

로또 추천번호 생성, 주변 로또판매점 바로가기, 로또 번호 선물하기 등 다양한 로또 정보를 제공하는 로또킷(lottoKit)입니다.

play.google.com

 

 

 

애플리케이션은 아래 포스트에서 설명한 구조로 구성되어 있습니다.

https://redballs.tistory.com/entry/React-Express-Typescript-애플리케이션-구성

 

React + Express + Typescript - 01. 서버 환경 구성

본 포스트에서는 Front-end는 리액트, Back-end는 Express 기반의 Node.js를 사용하여 하나의 프로젝트로 간단한 Application을 구성해 보겠습니다. 구성할 Application은 이전 포스트에서 진행했던 네이버 API

redballs.tistory.com

 

Back-end는 살짝 변경해서 NestJS 기반으로 구성했지만 별반 다를 내용은 없습니다. Front-end는 React + Typescript로 구성해 보았습니다. 원리는 수집한 자료를 기반으로 나름의 알고리즘으로 서버에서 분석을 해서 번호를 생성한 후 Front-end에 전달하면 Front-end에서 추천번호를 보여주는 방식입니다.

 

UX는 아래와 같이 구성되어 있습니다.

Lotto-Kit 메인 화면

 

로또 번호를 5개까지 생성할 수 있고 (마음에 들지 않는 번호는 재생성할 수 있습니다.) 근처에 있는 1등 당첨 판매점과 일반 로또 판매점을 조회할 수 있습니다. 또한 우리 앱에서 생성한 번호 중 실제로 1등~5등에 당첨된 번호가 있으면 [당첨내역]에서 조회할 수 있습니다. 

그리고 회차 별 당첨번호도 조회할 수 있고, 생성한 번호를 지인에게 선물할 수도 있습니다.

 

한 번쯤 애플리케이션에 관심이 있으신 분은 다운로드 하셔서 평가 부탁 드립니다.

 

구글 플레이스토어에서 "lottokit"을 검색하시거나 아래 URL로 바로 다운로드 하실 수 있습니다.

https://play.google.com/store/apps/details?id=com.nextact.lottoapp

 

로또킷(lottoKit) - Google Play 앱

로또 추천번호 생성, 주변 로또판매점 바로가기, 로또 번호 선물하기 등 다양한 로또 정보를 제공하는 로또킷(lottoKit)입니다.

play.google.com

 

300x250

+ Recent posts