시연 영상
- 메인페이지, 로그인, 제품상세, 장바구니
- 결제, 주문완료, 마이페이지, 주문상세
- 2주간 진행한 팀 프로젝트 과정 전체에 대한 소개
- 첫 팀프로젝트를 하며 배운 점
- 코드에서 발생한 에러와 해결과정을 통해 배운 것
- 첫 팀 프로젝트 (협업) 후기
- 처음으로 진행한 팀 프로젝트는 "쇼핑몰 웹 서비스 프로젝트"로 진행
- 애견 용품들을 타겟으로 한 쇼핑몰을 제작
- 애견용품들을 카테고리 별로 확인하고,
- 원하는 제품들은 장바구니에 담으며,
- 주문 및 배송을 할 수 있는 쇼핑몰 웹 서비스 제작 프로젝트
- 본인 담당 부분 (아래 자세한 내용 설명)
- 장바구니 페이지 (수량 증감, 제거, 주문 이동)
- 결제 페이지
- 주문상세 내역 페이지
- 주문완료 페이지
팀구성
- 프론트엔드 2명 ( 이준석_(팀장), 김차미 )
- 백엔드 2명 ( 김지우, 전수진 )
- 진행기간 : 2023년 04월 17일 ~ 2023년 04월 28일 (12일)
핵심기능
- 회원가입, 로그인, 로그아웃, 회원정보 수정 등 유저 정보 관련 CRUD
관리자 권한으로 로그인 시 아래와 같은 기능 추가- 제품 목록을 조회 및, 제품 상세 정보를 조회 가능함.
- 장바구니에 제품을 추가할 수 있으며, 장바구니에서 CRUD 작업이 가능함.
- 장바구니는 서버 DB가 아닌, 프론트 단에서 저장 및 관리됨 (localStorage)
- 장바구니에서 주문을 진행하며, 주문 완료 후 조회 및 삭제가 가능함
1) 프로젝트 기획 단계
- 팀 규칙 설정
- 컨벤션 정하기
- File Setting
.root
├── public
└── src
├── app.js
├── db
│ ├── models
│ └── schemas
├── routers
├── services
└── views (프론트엔드)
- 위 기능들에 대한 업무 분담
- UI 참고 페이지 설정
- 핵심 기능 정리 및 우선 순위 정하기
- 도메인 및 기능별 url 정의
2) 프로젝트 진행
- 매일 1시간 팀 스크럼 진행 (진행상황 및 이슈 공유)
- 추가 기능 or 수정 사항 조율
- HTML 레이아웃 설정 및 제작
- JavaScript 기능 구현
- CSS 수정
3) 구현 페이지 JavaScript 기능 설명
- 장바구니 페이지
- 장바구니에 속한 상품 관련 데이터가 저장되어서, 페이지를 새로고침해도 장바구니에 상품들이 그대로 보존
- 장바구니 추가 - 사용자는 상품을 장바구니에 추가할 수 있다.
- 장바구니 수정 - 사용자는 장바구니에 속한 상품의 수량을 수정할 수 있다.
- 장바구니 조회 - 사용자는 장바구니에 담긴 상품 목록을 확인할 수 있다.
- 장바구니 가격 조회 - 사용자는 장바구니에 담긴 상품들의 총 가격을 확인할 수 있다.
- 장바구니 부분 삭제 - 사용자는 장바구니에서, 일부 상품을 골라서 제거할 수 있다.
- 결제페이지 이동 전 로컬스토리지에서 토큰을 통해 로그인 확인
=> 로그인 확인 시 결제페이지 이동
=> 미로그인 시 로그인 페이지 이동
- 결제페이지
- 유저의 정보를 서버에 받아 기본 주소로 배송지 설정
- 배송지 및 수령인 정보 변경 기능
=>변경 선택 시 각 입력칸 유효성 평가 진행- 결제 방법 선택 : 카드, 무통장 입금
=> 카드 선택 시 각 입력칸 유효성 평가 진행- 주문성공 시 주문내역 서버로 전송
- 결제 성공 페이지
- 마이페이지 또는 메인페이지로 이동 가능
- 주문내역
- 결제 페이지에서 저장한 정보들을 불러와 조회
- 주문취소 ⇒ 서버에 주문상태 변경 요청
1. 장바구니 관련 데이터는 백엔드 데이터베이스가 아닌, Web Storage에서 관리
- 유저가 로그인하지 않아도 장바구니 사용을 할 수 있어야 하기 때문에
- 장바구니와 같은 임시 데이터를 클라이언트 측에서 관리하는 것이 효율적
- 상품 추가, 삭제, 수량 변경 등 잦은 서버요청을 할 수 있는 기능이지만,
빠른 응답과 부하 감소를 통한 원활한 사용자 경험을 제공할 수 있습니다.
2. sessionStorage가 아닌 localStorage를 사용한 이유
장바구니는 브라우저를 껐다가 다시 접속하더라도 상품이 남아있어야 한다고 생각했기 때문
- sessionStorage는 브라우저 종료 시 데이터 삭제됨
- localStorage는 탭이나 창을 닫아도 데이터는 브라우저에 그대로 남아있음
3. localStorage에 데이터를 삽입할 때 형태
장바구니에 상품을 담을 때, 두가지 방법을 고민했다.
- 상품의 Id를 담아 장바구니 페이지에 접속했을 때 다시 API요청을 하는게 좋을까
- 정보(사진, 이름, 가격 등)들을 모두 담아 바로 꺼내어 쓰는게 좋을까
localStorage의 표준 스펙을 찾아보니 가급적 1MB 이상의 큰 데이터를 쓰는 것을 피하는 것이 좋고, 5MB를 최대 용량으로 권장하고 있다.
제품을 담았을 때 데이터 용량이 크지 않고 API요청을 반복하는 게 더 비효율적이라 생각해 두번째 방법을 사용했다.
1. innerHTML, innerText, textContent 중에 뭐 쓰는 게 좋을까
- 바닐라js로 작업했기 때문에 DOM에 직접 접근해야할 코드가 많았다. 이때 상황에 따른 접근 방법이 필요
- 블로그에 따로 포스팅
2. 공통으로 사용되는 hrader, footer, nav 태그들을 파일 작성 후 복사하여 사용하는 파일마다 직접 파일에 붙혀넣음
(중복 코드 다수 발생)
- 해결방법 : JavaScript파일로 작성 모듈형식으로 HTML파일에 import하는 형식으로 변경
=> 반복되는 코드 제거, 관리 유용
3. Axios import 에러
- 바닐라 js로 npm으로 Axios 사용에서
Uncaught TypeError: Failed to resolve module specifier "axios". Relative references must start with either "/", "./", or "../".
에러 발생- 해당 에러 관련 블로그
해결 방법으로 parcel이라는 번들러를 사용이 있는 데 Axios 때문에 또 module을 적용하기엔 비효율적이라 생각해 CDN 방법으로 대체
4. 주문요청 페이지에서 회원정보 요청 api 필요
- 결제페이지 이동 시 마다 매번 배송정보를 입력함
=> 회원정보에 있는 주소정보를 기본배송지로 기능 추가- 새로운 기능추가로 api 생성필요
=> 촉박한 시간과 배포 때문에 백엔드에서 api를 새로 만들 시간 부족- 해결방법: 기존에 다른페이지에서 회원정보를 요청하는 api를 발견하고 해당 api 사용
5. 서버에 요청 데이터 요청 시 URL 앞에 api를 붙혀야 함
- 서버에 요청을 보낼 때, 일반적으로 앞에 "api/"를 붙이는 것이 일반적인 관례
- RESTful API에 대한 공부가 더 필요
추가
ex)api/orders
와/api/orders
앞에 / 유무 구분 필요
- "/api/orders"는 절대 경로(absolute path)로, URL의 루트(root)부터 시작하여 "/api/orders"로 정확히 지정된 경로를 가리킴
- "api/orders"는 상대 경로(relative path)로, 현재 위치를 기준으로 "api/orders"라는 경로를 나타냄
6. 장바구니 목록 중 아이템 삭제 기능 구현 중
- 배열의 형태로 for문으로 차례대로 순회하며 해당 id를 가진 아이템 제거
- 오름차순으로 순회하면 아이템을 삭제 시 실시간으로 반영되어 다음 아이템 제거 시 영향을 줌
- 해결방법: 내림차순으로 for 진행
//변경 전 for (let i = 0 ; i < orderTarget.length; i++) { localStorageEventHandle(orderTarget[i].id, 'order'); } //------------------------------------------------- //변경 후 for (let i = orderTarget.length - 1; i >= 0; i--) { localStorageEventHandle(orderTarget[i].id, 'order'); }
7. import 파일 경로 지정 시 상대경로가 아닌 절대경로로 지정
- 유지보수가 상대경로 지정보다 쉬움
- ex)
href="../../../../../../products/main/footer/footer.css"
8. input 작성 시 마다 유효성 평가
- 결제페이지에서 개인정보나 카드정보를 입력받을 때 형식에 맞는지 유효성평가 진행
- keypress이벤트 보다 input이벤트가 더 부드러움
9. 비동기 처리 시 async await 또는 promise 문법 하나만 사용
- 두가지 모두 사용 시 가독성 저하
=> async await으로 통일
10. 주문내역 생성 서버 요청 시 500에러 발생
- Branch merge 중 실수로 백엔드 코드가 수정됨
- merge conflict시 정확한 확인 필요
createOrder: async (orderInfo) => { const order = await Order.create(orderInfo); return updatedOrder; // 위 코드 처럼 반환되는 값이 변경됨 },
첫 프로젝트에서 팀장역할을 맡아 여러가지 부분에서 아쉬웠다.
- 주어진 짧은 시간과 적은 인원들로 많은 기능들을 욕심내어, 해당 기능들을 소화하고 싶어 스트레스를 많이 받았다.
=> 다 할 수 없다고 판단해 팀원들과 기능들의 우선순위를 정해 수행- 내가 생각한 것이 당연히 상대방과 같지 않다.
- 주문취소 기능에서 한 번에 여러개 상품을 주문할 때
- 본인은 주문 취소 단위가 상품마다 라고 생각함
- 백엔드 팀원은 취소 단위가 주문마다 라고 생각함
- 서로의 대화를 이해하지 못했던 경험이 있음
- 좋은 코드와 협업을 위한 RESTful API 공부하기