// 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를 입력하고 가져올 수 있다. 물론 더 좋은 엔진과 코드를 사용 할 수 있지만... 지갑이...
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함수 안이다.
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에 저장한다.
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 쿼리에 사용자가 입력한 값을 넣어주면된다.
만약 사용자가 요청한 이미지의 이름이 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 바깥)
// 이미지 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는 다르다고 생각했기 때문이다.
구글맵으로 마커, 길찾기 등등을 하고있는 중 script error에러가 자주 발생했다. 발생 지점도 컴파일하는 index.js파일 내에 있어 무엇이 잘 못 되었는지 알 수가 없었다. 귀납적으로 계속 리팩토링 해본 결과 google.maps.Map(document.getElementById("gmp-map") 를 중복해서 실행해서 일어난 일이었다.이를 해결하기 위해 싱글톤 방식으로 map변수 하나를 메모리에 할당 후 필요할 때마다 값만 바꿔 주었다.
# 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/로 하여 해결하였다.
이미지를 넣을 때 sudo chown ‘사용자 이름’:’사용자 그룹 ’로 사용자에 권한을 주자.... 그 외에 파일 생성을 할 때도 권한을 줘야한다. 아무것도 안했을 때 사용자의 이름은 ubuntu이다.
기존 프로젝트와 달리 react를 처음 사용해보는 프로젝트라 다양한 부분에서 버그가 많이 났다. 하지만 대부분 useEffect나 async같은 실행 순서의 문제였고 react-query같은 라이브러리 숙련도의 문제가 컸다. 그 외에 시간이 많이 걸린 버그는 위에 작성하였고, 그 중 도메인과 관련된 버그는 원인을 찾는것 보다 해결하는데 더 많은 시간이 걸렸다... aws로 배포하여 곧 지갑과 연결되기 때문에 도메인과 배포와 관련된 지식 또한 많이 공부해야겠다.