강남 맛집 50곳의 정보를 정리해줍니다.
맛집 검색 사이트에서 50곳을 가져와 엑셀 파일로 정리해줬습니다.
그러나 맛집이 30개만 보여주어서 제가 좋아하는 카페도 20곳을 넣어줬습니다.
다음과 같이 총 50개의 데이터를 뽑아 정리했습니다.
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| store_name | varchar(30) | YES | | NULL | |
| address | varchar(255) | NO | | NULL | |
| x | varchar(255) | YES | | NULL | |
| y | varchar(255) | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
Field Name | Value |
---|---|
id | 가게들의 구분 번호 |
store_name | 가게 이름 |
address | 가게 주소 |
x | 지도상에 가게의 x좌표 |
y | 지도상에 가게의 y좌표 |
테이블에 값을 추가해주기 위해 SQL문으로 변경해주겠습니다.
SQL문으로 변경해주기 위해
엑셀에 새로운 필드에 값을 추가해준다.
="INSERT INTO [테이블명] (필드1, 필드2, 필드3 ...) VALUES (값1("&레이블&, &레이블&, &레이블& ...)";
을 추가하여 드래그를 해주면
자동으로 레이블에 위치한 값들이 들어갑니다.
이를 복사하여 값을 넣어줍니다.
다음과 같이 레코들이 저장됩니다.
데이터베이스를 연결하여 address
를 읽고 그 값을 geocoder에 넣어 좌표 값을 얻어오겠습니다.
php는 공통으로 사용되는 코드를 미리 정의해놓고 include를 통해 작성된 코드를 불러올 수 있습니다.
예를 들어,
- hsy.php
하세요.
가 작성 돼 있으면
- hi.php
안녕
include "hsy.php";안녕하세요.
와 같은 값이 나타납니다.
이를 이용하여
데이터베이스에 연결하는 코드를 공통 코드로 작성해주기 위해 다음과 같이 작성해줍니다.
<?php $server = "localhost"; $user = "[데이터베이스 유저 이름]"; $password = "데이터베이스 비밀번호"; $dbname = "[테이블명]"; $connect = mysqli_connect($server, $user, $password); $db_conn = mysqli_select_db($connect, "[데이터베이스명]"); ?>
<html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!-- PHP 작성 --> <?php include "db_conn.php"; $sql = "SELECT * FROM store WHERE x is null order by id asc"; $result = mysqli_query($connect, $sql); if($result) { ?> <script> alert("연결에 성공했습니다.");</script> <?php } else{ ?> <script> alert("연결에 실패했습니다.");</script> <?php } ?> <?php // SQL을 설정하여 좌표값이 비어있는 id를 순차적으로 조회 while ($row = mysqli_fetch_array($result)) { echo $row['address']."<br>"; ?> <script> var geocoder = new kakao.maps.services.Geocoder(); // 주소-좌표 변환 객체를 생성합니다 // 주소로 좌표를 검색합니다 geocoder.addressSearch('<?= $row['address'] ?>', function(result, status) { // 정상적으로 검색이 완료됐으면 if (status === kakao.maps.services.Status.OK) { var coords = new kakao.maps.LatLng(result[0].y, result[0].x); console.log(result[0].y + " / " + result[0].x); save_geo(result[0].y, result[0].x, <?=$row['id'] ?>); } else { } }); </script> <?php } mysqli_close($connect); ?> <script> function save_geo(x, y, id) { var form = document.createElement('form'); form.setAttribute('method', 'post'); form.setAttribute('target', '_blank'); form.setAttribute('action', 'save_geo.php'); document.charset = "UTF-8"; var hiddenField1 = document.createElement('input'); hiddenField1.setAttribute('type', 'hidden'); hiddenField1.setAttribute('name', 'id'); hiddenField1.setAttribute('value', id); var hiddenField2 = document.createElement('input'); hiddenField2.setAttribute('type', 'hidden'); hiddenField2.setAttribute('name', 'x'); hiddenField2.setAttribute('value', x); var hiddenField3 = document.createElement('input'); hiddenField3.setAttribute('type', 'hidden'); hiddenField3.setAttribute('name', 'y'); hiddenField3.setAttribute('value', y); document.body.appendChild(form); form.appendChild(hiddenField1); form.appendChild(hiddenField2); form.appendChild(hiddenField3); form.submit(); } </script> </body> </html>
가져온 값을 폼에 넣은 후, 좌표를 저장하는 기능을 가진📄 save_geo.php에 값을 넘겨 데이터베이스에 저장합니다.
<html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <?php include "db_conn.php"; $id = $_POST['id']; $x = $_POST['x']; $y = $_POST['y']; echo "id : ".$id." = "; echo "x : ".$x." / "; echo "y : ".$y; $sql = "UPDATE store SET x=$x, y=$y WHERE id=$id"; $result = mysqli_query($connect, $sql); mysqli_close($connect); ?> </body> </html>
다음과 같은 코드를 이용해서 페이지를 실행시키면
자동으로 값들이 넘어가고
console.log()
를 통해 얻어진 값들을 확인할 수 있습니다.
+----+------------------------------------+-------------------------------------------------------------------------------------------+------------------+------------------+
| id | store_name | address | x | y |
+----+------------------------------------+-------------------------------------------------------------------------------------------+------------------+------------------+
| 1 | 고메램 | 서울특별시 강남구 삼성로96길 15 송영빌딩 | 37.5090904790547 | 127.057075333082 |
| 2 | 육전식당 | 서울특별시 강남구 테헤란로8길 11-4 신도빌딩 1F | 37.4980495536809 | 127.031990525034 |
| 3 | 묵전 | 서울특별시 강남구 언주로168길 22 | 37.5260905512061 | 127.035349303391 |
| 4 | 대우부대찌개 | 서울특별시 강남구 테헤란로25길 34 | 37.5027507897379 | 127.035271910674 |
| 5 | 뽕나무쟁이 | 서울특별시 강남구 역삼로65길 31 1F | 37.5031995277517 | 127.052001164203 |
| 6 | 고갯마루집 | 서울특별시 강남구 테헤란로28길 27 | 37.4993986379075 | 127.03941842768 |
| 7 | 리북집 | 서울특별시 강남구 학동로2길 45 | 37.5087080835741 | 127.023588823983 |
| 8 | 돝고기506 | 서울특별시 강남구 역삼로17길 53 | 37.4982482701982 | 127.036206152949 |
| 9 | 땅코참숯구이 | 서울특별시 강남구 도곡로7길 28 | 37.4925710310399 | 127.034381266758 |
| 10 | 카브루브루펍 | 서울특별시 강남구 압구정로79길 19 레인애비뉴 101호 | 37.5253737056184 | 127.048780188752 |
| 11 | 명인등심 | 서울특별시 강남구 도산대로100길 13 | 37.5238228613413 | 127.052038439495 |
| 12 | 모범갈빗살 | 서울특별시 서초구 서초대로73길 40 | 37.5009272542176 | 127.024557940948 |
| 13 | 송쉐프 | 서울특별시 강남구 도산대로1길 40 | 37.5193386747828 | 127.019615704918 |
| 14 | 일일향 | 서울특별시 서초구 서초대로78길 22 | 37.4958057253368 | 127.027547429997 |
| 15 | 홍백 | 서울특별시 강남구 삼성로 571 | 37.5123480626965 | 127.053210303682 |
| 16 | 구스아일랜드 브루하우스 | 서울특별시 강남구 역삼로 118 | 37.4934315723756 | 127.032179999229 |
| 17 | 제주몬트락 | 서울특별시 강남구 강남대로78길 16 | 37.4949414141627 | 127.030734893816 |
| 18 | 초선과여포 | 서울특별시 서초구 서초대로78길 44 | 37.493807077575 | 127.028452355866 |
| 19 | 삼육가 | 서울특별시 강남구 강남대로114길 8 | 37.5056918554336 | 127.025018227907 |
| 20 | 셰막 | 서울특별시 강남구 강남대로 442 | 37.5020628314474 | 127.026055773707 |
| 21 | 호남마을 | 서울특별시 강남구 역삼로7길 2 | 37.4943456433573 | 127.033479240817 |
| 22 | 파크루안 | 서울특별시 강남구 논현로 430 | 37.499001193592 | 127.038355416717 |
| 23 | 문어앤치킨 | 서울특별시 강남구 삼성로104길 24 | 37.5111425206875 | 127.056692899298 |
| 24 | 온돌더프라임 | 서울특별시 강남구 테헤란로 129 | 37.4999326440603 | 127.032367803374 |
| 25 | 천미미 | 서울특별시 강남구 강남대로152길 11 | 37.517382475711 | 127.020330650586 |
| 26 | 신동궁감자탕 | 서울특별시 강남구 테헤란로10길 11 | 37.4976452465943 | 127.032741357124 |
| 27 | 육시리 | 서울특별시 강남구 논현로 420 | 37.4978572977128 | 127.038840424582 |
| 28 | 삼미숯불갈비 | 서울특별시 강남구 학동로2길 30 | 37.5094736193094 | 127.022917114554 |
| 29 | 창고43 | 서울특별시 강남구 강남대로 362 | 37.4953055764745 | 127.029416048324 |
| 30 | 성북동청국장 | 서울특별시 강남구 봉은사로104길 6 | 37.5139610210377 | 127.061937258246 |
| 31 | 장꼬방 | 서울특별시 서초구 강남대로61길 27 | 37.4983360267419 | 127.023793980532 |
| 32 | 어퍼앤언더 | 서울특별시 강남구 강남대로102길 28 B1-2F | 37.5027388377688 | 127.027719504577 |
| 33 | 썸띵어바웃커피 | 서울특별시 강남구 강남대로102길 30 1-3F | 37.5028324298668 | 127.027814764233 |
| 34 | 원아베뉴 | 서울특별시 강남구 테헤란로7길 11 | 37.4998890201223 | 127.03038996316 |
| 35 | 타짜도르 | 서울특별시 서초구 서초대로77길 61 SG타워 1F | 37.5024331030407 | 127.023686484894 |
| 36 | 마키아티 | 서울특별시 강남구 강남대로84길 15 효성해링턴타워 더 퍼스트 106호 | 37.4970034183304 | 127.02983569457 |
| 37 | 겟썸커피 다운스테어 | 서울특별시 강남구 봉은사로6길 30 | 37.5030771832102 | 127.027255717013 |
| 38 | 스크렘 | 서울특별시 강남구 봉은사로4길 17 | 37.5037271673496 | 127.026468923524 |
| 39 | 리퍼크 | 서울특별시 강남구 강남대로 382 메리츠타워 1F | 37.4970486776201 | 127.028627058312 |
| 40 | 트리오드 | 서울특별시 강남구 강남대로94길 28 유니언타운 LF | 37.4996425246433 | 127.029602310921 |
| 41 | 밀도 | 서울특별시 서초구 강남대로 419 1F | 37.4998007315864 | 127.026210824749 |
| 42 | 커먼위켄드 | 서울특별시 강남구 봉은사로6길 34 스타제이빌딩 1F | 37.5028323549489 | 127.027363971961 |
| 43 | 꽃을피우고 | 서울특별시 강남구 봉은사로18길 80 | 37.5016033846107 | 127.027357416481 |
| 44 | IFMT | 서울특별시 강남구 역삼로 121 | 37.4940648495544 | 127.032656566707 |
| 45 | 베이커리로컬 | 서울특별시 강남구 강남대로102길 15 | 37.5022103870338 | 127.026988276372 |
| 46 | 정월 | 서울특별시 강남구 강남대로102길 46 1F | 37.5036597194557 | 127.028626641848 |
| 47 | 브릭샌드 | 서울특별시 강남구 강남대로 374-2 강남역 신분당선 B1 4호 | 37.4962880387828 | 127.028664424942 |
| 48 | 알베르 | 서울특별시 강남구 강남대로102길 34 | 37.5030565318496 | 127.028103011462 |
| 49 | 가배도 | 서울특별시 강남구 강남대로110길 13 2-3F | 37.504032006687 | 127.02593114799 |
| 50 | 카페마마스 | 서울특별시 서초구 서운로 138 | 37.495994185 | 127.025267395048 |
+----+------------------------------------+-------------------------------------------------------------------------------------------+------------------+------------------+
이제 이 좌표값들을 갖고 카카오 맵 API를 이용하여 카카오 지도를 띄우고 지도에 해당 가게들의 위치를 저장해보겠습니다.
카카오 API 사용을 위한 KEY값을 발급해주고,
지도를 생성합니다.
그 후, 데이터베이스를 연결해 좌표값을 가져와 각 마커에 넣어주고 뿌려줍니다.
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=c3f57535868ce6095b1eccaba9087bf2&libraries=services"></script> <link rel="stylesheet" href="macarong_Toy.css"> <script src="macarong_Toy.js"></script> </head> <body> <div > <div id='map' class="display:inline-block" style="width: 100%; height: 100%;"></div> <div id="btnSection"> <button onclick="makeMarker()" id="btnViewAll">전체보기</button> </div> <!-- <div class="display:inline-block" style="background-color:blue; width:10%; height:200px; margin-left:600px" ></div> --> <div id="listView"> </div> <script> var mapContainer = document.getElementById('map'), // 지도를 표시할 div mapOption = { center: new kakao.maps.LatLng(37.496486063, 127.028361548), // 지도의 중심좌표 level: 4 // 지도의 확대 레벨 }; // 지도를 표시할 div와 지도 옵션으로 지도를 생성합니다 var map = new kakao.maps.Map(mapContainer, mapOption); </script> <p id="result"></p> <?php include "db_conn.php"; $sql = "SELECT * FROM store"; $result = mysqli_query($connect, $sql); ?> <script> var positions = []; </script> <?php while ($row = mysqli_fetch_array($result)) { // echo $row['store_name'] . "<br>"; // echo $row[' x'] . "<br>"; // echo $row['y'] . "<br>"; ?> <script> positions.push({ title: '<?= $row['store_name'] ?>', content: '<?= $row['store_name'] ?>', latlng: new kakao.maps.LatLng(<?= $row['x'] ?>, <?= $row['y'] ?>, coords = new kakao.maps.Coords(<?= $row['x'] ?>, <?= $row['y'] ?>)) }); </script> <?php } ?> <script> var markers = []; var storeMarkers = []; var cafeMarkers = []; var cafeImageSrc = 'images/markers/markerCoffee.png', // 마커이미지의 주소입니다 imageSize = new kakao.maps.Size(31, 35), // 마커이미지의 크기입니다 imageOption = {offset: new kakao.maps.Point(27, 69)}; // 마커이미지의 옵션입니다. 마커의 좌표와 일치시킬 이미지 안에서의 좌표를 설정합니다. var cafeImage = new kakao.maps.MarkerImage(cafeImageSrc, imageSize, imageOption); var storeImageSrc = 'images/markers/markerStore.png', // 마커이미지의 주소입니다 imageSize = new kakao.maps.Size(31, 35), // 마커이미지의 크기입니다 imageOption = {offset: new kakao.maps.Point(27, 69)}; // 마커이미지의 옵션입니다. 마커의 좌표와 일치시킬 이미지 안에서의 좌표를 설정합니다. var storeImage = new kakao.maps.MarkerImage(storeImageSrc, imageSize, imageOption); // 마커가 표시될 위치입니다 for (var i = 0; i < 36; i++) { // 마커를 생성합니다 var marker = new kakao.maps.Marker({ // map: map, // 마커를 표시할 지도 position: positions[i].latlng, // 마커의 위치 image : storeImage }); // marker.title = positions[i].title; // 마커에 표시할 인포윈도우를 생성합니다 var infowindow = new kakao.maps.InfoWindow({ content: positions[i].content, // 인포윈도우에 표시할 내용 }); markers.push(marker); cafeMarkers.push(marker); kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow)); kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow)); kakao.maps.event.addListener(marker, 'mouseover', markerCircle(map, marker)); kakao.maps.event.addListener(marker, 'mouseout', clearCircle(map, marker)); kakao.maps.event.addListener(marker, 'click', circleInMarker(marker, circle)); kakao.maps.event.addListener(marker, 'click', testClick(marker)); // 마커가 지도 위에 표시되도록 설정합니다 marker.setMap(map); } for (var i = 36; i < positions.length; i++) { // 마커를 생성합니다 var marker = new kakao.maps.Marker({ // map: map, // 마커를 표시할 지도 position: positions[i].latlng, // 마커의 위치 image : cafeImage }); // marker.title = positions[i].title; // 마커에 표시할 인포윈도우를 생성합니다 var infowindow = new kakao.maps.InfoWindow({ content: positions[i].content, // 인포윈도우에 표시할 내용 }); markers.push(marker); storeMarkers.push(marker); kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow)); kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow)); kakao.maps.event.addListener(marker, 'mouseover', markerCircle(map, marker)); kakao.maps.event.addListener(marker, 'mouseout', clearCircle(map, marker)); kakao.maps.event.addListener(marker, 'click', circleInMarker(marker, circle)); kakao.maps.event.addListener(marker, 'click', testClick(marker)); // 마커가 지도 위에 표시되도록 설정합니다 marker.setMap(map); } function testClick(marker) { return function() { } } // 인포윈도우를 표시하는 클로저를 만드는 함수입니다 function makeOverListener(map, marker, infowindow) { return function() { infowindow.open(map, marker); }; } // 인포윈도우를 닫는 클로저를 만드는 함수입니다 function makeOutListener(infowindow) { return function() { infowindow.close(); }; } var circle; kakao.maps.event.addListener(map, 'click', function(mouseEvent) { var latlng = mouseEvent.latLng; if (circle) { circle.setMap(null); } circle = new kakao.maps.Circle({ center: new kakao.maps.LatLng(latlng.getLat(), latlng.getLng()), // 원의 중심좌표 입니다 radius: 1000, // 미터 단위의 원의 반지름입니다 strokeWeight: 1, // 선의 두께입니다 strokeColor: '#75B8FA', // 선의 색깔입니다 strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다 strokeStyle: 'line', // 선의 스타일 입니다 fillColor: '#CFE7FF', // 채우기 색깔입니다 fillOpacity: 0.7, // 채우기 불투명도 입니다 }); circle.setMap(map) }); var circleInMarker = []; function circleInMarker(marker, circle) { return function() { circleInMarker = []; var center = marker.getPosition(); var radius = 1000; var line = new kakao.maps.Polyline(); markers.forEach(function(marker) { var path = [marker.getPosition(), center]; line.setPath(path); var dist = line.getLength(); if (dist < 1000) { marker.setMap(map) circleInMarker.push(marker); } else { marker.setMap(null); } }); } } function clearCircle(map, marker) { return function() { if (clearCircle) { circle.setMap(null) } } } function markerCircle(map, marker) { return function() { if (circle) { circle.setMap(null); } circle = new kakao.maps.Circle({ center: new kakao.maps.LatLng(marker.getPosition().getLat(), marker.getPosition().getLng()), // 원의 중심좌표 입니다 radius: 1000, // 미터 단위의 원의 반지름입니다 dd strokeWeight: 1, // 선의 두께입니다 strokeColor: '#75B8FA', // 선의 색깔입니다 strokeOpacity: 1, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다 strokeStyle: 'line', // 선의 스타일 입니다 fillColor: '#CFE7FF', // 채우기 색깔입니다 fillOpacity: 0.7, // 채우기 불투명도 입니다 }); circle.setMap(map) } } </script> <!-- <script> function makeMarker() { markers.forEach(function(marker) { marker.setMap(map); }); } </script> --> <script> function showStore() { } </script> </div> </body> </html>
버튼을 추가하여 마커를 클릭하여 1km 근방의 매장들을 보여주었을 때, 다시 전체매장을 보기 위해 버튼을 따로 추가해주었습니다.
#btnSection { position: absolute; top:50; left:50; z-index: 2; } button { width: 120px; height: 40px; border: 0px solid rgb(200, 200, 200); border-radius: 5px; background-color: rgb(255, 157, 0); color: white; font-size: 15px; font-weight: 500; box-shadow: 0px 1px 10px rgb(50, 50, 50); }
카카오에서 맵 위에 컴포넌트를 띄우기 위해 여러가지 방법이 있지만, z-index
값을 주어 맵 위에 올리는게 편리해서 다음과 같이 적용했습니다.
//기존의 모든 마커들을 보여주는 기능 구현 function makeMarker() { markers.forEach(function (marker) { marker.setMap(map); }); }
매장마다의 marker들을 생성하여 데이터와 마커 이미지를 적용하여 카카오맵에 설정해줍니다.
클릭 마커 기준 반지름 1km 근방의 매장만을 보여줍니다. (총 2km 반경)
안 보이는 매장을 다시 모두 set
해주어 매장 위치의 마커를 모두 표시하여줍니다.
마커가 아닌 맵을 클릭했을 시 맵에서 클릭한 좌표를 중심으로 1km의 원을 그려줍니다.