위코드에서 2차 프로젝트를 진행 하였습니다. 팀원은 프론트(웹) 2명, 프론트(앱) 2명, 백엔드 2명으로 총 6명에서 프로젝트를 진행하였습니다. 우리팀은 1차 프로젝트와는 다르게 2명의 React Navtive(RN전사?) 와 함께 'Frip'사이트를 웹, 앱 클론 프로젝트 하였습니다.
개발기간 : 2020.12.28 ~ 2021.01.08 (약 2주)
세상 모든 액티비티, 프립 주말엔 서핑, 퇴근 후엔 베이킹
색다른 여가생활을 원한다면 Let's Frip!
GithubRepo: R.I.P
유튜브비디오녹화: R.I.P 동영상
-프로젝트 사용 기술 스택 🤩
Front-end
React
React-router-dom
Javascript
HTML, Styled-components
CRA, npm
Git & Github
ESLint , Prettier
trello
Back-end
Python
Django
MySQL
AWS
JWT
Bcrypt
Faker & Google Trans
Gunicorn
Nginx
Git & Github
trello
캐러셀 슬라이드는 버튼을 누르면 사진이 왼쪽, 오른쪽으로 슬라이드 되어 다음 컨텐츠를 노출시킬 수 있는 흔히(?) 볼 수 있는 슬라이드 기능이다. 이런 슬라이드 기능은 React에서는 다양한 라이브러리가 모듈화 되어있지만 요번 프로젝트 미션이 라이브러리를 사용하지 않고 구현하기 였기 때문에 까다로운 작업이였다.
export default function OptionSlide({ img1, img2, img3, count, height }) {
const TOTAL_SLIDES = count;
const [currentSlide, setCurrentSlide] = useState(0);
const nextSlide = () => {
const isMaximum = currentSlide >= TOTAL_SLIDES;
setCurrentSlide(isMaximum ? 0 : currentSlide + 1);
};
const prevSlide = () => {
setCurrentSlide(currentSlide === 0 ? TOTAL_SLIDES : currentSlide - 1);
};
useEffect(() => {
const interval = setInterval(() => {
setCurrentSlide(currentSlide => currentSlide + 1);
if (currentSlide >= TOTAL_SLIDES) {
setCurrentSlide(0);
}
}, 3000);
return () => clearInterval(interval);
}, [TOTAL_SLIDES, currentSlide]);
return (
<Container>
<SliderContainer currentSlide={currentSlide}>
<Slide img={img1} height={height} />
<Slide img={img2} height={height} />
<Slide img={img3} height={height} />
</SliderContainer>
<ButtonContainer>
<Button1 onClick={prevSlide}>
<img src="images/다운로드 (4).svg" alt="arrow" />
</Button1>
<Button2 onClick={nextSlide}>
<img src="images/다운로드 (4).svg" alt="arrow" />
</Button2>
</ButtonContainer>
</Container>
);
}
내가 구현한 코드는 TOTAL_SLIDES라는 변수에 이미지 갯수를 지정해 주고(컴포넌트를 재사용하려고 보니 각자 슬라이더에서 이미지 갯수가 달라 부모에서 Props로 넘겨받았다.) 왼쪽 오른쪽 버튼에 각각 currentSlide를 변화시켜 이미지가 변경되는 방식이다. 또한 자동으로 슬라이드가 넘어가게 interval이라는 함수를 만들어 setInterval이벤트를 주어 3초마다 넘어가게 구현을 했다.
캐러셀 슬라이드를 구현할 때 맨 처음 구현했던 코드는 SliderContainer에 Ref로 직접 접근을 해서 Transform하는 형식으로 구현을 했었는데, Styled-components를 이용하기 시작하면서
const SliderContainer = styled.div`
width: 100%;
display: flex;
transition: all 0.5s ease-in-out;
transform: translateX(-${props => props.currentSlide}00%);
`;
props로 currentSlide 를 받아올 수 있게 되면서 Ref의 접근을 하지 않아도 되었던 점. (styled-component는 강력하다.)
이번 리뷰하기 게시판은 텍스트와 함께 이미지 파일을 전송해 줘야하는 후기 형식이였다. 간단한 제목과 텍스트, 사용자 닉네임을 백엔드에게 보내 데이터를 쌓는 형식은 1차때 해봤는데,요번에는 사진을 보내려고 하니 의문점이 들었다. (1) 사진은 보통 URL 형식으로 주고 받는데, 내 로컬에 있는 URL을 보내봤자 사용 할 수 없는 URL이 될테고 (2) 그렇담 파일을 백엔드에게 직접적으로 전송 하는건가? 등 고민을 하다가 든든한 PM 원희님께 가서 물어봤더니 이미지 파일을 Form-data 형식으로 가공을 하여 보내주면 이미지 파일을 저장하는 서버에 쌓아두고 거기서 URL을 뽑아 다시 프론트단에게 전송하는 방식으로 사용한다고 알려주셨다 ! 바로 form-data의 형식을 보자
const pushReviewData = () => {
const formData = new FormData();
formData.append("file", img);
formData.append("content", reviewText);
formData.append("star_rating", 1);
axios({
method: "post",
url: `http://13.209.17.252:8000/board/product/${props.location.state.detailFripData.id}/review`,
data: formData,
headers: {
"Content-Type": "multipart/form-data",
Authorization: TOKEN,
},
})
백엔드에서 요청한 정보는 file, content, star_rating 이였고 img는
const handleChangeFile = event => {
{...}
if (event.target.files[0]) {
setImage(event.target.files[0]);
}
};
파일이 선택될 때 setImg를 통해 넣어준다. 이렇게 Form-data 형식으로 가공을 한 뒤 백엔드에게 보내주면 우리는 내 로컬에 있는 이미지를 백엔드에게 전송 할 수 있게 된다!(백엔드에서도 저것을 처리해야 되는 로직이 있어야 된다고 들었다. 갓원희짱)
1차 프로젝트를 마치고 바로 2차 프로젝트에 달리면서 모두들 체력적으로나, 정신적으로나 힘들었을텐데 다들 묵묵히 맡은 역할을 해나가는거 보면서 정말 든든했습니다. 서로 물어보고 같이 찾아보면서 막힌 부분을 해결하며 결국 완성 시킨 프로젝트 너무나 자랑스럽습니다.