장바구니 api 구현하기

·2022년 4월 17일
0

API 장바구니

목록 보기
3/5

원래 써야하는게 있었는데 하루좽일 가지고 놀고 있어서 리프뤠시 하는 겸 올리는
장바구니 api 구현 진행상황

검색? 모조리 다 직접 짜보고 있다. 왜냐하면 api를 이렇게까지 혼자서 짜본 적이 없어서
뭔가 고민을 해보고 찾아보면서 구현을 하는 것이 좋다고 생각했기 때문에
삽질이란 삽질을 무한적으로 때려박는중이다.


왜 장바구니?

사실 내가 만드는 홈페이지에는 장바구니가 필요가 없다.
한번에 가능한 결제가 한가지 뿐이라서, 애초에 포인트로 단발성으로 구매를 하는 것이라 필요는 없다.

근데 일반적으로 쿠팡,네이버쇼핑에서 장바구니가 없다는 모습은 상상을 할 수 없었기 때문에
결제 검증,환불 과제는 진작 끝낸김에 고민을 계속 하면서 진행을 해보고 있었다.

장바구니는 휘발성이다?

내가 생각한 맨 처음의 구조는 이렇게 됐다.

  1. 유저가 계정을 생성한다.
  2. 유저가 계정을 생성함과 동시에 본인과 1:1로 묶여있는 장바구니 테이블이 생성된다.

라는 기조로 짜기 시작했는데........ 찾아보니 장바구니는 휘발성이란다.
이유는 결국은 데이터를 관리하는 모든 것은 과 밀접한 관계가 있기 때문에
불필요한 데이터는 저장하지 않는 편이 좋다는 것이였다.

생각을 해보면 우리도 쿠팡에서 물건을 아무것도 담아두지 않으면 그냥 문자열 하나만 보여준다.

이렇다면 장바구니는 유저가 물건을 담는 순간 테이블이 생성되며
주문을 한다거나, 삭제를 해서 테이블에 존재하는 값이 0이 될 경우에는
해당하는 테이블을 삭제하는 것이 데이터를 다룸에 있어서 유용하다.

더불어서 장바구니는 그렇게 귀중한 시스템도 아니라는 것도 있었는데
유저가 장바구니에 있는 시간은 쇼핑몰이라 하더라도 큰 비중을 차지하지 않기 때문에
장바구니에 담아놓은 물건을 다시 보여주는 그런 복잡한 것을 할 필요는 없다. 라는 내용도 보았다.

그럼 장바구니의 데이터베이스 구조는?

일단 기초 동작 원리는 이렇게 돌아간다.

  1. 유저가 상품을 장바구니에 담기 버튼을 클릭한다.
  2. 해당 유저의 id가 담겨져있는 새로운 장바구니 테이블이 생성된다.
  3. 유저가 클릭한 상품의 데이터가 들어있는 정보를 2번의 테이블에 집어넣어준다.

즉 장바구니 테이블에는
유저를 참조할 수 있어야하고
상품을 참조할 수 있어야한다.

왜냐하면? 유저는 당연히 참조를 해줘야하고
상품을 참조하지 않을 경우에는 장바구니에 물건이 들어가있다고 한들 어떤 물건이 들어가있는지
화면에 표현을 해주지 못할 것이라고 생각을 했다.
뭐 굳이 따로 불러오는 것도 방법이지만, 관계를 맺어주는 편이 좋다고 봤다.

아래와 같은 형식이다.

최상단부터
id는 카트의 id
createAt은 카드 생성 시간
items 내부는 해당하는 상품의 정보 (ManyToMany)
user 내부는 카트를 소유하고 있는 유저의 id (OneToOne FK)

의 구조로 되어있다.


뭐 찾아보면 유저는 다양한 카트를 가질 수 있다. 라고 해서
유저와 카트는 OneToMany의 구조를 가진다. 라며 이야기를 하는 글도 보았는데....

그냥 소프트 딜리트 말고 테이블 자체를 날려버린다면 OneToOne도 되지 않을까? 라는 생각을 했다.
(아닌거 같으면 바꾸면 된다. 얼마나 걸린다고)

현재까지 만들어놓은 CRU 구조

아직 삭제하기는 만들지 못했다.
왜냐면..... 업데이트에서 필요한 것들을 아직까지 구현을 못해서...ㅎ...ㅎ

Create

일단 나는 Create가 없다.
왜냐하면 나는 OneToOne이라 한개만 생성이 가능하기 때문에
그냥 업데이트에 다 때려박아놨다. 이쪽이 더 편할 것 같아서
문제가 있다면 그냥 분리해버리면 그만이다.

(어 이래서 ManyToOne인건가? 잠깐만)


Read

Read도 만들진 못했는데, 그냥 이렇게 하면 될 것 같다.

const Read_User_Data = await this.CartRepository.findOne({
        where: { id: user_cart.id },
        relations: ['items', 'user'],
      });

Cart의 레포지토리에서 해당하는 유저의 카트의 아이디를 받아와서
관계가 있는 items(난 이게 지금 상품 테이블이다) user 두개를 참조하여
리턴을 해주는 것으로 생각하고 있다.

실제로 작업을 진행할 때는 로그인이 필수적으로 있어야하기 때문에
@UseGuards 가 붙어있을텐데, 나는 토큰에 유저의 고유 id를 넣어놓은 상태이기 때문에

그 구조를 코드로 구현을 한다고 하면

async read({ currentUser }) {
    const user = this.CartRepository.createQueryBuilder()
      .where({ user: currentUser.user_id })
      .getOne();
      //
      const Read_User_Data = await this.CartRepository.findOne({
        where: { id: user.id },
        relations: ['items', 'user'],
      });
      //
     return Read_User_Date
  }

이런식이 되지 않을까? 생각을 하고 있다.

투스탭을 할 필요가 없을 것 같은데, 계속 고민중인 부분이
내가 받아오는 것은 프론트에서 받아오는 것은 토큰에 담겨져있는 유저의 id와 eamil 두개밖에 없다.

그러나 유저를 제외한 모든 테이블에서는 유저의 id를 관계로 묶어놔서 참조하고 있을 뿐인데

그 참조하고 있는 데이터를 기반으로 속에 들어있는 모든 값들을 어떻게 불러내야하는건지 모르겠다.

위의 코드는 받아온 커런트유저의 아이디가 조인이 되어있는 Cart의 데이터만 받아오는데
내가 필요한 것은 조인이 되어있는 Cart의 데이터를 가져오면서 조인이 되어있는 모든 테이블 인데
그게 마음처럼 되지 않아서 고민이다. 블로그가 유명해지면 남들도 많이 보고 적어줄텐데
정말 개인 만족용으로 쓰고있다보니 흑흑 이걸 누구한테 물어봐야할까

근데 위에서 사용한 > createQuerybuilder에 relations타입이 있긴 하던데 좀 더 찾아봐야할 것 같다.

그것만 할 수 있다면 전반적으로 내 코드들이 엄청 줄어들 것이라고 생각한다.
(줄여놔도 남이 보기 어렵진 않을 것 같은데...)

Update

업데이트는 지금 구현중이라 코드가 엄청 난잡한데... 천천히 주석달아서 올려본다.

  async update({ updateCartInput }) {
    const { items, ...date } = updateCartInput;
    //
    // 일단 받아오는 값이 items과 유저 아이디라서 구조분해할당으로 쪼개놓는다.
    //
    const PlusCart = await this.itemRepository.findOne({
      where: { id: items[0] },
    });
    //
    // item(상품은 원래 디비에 저장되어있음!)
    // 그것을 기반으로 가져온다. 아이템이 배열의 형식이라서 [0]을 하긴 했는데
    // 한개씩 받아서 추가한다면 굳이 배열의 형식을 써야할까? 그냥 String으로 해도 될 것 같다.
    // 아 동일한 상품이 여러개 들어올 수도 있으니 배열로 해야하나. . . .. ?
    //
    const user_cart = await this.CartRepository.createQueryBuilder()
      .where({ user: date.user_id })
      .getOne();
      // 최상단에서 받아온 유저의 아이디로 조인되어있는 Cart 테이블 검색
      //
    if (!user_cart) {
    //
    // 장바구니가 없을 경우 생성
    //
      const arr = [];
      arr.push(PlusCart);
      console.log('추가');
     //
     // items이 배열의 형식으로 구현이 되어있고 가져온 값들 또한 
     // 배열의 형식이라서 따로 뽑아서 담아주고 있는데 그냥 때려박아도 될 것 같다. 
     //이 부분도 고민을 좀 해봐야한다. 
     //
      const createCart = await this.CartRepository.save({
        user: { user_id: date.user_id },
        name: '10',
        items: arr,
      });
     // name은 참고좀 한다고 넣어놨다. 나중에는 장난감 외 5개 이런식으로 구현이 될 것이다.
     // 나중에는 가격 탭이 들어갈 예정이다, items에서 뽑아온 amount의 총액이 된다.
      return createCart;
     // 프론트로 리턴
    } else {
      // 장바구니가 있을 경우 수량 증가
      // 같은 물품이 들어온다면 금액을 원래 금액 * 2를 하고
      // 수량이라는 데이터를 상품쪽에 달아놓고, +1을 해준다
      const Read_User_Data = await this.CartRepository.findOne({
        where: { id: user_cart.id },
        relations: ['items', 'user'],
      });
      // 카트가 존재한다면 위에서 선언한 것으로 Cart 테이블을 불러온다
      //================================================================
      // @@@@@@구현 중인 내용 @@@@@@@@
      // 뭘 구현하고 있냐면 동일한 물건이 들어왔을 경우
      // 해당하는 물건의 수량을 +1을 해주고 가격 또한 *2를 해주는 작업을 하고 있다.
      // 가급적 병렬처리를 할 수 있도록 map이나 forEach로 작업을 해보려고 하고 있는데
      // 장바구니는 보통 한번에 한개의 물품 혹은 한개의 물품이지만 다량의 수량이 들어오기 때문에
      // 그냥 for문으로 해도 되지 않을까? 라는 생각에 잠겨있다.
      //
      //   Read_User_Data.items.map((ele, idx) =>
      //     ele.id.includes(PlusCart.id) ? console.log('aaaa') : console.log('bbb'),
      //   );
      //
      //   Read_User_Data.items.forEach((ele) => ele.id.includes(PlusCart.id) 
      // ? ele.amount += ele.amount);
      // console.log(Read_User_Data.items[0].id);
      //================================================================
      //
      Read_User_Data.items.push(PlusCart);
      // 위에 카트에 선언되어있는 테이블에 items에 새로 불러온 상품에 대한 정보를 밀어넣는다.
      console.log('업데이트');
      const Update_Cart = await this.CartRepository.save({
        ...Read_User_Data,
      });
      //
      // 상품이 추가적으로 밀려 들어간 것이 Read_User_Data에 담겨있기 때문에
      // 그것을 다시 저장해준 후, 리턴을 해준다.
      return Update_Cart;
      // 여기서도 고민이 조금 있는데, 이대로 보내버리면 유저의 정보 또한 보여진다.
      // 이래서 음....... 그냥 리턴을 안해도 되는 것 같은데 어떻게 해야할지 고민이다.
    }
  }
}

코드에 내용을 전부 다 달아버렸.....네?

Delete

제일 문제인 삭제다.

나는 아직 삭제에 대한 정확한 지식이 존재하지 않기 때문인데

Typeorm에서 지원하는 soft delete를 사용하더라도
테이블 자체의 값이 >사라지는 것<은 아니기 때문에
1:1 관계가 유지된다면서 테이블이 추가로 생성되지 않는다면 정말
갈아엎어야해서 내일 좀 질문을 해봐야 할 것 같다.

그래서 생각하는 장바구니 api의 전체 구조

  1. 유저가 물건을 추가하면 그 즉시 장바구니 테이블이 생성된다.
  2. 그 테이블에는 유저의 정보와 물건의 정보가 포함되어있다.
  3. 물건이 다량으로 추가 될 경우 물건의 정보가 배열의 형식으로 쌓여가는 구조로 되어있다.
  4. 물건을 지울 경우 장바구니의 물건들 배열에서 그 물건의 정보만 사라진다.
  5. 모든 물건이 사라질 경우 혹은 주문을 할 경우 장바구니 테이블은 삭제된다.

어?
삭제되면 주문현황은 또 어떻게 보여주지? 참조를 할게 없는데?
..........?
이래서 정말 ManyToOne인가? 진짜?

profile
물류 서비스 Backend Software Developer

0개의 댓글