Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

미래학자

31 우버 클론 코딩 (nomad coders) 본문

카테고리 없음

31 우버 클론 코딩 (nomad coders)

미래학자 2019. 6. 24. 14:14

#2.61 NearbyDrivers Query

사용자는 주변에 운행 중인 드라이버 정보를 가져올 수 있다. Home으로 접근을 했을 때, 운전자가 아니면 운전자 쿼리를 날려서 운전자의 정보를 가져오도록 하자. 강의를 진행하다보니 역시 HomeContainer가 좀 방대해지는 기분이었다.

  • src/routes/Home/Home.queries.ts GET_NEARBY_DRIVERS를 정의했다. yarn codegen을 해주고

      ...
    
      export const GET_NEARBY_DRIVERS = gql`
        query getDrivers {
          GetNearbyDrivers {
            ok
            drivers {
              id
              lastLat
              lastLng
            }
          }
        }
      `;
  • src/routes/Home/HomeContainer.tsx

      ...
      import { 
        getDrivers,
        reportMovement,
        reportMovementVariables,
        userProfile } from "../../types/api";
      import { GET_NEARBY_DRIVERS, REPORT_LOCATION } from './Home.queries';
      import HomePresenter from "./HomePresenter";
    
      ...
    
      class ProfileQuery extends Query<userProfile> {}
      class NearbyQuery extends Query<getDrivers> {}
    
      class HomeContainer extends React.Component<IProps, IState> {
    
      ...
    
        public render() {
          const { isMenuOpen, toAddress, price } = this.state;
          return (
            <ProfileQuery query={USER_PROFILE}>
              {({ data, loading: profileLoading}) => (
                <NearbyQuery query={GET_NEARBY_DRIVERS}>
                  {() => (
                    <HomePresenter 
                      loading={profileLoading}
                      isMenuOpen={isMenuOpen} 
                      toggleMenu={this.toggleMenu}
                      mapRef={this.mapRef}
                      toAddress={toAddress}
                      onInputChange={this.onInputChange}
                      onAddressSubmit={this.onAddressSubmit}
                      price={price}
                                      data={data}
                    />
                  )}
                </NearbyQuery>
              )}
            </ProfileQuery>
          )
        }
    
      ...
  • src/routes/Home/HomePresenter.tsx <AddressBar/>, <ExtendedButton/> 의 위치를 조금 수정했다.

      ...
      import styled from "../../typed-components";
      import { userProfile } from "../../types/api";
    
      ...
    
      interface IProps {
        ...
        data?: userProfile;
      }
    
      const HomePresenter: React.SFC<IProps> = ({
        ...
        data: { GetMyProfile: { user = null } = {} } = { GetMyProfile: {}},
      }) => (
        <Container>
          <Helmet>
            <title>Home | Nuber</title>
          </Helmet>
          <Sidebar
            sidebar={<Menu/>}
            open={isMenuOpen}
            onSetOpen={toggleMenu}
            styles={{
              sidebar: {
                background: "white",
                width: "80%",
                zIndex: "10"
              }
            }}
          >
            {!loading && (<MenuButton onClick={toggleMenu}>
              <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fillRule="evenodd" clipRule="evenodd"><path d="M24 18v1h-24v-1h24zm0-6v1h-24v-1h24zm0-6v1h-24v-1h24z" fill="#1040e2"/><path d="M24 19h-24v-1h24v1zm0-6h-24v-1h24v1zm0-6h-24v-1h24v1z"/></svg>
            </MenuButton>)}
            {user && !user.isDriving && (
              <React.Fragment>
                <AddressBar
                  name="toAddress"
                  onChange={onInputChange}
                  value={toAddress}
                  onBlur={() => ""}
                />
                <ExtendedButton
                  onClick={onAddressSubmit}
                  disabled={toAddress === ""}
                  value={price ? "Change address" : "Pick Address"}
                />
              </React.Fragment>
            )}
            {!price ? false : (
              <RequestButton
                onClick={onAddressSubmit}
                disabled={toAddress === ""}
                value={`Request Ride ($${price})`}
              />
            )}
            <Map ref={mapRef}/>
          </Sidebar>
        </Container>
      )
    
      export default HomePresenter;

이번 강의는 딱 어떤 기능이 떨어지지는 않다. 그리고 실행시키면 깨진다.

#2.62 NearbyDrivers Query part Two

니콜라스의 커밋 로그를 봤는데 2.62, 2.63, 커밋 로그가 없다.. 그래서 코드가 조금 헷갈린다. 그리고 강의가 끝나서도 뭔가 동작이 애매했다.

  • src/routes/Home/HomeContainer.tsx skip속성을 사용해서 드라이버가 아닐 때만 주변 운전자를 요청하도록 했다. 그리고 완료 될 때 handleNearbyDrivers을 호출하였다. 지금은 주변 운전자 정보를 콘솔에 찍히도록 했다.

      ...
    
        public render() {
          const { isMenuOpen, toAddress, price } = this.state;
          return (
            <ProfileQuery query={USER_PROFILE}>
              {({ data, loading: profileLoading}) => (
                <NearbyQuery 
                  query={GET_NEARBY_DRIVERS}
                  skip={
                    !!( data &&
                      data.GetMyProfile &&
                      data.GetMyProfile.user &&
                      data.GetMyProfile.user.isDriving
                    )
                  }
                  onCompleted={this.handleNearbyDrivers}
                >
                  {() => (
                    <HomePresenter
                      ...
                    />
                  )}
                </NearbyQuery>
              )}
            </ProfileQuery>
          )
        }
    
      ...
    
        public handleNearbyDrivers = (data: {} | getDrivers) => {
          if ("GetNearbyDrivers" in data) {
            const {
              GetNearbyDrivers: { drivers, ok }
            } = data;
            if (ok && drivers) {
              console.log(drivers);
            }
          }
        }
      };
    
      export default graphql<any, reportMovement, reportMovementVariables> (
        REPORT_LOCATION,
        {
          name: "reportLocation"
        }
      )(HomeContainer);

여기서 테스트는 운전자와 사용자 두개의 클라이언트가 필요하다. 크롬 시크릿창 하나를 띄워서 다른 아이디로 로긴한 후 운전자로 바꾼 다음에 사용자에서 새로고침 해보자.

그리고 운전 모드를 아니게 바꾸고 새고로침 해보자. 빈 배열이 들어오면 정상적으로 처리가 된 것이다.

#2.63 Drawing Nearby Drivers part One

주변에 드라이버를 찾았다. 그리고 지도에 드라이버를 마커로 나타내고 싶다.

  • src/routes/Home/HomeContainer.tsx drivers 라는 마커를 담을 멤버를 배열로 추가했다. loadMap 에서 실패할 경우 재요청하도록 수정을 했다. handleNearbyDrivers 에서는 마커를 생성해서 지도에 나타내도록 수정했다.

      ...
        public directions: google.maps.DirectionsRenderer | null = null;
        public drivers: google.maps.Marker[];
    
        ...
    
        constructor(props) {
          super(props);
          this.mapRef = React.createRef();
          this.drivers = [];
        }
    
      ...
    
        public loadMap = (lat, lng) => {
          const { google } = this.props;
          const maps = google.maps;
          const mapNode = ReactDOM.findDOMNode(this.mapRef.current);
          if (!mapNode) {
            this.loadMap(lat, lng);
            return;
          }
          ...
        };
      ...

      public handleNearbyDrivers = (data: {} | getDrivers) => {
        if ("GetNearbyDrivers" in data) {
          const {
            GetNearbyDrivers: { drivers, ok }
          } = data;
          if (ok && drivers) {
                    const { google } = this.props;
            for (const driver of drivers) {
              if(driver && driver.lastLat && driver.lastLng) {
                const markerOptions: google.maps.MarkerOptions = {
                  icon: {
                    path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
                    scale: 5
                  },
                  position: {
                    lat: driver.lastLat,
                    lng: driver.lastLng
                  }
                };
                const newMarker: google.maps.Marker = new google.maps.Marker(markerOptions);
                this.drivers.push(newMarker);
                newMarker.set("ID", driver.id);
                newMarker.setMap(this.map);
              }
            }
          }
        }
      }
    };
    ...

화살표 같은 것으로 드라이버를 표시했다.

#2.64 Drawing Nearby Drivers part Two

드라이버는 계속해서 이동을 하고 우리는 그 위치를 실시간으로 받아야 한다. 니콜라스는 polling 방식을 사용하는 방법이랑 refetching 하는 방법 두 가지가 있다고 했고, 두 개의 차이는 refetching은 무조거 가져오는 것이고, poling은 데이터가 있을 때만 가져온다고 한다.

  • src/routes/Home/HomeContainer.tsx

      ...
              <NearbyQuery 
            query={GET_NEARBY_DRIVERS}
            pollInterval={1000}
            skip={
              !!( data &&
                data.GetMyProfile &&
                data.GetMyProfile.user &&
                data.GetMyProfile.user.isDriving
              )
            }
            onCompleted={this.handleNearbyDrivers}
          >
      ...

이렇게 하고 운전자의 위치를 조금씩 변경해보자.

사실 우리가 원하는 것 운전자의 이동에 따라 마커가 이동하는 것인데 지금은 새로 생성하고 있다. 다음 강의에서 마커를 이동하는 것을 진행할 예정이다. 아까 Marker를 만들 때 ID를 지정해준것이 있는데, 이렇게 컨트롤 하기 위함이다.

#2.65 Drawing Nearby Drivers part Three

이번에는 운전자의 위치가 변하는 것에 따라 지도에서 업데이트 하도록 하자. 마커 중 id를 통해서 특정 마커를 찾아서 업데이트 해주면 된다. 니콜라스는 분기 처리했지만, 나는 create, update 함수를 생성하여 만들었다.

  • src/routes/Home/HomeContainer.tsx

      ....
    
        public handleNearbyDrivers = (data: {} | getDrivers) => {
          if ("GetNearbyDrivers" in data) {
            const {
              GetNearbyDrivers: { drivers, ok }
            } = data;
            if (ok && drivers) {
              for (const driver of drivers) {
                const existingDriverMarker: google.maps.Marker | undefined = this.drivers.find((driverMarker: google.maps.Marker) => {
                  const markerID = driverMarker.get("ID");
                  return markerID === driver!.id;
                });
                if(existingDriverMarker) {
                  this.updateDriverMarker(existingDriverMarker, driver);
                } else {
                  this.createDriverMarker(driver);
                }
              }
            }
          }
        }
    
        public createDriverMarker = (driver) => {
          if(driver && driver.lastLat && driver.lastLng) {
            const { google } = this.props;
            const markerOptions: google.maps.MarkerOptions = {
              icon: {
                path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
                scale: 5
              },
              position: {
                lat: driver.lastLat,
                lng: driver.lastLng
              }
            };
            const newMarker: google.maps.Marker = new google.maps.Marker(markerOptions);
            if(newMarker) {
              this.drivers.push(newMarker);
              newMarker.set("ID", driver!.id);
              newMarker.setMap(this.map);
            }
          }
          return;
        }
    
        public updateDriverMarker = (marker: google.maps.Marker, driver) => {
          if(driver && driver.lastLat && driver.lastLng) {
            marker.setPosition({
              lat: driver.lastLat,
              lng: driver.lastLng
            });
            marker.setMap(this.map);
          }
          return;
        }
      };
    ....

자연스럽지는 않지만 이미 존재하는 드라이버의 위치가 업데이트 되도록 처리를 했다.
Comments