- 제주패스는 제주 여행의 슈퍼 앱으로 전세계 실시간 최저가 항공, 제주 맛집까지 제주도 여행의 모든 것을 확인할 수 있는 웹 사이트 입니다.
- 저희는 그 중에서도 '항공 페이지'에 집중하여 구현하였습니다.
- 또한 '깨깨오항공', '코팡항공' 등으로 정해놓고 이 기업들을 향해 날아가는 컨셉으로 재미있게 구성해보았습니다!
2022년 8월 1일(월) ~ 2022년 8월 12일(금) : 총 10일
Front-End : 🐥 노정은, 🐶 엄성훈, 🐱 이현주, 🦆 이혜진
Back-end : ⚽️ 손찬규, 🦅 박정용
- ESG 페이지 🐶 / Ant Design(Pull Page)
- 항공 메인 페이지 🐶 / Swiper(Carousel), Ant Design(Carousel)
- 항공권 예약 페이지 🐶
- react swiper, anti design 라이브러리를 사용해서 mock data로 화면 렌더링
- 웹페이지를 보다보면 회사소개서 같은 페이지에서 한번쯤 보게되는 풀페이지
Siwper 라이브러리의 사용방법과 styled component에서의 단순 적용하는 방법에 대해 학습을 하였다.
- 필요한 컴포넌트를 상단에 임포트 하고, useEffect로 비동기로 목데이터를 호출하였고,
const StyledSwiper = styled(Swiper)
확장기능을 사용하여 다른 className을 부여하였다. - 이미지는
<CarouselImg imgUrl={imgUrl} />
style props 내려주어,background-image: url(${props => props.imgUrl})
처리하였다. - 화면만 작동되게 처리하였기 때문에, 좋은 코드가 전혀 아니다. 많이쓰이는 slick, swiper에 대해 더 따로 공부해 둬야겠다.
단순한 목데이터들.... 현업에 가면 수십개가 달려있겠지?
[
{
"id": 1,
"mtitle": "MAKE JEJU BETTER",
"title": "그린 앰버서더는 제주를 지키기 위한 MAKE JEJU BETTER 캠페인의 멤버십입니다",
"video": "esg_main_video.mp4"
},
{
"id": 2,
"title": "우리는 제주 여행을 통해 새로운 경험과 즐거움 그리고 힐링을 하고 떠납니다",
"imgUrl": "bg_img01.jpg"
},
...
]
import { Swiper, SwiperSlide } from 'swiper/react';
import { Pagination, Keyboard, Mousewheel } from 'swiper';
import 'swiper/css';
import 'swiper/css/pagination';
const [slidesData, setSlidesData] = useState([]);
useEffect(() => {
fetch('/data/esgdata/esgData.json', {
method: 'GET',
})
.then((res) => res.json())
.then((data) => {
setSlidesData(data);
});
}, []);
return (
<SwiperContainer>
<StyledSwiper
direction='vertical'
pagination={{
clickable: true,
}}
modules={[Pagination, Keyboard, Mousewheel]}
mousewheel={true}
keyboard={true}
>
{slidesData.map(({ id, video, imgUrl, mtitle, title }) => (
<SwiperSlide key={id}>
{video && <video src={video} type='video/mp4' autoPlay loop muted />}
<CarouselImg imgUrl={imgUrl} />
<CarouseTitWrap>
<CarouseMainTit>{mtitle}</CarouseMainTit>
<CarouseTit>{title}</CarouseTit>
</CarouseTitWrap>
</SwiperSlide>
))}
</StyledSwiper>
</SwiperContainer>
);
const StyledSwiper = styled(Swiper)`
width: 100%;
height: 100%;
.swiper-pagination-bullets {
right: 50px;
}
`;
const CarouselImg = styled.div`
position: relative;
width: 100vw;
height: 100vh;
background-image: url(${(props) => props.imgUrl});
background-repeat: no-repeat;
background-size: cover;
border-radius: 2px;
`;
- Siwper 라이브러리를 사용 후 다른 라이브러리에서도 Carousel 을 만들 수 있음을 알게 되었다. 다른 라이브러리도 사용해 보고자 antd의 캐러샐을 사용 하였다.
- 좌우 화살표를 커스텀을 해보고자 간단한 예제들을 찾아보았고, 아래와 같이 작성하였다. 그러하자 아래와 같이 오류가 뜨는 것 이였다.
import 'antd/dist/antd.min.css';
import { Carousel } from 'antd';
const SlickArrowLeft = (props) => <button {...props} className='slick-prev' />;
const SlickArrowRight = (props) => <button {...props} className='slick-next' />;
const settings = {
infinite: true,
speed: 800,
slidesToShow: 2,
slidesToScroll: 2,
autoplay: true,
draggable: true,
arrows: true,
prevArrow: <SlickArrowLeft />,
nextArrow: <SlickArrowRight />,
};
<Carousel {...settings}></Carousel>;
[stack overFlow]
Don't pass down all the props that you receive for custom arrows.
nextArrow: ({ firstProp, secondProps, ...otherProps }) => (
<element {...props that you want} />
)
Something like this should work.
한번에 Props으로 모두 내리지 말라는것...
const SlickArrowLeft = ({ currentSlide, slideCount, ...props }) => (
<button {...props} className='slick-prev' />
);
const SlickArrowRight = ({ currentSlide, slideCount, ...props }) => (
<button {...props} className='slick-next ' />
);
The unknown-prop warning will fire if you attempt to render a DOM element with a prop that is not recognized by React as a legal DOM attribute/property. You should ensure that your DOM elements do not have spurious props floating around.
이유에 대한 설명과 해결법은 위와 같이 해결 할 수 있었다.
- 이번엔 다시 React Swiper 라이브러리로 카드 배너를 만들어 보았다.
- 필요한 요소들을 import하고, 옵션들을 설정하고, styled를 확장하여, 기존에 설정된 클래스들의 color을 blue -> White로 설정만 해주었다.
- 처음 다뤄보는 라이브러리 캐러샐 기능들인데, 커스텀하기가 생각보다 어려웠다.
- 좋은 코드를 위해 더 만들어보며, git도 많이 살펴봐야겠다...
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination, Autoplay } from 'swiper';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
<StyledSwiper
modules={[Navigation, Pagination, Autoplay]}
pagination={{
clickable: true,
dynamicBullets: true,
}}
navigation
spaceBetween={30}
slidesPerView={3}
loop='true'
autoplay={{
delay: 4000,
disableOnInteraction: true,
}}
>
{carouselData.map((imgData) => {
return (
<SwiperSlide key={imgData.id}>
<CarouselImg imgUrl={imgData.imgUrl} />
</SwiperSlide>
);
})}
</StyledSwiper>;
const StyledSwiper = styled(Swiper)`
.swiper-button-next {
color: white;
--swiper-navigation-size: 30px;
}
.swiper-button-prev {
color: white;
--swiper-navigation-size: 30px;
}
.swiper-pagination-bullet {
background: white;
}
`;
- 2차 프로젝트에서는 내가 맡은 부분은 백엔드와 통신 부분이 없었고, 재사용이 많은 카드형태의 컴포넌트 작업이 많았다.
- 백엔드에서 작업이 완료되기전에 목데이터로 미리 작업을 한다고도 하여, 간단히 필요한 부분만 만들어서 컴포넌트를 만들어서 재활용 하였다.
- 실제 예약싸이트들은 수많은 데이터들이 들어갈텐데...
const AirMain = () => {
const [jejuData, setJejuData] = useState([]);
const [japanData, setJapanData] = useState([]);
const [popularData, setPopularData] = useState([]);
useEffect(() => {
fetch('/data/tripdata/jejuData.json', {
method: 'GET',
})
.then((res) => res.json())
.then((data) => {
setJejuData(data);
});
fetch('/data/tripdata/japanData.json', {
method: 'GET',
})
.then((res) => res.json())
.then((data) => {
setJapanData(data);
});
fetch('/data/tripdata/PopularData.json', {
method: 'GET',
})
.then((res) => res.json())
.then((data) => {
setPopularData(data);
});
}, []);
return (
<>
<ModalComponent />
<AirMainContainer>
<MainBox>
<AirTripCard
title='🍊 제주 여행은 언제나 즐거워 🌴'
data={jejuData}
/>
<AirTripCard title='🎏 간만에 일본에 가볼까? 🏯' data={japanData} />
<AirTripCard title='✈️ 인기 노선별 최저가 🚗' data={popularData} />
</MainBox>
</AirMainContainer>
</>
);
};
- 이제 input 값들은 const { name, value } = e.target의 속성들을 꺼내어, [name]: value 로 처리해서 넘겨주는 방법은 처리할 수 있게 되었다.
- 목데이터가 저런식으로 있다면, person 3명의 가상의 배열을 만들어서 컴포넌트와 데이터들을 전달을 해야하는데, 불변을 유지하며, 어떤 메서드를 사용하며 할 줄 몰랐다.
- 혼자 해결 못하는 문제에 직면하면서, 동기들에게 도움을 얻어 가상의 배열을 만들 수있었고, 어떻게 찾았으며 공부해가면 알 수 있는지에 대해서도 많이 물어봤던 것같다.
new Array를 이용해서 새로운 배열을 만들어, 불변을 유지하며 person 수만큼의 컴포포넌트와 input 입력값을 가공해서 넣어줄 수 있었다.
{
"originResults": {
"code": "01023451212",
"airline": "대한항공",
"en_airline": "BOEING 789-9",
"flight_time": "1시간 05분",
"user_information": {
"user_name": "홍길동",
"user_email": "[email protected]"
},
"person": 3,
"total_price": 118000,
}
}
<TabLi>
{[...new Array(originResults.person)].map((_, idx) => {
const handlePassengerData = e => {
const { name, value } = e.target;
setPassengerData({
...passengerData,
[idx]: { ...passengerData[idx], [name]: value },
});
};
return (
<TabContentAdult
key={idx}
onChange={handlePassengerData}
>
.....
메인페이지 | ESG페이지 |
---|---|
항공페이지 | 로그인 |
---|---|
모달페이지 | 로딩페이지 |
---|---|
리스트페이지 | 예약결제페이지 |
---|---|
-
GitHub : 각 페이지별 branch 관리.
-
Slack : 팀원간의 실시간 소통 창구.
-
Trello : 기능 단위로 카드를 생성, Sprint 단위로 진행했는지와 Stand up 미팅 툴로 활용.
-
Notion : 회의정리 기록, 오늘의 공유/질문 사항, 현재 진행 사항, blocker 공유, 기능 단위 페이지 셍성 후 공유 및 기록.
트렐로 | 노션 |
---|---|
깃허브 | 슬랙 |
- 이 프로젝트는 제주패스를 참조하여 학습목적으로 만들었습니다.
- 학습용으로 만들었기 때문에 이 코드를 활용하여 이득을 취하거나 무단 배포할 경우 법적으로 문제될 수 있습니다.