const timeContainer = document.querySelector("#clock")
function timer(){
const now = new Date()
timeContainer.innerText =
`${now.getHours()}:${String(now.getMinutes()).padStart(2,"0")}:${String(now.getSeconds()).padStart(2,"0")}`
}
timer()
setInterval(timer,1000)
현재시간을 알려주는 함수 부분이다. new Date() 로 현재의 시간을 받고 아래에서 innerText로
now.getHours() : 시간
now.getMinutes() : 분
now.getSeconds() : 초
값들을 넣어준다. 여기서 padStart라는 메서드를 사용했는데 이 메서드로 원래 6 이렇게 한 개만 보이던 숫자를 06처럼 앞에 0을 붙여서 2글자로 만들어준다 이미 두 글자이면 같은 글자가나 온다 padStart는 문자열에만 사용가능하기 때문에 String으로 숫자를 문자열로 변경한 다음 사용해 줬다.
시간을 초가 바뀔 때마다 새로 그려줘 야하기 때문에 setInterval로 1초마다 새로 시간을 받아오고 그려주게 했다.
setInterval을 쓰면 1초가 지난 뒤에 함수가 실행되기 때문에 처음 창을 열 때 아무것도 실행되고 있지 않기 때문에 timer() 함수를 호출해서 한번 먼저 실행되게 해 줬다.
리액트 라우터 나 라이브러리를 사용하지 않고 순수 자바스크립트만으로 라우터를 구현해서 Single Page Application을 구현하는 문제를 학원에서 풀어보았다. 이런 걸 해볼 때마다 라이브러리 만든 사람방향으로 절을 올리고 문제를 만든 사람 방향으로는 두 번 절을 올리고 싶은 심정이다. 한번 풀어보쟈!
코드샌드박스로 완성본을 올려놓고싶었는데 같은 코드가 코드샌드박스에선 실행이 안된다.. 어째서...ㅜㅜ
원래는 중간중간 진행상황같은걸 정리해서 올려보고싶었는데 프로젝트를 진행하는데만 힘을너무쏟아서 블로그쪽은 생각도 안하고있었다.. 멀티가 이렇게나 안되다니..
프로젝트 끝난것도 저번주 월요일인데 프로젝트 끝나고 일요일에 정처기 필기시험이있어서 공부하느라 또 정리를 못하고 결국 22일이 되어버렸다.
그래도 어떤방법으로 만들었고 어떤걸 만들고싶었었는지 다시한번 정리해놔야 나중에 어떻게만들었는지
기억이 날것같아서 정리를 해볼려고한다.
프로젝트이름
The나들이
(떠나자 나들이 라는 의미로 만들었다)
기능
설정되있는 여행지 중에서 사용자가 선택하고 추천해주는 여행지중에서 여행 경로를 확인하고 관련한 정보를 얻을수있는 사이트
사용한라이브러리
@react-google-maps/api 구글지도기능을 이용하기위해 설치
redux-devtools/extension redux상태를 파악할수있는 미들웨어
html2/canvas 화면을 캡쳐할수있게 해주는 라이브러리
antd 만들어진 스타일을 이용할수있는 라이브러리
node-sass scss를 이용해서 스타일을 적용하기위해 사용
axios get,post요청을 하기위해서 사용.
react-cookie 로그인 정보를 쿠키로 저장하기위해서 사용.
react-datepicker 캘린더를 만들어주는 라이브러리
react-router-dom 라우터로 컴포넌트를 마운트시키는 라이브러리
react-redux 리덕스로 상태값을 관리하는 라이브러리
react-icons 아이콘을 만드는 라이브러리
styled-components 스타일이 적용된 컴포넌트를 만들어주는 라이브러리
필요할때마다 하나씩 설치를 하다보니 꽤 많은 라이브러리를 사용하게 됐다.. 점점 용량이 커지고 npm install 할때 걸리는 시간이 늘어나는게 이게 성능적으로 문제가 없나 싶었다.
꼭 필요한 라이브러리만 설치해서 사용하는게 좋은것같다.
(scss랑 styled-components 같이쓴건 좀 오바였지 싶다..)
데이터베이스
City 테이블 :각 나라별 위치좌표,이름,구글맵스에서 사용할 확대값 ,관련 블로그포스팅주소 , 월별 여행관련정보
SpotPlace 테이블: 나라의 추천관광지 이름,좌표,추천관광시간,이미지주소,추천경로순서
member 테이블: 회원가입관련자료: 유저코드,닉네임,비밀번호(암호화),이름,이메일
구현된 화면
팀원이 4명이고 다들 해외에서 살다오거나 연수경험이 있어서 각자 자기 지역을 담당해서
4개의 추천여행지를 만들었다
(캐나다,하얼빈,싱가포르:이건내가함!,제주도)
1200px 과 768px 에 반응형 디자인을 만들었다.
이제 실질적인 기능들이 모두 들어있는 일정만들기 페이지로 들어가보면.
기능이 좀 많은데 현재시간으로 제주도 날씨를 알려주고 (wheatherOnAPI를 이용했다)
달력으로 여행을할 기간을 찍으면 그 기간에 해당하는 여행팁을 알려준다.
오른쪽이 우리가 만들어놓은 추천 여행지인데 +를 눌러서 하나씩 일정을 추가시키면
오른쪽에서는 빠지고 왼쪽에 장소 추가되고 지도위에 마커가뜨고 마커들끼리 선으로 이어져서 동선을 쉽게 확인할수있다. (googlemapsAPI Marker , polyline 이용)
오른쪽 추천장소에 마우스를 올리면 해당장소로 지도의 포커스가 옮겨진다
(googlemaps 의 panTo기능을 이용)
아래 여행팁에 가려져있는 부분에 가고싶은 장소를 검색하면 해당장소로 포커스가 옮겨진다.
(구글지도의 자동완성기능사용 autocomplete)
지도위의 옵션버튼
여행기를 클릭하면 여행기블로그 포스팅이 연결된 팝업을 띄워준다.
<iframe> 태그를 이용해서 블로그 포스팅이있는 주소를 연결해서 보여줌
추천일정을 클릭하면 우리가 설정한 추천여행지와 추천코스 순서대로 보여준다
추천장소의 이미지가 슬라이더로 자동으로 넘어가고 아래 태그로 추천일정을 하나씩 선택하거나 모두선택버튼으로 모두선택할수있다. 선택하면 왼쪽에 장소가추가되고 지도에 마커와 경로가 그려진다.
장소들의 순서는 가장 좋다고생각하는 동선으로 순서를 정해서 하루정도에 돌아볼수있는 코스로 짜봤다.
이제 일정을 모두 정하고 나면 일정생성버튼을 클릭하면 만든 경로의 이미지를 캡쳐해서 저장해준다.
기능들을 만들면서 좀 힘들었던 점이 많았는데 구글맵스 API가 자바스크립트로 사용하는 예시들은 인터넷에 많이 있는데 리액트라이브러리로 만드는 방법들은 예시가 많이 없어서 공식문서를 보고 여러가지 방법을 시도해서 만들었다. 사용된 예시가 많이 없어서 참고할만한게 별로없어서 힘들었다.
그리고 이미지를 주소에서 받아오다보니 이미지를 제대로 못받아와서 엑스박스가 뜨는경우가 있었다. 이런 문제를 처음봐서 당황했는데 이미지 태그에 옵션을 추가해주는걸로 해결할수있었다.
<img src={img} referrerpolicy="no-referrer" />
이미지를 캡쳐하는 라이브러리인 html2canvas도 처음사용해보는거라 어려웠는데 구글mapsAPI를 사용하니까 계속 CORS에러가 나서 이것도 열심히 구글링해서 구문한줄 추가해주니까 해결되었다.
이제 3월 23일 수료가 2달 앞으로 다가오면서 슬슬 포트폴리오 압박이 들어오기 시작했다..
무조건 리액트로 만들어야하는데 아직 리액트로 엉성한 사이트도 제대로 만들어보지 못해서 걱정이 크다..
검색하다 보면 어떻게든 또 만들겠지 라는 생각으로 일단 함께할 팀원을 모아서 총 4명이서 여행코스를 만들어주는 사이트를 만들기로 했다. 각자 해외에 살다 온 경험이 있는 분들이라서 각 지역별로 추천관광지를 리스트업 하고 사용자가 추천관광지를 클릭하면 지도에 마크가 찍히고 서로 선으로 이어져서 여행동선을 한눈에 보기 편하게 만들어주는 사이트이다.
이걸 위해서 지도이미지와 마크 그리고 경로를 그려주는 기능이 필요했는데 마침 구글 maps API에서 모든 기능을 다 지원한다고 해서 일단 구글맵스를 공부하면서 어떻게 적용할지 고민하고 있다.
리액트로 처음 페이지를만들면서 가장 고민되는 점은 어떤 변수들을 상태로 관리해야 하는지이다. 아직 해결은 못했지만 돌아가는 구성을 하나씩 맞춰보면서 사용자와 상호작용하면서 계속해서 변경되는 값들을 상태값으로 관리하려고 생각 중이다.
모듈이 여러 개일 때는 리듀서가 여러 개이기 때문에 리듀서들을 하나로 합쳐서 store를 만들 때 사용한다.
modules/index.js
import { combineReducers } from "redux";
import counter from "./counter";
const rootReducer = combineReducers({ counter })
export default rootReducer
3. 스토어 만들기 , 전역에 스토어 사용하기
만들어진 rootreducer를 이용해서 스토어를 만들고 스토어를 사용할 수 있게 하위컴포넌트들을 묶어준다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { legacy_createStore as createStore } from 'redux';
import rootReducer from './modules';
import { Provider } from 'react-redux';
//스토어 만들기.
const store = createStore(rootReducer)
console.log(store.getState()) //상태값 조회
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
useSelector로 counter의 상태값을 number로 받아서 Counter컴포넌트에 props로 보내준다.
useDispatch로 dispatch함수를 받아와서 디스패치를 실행하는 함수를 만들었다 onIncrease ,onDecrease
dispatch에 액션객체로는 액션생성 함수를 넣어줬다.
이렇게 만들어준 함수를 Counter컴포넌트에 props로 넘겨준다.
이제 이 컨테이너 컴포넌트를 app.js에 넣어서 렌더링만 해주면 된다.
이제 여기에 미들웨어를 하나 만들어서 값이 리듀서로 가기 전에 작업을 할 수 있게 해 주겠다.
*리덕스 미들웨어 미들웨어는 함수를 연달아서 두 번 리턴하는 함수.
const middleware = store => next => action =>{
//하고싶은작업..
}
function middleware(store){
return function(next){
return function(action){
//하고싶은작업..
}
}
}
위와 같은 구조가 미들웨어의 구조이다 store는 파라미터로 들어가고 함수를 두번 리턴해준다.
각 함수에서 받아오는 파라미터 -store 리덕스 스토어 store dispatch( ) / getState( ) / subscribe( ) 내장함수 들어있음 -next 액션을 다음 미들웨어에 전달하는 함수 ex) next(action) -action 은 현재 처리하고 있는 액션객체
-next(action)을 해주지 않으면 action이 reducer나 다음 미들웨어로 넘어가지 않아서 에러가 생긴다.
middlewares/myLogger.js
const myLogger = store => next =>action=>{
//액션 출력하기
console.log(action)
//next는 다음미들웨어 또는 리듀서 에게 액션을 전달.
const result = next(action); //다음 미들웨어가 없어서 리듀서가호출되고 상태값이 변함
// 업데이트 이후의 상태를 출력
console.log(store.getState());
//여기에서 반환하는 result값은 dispatch(action)의 결과물.
return result
}
export default myLogger;
지금 보내주는 액션을 콘솔에 출력해 주고 action을 다음미들웨어로 넘겨준다.
미들웨어로 action이 넘어간 다음 바뀐 상태값을 출력해 준다.
이제 만들어진 이 미들웨어를 사용해 보자
store를 만들 때 미들웨어를 같이 넣어주면 된다.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { applyMiddleware , legacy_createStore as createStore } from "redux";
import { Provider } from "react-redux";
import App from "./App";
import rootReducer from "./modules";
import myLogger from './middlewares/myLogger';
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
const store = createStore(rootReducer,applyMiddleware(myLogger));
console.log(store.getState());
root.render(
<StrictMode>
<Provider store={store}>
<App />
</Provider>
</StrictMode>
);
applyMiddleware를 import 해서 createstore안에 넣어주고 사용할 미들웨어를 파라미터로 넣어준다.
콘솔을 확인해 보면 어떤 액션이 들어가고 상태값이 어떻게 변하는지 미들웨어에서 출력을 해준다
이렇게 리듀서로 들어가는 자료들을 중간에서 받아서 출력해 줄 수도 있고 변경해서 다시 보내줄 수도 있는 게 미들웨어의 역할이다.
만들어진 미들웨어 써보기
redux-logger
npm install --save react-redux
redux-logger는 만들어져 있는 미들웨어이며 이전상태값과 액션 다음상태값을 출력해준다.
원래 저기에 이전상태값이랑 action객체 다음 상태값이 다 떠야되는데 왜 안뜨는건지...
원래는 이렇게 상태값과 액션객체가 뜬다.
피드백
사실 미들웨어는 저번주 금요일에 배웠던 개념인데 이해가 너무안되서 정리를 좀더 해보고 오늘 학원에서도 다시해보고 thunk를 배우면서 어느정도 개념에 대해서 받아들여진것같다. 역시 코딩은 안되면 일단 반복하면 되는구나..
다음 포스팅은 thunk에 대해서 할것같은데 이것도 아직 제대로 알아먹은건아니라서 두루뭉술하게 될지도. 일단 인강이나 이런거 찾아보고 내 뇌속의 개념을 조금더 보충해서 알아보기쉬운 포스팅을 쓸려고 하고있다. 그냥 코드만 따라하는건 아무나 하는거고 코드를 보고 이해할수있는게 중요한것같다. 이제 포트폴리오 준비도 해야되고 정신도 없지만 . 배우는 개념들은 모두 놓치지않고 이해하고싶다.