• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

시퀄라이즈 여러 쿼리 보내기, Promise async await 개념

21.12.22 15:14 작성 조회수 141

0

안녕하세요. 항상 강의 보면서 도움 많이 받고 있습니다.

 

프론트에서 체크박스로 제품을 열람가능한 유저를 선택해서

DB에서 기존 데이터를 삭제하고 체크한 유저를 다시 추가해주는 식으로

제품열람가능 유저를 업데이트 하려고 하는데요

 

제품모델과 유저 모델은

  Item.associate = (db) => { // 릴레이션(관계) 정의
    db.Item.belongsTo(db.User); // 제품 등록한 유저
    db.Item.belongsToMany(db.User, { through: 'ItemUsers', as: 'ItemViewUsers'}); // 제품 노출할 유저
};
..................
  User.associate = (db) => { // 릴레이션(관계) 정의
    db.User.belongsToMany(db.User, { through: 'UsersRelation', as: 'Providers', foreignKey: 'customerId' }); // 판매자-구매자 관계
    db.User.belongsToMany(db.User, { through: 'UsersRelation', as: 'Customers', foreignKey: 'providerId' }); // 판매자-구매자 관계
    db.User.belongsToMany(db.Item, { through: 'ItemUsers', as: 'UserViewItems' }); // 열람가능한 제품
};

이런식입니다.

 

프론트는 Form과 Checkbox로 단일 ItemId와 UserId 배열을 보내주고 

백에서 req.body로 받는 데이터는 

{ id: '1', values: { customerIds: [ 'tttt', 'ttt3' ] } } 

이런식으로 받습니다.

 

// 제품에 열람가능한 고객 등록
router.post('/add-customer', isLoggedIn, async (req, res, next) => {
  try {
    console.log('고객등록 req.body',req.body);
    console.log(req.user.id)
    const item = await Item.findOne({
      where: { id: req.body.id}
    });
    if (!item) {
      return res.status(404).send('해당 제품이 존재하지 않습니다.');
    }
    if (item.UserId !== req.user.id) {
      return res.status(404).send('권한이 없습니다.');
    }

    const itemViewUsers = await item.getItemViewUsers();
    //제품 열람가능한 유저 전부 제거
    if (itemViewUsers) {
      await Promise.all(
        itemViewUsers.map( customer => {
          console.log('\x1b[36m 유저 제거 시도', customer.id);
          item.removeItemViewUsers(customer.id);
        })
      );
    }

    // 제품 열람가능한 유저 추가 (체크박스로 체크된)
    if(req.body.values.customerIds){
      await Promise.all(
        req.body.values.customerIds.map( customerId => {
          console.log('\x1B[31m 유저 추가 시도', customerId);
          const user = User.findOne({ where: { id: customerId }})
          if (!user){
            return res.status(404).send('해당 유저가 존재하지 않습니다.');
          }
          item.addItemViewUsers(customerId);
        })
      );
    }

    res.status(200).json(item);
  } catch (error) {
    console.error(error);
    next(error); // status 500
  }
});

 

유저 tttt가 이미 등록된 상태에서

유저 tttt, ttt3 를 선택해서 등록하면

DB에는 ttt3만 등록된 결과가 나옵니다...

실제 INSERT 문도 하나만 실행되네요.

항상 이런건 아니고 될때도 있습니다.

 

비동기 부분이 꼬여서 그런것같은데 제가 promise 개념이 약해서 

어떤식으로 해결할 수 있을지 궁금합니다.

Promise, async, await 부분은 제로초님 강의 꼭 다시 보겠습니다.

 

백엔드 로그는 이러합니다.

 

고객등록 req.body { id: '1', values: { customerIds: [ 'tttt', 'ttt3' ] } }

tester1

Executing (default): SELECT `id`, `codeName`, `name`, `packageName`, `unit`, `msrp`, `supplyPrice`, `imgSrc`, `createdAt`, `updatedAt`, `UserId` FROM `Items` AS `Item` WHERE `Item`.`id` = '1';

Executing (default): SELECT `User`.`id`, `User`.`password`, `User`.`company`, `User`.`name`, `User`.`phone`, `User`.`email`, `User`.`role`, `User`.`createdAt`, `User`.`updatedAt`, `ItemUsers`.`createdAt` AS `ItemUsers.createdAt`, `ItemUsers`.`updatedAt` AS `ItemUsers.updatedAt`, `ItemUsers`.`ItemId` AS `ItemUsers.ItemId`, `ItemUsers`.`UserId` AS `ItemUsers.UserId` FROM `Users` AS `User` INNER JOIN `ItemUsers` AS `ItemUsers` ON `User`.`id` = `ItemUsers`.`UserId` AND `ItemUsers`.`ItemId` = 1;

 유저 제거 시도 tttt

 유저 추가 시도 tttt

 유저 추가 시도 ttt3

Executing (default): DELETE FROM `ItemUsers` WHERE `ItemId` = 1 AND `UserId` IN ('tttt')

Executing (default): SELECT `id`, `password`, `company`, `name`, `phone`, `email`, `role`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 'tttt';

Executing (default): SELECT `createdAt`, `updatedAt`, `ItemId`, `UserId` FROM `ItemUsers` AS `ItemUsers` WHERE `ItemUsers`.`ItemId` = 1 AND `ItemUsers`.`UserId` IN ('tttt');

POST /item/add-customer/ 200 9.485 ms - 236

Executing (default): SELECT `id`, `password`, `company`, `name`, `phone`, `email`, `role`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 'ttt3';

Executing (default): SELECT `createdAt`, `updatedAt`, `ItemId`, `UserId` FROM `ItemUsers` AS `ItemUsers` WHERE `ItemUsers`.`ItemId` = 1 AND `ItemUsers`.`UserId` IN ('ttt3');

Executing (default): INSERT INTO `ItemUsers` (`createdAt`,`updatedAt`,`ItemId`,`UserId`) VALUES ('2021-12-22 05:56:16','2021-12-22 

05:56:16',1,'ttt3');

Executing (default): SELECT `id`, `password`, `company`, `name`, `phone`, `email`, `role`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 'tester1';

Executing (default): SELECT `User`.`id`, `User`.`company`, `User`.`name`, `User`.`phone`, `User`.`email`, `User`.`role`, `User`.`createdAt`, `User`.`updatedAt`, `Customers`.`id` AS `Customers.id`, `Customers`.`company` AS `Customers.company`, `Customers`.`name` 

AS `Customers.name`, `Customers->UsersRelation`.`createdAt` AS `Customers.UsersRelation.createdAt`, `Customers->UsersRelation`.`updatedAt` AS `Customers.UsersRelation.updatedAt`, `Customers->UsersRelation`.`customerId` AS `Customers.UsersRelation.customerId`, `Customers->UsersRelation`.`providerId` AS `Customers.UsersRelation.providerId` FROM `Users` AS `User` LEFT OUTER JOIN ( `UsersRelation` AS `Customers->UsersRelation` INNER JOIN `Users` AS `Customers` ON `Customers`.`id` = `Customers->UsersRelation`.`customerId`) 

ON `User`.`id` = `Customers->UsersRelation`.`providerId` WHERE `User`.`id` = 'tester1';

Executing (default): SELECT `id`, `password`, `company`, `name`, `phone`, `email`, `role`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 'tester1'

 

 

답변 1

답변을 작성해보세요.

1

왜 반복문으로 넣으시나요? item.addItemViewUsers(req.body.values.customerId) 이렇게 한 방에 등록 가능합니다.

Moa Kim님의 프로필

Moa Kim

질문자

2021.12.22

해당 id를 가진 유저가 있는지 확인하기 위해서 반복문으로 한거긴 한데

배열로 입력이 가능한지는 몰랐습니다. ㅠ 감사합니다.

 

addItemViewUsers부분만 반복문에서 빼고 배열넣어주는식으로 바꿨는데 동일하게 작동이 잘 안되서

삭제부분까지

    const itemViewUsers = await item.getItemViewUsers();
    if (itemViewUsers) {
      const deletedUsers = await item.removeItemViewUsers(itemViewUsers);
    }
    const itemUsers = await item.addItemViewUsers(req.body.values.customerIds);

이렇게 바꾸니 작동이 잘 되네요. 정말 감사드립니다! 😍😍

 

유저 있는지 검사는

    // 유저 아이디 있는지 검사
    if(req.body.values.customerIds){
      await Promise.all(
        req.body.values.customerIds.map( customerId => {
          const user = User.findOne({
            where: { id: customerId },
            attributes: ["id"],
          })
          if (!user){
            return res.status(404).send('해당 유저가 존재하지 않습니다.');
          }
        })
      );
  }

이런식으로 반복문으로 남겨도 될까요?

반복문인 경우 왜 작동이 안되는지도 궁금합니다. (비동기때문인지 시퀄라이즈 특성인지 제가 그냥 잘 못짠건지 ㅠ)

일단 반복문으로 쿼리를 날리는 건 db에 최악입니다. db에 엄청난 부담이 갑니다. N+1 문제 검색해보시고요. 저라면 그 사람들 전부를 한 쿼리에 SQL IN문으로 검사하고 자바스크립트단에서 없는 사람을 찾아낼 겁니다.

그리고 저게 작동 안 되는 이유는 저기에서 사용하신  반복문 안에서 res.send를 보내면 여러 번 보내질 가능성이 있습니다. res.send 여러 번 보내면 에러가 발생합니다.

Moa Kim님의 프로필

Moa Kim

질문자

2021.12.23

아~ 명쾌한 답변 감사드립니다. !! 😍😍