온라인 의류 쇼핑몰 웹사이트입니다. 평소 이커머스와 프론트엔드에 관심이 많았고, 저와 같은 관심사를 가진 사람들에게 도움이 되었으면 하는 마음으로 개발하였습니다.
혼자서 구현했기에, 부족한 Back-End를 Firebase 서비스를 통해서 프로토타입으로서 보완하였습니다. Firebase를 처음 사용하면서, Storeage, Firebase Auth 등의 개념을 깊게 다질수 있었고 NoSQL를 실제로 사용하면서 개발 초기에 빠르게 FE
만으로 프로토타입을 만드는게 더욱 의미가 있었습니다.
최근에 RestApi에서 GraphQL을 사용이 점점 늘어나면서, 처음으로 사용해보며 REST API를 하는것과는 다르게, URL을 통해 요청하는 방식이 아닌, 하나의 엔드포인트에 요청하는 쿼리에 따라 다른 응답을 받으면서 원하는 응답값만 받을 수 있다는 장점을 알게 되었습니다. HTTP 요청 횟수를 줄이고, 응답사이즈를 줄이면서 보다 유용성을 느낄 수 있었습니다.
원래 사이드 프로젝트로 BE 2명 / FE 1명으로 구성한 쇼핑몰 프로젝트를 시행했습니다. 협업 과정에서 BE의 작업 속도에 따라서 데이터를 받아야하는 FE입장에서는 진행함에 있어서 수동적으로 request에만 의존해야 했던 문제를 발견하였습니다. 이를 통해, 단순 FE단에서 먼저 구현해보고 BE와 나중에 추가적으로 병합할 수 있으면 작업 속도와 효율성이 좋을 것이라 판단하였습니다. 그렇게 FE단에서도 BE의 흉내를 낼 수 있는 Firebase
를 사용해보고 프로젝트를 만들어낼 때, 프로토타입으로 적용하기에 큰 이점이 될 수 있다는 것을 알게 되었습니다.
쇼핑몰의 컨셉으로는 평소 www.29cm.com
라는 의류쇼핑몰을 즐겨 찾아봅니다. 심플하면서도 감각적이고 모던한 분위기를 느끼게 하여 매력적이라 생각하였습니다. 큰 기능이라 할 수 없지만, 유저마다 상품 추가, 장바구니 등을 구현할 수 있다면 기본적인 BE에 대한 이해도 될 수 있다고 생각하여 이 프로젝트를 구상하였습니다. 이것이 바로, 이 프로젝트를 개발하게 된 배경입니다.
프론트엔드만 프로젝트를 수행하며 서비스를 구현해보기에는 백엔드에 대한 공부가 부담이었고, 이번에 Firebase를 공부하면서, 프론트엔드 개발자에게는 정말 필요한 서비스겠다는 생각을 하였습니다.
제목 | ||
---|---|---|
1. | React의 SSR 프레임워크인 Next.js 를 선택하게 된 이유 | 1) 블로그 바로가기 |
2. | Next.js의 SSR | 1) 블로그 바로가기 2) 이슈 바로가기 |
3. | graphql & firebase 연동 | 1) 블로그 바로가기 |
4 | Firebase 색인 | 1) 블로그 바로가기 |
5 | Firebase Auth | 1) 블로그 바로가기 |
개요 : 개인 프로젝트
주제 : 온라인 의류 쇼핑몰 프로젝트
feat: 파일, 폴더, 새로운 기능 추가
fix: 버그 수정
style: 코드 스타일 변경
docs: 문서 생성, 추가, 수정(README.md)
refactor: 코드 리팩토링
chore: 코드 수정 (JSON 데이터 포맷 변경 / scss 변경 등)
신규: new-something-you-made
수정: fix-something-you-fixed
개선: improve-something-you-made-better
base : 프로젝트에서 제공하는 기본이 되는 UI를 담당.
common : 프로젝트의 바탕이 되는 공통으로 사용하는 컴포넌트를 담당.
layouts : 프로젝트에서 공통으로 사용하는 레이아웃을 담당.
sections : 페이지를 구성하는 컨텐츠단위 컴포넌트를 담당.(페이지에서는 단순 라우팅만 역할하기 위함)
hooks : 공통으로 사용하는 hook을 담당.
pages : 사이트의 페이지 라우팅을 담당.
graphql : graphql 스키마 담당
service : query관련 라이브러리 (react-query/graphql-request)
recoil : 전역상태 관리를 위한 폴더 (recoil-atom/selector)
style : 전역으로 사용하는 GlobalStyles와 theme를 담당
types : 프로젝트의 전반적인 타입들을 담당.
utils : 프로젝트에서 사용하는 상수(constants)나 필요한 함수(helpers)를 담당.
schema : graphql typeDefs를 위한 스키마
resolvers : graphql resolver 담당
홈 : '/'
마이페이지 : '/mypage',
로그인페이지 : '/signin',
회원가입페이지 : '/signup',
남자 상품 전체 페이지 : 'shop/category?list=men&category_code=m_all'
여자 상품 전체 페이지 : 'shop/category?list=women&category_code=w_all'
장바구니 페이지 : '/cart',
주문조회 페이지 : '/mypage/orders',
좋아요 페이지 :'/mypage/likes',
이벤트 페이지 : '/event'
상세 페이지 : '/detail/:id'
검색 페이지 : '/search'
검색 결과 페이지 : '/search/keyword'
1:1 문의 페이지 : '/qna/
type Category {
category_lg: String!
category_md: String!
category_sm: String!
}
type Product {
id: ID!
brand: String!
name: String!
image_url: String!
origin_price: Int!
discount: Int!
category: Category!
createdAt: Float!
}
카테고리
category_lg
: men / women 대분류
category_md
: top / bottom / outer 등 중분류
category_sm
: long / short / hoody 등 소분류
type CartItem {
id: ID!
amount: Int!
uid: String!
createdAt: Float!
product: Product!
}
uid
: firebase Authentication에서 제공하는 고유의 uid
product
: 상품 스키마의 타입 type Search {
id: ID!
product: Product!
}
extend type Query {
searchItems(keyword: String!, cursor: ID): [Product!]
searchBrand(keyword: String!): [String]
}
searchItems
: 검색 keyword를 기준으로 product를 내보내주도록 구현searchBrand
: 검색 키워드 변경시에, 추천 브랜드를 나타내주기 위한, 추천 Brand가 보여지도록 구현 type Event {
id: ID!
image_main: String
image_lg: String
image_md: String
image_thumb: String
createdAt: Float!
}
extend type Query {
events: [Event!]
event(id: ID!): Event!
}
image_main
: /
경로의 슬라이드에 들어갈 이미지 URLimage_lg
: /
경로에 들어갈 Event 데이터image_thumb
: /event
페이지에 들어갈 썸네일 이미지 URLimage_lg
: /evnet:id
페이지에 들어갈 이미지 URLquery GET_PRODUCTS {
products {
id
brand
name
image_url
origin_price
discount
createdAt
category {
category_lg
category_md
category_sm
}
}
}
query GET_PRODUCT($id: ID!) {
product(id: $id) {
id
brand
name
image_url
origin_price
discount
createdAt
category {
category_lg
category_md
category_sm
}
}
}
id
값 arg에 따른 id값에 해당하는 데이터만 호출 query GET_SELECTED_PRODUCT(
$category_lg: String!
$category_md: String
$category_sm: String
$cursor: ID
) {
selectedProducts(
category_lg: $category_lg
category_md: $category_md
category_sm: $category_sm
cursor: $cursor
) {
id
brand
name
image_url
origin_price
discount
createdAt
category {
category_lg
category_md
category_sm
}
}
}
category_lg(대분류) / category_md(중분류) / category_sm(소분류)
arg에 따른, 특정 카테고리를 불러오도록 호출cursor
: 호출된 마지막 상품의 id
값을 기준으로 다음 데이터를 호출하기 위한, 페이지네이션 mutation ADD_PRODUCT(
$brand: String!
$name: String!
$image_url: String!
$origin_price: Int!
$discount: Int!
$category_lg: String!
$category_md: String!
$category_sm: String!
) {
addProduct(
brand: $brand
name: $name
image_url: $image_url
origin_price: $origin_price
discount: $discount
category_lg: $category_lg
category_md: $category_md
category_sm: $category_sm
) {
brand
name
image_url
origin_price
discount
category_lg
category_md
category_sm
}
}
Product
스키마에 들어갈 데이터를 전해준다. mutation ADD_CART($id: ID!, $count: Int, $uid: String!) {
addCart(productId: $id, count: $count, uid: $uid) {
id
amount
uid
createdAt
product {
id
brand
name
image_url
origin_price
discount
createdAt
category {
category_lg
category_md
category_sm
}
}
}
}
uid
값을 전달하여, 장바구니 페이지에서 cart 데이터를 호출할 시, uid
에 맞는 데이터를 불러오기 위함이다 query GET_SEARCH_ITEMS($keyword: String!, $cursor: ID) {
searchItems(keyword: $keyword, cursor: $cursor) {
id
brand
name
image_url
origin_price
discount
createdAt
category {
category_lg
category_md
category_sm
}
}
}
keyword
를 받게 되면, Product
데이터를 호출한다.cursor
: 검색 결과 페이지 /search/:id
에 무한 스크롤을 적용하도록 하기 위한 아규먼트이다.npm i 또는 npm install
yarn
# 1. client
cd client
yarn
yarn run dev
# 2. server
cd server
yarn
yarn run dev