react project 이슈 사항 정리

이무헌·2023년 8월 29일
0

react

목록 보기
19/19
post-thumbnail

1.개요

3주동안 리액트 프로젝트를 진행 하였다. 기존에 배웠던 지식과 사용했던 기술들로 구현해 보았다.

  1. AI (chat GPT)를 활용한 여행지 추천 서비스
  2. 구글 맵 API를 활용한 주변 관광지 추천 서비스
  3. pixa bay API를 활용해 GPT의 대답을 검색 프롬프트로 활용, 해당 관광지의 사진을 갖고옴

2.주요 기능들

1.GPT controller

// gptAPI 테스트 -----20230807 zerohoney

const { Configuration, OpenAIApi } = require("openai");

const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

const whoText = (whoArr) => {};

const sendPropmpt = async (gptData) => {
  const completion = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: [
      { role: "system", content: "You are a helpful assistant." },
      {
        role: "user",
        content: ` 여행 국가/도시:${gptData.location} ,
        
        여행 기간:${gptData.choiceDataHow.join(
          ","
        )}${gptData.date}
        ,
        
        파트너: ${gptData.choiceDataWho.join(
          ","
        )}


      이 조건으로 추천 여행장소를 다음과 같은 형식으로 출력해줘 {"location":'',attractions:[

          {"name":'',detail:'',attractionLocation:{"latitude": '', "longitude": ''}},
          ...
        ]} 
        설명: location은 입력한 장소의 값이고, attractions는 너가 추천해줄 장소의 배열이야. 그 안의 name은 8로 출력해줘!, detail은 장소에 대한 자세한 설명,attractionLocation은 그 장소의 위도와 경도야.
        형식은 json이야
        `,
      },
    ],
  });

  return completion.data.choices[0].message;
};

exports.saveUserPlan = async (req, res) => {
  const { gptData } = req.body;
  const ans = await sendPropmpt(gptData);
  res.json(ans);
};

해당 코드에서 알 수 있듯이 매우 간단하게 propmt를 입력하고 가져올 수 있다. 물론 더 좋은 엔진과 코드를 사용 할 수 있지만... 지갑이...

2.GOOGLE MAP API

1. 지도를 생성 시키는 로직


 const googleMapScript = document.createElement("script");
    googleMapScript.src = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLEMAP_API_KEY}&libraries=places`;
    googleMapScript.async = true;
    googleMapScript.onload = () => {
      console.log("** 구글 맵 실행됨! **");
      initMap(props);
    };
    document.head.appendChild(googleMapScript);

initMap으로 지도를 만들어준다. 아무리 설정을 해도 initMap을 실행시켜주지않으면 지도는 그려지지 않는다....
그렇다면 지도에 마커를 찍어주고, 경로까지 그려주는 여러 API 로직은 어디에다 구현할까? initMap함수 안이다.

2.어떤 지도를 생성 시킬 건데?


          myLatLng = {
            lat: Number(lat),
            lng: Number(lng),
          };
          map = new window.google.maps.Map(document.getElementById("gmp-map"), {
            zoom: 13,
            center: myLatLng,
            fullscreenControl: false,
            zoomControl: true,
            streetViewControl: false,
          });

          const request = {
            location: myLatLng,
            radius: "500",
            types: ["tourist_attraction"],
          };

          const service = new window.google.maps.places.PlacesService(map);

          service.nearbySearch(request, (results, status) => {
            if (status === window.google.maps.places.PlacesServiceStatus.OK) {
              // 평점 이상
              const filterResults = results.filter(
                (place) => place.rating >= 4.0
              );
              let send_latlng = [];
              let send_lat, send_lng;
              for (const place of filterResults.slice(0, 3)) {
                // send_lat = place.geometry.location.lat();
                // send_lng = place.geometry.location.lng();
                send_latlng.push(place);
                const marker = new window.google.maps.Marker({
                  position: place.geometry.location,
                  map: map,
                  title: place.name,
                });
              }
              setnearPlace(send_latlng);
            } else {
              console.log("구글맵 nearbySearch 이상 status", status);
            }
          });
myLatLng = {
            lat: Number(lat),
            lng: Number(lng),
          };
          map = new window.google.maps.Map(document.getElementById("gmp-map"), {
            zoom: 13,
            center: myLatLng,
            fullscreenControl: false,
            zoomControl: true,
            streetViewControl: false,
          });

map을 설정해주는 로직이다. 이 map을 기준으로 사용자에게 어떤 조건으로 지도를 보여줄지 결정한다.

const request = {
            location: myLatLng,
            radius: "500",
            types: ["tourist_attraction"],
          };

          const service = new window.google.maps.places.PlacesService(map);

          service.nearbySearch(request, (results, status) => {
            if (status === window.google.maps.places.PlacesServiceStatus.OK) {
              // 평점 이상
              const filterResults = results.filter(
                (place) => place.rating >= 4.0
              );
              let send_latlng = [];
              let send_lat, send_lng;
              for (const place of filterResults.slice(0, 3)) {
                // send_lat = place.geometry.location.lat();
                // send_lng = place.geometry.location.lng();
                send_latlng.push(place);
                const marker = new window.google.maps.Marker({
                  position: place.geometry.location,
                  map: map,
                  title: place.name,
                });
              }
              setnearPlace(send_latlng);
            } else {
              console.log("구글맵 nearbySearch 이상 status", status);
            }
          });

request는 nearbySearch로 넘겨주는 파라미터이며 tourist_attraction는 관광지, 즉 본인이 설정한 위도,경도 에서 가까운 관광지를 보여달라는 의미이다.
nearbySearch는 말 그대로 주변 장소를 불러오는 함수로, tourist_attraction가 포함된 request를 매개변수로 넘겨줬기 때문에 관광지에 대한 정보만 찾는다는 의미가 된다.
setnearPlace는 내가 만든 state로 구글맵에서 보내준 주변 관광지 정보를 nearPlace라는 state에 저장한다.

3.pixa bay API

 const getAttPic = async (queryKey) => {
    const apiKey = process.env.REACT_APP_PIXABAY_API_KEY;
    var URL =
      "https://pixabay.com/api/?key=" +
      apiKey +
      "&q=" +
      encodeURIComponent(queryKey);

    const getAttPicRes = await ipUrl.get(URL, {
      withCredentials: false,
    });
    return getAttPicRes.data;
  };

간단하다. &q 쿼리에 사용자가 입력한 값을 넣어주면된다.

3.버그 리포트

1.동적으로 리액트에서 img를 가져오는 방법

만약 사용자가 요청한 이미지의 이름이 a_use_img라 하자 이 때, 기존처럼 import로 가져오는 방식은 쓰질 못하다. 왜냐하면 import를 동적으로 하는것은 불가능하기 때문이다.

import img from './imgs'
const Test=()=>{
  
  return(
<img   src={img}/>
)
}

동적으로 가져오고 싶으면 이 구조를 다음과 같이 바꾸면된다.
=>


const Test=()=>{
  const imgPath=/imgs
  return(
<img   src={img}/>
)
}

다만 여기서 주의해야할 점은 절대경로로 할당 해야하는 것, 그리고 imgs폴더는 public 폴더 안에 있어야 한다는 것이다!(src 바깥)

2.redux state변경시 재랜더링이 안되는 문제

// 이미지 url이 포함된 gpt답변이다. 관광지의 이름과 위도경도,이미지,디테일이 들어간다.
export const attractionsWithImg = createSlice({
  name: "attractionsWithImg",
  initialState: [],
  reducers: {
    saveAttractionsWithImg: (state, action) => {
      state.push({
        attractionLocation: action.payload.attractionLocation,
        detail: action.payload.detail,
        img: action.payload.img,
        name: action.payload.name,
        nearAttraction: [],
      });
    },
    // 관광지 주위에있는 관광지 저장
    saveNearAttraction: (state, action) => {
      state.forEach((value, index) => {
        if (!action.payload.length === 0) {
          alert("로드중 오류가 발생하였습니다. 새로고침후 다시 이용해 주세요.");
        } else {
          if (value.name === action.payload[0].parentName) {
            state[index].nearAttraction = action.payload;
          }
        }
      });
    },
    resetAttractionsWithImg: (state, action) => {
      return [];
    },
  },
});

resetAttractionsWithImg에서 처음에 state 자체를 []로 할당하였다. state=[] 이런식으로, 하지만 랜더링이되지 않았고 값도 변경 되지 않았다. 알고보니 state의 불변성을 지키지 않았기 때문이었다. 기존 state를 변경 할 때도 return에 새로운 값을 주어 state를 변경했던 경험이 많았음에도 redux는 다르다고 생각했기 때문이다.

3.google map script 에러

구글맵으로 마커, 길찾기 등등을 하고있는 중 script error에러가 자주 발생했다. 발생 지점도 컴파일하는 index.js파일 내에 있어 무엇이 잘 못 되었는지 알 수가 없었다. 귀납적으로 계속 리팩토링 해본 결과 google.maps.Map(document.getElementById("gmp-map") 를 중복해서 실행해서 일어난 일이었다.이를 해결하기 위해 싱글톤 방식으로 map변수 하나를 메모리에 할당 후 필요할 때마다 값만 바꿔 주었다.

4.nginix 프록시 문제

  # First attempt to serve request as file, then
  # as directory, then fall back to displaying a 404.
root /home/front/build;
  try_files $uri /index.html;

}

location /api/ {
proxy_set_header HOST $host;
proxy_pass http://127.0.0.1:8080/;
proxy_redirect off;
proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection 'upgrade';
  proxy_set_header Host $host;
  proxy_cache_bypass $http_upgrade;
}

하나의 도메인에서 react,node를 배포하여 기본 경로는 build의 index.html로 해주었고, /api요청을 처음에 붙여 줄 때 서버의 api로 요청되도록 허였다.location /api/ 에서 /가 하나 더 있었는데 proxy_pass 에서는 http://127.0.0.1:8080 로 할당하여 404not found가 터졌다.... /api/를 /api로 하던가 http://127.0.0.1:8080를 http://127.0.0.1:8080/로 하여 해결하였다.

5.권한 주기

이미지를 넣을 때 sudo chown ‘사용자 이름’:’사용자 그룹 ’로 사용자에 권한을 주자.... 그 외에 파일 생성을 할 때도 권한을 줘야한다. 아무것도 안했을 때 사용자의 이름은 ubuntu이다.

4.느낀점

기존 프로젝트와 달리 react를 처음 사용해보는 프로젝트라 다양한 부분에서 버그가 많이 났다. 하지만 대부분 useEffect나 async같은 실행 순서의 문제였고 react-query같은 라이브러리 숙련도의 문제가 컸다. 그 외에 시간이 많이 걸린 버그는 위에 작성하였고, 그 중 도메인과 관련된 버그는 원인을 찾는것 보다 해결하는데 더 많은 시간이 걸렸다... aws로 배포하여 곧 지갑과 연결되기 때문에 도메인과 배포와 관련된 지식 또한 많이 공부해야겠다.

profile
개발당시에 직면한 이슈를 정리하는 곳

0개의 댓글

관련 채용 정보