[TypeScript] "장소선택 및 공유"앱 구현하기(Google 맵 포함)

HIHI JIN·2023년 3월 24일
0

typescript

목록 보기
8/10
post-thumbnail

타입스크립트 핸드북Udemy강의를 토대로 Typescript를 공부합니다.

프로젝트 설정

webpack과 typescript 개발환경 만들기

1. package.json에서 typescript : 현재 typescript버전으로 바꿔야 한다.
4보다 낮으면 최신버전으로 새로 깔아야 한다.
2. npm install --save-dev typescript ts-loader
3. npm install -D webpack webpack-cli webpack-dev-server html-webpack-plugin 
4. npm start

<간단한 웹 어플리케이션 기능 구현>
주소를 검색하고 한쌍의 좌표로 변환하고, 그 좌표를 렌더링할 것이다.
구글 API중 구글맵 자바스크립트 SDK의 도움을 받는다.
2개의 써드파티 라이브러리로 작업 진행
(HTTP전송 라이브러리(axios), 구글맵 라이브러리)


3rd party, 써드파티?
프로그래밍 개발과 개발자 사이에 프로그래밍을 도와주는 플러그인, 라이브러리, 프레임워크 등을 서드파티라 한다.
개인 개발자나 프로젝트 팀, 혹은 업체등에서 개발하는 라이브러리
즉 제 3자 라이브러리 = 3rd party
이처럼 제 3자로써 중간다리 역할을 하는 것을 써드파티라고 한다.


Geocoding request and response URL (공식 문서)
https://maps.googleapis.com/maps/api/geocode/json?address={addressInput.value}&key={YOUR_API_KEY}


구글 지오코딩(geocoding) API 발급받기 (공식 문서)
지오코딩 API : 주소를 한쌍의 좌표로 변환하거나 좌표를 주소로 변환하는데 사용할 수 있는 API
지오코딩에 get 요청해 좌표를 돌려 받을 것이다.
요청을 URL로 보내려면 API키가 필요하다.
구글계정과 신용카드 필요


써드파티 - axios패키지 사용하기
axios 패키지 : HTTP 요청을 전송하기 위한 용도이며, js 및 ts에서도 사용 가능하다.

npm install --save axios
import axios from 'axios';

설치하면 node module에 axios패키지가 생기는데,
그 안에 index.d.ts파일이 있다.
axios라이브러리 작성자들은 이미 라이브러리를 타입스크립트 변환 패키지와 함께 묶어 놓았다.
이 패키지나 파일은 기본적으로 자바스크립트 라이브러리가 무엇을 하고 어떤 타입을 사용하는지 타입스크립트에게 설명하고 있다.
이를 내장 타입변환을 갖춘 라이브러리라 한다. 추가 타입 설치가 필요없다.
내장 지원이 있으므로 바로 axios 사용 가능


써드파티 - 구글맵 라이브러리 사용하기
html파일에 구글맵 SDK script를 추가했으므로, 전역적으로 이용가능하지만
타입스크립트에서 'google'이라는 이름을 찾을 수 없다는 내용의 에러가 발생한다.

//해결방법!
declare var google: any;

var google로 선언하여 타입을 any로 설정하면,
'google'키워드가 존재하고 있음을 타입스크립트에게 알려줄 수 있다.
구글맵과 관련해 코드에 실수를 한다면,
declare var google덕분에 컴파일 시 에러는 발생하지 않지만
지도를 렌더링할때, 런타임 중에 에러를 발생시킨다.

개발 중에 에러 처리를 하고 싶다면, 구글맵에 타입지원을 해야 한다.
타입구글맵 패키지는 구글맵 관련 타입을 추가하는 패키지

npm install --save-dev @types/googlemaps

타입구글맵을 설치했다면 이제 var google을 선언하지 않아도 되고, googlemap과 관련된 코드 실수도 컴파일시 에러을 찾을 수 있고, 코드생성시 자동완성도 뜨게 된다.

*SDK?
SDK란 Software Development Kit의 약자, 소프트웨어 개발 도구 모음
SDK는 API, IDE, 문서, 라이브러리, 코드 샘플 및 기타 유틸리티가 포함될 수 있다.
SDK는 프로그램 및 응용 프로그램 개발의 복잡성을 줄이는 강력한 기능 집합이다.

예)
iOS SDK를 다운로드하면 개발자에게 iOS 애플리케이션을 만드는 데 필요한 모든 도구가 제공된다.
iOS SDK는 개발자가 컴퓨터에서 iPhone 시뮬레이션을 할 수 있는 iPhone 시뮬레이터도 포함하고 있다.
즉, 어떤 소프트웨어를 만들기 위한 도구 모음을 SDK라고 한다. 그리고 이 도구엔 API도 포함되어 있다.


완성 코드

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Understanding TypeScript</title>
    <!--구글맵 SDK를 어플리케이션에 추가하는 script를 추가해야 한다.-->
    <script
      async
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDh57nz_bescw4I5G02XiVWPtvV3NU8bJo"
    ></script>
    <script src="dist/bundle.js" defer></script>
    <link rel="stylesheet" href="app.css" />
  </head>
  <body>
    <div id="map">
      <!--나중에 이 div태그에 맵을 렌더링하는 것이 목적-->
      <p>Please enter an address!</p>
    </div>
    <form>
      <input type="text" id="address" />
      <!--주소 입력창-->
      <button type="submit">SEARCH ADDRESS</button
      ><!--제출하면 주소를 좌표로 변환하여 지도상에 렌더링하는게 목적-->
    </form>
  </body>
</html>

app.css

html {
  font-family: sans-serif;
}

#map {
  width: 90%;
  height: 20rem;
  border: 1px solid #ccc;
  margin: 2rem auto;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

form {
  text-align: center;
  margin: 2rem auto;
}

app.ts

import axios from 'axios';

const form = document.querySelector("form")!; //null이 아니라는 것을 의미
const addressInput = document.getElementById("address")! as HTMLInputElement;
//addressInput.value를 사용하려면 이 요소가 input요소임을 타입스크립트가 알아야 하므로 타입캐스팅

const GOOGLE_API_KEY = '나의_API_KEY';

//declare var google: any;

//axios.get메서드에 자체 타입 알리어스(사용자 지정 타입) 만들기
type GoogleGeocodingResponse = {
    results: { geometry: { location: { lat: number; lng: number } } }[];
    status: 'OK' | "ZERO_RESULTS";
    //status는 구글이 성공했는지 여부를 알려주는 문자열, OK혹은 다른값 여러개가 있지만
    //여기서는 두개의 값중 하나로 상태코드를 받았음을 타입스크립트에게 전달한다.
}

function searchAddressHandler(event: Event){ //Event타입
    event.preventDefault(); //submit기본동작인 요청 전송과 새로고침을 막아준다.
    const enteredAddress = addressInput.value;//브라우저에서 input에 입력한 주소값

    //주소에 따른 좌표를 받기위한 axios.get요청
    //enteredAddress는 사용자가 입력한 텍스트 그대로로, 특수문자, 공백, 쉼표 등이 포함될 수 있다.
    //encodeURI()는 입력한 문자열을 URL에서 호환가능한 문자열로 변환시켜주는 내장함수

    //get메서드를 제네릭메서드로 만들어 타입스크립트에게 우리가 기대하는 response를 전달할 수 있다.
    //제네릭 메서드로 만든 후 res.data에 .을 입력하면 자동완성으로 코드가 보여진다.
    axios
    .get<GoogleGeocodingResponse>(
    `https://maps.googleapis.com/maps/api/geocode/json?address=
    ${encodeURI(enteredAddress)}&key=${GOOGLE_API_KEY}`)
    .then(res => { //get요청을 하고 난 후 받아온 결과값(위도,경도 좌표객체가 나와야 한다.)
        //console.log(res);

        //상태코드가 ok가 아닐 경우 에러 대응
        if(res.data.status !== "OK") throw new Error('Could not fetch location');
        const coordinates = res.data.results[0].geometry.location;
        console.log(coordinates);
        //res객체에는 data필드가 있고, 그 안에 results필드가 있고 results필드는 배열이다.
        //배열안의 요소들이 구글이 검색한 결과이고 일반적으로 첫번째 결과가 가장 좋다.
        //요소에 들어가면 양식화된 주소가 있는데, 우리가 입력한 것에 비해 좀 더 완성된 주소로 바뀌어 있다.
        //다음은 geometry키가 있다. 열면 위치가 보이고, 우리가 원하는 위도와 경도 좌표를 갖춘 객체가 있다.
        //res.data > results > geometry > location : {lat: 0, lng: 0} =위도&경도를 찾는 경로
        //우리는 이 위도와 경도를 지도 상에 렌더링 시켜야 한다.

        
        //지도의 2가지 필수옵션 center,zoom
        //공식문서에 있는 google.map.constructor 함수를 인스턴스화 해야 한다.
        //좌표를 얻으면 이 코드를 실행시킨다.
        const map = new google.maps.Map(document.getElementById('map')!, { //map이 없을수도 있다고 에러 났음
            center: coordinates, //위도와 경도의 객체
            zoom: 16 //확대레벨, 숫자가 높을수록 지도가 확대되어 렌더링된다.
          });
          //지도가 렌더링 될 때, 마커도 표시하고 싶다면 공식문서에서 마커로 맵추가하기 참고!
          new google.maps.Marker({position: coordinates, map: map});
    })
    .catch(err => {
        console.log(err); //get요청 실패시 err를 찍어준다.
        alert(err.message);//위의 코드 상태코드가 ok가 아닐경우에 에러메세지를 보여준다.
    });
}

form.addEventListener('submit', searchAddressHandler);

app.js

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const axios_1 = __importDefault(require("axios"));
const form = document.querySelector("form");
const addressInput = document.getElementById("address");
const GOOGLE_API_KEY = '나의_API_KEY';
function searchAddressHandler(event) {
    event.preventDefault();
    const enteredAddress = addressInput.value;
    axios_1.default.get(`https://maps.googleapis.com/maps/api/geocode/json?address=
    ${encodeURI(enteredAddress)}&key=${GOOGLE_API_KEY}`)
        .then(res => {
        const coordinates = res.data.result[0].geometry.location;
        console.log(coordinates);
        const map = new google.maps.Map(document.getElementById('map'), {
            center: coordinates,
            zoom: 8
        });
        new google.maps.Marker({ position: coordinates, map: map });
    })
        .catch(err => {
        console.log(err);
        alert(err.message);
    });
}
form.addEventListener('submit', searchAddressHandler);
//# sourceMappingURL=app.js.map

package.json

{
  "name": "understanding-typescript",
  "version": "1.0.0",
  "description": "Understanding TypeScript Course Setup",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server",
    "build": "webpack"
  },
  "keywords": [
    "typescript",
    "course"
  ],
  "author": "Maximilian Schwarzmüller",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "@types/googlemaps": "^3.43.3",
    "babel-loader": "^9.1.2",
    "html-webpack-plugin": "^5.5.0",
    "lite-server": "^2.6.1",
    "ts-loader": "^6.2.2",
    "typescript": "^5.0.2",
    "webpack": "^4.46.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.3"
  },
  "dependencies": {
  	"axios": "^0.21.4"
  }

좌표 받아오기 성공!


지도 렌더링 성공!

⬆️ zoom : 8로 설정

⬆️ zoom : 16으로 설정

profile
신입 프론트엔드 웹 개발자입니다.

0개의 댓글