๐Ÿ”ฅ TIL - Day 3

Kim Dae Hyunยท2021๋…„ 9์›” 15์ผ
0

TIL

๋ชฉ๋ก ๋ณด๊ธฐ
3/93

๐Ÿ“Œ ๋‹จ์–ด์žฅ ํ”„๋กœ์ ํŠธ open-api ํ™œ์šฉ

โœ”๏ธ ๋‹จ์–ด์ •๋ณด ๋ฐ›์•„์˜ค๊ธฐ (owlbot api)

OwlBot-API

API ์š”์ฒญ
owlbot์ด ์ œ๊ณตํ•ด์ฃผ๋Š” token์„ api-key๊ฐ’์œผ๋กœ ํ•˜์—ฌ ๋‹จ์–ด๋ฅผ ๊ฒฝ๋กœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•˜๋ฉด ํ•ด๋‹น ๋‹จ์–ด์˜ ์—ฌ๋Ÿฌ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.
(๋‹จ์–ด์ด๋ฆ„, ์†Œ๋ฆฌ, ํ’ˆ์‚ฌ,์˜ˆ๋ฌธ,์ด๋ฏธ์ง€ ...)

API ์š”์ฒญ ๊ฒฐ๊ณผ ์˜ˆ์‹œ

{
  "word": "owl",
  "pronunciation": "oul",
  "definitions": [
    {
      "type": "noun",
      "definition": "a nocturnal bird of prey with large eyes, a facial disc, a hooked beak, and typically a loud hooting call.",
      "example": "I love reaching out into that absolute silence, when you can hear the owl or the wind.",
      "image_url": "https://media.owlbot.info/dictionary/images/hhhhhhhhhhhhhhhhhhhu.jpg.400x400_q85_box-15,0,209,194_crop_detail.jpg",
      "emoji": "๐Ÿฆ‰"
    }
  ]
}

api ์š”์ฒญ fstring์„ ์ด์šฉํ•ด์„œ ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋ฌธ์ž๋ฅผ ๊ฒฝ๋กœ๋ณ€์ˆ˜๋กœ ํ•˜๊ณ  ๋ฐœ๊ธ‰๋ฐ›์€ ํ† ํฐ์„ ํ—ค๋”์— ๊ตฌ์„ฑํ•ด์„œ api์„œ๋ฒ„๋กœ ์š”์ฒญํ•œ๋‹ค.

flask ๊ฒฝ๋กœ๋ณ€์ˆ˜ ์‚ฌ์šฉ๋ฒ•

@app.route('/test/<param>')
def test(param):

์š”์ฒญ ํ›„ ๋ฐ›์€ ๊ฒฐ๊ณผ๋Š” json์œผ๋กœ ๋ณ€ํ™˜ ํ›„ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•œ๋‹ค.

r = requests.get(f'https://owlbot.info/api/v4/dictionary/{keyword}',
                     headers={'Authorization' : f'Token {owlApiKey}'})

result = r.json()

๊นƒํ—™์— ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ์‹ถ๋‹ค๋ฉด??
key(token) ์ˆจ๊ธฐ๊ธฐ (xml๋กœ key ๋ฆฌ์†Œ์Šค ์ž‘์„ฑ)

[keys.xml]

<?xml version="1.0" encoding="utf-8" ?>
<resource>
    <string name="owlbot-key">[my-apikey]</string>
</resource>

xml๋กœ ์ž‘์„ฑ๋œ ๋ฆฌ์†Œ์Šค ๊ฐ€์ ธ์˜ค๊ธฐ

import xml.etree.ElementTree as et
tree = et.parse('keys.xml')
owlApiKey = tree.find('string[@name="owlbot-key"]').text

.gitignore์— keys.xml ์„ค์ •


โœ”๏ธ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๋‹จ์–ด์ •๋ณด ๋žœ๋”๋ง (jinja2)

{
  "word": "owl",
  "pronunciation": "oul",
  "definitions": [
    {
      "type": "noun",
      "definition": "a nocturnal bird of prey with large eyes, a facial disc, a hooked beak, and typically a loud hooting call.",
      "example": "I love reaching out into that absolute silence, when you can hear the owl or the wind.",
      "image_url": "https://media.owlbot.info/dictionary/images/hhhhhhhhhhhhhhhhhhhu.jpg.400x400_q85_box-15,0,209,194_crop_detail.jpg",
      "emoji": "๐Ÿฆ‰"
    }
  ]
}

ํด๋ผ์ด์–ธํŠธ๋Š” ๋‹จ์–ด๋ฅผ ์š”์ฒญํ•˜๋ฉด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์œ„์™€ ๊ฐ™์€ jsonํฌ๋ฉง ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š”๋‹ค.

๋ฐ›์€ jsonํฌ๋ฉง ๋ฐ์ดํ„ฐ๋ฅผ HTML์— ๋งž์ถฐ ๋ผ์›Œ์ค˜์•ผ ํ•˜๋Š”๋ฐ ์ด๋•Œ jinja2 ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•œ๋‹ค.

jinja2 ํ…œํ”Œ๋ฆฟ ๊ฐ„๋‹จ ๋ฌธ๋ฒ•
๊ฐ’ํ‘œํ˜„: {{ }}
ํ˜•๋ณ€ํ™˜: {{ a|int }} (a๋ฅผ ์ •์ˆ˜๋กœ ๋ณ€ํ™˜)
if๋ฌธ : {% if a>=0 %} ... {% endif %}
for๋ฌธ : {% for row in rows %} ... {% endfor %}

ํŒŒ์ด์ฐธ jinja2 ํ…œํ”Œ๋ฆฟ ์„ค์ • (mac)
[perference]-[Template Languages]-HTML์„ Jinja2๋กœ ์„ค์ •

์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ result๋ผ๋Š” key๊ฐ’์œผ๋กœ api์˜ ์‘๋‹ต์— ๋Œ€ํ•œ ์‘๋‹ต์ด ๋‚ด๋ ค์™”๋‹ค๋ฉด {{ result['word'] }} or {{ result.word }} ์™€ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๊ฐ’์„ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿ“Œ ๋ง›์ง‘์ง€๋„ ํ”„๋กœ์ ํŠธ - Naver open API ํ™œ์šฉ

โœ”๏ธ Naver open-api ์š”์ฒญ

๋„ค์ด๋ฒ„ ํด๋ผ์šฐ๋“œ ํ”Œ๋žซํผ์˜ ์ฝ˜์†”๋กœ ์ด๋™



์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋“ฑ๋ก

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฆ„ ์ง€์ • ํ›„ Web Dynamic Map, Geocoding ์„ ํƒ,
์„œ๋น„์Šค ํ™˜๊ฒฝ ๋“ฑ๋ก - web ์„œ๋น„์Šค URL ๋“ฑ๋ก

๋“ฑ๋ก ํ›„ ์ธ์ฆ์ •๋ณด ํ™•์ธ

Client ID
(X-NCP-APIGW-API-KEY-ID)
Client Secret
(X-NCP-APIGW-API-KEY)

โœ”๏ธ ๋„ค์ด๋ฒ„ ์ง€๋„ API ์‚ฌ์šฉ

htmlํŒŒ์ผ์— API ์š”์ฒญ ์Šคํฌ๋ฆฝํŠธ ์ถ”๊ฐ€

<script type="text/javascript"
            src="https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=[ํด๋ผ์ด์–ธํŠธID]"></script>

naver-map ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜ ์ •์˜

$(document).ready(function () {
    let mymap = new naver.maps.Map('map', {
      center: new naver.maps.LatLng(์œ„๋„, ๊ฒฝ๋„), 
      zoom: 10, 
      zoomControl: true, 
      zoomControlOptions: {
        style: naver.maps.ZoomControlStyle.SMALL,
        position: naver.maps.Position.TOP_RIGHT
      }      
   });	                               

center: ์ง€๋„์˜ ์‹œ์ž‘ ์œ„๋„,๊ฒฝ๋„ ์ง€์ •
zoom: ์ง€๋„ ์‹œ์ž‘์‹œ ์คŒ ๋ฐฐ์œจ ์ง€์ •
zoomControl: ์คŒ ์กฐ์ ˆ ๋ฒ„ํŠผ ํ™œ์„ฑํ™”
zoomControlOptions: ์คŒ ์กฐ์ ˆ ๋ฒ„ํŠผ ์Šคํƒ€์ผ ์„ค์ •

marker ์ถ”๊ฐ€

let marker = new naver.maps.Marker({
  position: new naver.maps.LatLng(์œ„๋„, ๊ฒฝ๋„), 
  map: mymap,
  icon: "{{ url_for('static', filename='image.png') }}" // ๋งˆ์ปค ์ด๋ฏธ์ง€
});

position: ๋งˆ์ปค์˜ ์‹œ์ž‘ ์œ„๋„,๊ฒฝ๋„ ์ง€์ •
map: ํ•ด๋‹น ๋งˆ์ปค๊ฐ€ ํ‘œ์‹œ๋  map ์ง€์ •
icon: ๋งˆ์ปค์˜ icon ์ง€์ •

infowindow ์ถ”๊ฐ€
infowindow: ๋งˆ์ปค ํด๋ฆญ์‹œ ๋‚˜ํƒ€๋‚˜๋Š” ์ฐฝ

let infowindow = new naver.maps.InfoWindow({
  content: `<div><h5>infoWindow content</h5></div>`,
});

marker์— ์ด๋ฒคํŠธ ์ถ”๊ฐ€ (ํด๋ฆญ์‹œ infowindow ์—ด๊ธฐ/๋‹ซ๊ธฐ)

naver.maps.Event.addListener(marker, "click", function () { // marker ํด๋ฆญ์‹œ ์ด๋ฒคํŠธ ์ง€์ •
  if (infowindow.getMap()) { // infowindow๊ฐ€ ์—ด๋ ค์žˆ๋‹ค๋ฉด true
    infowindow.close(); // ์—ด๋ ค์žˆ๋‹ค๋ฉด (true๋ผ๋ฉด) close
  } else {
    infowindow.open(map, marker);
  }
});

โœ”๏ธ Naver Geocoding api ์š”์ฒญ

Geocoding api๋Š” ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ ํ•ด๋‹น ์ฃผ์†Œ์˜ ์ƒ์„ธ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด์ฃผ๋Š” API์ด๋‹ค.

์‘๋‹ต ์˜ˆ์‹œ Json
ํ”„๋กœ์ ํŠธ์—์„œ ํ•„์š”ํ•œ ์ •๋ณด๋Š” ๊ฒฝ๋„, ์œ„๋„ ์ •๋ณด์ธ x, y ์ด๋ฏ€๋กœ ์š”์ฒญ ํ›„ ๋‘ ์ •๋ณด๋งŒ ํŒŒ์‹ฑํ•œ๋‹ค.

{
    "status": "OK",
    "meta": {
        "totalCount": 1,
        "page": 1,
        "count": 1
    },
    "addresses": [
        {
            "roadAddress": "๊ฒฝ๊ธฐ๋„ ์„ฑ๋‚จ์‹œ ๋ถ„๋‹น๊ตฌ ๋ถˆ์ •๋กœ 6 ๊ทธ๋ฆฐํŒฉํ† ๋ฆฌ",
            "jibunAddress": "๊ฒฝ๊ธฐ๋„ ์„ฑ๋‚จ์‹œ ๋ถ„๋‹น๊ตฌ ์ •์ž๋™ 178-1 ๊ทธ๋ฆฐํŒฉํ† ๋ฆฌ",
            "englishAddress": "6, Buljeong-ro, Bundang-gu, Seongnam-si, Gyeonggi-do, Republic of Korea",
            "addressElements": [
                {
                    "types": [
                        "POSTAL_CODE"
                    ],
                    "longName": "13561",
                    "shortName": "",
                    "code": ""
                }
            ],
            "x": "127.10522081658463",
            "y": "37.35951219616309",
            "distance": 20.925857741585514
        }
    ],
    "errorMessage": ""
}

API ์š”์ฒญ
address๋Š” ์Šคํฌ๋ž˜ํ•‘์„ ํ†ตํ•ด ์–ป์–ด์˜จ ๋ง›์ง‘์ฃผ์†Œ๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
์ด์ „๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ API์˜ ์‘๋‹ต๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•˜๊ธฐ ์œ„ํ•ด Json์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

r = requests.get(f"https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query={address}", headers=headers)

response = r.json()

API ์š”์ฒญํ—ค๋” ๊ตฌ์„ฑ (keyํฌํ•จ)

headers = {
    "X-NCP-APIGW-API-KEY-ID": "[ํด๋ผ์ด์–ธํŠธID]",
    "X-NCP-APIGW-API-KEY": "[ํด๋ผ์ด์–ธํŠธSecret]"
}

API ์‘๋‹ต ํŒŒ์‹ฑ

x = float(response["addresses"][0]["x"])
y = float(response["addresses"][0]["y"])


๋‘ ๊ฐœ ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด open api๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค. ์ž˜ ๋ชจ๋ฐฉํ•˜๊ณ  ์ž˜ ๊ฐ–๋‹ค ์“ฐ๋Š” ๊ฒƒ๋„ ๋งค์šฐ ์ค‘์š”ํ•œ ๊ฐœ๋ฐœ ๋Šฅ๋ ฅ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ˆ ์ž˜~ ์“ฐ๊ฒ ์Šต๋‹ˆ๋‹ค ๐Ÿ˜

profile
์ข€ ๋” ์ฒœ์ฒœํžˆ ๊นŒ๋จน๊ธฐ ์œ„ํ•ด ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๐Ÿง

0๊ฐœ์˜ ๋Œ“๊ธ€