세모세 프로젝트_완전연결

jkky98·2023년 11월 21일
0

Project

목록 보기
7/21

처음겪은 서비스 만들기에서 풀스택관점으로 모든 것을 하려다 보니 결국 꼬였다.
axios로 하여금 통신을 짤 때 동기,비동기 고려도 하지 않았으며, 화면 단에 무언가 뜨는 것에만 집착한 듯 하다. 브라우저에서 선택을 통한 프론트엔드에서 데이터를 1차 구성하여 백엔드로 보내고, then을 통해 백엔드로부터 요청에 의해 이루어진 응답 데이터를 받아 맵을 구성하는 등의 일련의 과정에서 과정 진행중에 하위 컴포넌트에서 동시에 데이터로 하여금 맵을 띄우려는 등 문제가 발생했다.

최상위 컴포넌트의 데이터 구성과 더불어 axios통신 및 백엔드의 엔드포인트까지 모든 것을 다시 수정했다. 멘토님께서 연결이 가장 어려울 것이다라고 하셨는데 이 상황을 겪으며 이해가 되었다. async/await나 Promise에 대해 공부할 수 있었다.

프로젝트 동료에게 맵 컴포넌트 제작을 부탁해서 받았다. 해당 코드는 맵 컴포넌트에 구성된 예시 data를 인자로 사용하고 있었으나, 메소드에 인자를 전달하는 방식이 아니어서 모든 것을 수정해야해서 다시 카카오맵 docs를 보며 맵 컴포넌트를 만들었다. 그 과정에서 props데이터는 수정이 되면 안된다는 것도 알았다.

<template>
  <div>
    <h2 class="map-title">Semose Kakao Map~!</h2>
    <div id="map"></div>
    <button @click="calculateDistance">Calculate Distance</button>
  </div>
  <div>
   <p>fdasfasgf : {{parentData}}</p>
  </div>
</template>

<script>

export default {
  name: "KakaoMap",
  data() {
    return {
      scorebox: null,
      address: { name: 'my house', lat: 37.0781534, lng: 127.0427976 },
      stores: [
        { name: "Starbucks", lat: 37.2796352, lng: 127.043346 },
        { name: "Burger King", lat: 37.2745815, lng: 127.045215 },
        { name: "Daiso", lat: 37.2754895, lng: 127.0423236 },
      ],
      map: null,
    };
  },
  created() {
    this.loadScript();
  },
  props: {
    parentData: {
      type: Object,
      required: true
    }
  },
  mounted() {

    if (window.kakao && window.kakao.maps) {
      this.loadMap();
    } else {
      this.loadScript();
    }

  },
  methods: {
  
    loadScript() {
      const script = document.createElement("script");
      script.src =
        "https://dapi.kakao.com/v2/maps/sdk.js?appkey=14d78dd0b00ee306c710756383b6e3e7&autoload=false&libraries=services,clusterer,drawing";
      script.type = 'text/javascript';
      script.onload = () => window.kakao.maps.load(() => this.loadMap());
      document.head.appendChild(script);
    },
    loadMap() {
      const container = document.getElementById("map");

      // 맵 기본위치 설정 및 사이즐 
      const options = {
        center: new window.kakao.maps.LatLng(this.parentData.address_latlng.lat, this.parentData.address_latlng.lng),
        level: 3,
      };
      // 기본 맵 띄우기
      this.map = new window.kakao.maps.Map(container, options);

      this.$nextTick(() => {
            this.addSetFull(this.parentData.info_store, 
            this.parentData.address_latlng, 
            this.parentData.bus);
          });
      // 맵 추가 기능
    },

    addMarker(lat, lng) {
            // 마커가 표시될 위치입니다 
      var markerPosition  = new window.kakao.maps.LatLng(lat, lng); 

      // 마커를 생성합니다
      var marker = new window.kakao.maps.Marker({
          position: markerPosition,
          clickable: true
      });

      // 마커가 지도 위에 표시되도록 설정합니다
      marker.setMap(this.map);
    },

    addLine(address, lat_end, lng_end, color='#FFAE00') {
      var linePath = [
      new window.kakao.maps.LatLng(address.lat, address.lng),
      new window.kakao.maps.LatLng(lat_end, lng_end)
      ]
        // 지도에 표시할 선을 생성합니다
      var polyline = new window.kakao.maps.Polyline({
          path: linePath, // 선을 구성하는 좌표배열 입니다
          strokeWeight: 5, // 선의 두께 입니다
          strokeColor: color, // 선의 색깔입니다
          strokeOpacity: 0.7, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
          strokeStyle: 'solid' // 선의 스타일입니다
      });
      polyline.setMap(this.map);  
    },
    customOverlay(lat, lng, name, distance) {
      var iwContent = name+distance, // 인포윈도우에 표출될 내용으로 HTML 문자열이나 document element가 가능합니다
          iwPosition = new window.kakao.maps.LatLng(lat, lng), //인포윈도우 표시 위치입니다
          iwRemoveable = true; // removeable 속성을 ture 로 설정하면 인포윈도우를 닫을 수 있는 x버튼이 표시됩니다

      // 인포윈도우를 생성하고 지도에 표시합니다
      new window.kakao.maps.InfoWindow({
          map: this.map, // 인포윈도우가 표시될 지도
          position : iwPosition, 
          content : iwContent,
          removable : iwRemoveable
      });
    },
    customOverlay_house(lat, lng) {
      var iwContent = "my house SCORE : "+ this.parentData.scorebox.score, // 인포윈도우에 표출될 내용으로 HTML 문자열이나 document element가 가능합니다
          iwPosition = new window.kakao.maps.LatLng(lat, lng), //인포윈도우 표시 위치입니다
          iwRemoveable = true; // removeable 속성을 ture 로 설정하면 인포윈도우를 닫을 수 있는 x버튼이 표시됩니다

      // 인포윈도우를 생성하고 지도에 표시합니다
      new window.kakao.maps.InfoWindow({
          map: this.map, // 인포윈도우가 표시될 지도
          position : iwPosition, 
          content : iwContent,
          removable : iwRemoveable
      });
    },

    // 지도 확대 축소를 제어할 수 있는  줌 컨트롤을 생성합니다
    optionZoomControl() {
      var zoomControl = new window.kakao.maps.ZoomControl();
      this.map.addControl(zoomControl, window.kakao.maps.ControlPosition.RIGHT);
    },
    optionTopographical() {
      this.map.addOverlayMapTypeId(window.kakao.maps.MapTypeId.TERRAIN);
    }, 
    addSetFull(stores, address, bus) {
      // Setting Option
      this.optionZoomControl();
      this.optionTopographical();
      // About address point
      this.addMarker(address.lat, address.lng)
      this.customOverlay_house(address.lat, address.lng)
      // About stores point
      stores.forEach((store) => {
        const {name, lat, lng, distance} = store
        this.addMarker(lat, lng);
        this.addLine(address, lat, lng);
        this.customOverlay(lat, lng, name, distance);
      });
      // About bus point
      bus.forEach((bus) => {
        const {name, lat, lng, distance} = bus
        this.addMarker(lat, lng);
        this.addLine(address, lat, lng, '#29B6F6');
        this.customOverlay(lat, lng, name, distance);
      });
    },

    reMap() {
      console.log("SUCCESS REMAP");
      this.loadMap();
      
    },

  },
};

</script>

<style>
#map {
  width: 1300px; /* 변경할 너비 값 */
  height: 800px; /* 변경할 높이 값 */
  margin: auto; /* 가운데 정렬을 위한 margin 속성 */
  display: block; /* 블록 요소로 표시하여 가로폭 전체를 차지하도록 설정 */
}
.map-title {
  color: white;
}
</style>

우리의 맵은 한번만 띄워야하는 것이 아닌, 데이터가 백엔드와 통신 이후에는 Reload 되어야 하므로 인자 전달이 중요하다. 초기화로 하여금 loadScript를 통해 카카오맵API와 통신을 하고, 디폴트 값을 먼저 맵 화면에 띄우도록 했다. 그리고 reMap()을 통해 요청이 끝나고 데이터가 프론트엔드에 도착하면 맵을 다시 로드한다.

카카오맵 API는 다양한 기능을 제공하며, 이것들을 메소드로 만들어 놓고 addSetFull메소드에서 받아온 데이터로 하여금 추가기능 메소드들을 실행하는 것으로 코딩했다.

이전에는 하나의 기능만 우선적으로 구현하기 위해 데이터로 위,경도와 해당 store의 name만 받았으나, 추가 기능을 만들 때마다 백엔드와 프론트엔드 통신을 모두 수정해야하는 번거로움에 가진 정보를 일단 모두 프론트로 보내기로 했다.

class DataView(APIView):
    def post(self, request):
        # POST 데이터를 JSON 형식으로 받아옵니다.
        data = request.data

        # 필요한 데이터 추출
        address = data.get("address")
        selected = data.get("selected", [])

        addr_lat, addr_lng = get_lat_lng_from_address(address)
        addr_info = {
            "name":000,
            "lat":addr_lat,
            "lng":addr_lng,
        }

        selected = json.loads(selected)

        score = score_accesibility(address, selected)
        scorebox = {'score':score}
        info_store = serve_latlng(address, selected)
        bus_data = get_bus_nearby(addr_lat, addr_lng)

        res = {
            'scorebox': scorebox,
            'info_store': info_store,
            'address_point': addr_info,
            'bus_data': bus_data,
        }
        return Response(res)

bus데이터도 추가했는데, 카카오맵에서 따로 버스정보를 제공하지 않기에 공공데이터포털에서 대한민국 버스정류장 정보 csv파일을 다운받아 DB에 수원 버스정류장 정보에 대한 것만 담아서 사용하기로 했다. bus데이터는 정류장이름, 위,경도, 검색위치기준 거리가 요청으로부터 응답된다.

만들어진 맵 컴포넌트를 MainPage에 연결하고, 조건을 설정후 SUBMIT 버튼을 누르면
다음과 같이 기준 주소를 중심으로 여러 포인트와 선, 걸리는 시간, 세권 점수가 나타난다. 인포윈도우가 겹쳐서 시각성이 매우 저해되고 있지만 일단 모든 것이 담긴다. 파란선은 가장 가까운 버스정류장을 나타내며, 버스정류장까지 걸리는 시간도 나타난다.

여러 조건을 테스트하며 DB에서 검색결과가 이상함을 깨달았다. 맥도날드를 가져와야 하는데 맥도날드 주차장을 가져오는 등 검색 필터링을 고쳐야할 것 같다. 감사하게도 프로젝트에서 구성된 DB에는 업종코드가 있다. 검색어와 업종코드를 동시에 만족하도록 하면 해당 문제는 해결될 것으로 보인다.

인포윈도우가 맵에서 움직이지 않고, 따로 스타일을 지정할 수 없어서 아마 커스텀 오버레이로 대체해야할 것 같다.

NEXT

  • 직방 등 부동산 어플리케이션 매물정보를 띄우고 원하는 매물을 누르면 우리의 서비스에 자동적으로 적용
  • 세권 점수 모델 구체화
  • 인포윈도우 개선 및 맵 시각적합리성 확보
  • 맵에서의 시각적 표현의 한계로 인해 아래에 INFO대시보드 추가
  • 결과 로딩시 로딩 화면 구성
profile
자바집사의 거북이 수련법

0개의 댓글