프로젝트 소개
- '피리마켓' '필리' 사이트를 모델링 한 프로젝트로, 약과 건강식품을 판매하는 커머스 사이트를 백지 상태에서 구현하였다.
- 건강을 먹다는 의미로 사이트의 이름을 'HealthEat(헬스잇)'으로 지었다.
- 기간 : 2022.11.14 ~ 2022.11.25
- ➡️ 프로젝트 깃허브 레포지토리 바로가기
- ➡️ 프로젝트 노션 페이지 바로가기
팀 소개
- 프론트엔드 3명
- 우석민 : Nav 바, 스토어 페이지 구현
- 이상윤(나) : 메인 페이지, 상품 상세 페이지 구현
- 이혜원 : 로그인 페이지, 회원가입 페이지 구현
- 백엔드 2명
- 이은영 : 찜하기 기능 구현
- 조상원(PM) : 로그인, 제품조회 기능 구현
개발 도구 및 적용 기술 (+트렐로)
- 프론트엔드
- JavaScript(ES6)
- React.js
- Sass
- React-router-dom
- Fontawesome
- 백엔드
- JavaScript(ES6)
- Node.js
- Express
- JSON Web TOKEN
- Bcrypt
- My SQL
- Multer
- 커뮤니케이션 및 버전 관리
- Slack
- Trello
- Postman
- Git / Github
작업 결과 (시연 영상)
백엔드 DB 모델링 및 END POINT
나의 작업 (내 구현 사항)
메인 페이지의 화면 슬라이더 구현
// PromotionSlide.js
// ... import 코드 생략
const PromotionSlide = () => {
const [currentSlide, setCurrentSlide] = useState(0);
const [sliderData, setSliderData] = useState([]);
const slideLength = sliderData.length;
// ... 데이터를 받아오는 코드 생략
useEffect(() => {
setCurrentSlide(0);
}, []);
// 5.5초 간격으로 다음 사진 슬라이드를 불러오는 auto 함수
const auto = () => {
slideInterval = setInterval(nextSlide, intervalTime);
};
let slideInterval = null;
let intervalTime = 5500;
useEffect(() => {
auto();
return () => clearInterval(slideInterval);
}, [auto, currentSlide, slideInterval]);
// 다음 사진 슬라이드로 넘어가는 함수
const nextSlide = () => {
setCurrentSlide((currentSlide + 1) % slideLength);
};
// 이전 사진 슬라이드로 넘어가는 함수
const prevSlide = () => {
setCurrentSlide((currentSlide - 1 + slideLength) % slideLength);
};
return (
<article className="promotion-slide">
{sliderData.map((slide, index) => {
return (
<div
className={index === currentSlide ? 'slide current' : 'slide'}
key={index}
>
{index === currentSlide && <img src={slide.image} alt="slide" />}
</div>
);
})}
// ...
// 생략
// ...
</article>
);
};
export default PromotionSlide;
메인 페이지의 카테고리 리스트 구현 및 각 항목에 알맞은 제품을 표시할 수 있도록 스토어 페이지로 연결
// MenuList.js
// ... import 코드 및 상수 데이터 코드 생략
const MenuList = () => {
return (
<article className="menu-list">
<div className="menu-list-title">
<span className="menu-list-title-name">고민별 상품 보기</span>
</div>
<div className="menu-list-items">
{MENU_LIST_DATAS.map((data, idx) => (
<ListItem key={idx} menuListDatas={data} />
))}
</div>
</article>
);
};
export default MenuList;
// ListItem.js
import React from 'react';
import { useNavigate } from 'react-router-dom';
const ListItem = ({ menuListDatas }) => {
const { category, name, image } = menuListDatas;
const navigate = useNavigate();
return (
<div className="menu-list-each-item">
<div
className="menu-icon"
onClick={() => {
if (category >= 1 && category <= 4)
navigate(`/store?category=${category}`);
}}
>
<img src={image} />
</div>
<p className="menu-name">{name}</p>
</div>
);
};
export default ListItem;
백엔드에서 제품의 상세 데이터를 받아 상품 디테일 페이지에 표시 + 구매 수량에 맞춰 상품 가격 변경
// 구매 수량에 맞춰 상품 가격이 변하도록 구현한 코드
// BuyBar.js
// ... import 코드 생략
const BuyBar = ({ productID, productData }) => {
const [quantity, setQuantity] = useState(1);
const [totalPrice, setTotalPrice] = useState(0);
const navigate = useNavigate();
const { name, information, brand_name, price, discount_rate } = productData;
useEffect(() => {
setTotalPrice(parseInt(price) * quantity);
}, [price, quantity]);
// 조건부 랜더링
// : 첫 랜더링 시 (랜더링 이후 실행되는) useEffect의 특성으로 인해 백엔드로부터 데이터가 수신되지 않으므로
// 작성한 코드이다.
if (totalPrice === 0) return null;
const plusQuantity = () => {
setQuantity(prev => prev + 1);
};
const minusQuantity = () => {
// quantity(구매 수량)가 0 이하로 내려가지 않도록 작성한 방어 코드
if (quantity >= 2) {
setQuantity(prev => prev - 1);
}
};
return (
<>
// .. 생략
<div className="product-price">
<span className="price discounted-price">
{`${Math.round(totalPrice * (1 - discount_rate)).toLocaleString(
'ko-KR'
)}원`}
</span>
<span className="price fixed-price">
{`${Math.round(totalPrice * 1).toLocaleString('ko-KR')}원`}
</span>
<span className="price discount-rate">{`${
discount_rate * 100
}%`}</span>
</div>
// .. 생략
</>
);
};
export default BuyBar;
프로젝트 후기 및 느낀 점
첫 팀 프로젝트를 통해 배웠던 것들
- 브랜치를 생성해서 서로 작업하는 도중 초기 세팅을 수정해야 할 일이 생겼을 때 해결 방법
- 다른 팀원에게 영향이 가는 수정 사항일 경우 새로운 브랜치를 생성하여 수정 사항을 반영한다.
- 다른 팀원에게 영향이 가지 않는 수정사항일 경우 자신이 작업하고 있던 브랜치에 수정 사항을 반영한다.
- 브랜치에서 다른 브랜치로 이동하고자 할 때, 현재 브랜치의 작업 내역을 모두 커밋한 후 이동해야 작업 내역이 꼬이지 않는다.
- 브랜치 이름 작성 방식
- 보통 branch명은 camelCase로 작성한다.
- 예시 : feature/productDetail
- 브랜치 기능 별 라벨 명
- feature : 기능 구현할 때
- develop : 여러 기능을 합쳐서 테스트할 때
- release : 배포 전 최종 테스트할 때
- hotfix : 배포 후 급하게 버그를 수정할 때
- 팀 내 컨벤션은 미리 정해두는 것이 좋다.
- (예시) Git Branch 이름은 컴포넌트 이름과 동일하게 짓되, camelCase로 작성합니다.
- (예시) CSS 클래스 이름은 kebab-case 방식으로 작성한다. 이름은 자유롭게 짓되, 이름을 보고 그것이 어떤 태그를 가리키는 지 파악할 수 있는 이름으로 작성합니다.
- Daily Standup Meeting의 목적과 잘하는 법
- 기한 내에 제품을 개발할 수 있도록 생산성을 높이기 위해서 Daily Standup Meeting을 진행한다.
- 최대한 짧고 간결하게, 내가 한 것, 앞으로 해야할 것, 현재 막힌 부분 (문제 사항) 서로 공유하기
- 미리 준비해오기
잘했던 점
- 팀원 모두가 서로에 대한 배려와 존중을 기반으로 상대방의 의견을 잘 들어주고 소통한 점
- 상황과 상관없이 함께 목표를 달성하겠다는 의지를 보여준 점
- 프로젝트를 진행하며 Notion, Figma 등 필요한 도구들을 바로바로 적용한 점
- 백엔드와 프론트엔드끼리 서로의 지식을 공유하고 배우려 한 점
아쉬웠던 점
- 스프린트 때 자세하게 티켓을 나누지 못한 점 (티켓의 내용을 세분화를 하지못하고 스토어, 상세페이지 등 크게 파트만 나누었다)
- 각자 자신의 업무에 몰입하느라, 모르고 어려운 것들(blocker)에 대해 팀원들과 공유하고 질문하는 것이 잘 되지 않았던 점
- 팀원 중 코로나 환자가 발생하여 온라인으로 미팅을 진행해야 했던 경우 서로 소통의 어려움이 있었던 점 (예: 온라인 참여자의 존재를 잠시 잊어버리고 오프라인 참여자들끼리만 대화를 이어나갔던 점)
- BE와 FE가 너무 분리되어 진행되다 보니 파트별 진행 상황을 서로 잘 알지 못했던 점
- 스프린트를 정보 공유의 목적으로만 진행해서 시간관리가 잘 되지 않았던 점
- 시간 관리를 위해 공통된 목적에 대한 데드라인을 정하지 않았던 점
- Fontawesome 같은 외부 라이브러리의 사용을 팀원들에게 깜박하고 미리 말하지 않은 채 내 클라이언트에만 설치하여 사용했던 점 (나중에 서로의 작업물을 main 브랜치에 merge하여 테스트를 진행할 때 충돌이 발생했다)
- 브랜치명, 클래스명, 변수명 컨번션을 미리 정하지 못하고 프로젝트 도중 정해 팀원들과 잘 공유되지 못했던 점
다음 프로젝트 때 개선할 포인트
- 스프린트 회의 때 시간이 좀 걸리더라도 최대한 자세하게 티켓을 세분화하여 전체적인 큰 그림을 모든 팀원들이 잘 이해한 상태로 프로젝트를 진행할 수 있게끔 하자.
- blocker들을 기록하고, 그 blocker에 대한 데드라인을 정해 프로젝트가 일정에 맞게 진행될 수 있도록 하자.
- 정확한 시간대를 정하여 프로젝트 진행하자.
- 개개인의 체력 및 컨디션 관리. 영양제도 좋지만, 기초 체력은 운동에서 나온다!
- 첫 번째 미팅 때 기획을 최대한 세분화해서 명확하게 잡아야 한다! (BE & PE 파트너를 정해서 하나의 기능 구현을 같이 진행하는 것도 좋은 방법)
- PM은 역할을 제대로 파악하여 계획한 일정들이 미뤄지지 않도록 힘써야한다.
- 팀원 각자 책임감을 갖고 서로 도울 수 있는 부분을 생각해보자.
- 데이터 형태를 어떻게 보내고 받을건지 → ERD를 함께 만들어보면 좋을 것 같다.
- 구글 드라이브 또는 노션에 필요한 데이터, 이미지등을 정리하는 공간을 만들어두면 좋을 것 같다.
느꼈던 점
팀 프로젝트가 처음이다 보니, 시작부터 모든 것이 낯설고 어색하게 다가왔다. 나만 그런 것이 아니라 모든 팀원 분들이 다 그렇게 느꼈으리라 생각한다. 하지만 그렇기에 프로젝트를 통해 배운 것 또한 많지 않았나 싶다. 고맙게도 처음 프로젝트를 기획할 때부터 모든 팀원 분들이 적극적으로 의견을 내주셨고, 또 서로 배려하는 자세를 갖고 적극적으로 프로젝트에 임해주셔서 나 또한 프로젝트에 더더욱 열심히 참여하려 열정을 낼 수 있지 않았나 싶다.
프로젝트 중 잘했던 점을 생각해보면, 같은 프론트엔드 팀원들을 적극적으로 도와주었다는 점을 꼽을 수 있을 것 같다. 당시 팀원 중 개발 관련 경험이 내가 제일 많아서 그랬는지, 팀원들이 개발 도중 어려운 일이 생기면 나에게 먼저 질문을 많이 해주셨다. 그래서 그럴 때마다 적극적으로 팀원을 도와가며 프로젝트가 중간에 막히지 않도록 신경썼다.
또 하나 잘했다고 생각이 드는 점을 기록하면, 프로젝트 중 모르는 것이 생겨 멘토님께 질문을 하기 전, 혼자 우선 고민을 해본 후 질문할 내용을 미리 정리했다는 점과, 개발 도중 만났던 오류를 그냥 넘기지 않고 따로 정리했다는 점이다. (내 깃허브의 ERROR NOTE 저장소는 이렇게 탄생하게 되었다.)
반면 아쉬웠던 점 역시 있었다. 특히 프로젝트 중 내 실수로 팀원들에게 미안해할만한 일이 생겼던 적이 있었다. 위에서도 언급을 했지만, FontAwesome 패키지를 내 로컬에만 설치하여 프로젝트에 활용했고 이를 팀원들에게 깜박하고 말하지 않았던 것이다. 결국 이로 인해 최종 테스트 때 패키지 충돌이 발생하였고, 팀원들에게 당혹감을 주고 나서야 비로소 '아차' 싶었다. 이번 일을 통해 팀원들과 꼭 공유해야 할 내용은 그때그때 바로 말해서 앞으로는 이런 일이 생기지 않도록 조심해야겠다는 생각이 들었다. 좋은 경험을 했다.
사실 프로젝트 시작 전, 2주도 채 되지 않는 짧은 기간 안에 프로젝트를 완성하는 것이 가능할까 싶은 생각이 강했다. 심지어 팀원 분들이 돌아가며 코로나에 걸리는 바람에 더욱 프로젝트 완성에 대한 확신이 부족했다. 그렇지만 이런 어려운 상황 속에서도 다들 맡은 바 최선을 다해준 덕분에 큰 문제없이 기간 내에 무사히 프로젝트를 마무리할 수 있었다.
비록 배포까지 하지 못한 점과 최종 결과물에 대한 아쉬움이 남는 것은 사실이지만, 기능 구현보다는 팀 프로젝트를 통해 최대한 다양한 경험을 해보자는 마인드여서 그랬는지, 충분히 만족스러운 프로젝트였다고 말할 수 있을 것 같다.
마지막으로 이 자릴 빌어 함께 고생해준 팀원분들 모두에게 고맙고 고생했다는 말 남기고 싶다.
'✪ 취미, 경험 회고 및 일상 > [회고] IT 관련 경험 회고' 카테고리의 다른 글
🦁 멋쟁이 사자처럼 11기 합격 후기 (서류, 면접) (0) | 2023.03.22 |
---|---|
위코드 2차 팀 프로젝트 'WeMong' 회고 (0) | 2022.12.11 |
코드숨(CodeSoom) 리엑트 11기 마지막 회고 및 후기.. (부제 : 나는 실패했다) (0) | 2022.10.09 |
코드숨(CodeSoom) 리엑트 11기 5주차 회고 (0) | 2022.09.05 |
코드숨(CodeSoom) 리엑트 11기 4주차 회고 (0) | 2022.08.29 |