Project
내 인생에 첫 web 프로젝트를 시작하게 되었다. 사이트는 술담화였는데 이 사이트를 이용하지 않는 나에게는 생소한 부분이 많았지만, 사이트를 하나하나 보면서 술을 판매하는 커머스 사이트라는 것을 알았다.
술담화 같은 경우 레이아웃이 반복되는 부분이 많았는데 이 부분에서 리액트를 활용하기 좋은 사이트라고 생각이 들었다. 첫 프로젝트인 만큼 프론트엔드와 백엔드 소통이 쉽지는 않았다. 하지만 한 목표를 향해 달려와 준 팀원분들 덕분에 기한 안에 팀 프로젝트를 성공적으로 끝낼 수 있었다.
첫 React를 이용한 프로젝트였다. 혼자 자바스크립트를 이용하여 클론사이트를 만들어는 보았다. 이때마다 자바스크립트도 충분히 편한데 굳이 리액트를 사용해야 할까? 라는 생각이 들었다. 하지만 프로젝트를 하면서 왜 React를 사용하는가를 알게되었다. 그 이유를 몇가지로 나누어 볼 수 있을꺼 같다.
2022.04.25 ~ 2022.05.06.
프론트엔드 4명
백엔드 2명
협업도구
🔥 => 내가 구현한 기능 & 참여한 기능
메인화면
: 반복적인 레이아웃 구성을 컴포넌트화 시켜 구성하였다.
구독페이지
: 구독페이지 내부에 버튼 클릭시 생성되는 모달창과 하단부에 캐러셀을 구성하였다.
로그인
: 프로젝트 안에서 백엔드와 처음으로 데이터패치를 하고 성공하였다,
토큰권한에 따라서 만약에 로그인이 되어 있지 않다면 로그인페이지로 넘어가게 구성하였다.
상세페이지
: 제품리스트가 있는 메인페이지와 카트페이지를 연결해주고, 댓글입력, 삭제, 수량 조절 기능 & 장바구니 담기 기능을 구현하였다.
회원가입
: 술담화 회원가입 유효성 검사에서 생각보다 까다로웠다. 이 부분 팀원들과 상의한 끝에 정규표현 식으로 해결을 하여 프론트쪽에서 1차 검사를 하여 양식에 맞지 않으면 회원가입을 불가하게 만들었다.
const goToLogin = e => {
e.preventDefault();
if (idInput && pwInput) {
fetch('http://10.58.2.197:8000/users/login', {
method: 'post',
body: JSON.stringify({
email: idInput,
password: pwInput,
}),
})
.then(response => response.json())
.then(result => {
if (result.message === 'SUCCESS') {
localStorage.setItem('token', result.token);
alert('환영합니다!');
navigate('/detail/14');
} else {
alert('확인해주세요');
}
});
} else {
alert('이메일 및 비밀번호를 확인해 주세요');
}
};
기억하고 싶은 첫번째 코드는 로그인 로직이다.
idInput 에 담긴 값과 pwInput 에 모두 값이 담기면 서버로 그 값을 보내서 서버에 저장된 id값과 pw값과 일치되는지 확인 후 그 값이 맞다면 토큰값을 부여하고 같지 않다면 alert 창을 통해 유저에게 입력된 값이 맞는지 확인하게 해주는 로직이다.
라이브러리를 사용하지 않고 순수 리액트를 통해서 구현한 모달창 역시 꽤 기억에 남는 코드이다.
const CardBox = () => {
const [modalOpen, setModalOpen] = useState(false);
const outModal = useRef();
const [product, setProduct] = useState([]);
const [modalId, setModalId] = useState();
const fetchData = () => {
fetch(`http://10.58.2.197:8000/products/${modalId}/subscribe`)
.then(response => response.json())
.then(data => {
setProduct(data.subscribe_detail[0]);
});
};
useEffect(() => {
typeof modalId !== 'undefined' && fetchData();
}, [modalId]);
const openModalFn = id => {
setModalOpen(true);
setModalId(id);
};
modalOpen state 를 초기값을 false 로 설정해주고 openModalFn 을 통해서 true 로 변경시켜서 모달창을 띄운다.
또한, modal 에서 사용할 데이터를 setProduct 에 담아서 저장하였다
useEffect(() => {
const handleEscapeKey = event => {
if (event.code === 'Escape') {
setModalOpen(false);
}
};
document.addEventListener('keydown', handleEscapeKey);
const handleClickOutside = e => {
if (outModal.current && !outModal.current.contains(e.target)) {
setModalOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscapeKey);
};
}, []);
return (
<CardBoxComponent
key={id}
id={id}
openModal={() => setModalOpen(true)
openModalFn={openModalFn}
modalOpen={modalOpen}
/>
);
})}
{modalOpen && (
<Modal
closeModal={() => setModalOpen(false)}
outModal={outModal}
product={product}
/>
)}
</div>
);
};
event.code. 를 통해서 "esc" 버튼을 눌렀을때 setModalOpen(false) 로 변경시켜서 모달창을 꺼주게 구현하였고,
"esc" 로직과 비슷한 로직으로 addEventListner 을 이용해서 모달 외부 클릭시 혹은 "X" 버튼을 클릭시 setModalOpen(false) 로 변경시켜서 모달창을 꺼주게 구현하였다.
const Modal = ({ outModal, closeModal, product }) => {
return (
<div className="modal" ref={outModal}>
<div className="madalBack" onClick={closeModal} />
<div className="modalPage">
<button className="modalBtn" onClick={closeModal}>
X
</button>
<div className="modalbox">
<div className="modalOpen">
<img
className="modalImage"
src={product.product_image}
alt="이미지"
/>
<div className="descriptionDetail">
{product.description_detail}
</div>
<div className="descriptionTag">{product.description_tag}</div>
<div className="alcholPercentage">
도수 : {product.alcohol_percentage}%
</div>
<div className="price">
가격 : {parseInt(product.price).toLocaleString()}원
</div>
</div>
</div>
<div>
<style jsx="true">{`
body {
overflow: hidden;
}
`}</style>
</div>
</div>
</div>
);
};
outModal, closeModal, product 를 props 로 받아와서
모달 내부에 사용하는 데이터를 뿌려주었고, 각 버튼에 onClick event 를 활용해서 모달을 닫는 기능을 부여하였다.
캐러셀 또한 라이브러리 없이 순수 리액트를 사용해서 구현하였다.
const Subscribe = () => {
const [index, setIndex] = useState(0);
const leftclickhandler = () => {
return index !== 0 && setIndex(index - 1);
};
const rightclickhandler = () => {
return index !== 2 && setIndex(index + 1);
};
const changeBtn = idx => {
setIndex(idx);
};
상위 components 인 Subscribe 에서 유저 눈에 가시화된 회전하는 화면단을 index로 저장하고 최초값을 0으로 설정해주었고,
좌측, 우측을 막는 동시에 좌우로 회전하는 함수를 index - 1, index + 1 를 통해서 구현하였다.
그리고, changeBtn 함수에 setIndex를 설정해주는 값을 걸어서 해당 index 에 해당하는 버튼을 클릭시 해당된 화면으로 넘어가게 구현하였다.
const Carousel = ({
leftclickhandler,
rightclickhandler,
index,
changeBtn,
}) => {
return (
<div className="Carousel">
<div className="dontWorryWrapper">
<div className="leftIcon">
<button onClick={leftclickhandler} />
</div>
<div className="dontWorrySlideBox">
<div
className="dontWorrySubSlideBox"
style={{
transform: `translateX(${index * -375}px)`,
transition: `transform 1s`,
}}
>
<div className="rightIcon">
<button onClick={rightclickhandler} />
</div>
캐러셀 좌, 우측에 버튼에 onClick event에 leftclickhandler, rightclickhandler 를 걸어주었다
또한 dontWorryBox 박스에 style을 적용하여서 전체 캐러셀에 1/3 만큼 유저눈에 보이게 구현하였다.
<ul className="buttonWrapper">
<li className="slideButton" onClick={() => changeBtn(0)} />
<li className="slideButton" onClick={() => changeBtn(1)} />
<li className="slideButton" onClick={() => changeBtn(2)} />
</ul>
changeBtn 을 이용해서 index를 변환하여 각 버튼 클릭시 해당하는 화면단으로 이동시키게 구현하였다.
프로젝트를 진행하는 2주라는 기간 중 실질적으로 프로젝트에 집중할 수 있는 시간은 매우 한정적이었다.
팀원들과 함께 작업의 중요도, 우선도 등을 정하여 확실하게 진행할 수 있는 작업 등을 우선적으로 진행하였다. 이 때 기간 내 작업 등을 효율적으로 관리하기 위해 사용했던게 Trello였다. 내가 계획하였던 작업의 진행도 등을 객관적으로 판단하고, 진행되고 있는 부분을 보완하여 좀 더 탄탄하게 프로젝트를 마무리 할 수 있게 일정 관리를 한 것이 많은 도움이 되었다.
소통
프로젝트를 하면서 개인적으로 아쉬운 부분이 많다
먼저, 백엔드와 소통할때 그 용어와 개념들이 생소해서 완벽한 소통을 하지 못했다 서로 같은 내용의 대화를 다른 언어로 하는 느낌이었다 하지만 프로젝트에 더 심도있게 들어가고, 젖어 들 때 쯤 백엔드에서 하는 말들과 내용을 어느 정도 이해 할 수 있었다 조금 더 빨리 잘 이해했다면 착오없이 더 좋은 성과를 낼 수 있을까 라는 생각을 한다.
몰입의 부작용, 나무만 보는 시야
어는 작은 목표에 집중해서 코드를 치고 있으면 문뜩문뜩 내가 뭘 하고 있는지 란 생각을 할 때가 있었다.
독서를 할 때 목차를 보면서 독서하는 습관이 있는데 그 이유는 내가 지금 집중해서 읽는 책의 내용이 이 책 전체에서 어디에 해당되는지 무엇을 가르키는지 그 방향을 체크 하기 위해서다.
하지만 개발을 할때는 그 목표를 설정하고 설계없이 달려들기 바빴다 그래서 그 몰입의 끝에 문뜩 내가 뭘 하고 있는지 놓칠때가 종종 있었다 다음 프로젝트 혹은 앞으로 개발을 할 때는 나무를 보고 달리는 것도 너무 중요하지만 숲을 주기적으로 보는 시야를 가져야 겠다는 생각을 했다.
코드
2주가 지난 시점에서 내가 쓴 코드를 쭉 들여다 보고 있으면 정말 솔직히 아직 이해하지 못한 부분들이 있다.
동기분들의 도움을 받은 부분, 구글에서 긁어서 붙인 부분, 디버깅 중 우연히 구현된 부분 등 내 코드, 로직이라고 말하기 너무 부끄러운 부분들이 많다 하지만, 내 로직이 아니라고, 내 머리속에서 나오지 않았다고 나를 자책하고 몰아세워도 봤지만 그럴수록 나만 더 괴롭고, 오히려 개발하는데 있어서 동력을 떨어지게 했다.
아직 개념이 부족해서, 경험이 부족해서, 공부한 기간이 얼마되지 않았는데 너무 많은 부분을 나에게 바란거 같기도 하다 조급해 하지 말고 조금씩 하루하루 깔끔한 코드를 작성하고 싶다.
촉박한 기간내에 팀원들과 공동의 목표를 이뤘기에 굉장히 만족스러웠다.
하지만 개인적으로는 아쉬운 부분이 너무 많았다
최초 기획에 없던 구독페이지를 개인적인 욕심으로 만들었다 어찌보면 팀 보다 내 경험을 우선시 했던 거 같다 그로 인해서 백엔드에서 구축해야할 데이터도 추가되어서 부담을 드린 거 같다 다음 프로젝트 할때에는 팀을 우선시해서 공동의 목표에 더 크게 기여하고 싶다.
그렇지만 좋은 팀원들과 함께 일하고, 그 속에서 성장 할 수 있어서 너무 감사하고 행복했다
조금 더디고 느려도 올바른 방향으로 꾸준히 성장하고싶다.