weather_level
🎬 유튜브 링크
📕팀 노션
weather_level 프로젝트의 프론트 엔드 Repository 입니다.
개요
- 명칭 : weather level (외출 난이도)
- 개발 인원 : 6명
- Front end: 전재민, 조윤경
- Back end: 채수연, 강상연, 김동현
- Designer: 공은지
- 개발 기간 : 2021.04.23 ~
- 주요 기능
- 사용자의 선호도를 토대로 알고리즘을 통해 날씨 점수 출력
- 날씨 정보 큐레이팅
- 개발 언어 : JavaScript
- 개발 라이브러리 : React.js
- 형상 관리 툴 : git
- 협업 툴 : notion, 구글 스프레드 시트, 구글 드라이브
- 간단 소개 : 리액트 - 스프링 협업의 날씨 정보 제공 서비스
프로젝트 특징
- weather level (외출 난이도)
- 날씨 정보와 사용자의 선호도를 토대로, 알고리즘을 통해 날씨 점수를 제공하는 서비스
- 직관적이고 알아보기 쉬운 날씨 정보 큐레이팅
- 프론트엔드와 백엔드를 분리하여 프로젝트 개발
- 각 파트별로 Repository를 생성 후 작업
- 프론트: AWS S3, Cloud Front
- 백엔드: AWS EC2
- 빌드 후, S3와 EC2를 연동
- API 명세서에 따라 API호출 및 응답 확인
- HTTPS 통신
- SSL 인증서 적용
- 사용자의 현재 GPS 정보를 기반으로한 날씨 정보 제공
- 사용자가 원하는 지역 선택 가능
- 사용자가 설정한 우선순위에따라서 날씨 정보 카드를 우선적으로 보여줌
- 카카오톡으로 자신의 오늘 날씨 점수를 공유
- 뉴모피즘과 귀여운 날씨 캐릭터가 가미된 UI
- 반응형 디자인
- 포터블 모드: 주요 모델 지원 UI
- 데스크탑 모드: 데스크탑 환경에서 한눈에 볼 수 있는 UI
- PWA
- Progressive Web Apps
- Lighthouse 조건을 충족
- 안드로이드 -크롬, iOS - 사파리에서 홈 화면에 바로가기 설치 가능
- 안드로이드 플레이 스토어 배포 예정
- 사용자의 현재 GPS 정보를 기반으로한 날씨 정보 제공
핵심 트러블 슈팅
range event 버블링
- 세번째 슬라이더에서 range를 조절할 때 슬라이더가 같이 움직여버리는 이슈
- onChange에 stopPropagation을 넣어도 해결이 안 되었음
- touchstart 이벤트 제어를 위해서 바닐라 자바스크립트를 활용
- Range를 감싸는 요소에 className 부여
<RangeWrapper
className="wrapper"
>
{rangeList}
<ShowButton onClick={handleRangeHidden}>
{isHidden ? <MdKeyboardArrowDown /> : <MdKeyboardArrowUp />}
</ShowButton>
</RangeWrapper>
- 터치이벤트 버블링 방지 함수 정의
const stopTouchStart = (e) => { e.stopPropagation(); }
- useEffect에서 eventListener 처리
React.useEffect(() => {
// RangeWrapper
const wrapper = document.querySelector('.wrapper');
wrapper.addEventListener('touchstart', stopTouchStart);
return () => {
wrapper.removeEventListener('touchstart', stopTouchStart)
}
}, [])
말풍선 setTimeout 관련 이슈
- 팝업을 여러번 클릭했을 때 마지막 클릭이 3초간 유지가 안됨
- clearTimeout 관련 문제라고 생각함
- 실패1. clean up 함수 안에서 clearTimout -> Score 컴포넌트 안에 SpeechBubble이라는 컴포넌트를 온클릭 했을 때 조건부 렌더링으로 노출시키는 상황이라 clean up이 Bubble 컴포넌트가 생기고 사라질 때도 일어남
- 실패2. Score.tsx에서 setTimer을 let 변수로 선언하고 onClick 할 때 clearTimeout을 한다.
실패!!! timer console을 찍을 때 undefined가 나옴. 리렌더링 되면서 timer는 사라지고 비동기만 돌아가는 듯함
const Score = (props)=>{
let timer ;
const onClickLogo = () => {
console.log(timer);
clearTimeout(timer);
dispatch(weatherActions.getIconMessage(nowIcon));
setIsShowBubble(true);
timer = setTimeout(() => {
setIsShowBubble(false);
}, 3000);
};
...
}
- 해결 : setTimeout을 useState로 관리
const [timerState, setTimerState] = useState(null);
const onClickLogo = () => {
clearTimeout(timerState);
dispatch(weatherActions.getIconMessage(nowIcon));
setIsShowBubble(true);
const timer = setTimeout(() => {
setIsShowBubble(false);
}, 3000);
setTimerState(timer);
};
빌드가 실시간 적용이 안되는 문제
- 수정을 하고 새로 배포했을 때, 수정사항이 반영되지 않음
- cloud front 캐시 문제
CloudFront에서 특정 파일 캐시 방지
swiper index 이슈
- swiper index 이슈
- 메인컨텐츠에서 preference를 조절하고 저장 이후 첫 슬라이더로 이동이 되었으면 좋겠다는 피드백
- 카드 상세 페이지에서 이전 버튼 클릭 시 무조건 첫 번째 인덱스로만 보여주는 문제
- 설정페이지, 지역 설정 페이지에서 다시 메인으로 돌아갔을 때 무조건 첫 번째 인덱스로 보여주는 문제
- swiper 객체 생성
... // swiper 객체를 생성하기 위한 useState const [swiper, setSwiper] = useState(null); // Swiper의 onSwiper props로 setSwiper를 넘기면 객체 생성 ... <Swiper pagination={{ clickable: true }} className="swiper" onSwiper={setSwiper} style={{ width: '100%', height: '80%', }} onSlideChange={onSwiper} > ...
- moveCurrentSlide 선언 후 Presetting 컴포넌트에 moveToMain props로 넘기기
- 저장 버튼을 클릭시 넘겨 받은 moveToMain props의 함수 실행
- preference 저장 시, 0번 인덱스(처음)으로 이동
// 슬라이더 인덱스 이동 const moveCurrentSlide = (idx) => { swiper.slideTo(idx, 250, true); swiper.slideReset(); }; ... <SwiperSlide> ... // <PreSetting isMain moveToMain={() => moveCurrentSlide(0)} /> ... </SwiperSlide>
- slider의 index를 저장하는 모듈 생성
// slider.ts // 슬라이더의 인덱스를 받아오는 액션 const setSliderIndex = createAction<unknown>('slider/SET_SLIDERINDEX'); const slider = createReducer(initialState, { [setSliderIndex.type]: (state: sliderType, action: PayloadAction<number>) => { state.curIndex = action.payload; }, }); export const sliderActions = { setSliderIndex, }; export default slider;
- 현재 슬라이더를 인덱스 스토어에 저장하는 함수 선언(Main.tsx)
// 현재 슬라이더 인덱스 스토어에 저장 const onSwiper = (swiper) => { dispatch(sliderActions.setSliderIndex(swiper.realIndex)); };
- Swiper의 onSlideChage props에 onSwiper 넘기기
- 슬라이드가 될 때마다 onSwiper 실행
...
<Swiper
pagination={{ clickable: true }}
className="swiper"
onSwiper={setSwiper}
style={{
width: '100%',
height: '80%',
}}
onSlideChange={onSwiper}
>
...
- Main.tsx가 렌더링 되었을 때 해당 인덱스로 이동시키기
// swiper 객체가 생겼을 때 실행
useEffect(() => {
if (swiper) {
moveCurrentSlide(curIndex);
}
}, [swiper]);
## Reference
Contetnts
사용 예시(스마트폰)
반응형 디자인 (태블릿)
'Projects' 카테고리의 다른 글
디자인패턴 (0) | 2022.07.03 |
---|---|
AWS Amplify(1) (0) | 2022.04.03 |
gaemangtalk (0) | 2021.04.22 |
hanghae99_books (0) | 2021.04.08 |
my_pet_is_the_best (0) | 2021.04.02 |