Collector’s 프로젝트 회고
엘리스 SW 엔지니어 트랙 2기 첫 번째 프로젝트 Collector’s 회고
시작
엘리스에서 사실상 첫 번째 팀 프로젝트를 진행하게 되었다. 개인 프로젝트는 자신의 일정에 맞게 유동적으로 진행할 수 있었는데, 팀 프로젝트는 어떤 방식으로 진행되고, 협업하고, 피드백하는 지가 제일 궁금했다.
가장 걱정스러웠던 점은 Git을 사용하다가 문제가 되어 팀에 민폐를 끼치진 않을지였다. 팀으로 작업하는 것이 처음이라 add, commit, push만 활용했던 이전과 달리 문제가 되면 안될 거라고 생각해 미리 유튜브로 Git 관련 영상을 찾아보기도 했다. 이번 프로젝트를 진행하며 Git을 문제가 되지 않을 정도로는 활용할 수 있게 하는 것이 목표 중에 하나이다.
추가적으로 이번 프로젝트는 Vanilla JavaScript로 진행되기 때문에 JavaScript에 한결 익숙해지는 시간이 되었으면 한다. 그리고 다른 CSS 라이브러리를 활용하지 않고 Pure CSS로 반응형 디자인을 한 번 구현해보면서 CSS를 조금 더 다룰 수 있게 되었으면 좋겠다.
프로젝트
프론트엔드 2명, 백엔드 2명으로 구성된 우리 팀은 첫 미팅때부터 약간 컨셉을 좋아했다. (무난한 주제가 우리 팀원들을 컨셉괴물로 만들었다..)
첫 번째 프로젝트의 주제는 쇼핑몰 웹 사이트를 구현하는 것이었다. 물론 주제가 정해진 것이 어떤 측면에서는 기획할 시간을 줄여주기 때문에 편할 수도 있겠지만, 팀원들과 직접 어떤 웹 사이트를 구현할지 기획하는 시간이 없어 매우 아쉬웠다. (괜히 부트캠프 이력서가 동일한게 아닌듯;)
아무튼 쇼핑몰 웹 사이트를 구현해야 하기 때문에 어떤 상품을 판매하는지에 대해 논의하며 중요 컨셉과 디자인 시안을 Notion과 Figma를 활용해 정리하고, 결국 넷 모두 컨셉을 버리지 못하고 초능력, 마법같은 능력을 판매하는 Collector’s 쇼핑몰을 구현하기로 했다.
온전히 JavaScript로만 구현하여 MPA 형태로 구현되었고, Pure CSS를 활용했다. 추가적인 기술 스택은 GitHub의 README.md에서 확인할 수 있다.
프로젝트 주요 기능
프로젝트 주요 기능은 계속적으로 추가되어 다음과 같다.
전체 기능
- GNB(Global Navigation Bar)
- 사이드 퀵 메뉴와 TopScroll 버튼
유저 기능
- 회원 가입 및 로그인
- 소셜 로그인 - 카카오, 네이버, 구글
- 로그아웃 및 회원 탈퇴
- 홈페이지 메인
- 매진 임박 상품 추천 섹션
- 신상품 섹션
- 10,000원 이하 상품 섹션
- 최근 본 상품 기능
- 상품 목록 페이지
- 카테고리별 상품 조회 기능
- 상품명, 상품 태그 검색 기능
- 매진 임박 스티커 기능
계정 관리 기능
- 회원 정보 확인 기능
- 구매 누적 금액에 따른 회원 등급을 통해 구매 유도
- 구매 상품 내역에 따른 Stat 부여를 통해 구매 유도
- 회원 정보 수정 기능
- 비밀번호 변경 기능
- 회원 탈퇴 기능
주문 기능
- 장바구니 기능
- 상품 추가
- 수량 조절 가능
- 전체 삭제, 선택 삭제 기능
- 품절 상품 자동 제거 기능
- 주문하기 기능
- 배송지 정보 입력 - Daum api
- 요청 사항 선택 기능 - 직접 추가 가능
- 회원 등급에 따른 할인 혜택 부여
- 주문 내역 조회
- 주문 취소 기능
관리자 기능
- 상품 등록 기능
- 상품 상세 정보 입력 기능
- 상품 이미지 삽입
- 상품 검색 키워드 설정 기능
- 상품 관리 기능
- 상품 삭제 기능
- 상품 정보 수정 기능
- 상품 상세 정보 수정 기능
- 상품 검색 키워드 수정 기능
- 주문 내역 조회 기능
- 전 회원 주문 내역 조회
- 배송 상태 변경
- 주문 취소
내가 담당한 작업은 다음과 같다. 욕심은 많고 마감은 짧아서 MVP 위주로 개발을 진행한 후 시간이 되면 추가적인 기능을 계속 집어넣자고 했는데, 시간이 없는데도 추가적인 기능을 계속 추가했다..
-
UI 작업
- 홈페이지
- 상품 목록 페이지
- 계정 관리 페이지
- 회원 정보 페이지
- 회원 정보 수정 페이지
- 회원 탈퇴 페이지
- 주문 조회 페이지
-
기능 작업
- 유저 CRUD
- 최근 본 상품 기능
- Sticky 사이드 메뉴바
- 게이미피케이션을 위한 유저 Stat 기능
UI 작업은 그렇다 치고 최근 본 상품 기능과 유저 Stat 기능은 구현을 시작할 때 기획할 때부터 애를 먹었고, 시간도 부족해서 마감 마지막 3일동안은 거의 4시간씩만 자면서 작업을 한 것같다.
협업과 피드백
기본적으로 협업은 두 가지로 나누어 진행했다.
- 문서: Notion 페이지를 기반으로 진행.
- 소통: Discord, 게더타운을 기반으로 진행.
처음 협업을 진행하는 것이어서 최대한 동기적으로 소통하려고 노력했는데, 어떤 아티클에서 개발자는 비동기적으로 협업하는 것도 필요하다고 하는 것을 읽었다. 비동기적 커뮤니케이션이란 즉시 답장이 오지 않을 것이 전제된 상태에서 메시지를 주고받는 커뮤니케이션 방식으로, 실시간으로 답장을 하는 것이 아니라 자신의 업무를 마무리하고 답장하는 방식을 의미한다.
아무튼 두 방식을 적절히 혼합하여 사용하려고 노력했다. 주간에 모두가 작업을 진행하는 시간에는 최대한 비동기적으로 답장이 오지 않을 것임을 예측하고 질문이나 논의 거리를 자세히 작성하여 올려두고, 저녁 이후에는 직접 일대일 대화로 풀어나갔다.
가장 우려됐던 점은 잠을 남들보다 적게 자기 때문에 오랜 시간 작업을 하는데, 다른 팀원들이 같이 무리할까봐 걱정됐는데, 서로 일정과 시간을 공유하여 모두가 적절히 작업할 수 있었다.
오!! 이건 좀 잘한 것 같아!
비동기적 커뮤니케이션과 동기적 커뮤니케이션의 조화
물론 주간에도 동기적으로 커뮤니케이션할 수 있겠지만, 모두가 각자의 작업을 하고 있는데 모여서 이야기를 나누기란 귀찮기도 하고 작업 흐름이 끊기므로 문제가 될 수 있어서 제안한 방식이다.
주간에는 비동기적으로 커뮤니케이션을 진행하여 논의 사항이나 질문 사항을 자세히 남겨 두면, 시간이 되는 사람이 확인하는 대로 답을 하기로 약속하고, 비동기적인 협업 방식을 선택했다.
이러한 방식으로 인해 작업 흐름이 끊기지 않고 이어져 조금 더 효율적으로 작업할 수 있었다!
최근 본 상품 기능 개발
최근 본 상품 기능을 개발하기 위해 우선 사용자가 특정 상품의 상세 페이지에 진입하면, URL의 parameter를 통해 상품의 id 값을 가져오고, 이를 바탕으로 로컬 스토리지에 저장하는 방식으로 구현했다. 로컬 스토리지에는 만료 메서드가 없기 때문에 따로 만료 기한을 설정해 만료되는 경우 제거해주었다.
// 최근 본 상품 추가 로직
function addRecentItem(itemId, itemName, imgUrl) {
// 만료 기한 설정
const expireDate = 1000 * 10;
//1000 * 60 * 60 * 24; // 24시간
// 최근 본 상품 로컬 스토리지에 저장할 데이터 구조
const recentData = {
itemId,
itemName,
imgUrl,
expire: new Date().getTime() + expireDate,
};
// 최근 본 상품이 있는지 확인
let recentItemList = JSON.parse(localStorage.getItem("recentItem"));
// 없으면 새로 생성
if (!recentItemList) recentItemList = [];
// 동일한 상품이 있으면 추가하지 않음
let isItemExist = false;
isItemExist = recentItemList.some((item) => {
return item.itemId == itemId;
});
if (!isItemExist) {
// 3개 이상이면 첫 번째 아이템 삭제
if (recentItemList.length === 3) recentItemList.shift();
// 최근 본 상품 추가
recentItemList.push(recentData);
// 로컬 스토리지에 추가
localStorage.setItem("recentItem", JSON.stringify(recentItemList));
isItemExist = false;
}
}// UI 렌더링
const addQuickMenuElement = (_quickMenu) => {
_quickMenu.innerHTML = quickMenu;
addRecentItem();
// 최근 본 상품 추가하기
function addRecentItem() {
const quickItems = selectElement(".quick-items");
let recentItems = JSON.parse(localStorage.getItem("recentItem")) || [];
if (!recentItems) return;
// 추가하기
recentItems.forEach(({ itemId, itemName, imgUrl }) => {
const recentItemList = `
<li class="recent-item">
<a href="/item/?id=${itemId}">
<img src="${imgUrl}" alt="${itemName}">
<p>${itemName}</p>
</a>
</li>
`;
quickItems.insertAdjacentHTML("beforeend", recentItemList);
});
}
};아.. 이건 좀 아쉬운 것 같아..
다만, JavaScript로 프로젝트를 진행하면서 모듈화나 컴포넌트로 처리하는 부분이 부족했던 것 같다. 단순히 MPA로 구현하면서 전체 페이지가 새로고침되는 부분이나 각각 로딩 시간이 달라 UI가 각각 렌더링 되는 부분도 아쉬웠다. 특히 상세 페이지에 진입할 때 이미지를 제외한 UI가 먼저 렌더링되는 부분은 개선이 필요할 것 같다.
이러한 문제점을 해결하기 위해 React를 활용하므로, React를 배운 뒤에 React로 쇼핑몰을 새롭게 구현해보아도 좋을 것 같다!
마무리
부족한 점이 많았던 첫 번째 프로젝트였고, 아쉬운 점이 없다고 말할 수는 없겠지만 최선을 다했기에 좋은 경험이라고 생각한다. 2주간 744개의 Commit을 함께하면서 재밌고 즐겁게 프로젝트를 진행한 것 같아서 정말 좋았고, 팀원들과도 친해져서 계속 함께 성장하는 개발자가 됐으면 좋겠다..!
프로젝트 회고 끝!👏