인프런 커뮤니티 질문&답변

cumulonimbus69님의 프로필 이미지

작성한 질문수

[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지

스케줄링 구현하기

트랜잭션 기능을 서비스 함수로 구현하는 방법

24.07.05 13:51 작성

·

135

·

수정됨

0

안녕하세요 스스로 해보기를 위해 강의 후반부에 있는 sequelize 에서 제공하는 transaction 기능을 추가하여 낙찰한 경우에 t수행하는 상품에 낙찰자 ID를 넣고 낙찰자의 금액을 까는 함수를 서비스 함수로 처리해서 하려고 하는데 상품등록을 하는 단계에서 다음과 같은 에러가 발생합니다 .

어떻게 하면 될까요?

image.png

관련된 코드 부분을 같이 올립니다

sequelize trasnation 을 적용해서 auctionedOff 함수를 다음과 같이 생성하였습니다

services/index.js

const sequelize = require('sequelize');
const { Auction , Good, User}= require('../models');

exports.auctionedOff = async ( good ) => {
  const t = await sequelize.transaction();
  try {
    const success = await Auction.findOne({
      where: { GoodId: good.id },
      order: [['bid', 'DESC']],
      transaction : t,
    });
    if (!success) { return; }
    await good.setSold(success.UserId); //낙찰
    await User.update({
      money: sequelize.literal(`money - ${success.bid}`),
    }, {
      where: { id: success.UserId },
      transaction: t,
    });
    await t.commit();
  } catch (error) {
    await t.rollback();
  }
};

그리고 이 함수를 다음과 같이 contollers/index.js 파일에서 아래 코드 처럼 불러서 실행 하고자 했습니다

const { Op } = require('sequelize');
const { Good, Auction, User, sequelize } = require('../models');
const auctionedOff = require('../services');
const schedule = require('node-schedule');


exports.createGood = async(req, res, next) => {
  try {
    const { name, price } = req.body;
    const good = await Good.create({
      OwnerId: req.user.id,
      name,
      img: req.file.filename,
      price,
    });
    const end = new Date();
    end.setDate(end.getDate() + 1); // 하루 뒤
    const job = schedule.scheduleJob(end, auctionedOff(good));
    job.on('error', (err) => {
      console.error('스케줄링 에러', err);
    });
    job.on('success', () => {
      console.log(`${good.id} 스케줄링 성공`);
    });
    res.redirect('/');
  } catch (error) {
    console.error(error);
    next(error);
  }
};

답변 5

0

cumulonimbus69님의 프로필 이미지

2024. 07. 08. 16:48

가르쳐 주신 구문으로 변경을 하니 동작이 잘 됩니다 감사합니다

연결해주신 유투브 내용도 잘 보았는데 그래도

targets.forEach((good) => auctionedOff(good)());

이 부분에서 마지막에 빈 괄호를 붙이는 의미를 해석하기 어려워 마지막으로 한 번 더 질문 드립니다

연결 해주신 유투브 내용을 바탕으로 "auctionedOff(good) 뒤에 있는 빈괄호는 auctionedOff(good)의 리턴값인 { }로 감싸진 부분을 불러오기 위함이고 이 부분은 변경 전 ForEach함수 괄호 내의 화살표 이후 {} 블럭과 동일하므로 대체할 수 있다" 로 해석 하면 될지 질문 드립니다

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 08. 17:42

이거 그냥 간단하게 생각하시면 됩니다.

forEach는 (good) => auctionedOff(good)() 함수를 호출합니다.

auctionedOff(good)의 리턴값은 함수입니다. 그래서 실행되려면 뒤에 ()를 한 번 더 붙여야 합니다.

0

cumulonimbus69님의 프로필 이미지

2024. 07. 08. 13:56

말씀하신데로 auctionedOff 함수를 다음과 같이 바꾸어 실행하니 잘 돌아갑니다.

수정한 함수 :

const sequelize = require('sequelize');
const { Auction , Good, User}= require('../models');

exports.auctionedOff = ( good ) => async () => {
  const t = await sequelize.transaction();
  try {
    const success = await Auction.findOne({
      where: { GoodId: good.id },
      order: [['bid', 'DESC']],
      transaction : t,
    });
    if (!success) { return; }
    await good.setSold(success.UserId); //낙찰
    await User.update({
      money: sequelize.literal(`money - ${success.bid}`),
    }, {
      where: { id: success.UserId },
      transaction: t,
    });
    await t.commit();
  } catch (error) {
    await t.rollback();
  }
};

그런데 위의 형태를 checkAuction.js 부분에 24시간이 지난 아이템에 대해 낙찰자를 지정하는 ForEach 함수 부분에도 적용해 보려고 했는데 여기는 안되는데 이유가 뭘까요?

시도한 코드 :

module.exports = async () => {
  console.log('checkAuction');
  try {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1); // 어제 시간
    const targets = await Good.findAll({ // 24시간이 지난 낙찰자 없는 경매들
      where: {
        SoldId: null,
        createdAt: { [Op.lte]: yesterday },
      },
    });
    targets.forEach(auctionedOff(good));

아래와 같이 auctionedOff 로 바꾸기 전에 확인차 수정 했을 때는 정상적으로 낙찰된 물건에 대해 낙찰자를 지정하고 낙찰자의 금액도 빼는 프로세스가 잘되었습니다

module.exports = async () => {
  console.log('checkAuction');
  try {
    const yesterday = new Date();
    yesterday.setDate(yesterday.getDate() - 1); // 어제 시간
    const targets = await Good.findAll({ // 24시간이 지난 낙찰자 없는 경매들
      where: {
        SoldId: null,
        createdAt: { [Op.lte]: yesterday },
      },
    });
    targets.forEach(
       async (good) => {
      const t = await sequelize.transaction();
      try {
        const success = await Auction.findOne({
          where: { GoodId: good.id },
          order: [['bid', 'DESC']],
          transaction : t,
        });
        if (!success) { return; }
        console.log('***checkauction',success.UserId);
        await good.setSold(success.UserId);
        await User.update({
          money: sequelize.literal(`money - ${success.bid}`),
        }, {
          where: { id: success.UserId },
          transacion: t,
        });
        await t.commit();
      } catch (error) {
        await t.rollback();
      }
    });

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 08. 14:04

targets.forEach((good) => auctionedOff(good)());

good 변수가 없습니다.

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 08. 14:05

https://www.youtube.com/watch?v=NS1cIsWlFGI&list=PLcqDmjxt30Rt9wmSlw1u6sBYr-aZmpNB3&index=2

이거 고차함수 한 번 보시면 좋을 것 같습니다

0

cumulonimbus69님의 프로필 이미지

2024. 07. 05. 17:35

감사합니다 앞서 발생한 메시지는 해결 되었는데 가르쳐주신데로 수정한 이후에 다음과 같은 오류 메시지가 또 뜹니다

image.png

참고 하시라 로그 에러 메시지도 첨부 합니다

눈에 띠는 것은 "RangeError" 라고 뜹니다만

앞서는 "TypeError" 였는데 반해서..

GET /main.css 304 0.333 ms - -
Executing (default): SELECT `id`, `email`, `nick`, `password`, `money`, `createdAt`, `updatedAt`, `deletedAt` FROM `users` AS `User` WHERE (`User`.`deletedAt` IS NULL AND `User`.`id` = 1);
Executing (default): INSERT INTO `goods` (`id`,`name`,`img`,`price`,`createdAt`,`updatedAt`,`OwnerId`) VALUES (DEFAULT,?,?,?,?,?,?);
RangeError: The job method must be a function.
    at Object.scheduleJob (/Users/skychome/Desktop/My_Work/node_work/node_practice/node_auction/node_modules/node-schedule/lib/schedule.js:34:11)
    at exports.createGood (/Users/skychome/Desktop/My_Work/node_work/node_practice/node_auction/controllers/index.js:48:26)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Executing (dd5254a0-4b7a-4cbd-bdd2-5b2f494da1e1): START TRANSACTION;
POST /good 500 116.267 ms - 1684
Executing (dd5254a0-4b7a-4cbd-bdd2-5b2f494da1e1): SELECT `id`, `bid`, `msg`, `createdAt`, `updatedAt`, `deletedAt`, `UserId`, `GoodId` FROM `auctions` AS `Auction` WHERE (`Auction`.`deletedAt` IS NULL AND `Auction`.`GoodId` = 13) ORDER BY `Auction`.`bid` DESC LIMIT 1;
GET /main.css 304 0.313 ms - -
제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 05. 17:57

const job = schedule.scheduleJob(end, auctionedOff(good));

여기서 함수를 넣으셔야 하는데 undefined를 넣으셨네요. 고차함수를 사용하세요

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 05. 13:59

const { auctionedOff } = require('../services');

0

인프런 AI 인턴님의 프로필 이미지

2024. 07. 05. 13:56

안녕하세요, 인프런 AI 인턴입니다.

cumulonimbus69님께서 질문하신 “트랜잭션 기능을 서비스 함수로 구현하는 방법”과 관련해 Sequelize에서 트랜잭션을 활용하는 방법과 auctionedOff 함수 구현과 관련된 에러 해결 방안을 제공하고자 합니다.

문제의 핵심은 auctionedOff 함수가 정의되었으나, 그 함수를 올바르게 호출하는 방법과 Sequelize에서 트랜잭션을 활용하는 방법에서 발생한 것으로 보입니다. 에러 메시지 “auctionedOff is not a function“는 require를 통해 모듈을 불러올 때 발생한 것 같습니다.

트랜잭션을 적용한 함수 구현에 대해 보다 구체적인 도움이 될 수 있는 관련 질문을 참조하기를 권장드립니다.

  • 트랜잭션 - Sequelize의 트랜잭션 처리 예시에 대한 질문과 답변입니다. 이후 해결된 질의응답을 참고하시면 Sequelize에서 트랜잭션을 어떻게 사용하는지에 대한 기본적인 이해를 도울 수 있습니다.

cumulonimbus69님의 코드에서 문제가 되는 부분은 auctionedOff 함수를 node-schedule의 콜백으로 전달하는 방식에 있어 보입니다. scheduleJob의 콜백 함수는 특정 시간에 실행되어야 하므로, auctionedOff(good)를 호출하는 대신 함수 자체를 전달해야 합니다. 이를 위해서는 auctionedOff 함수가 함수를 반환하도록 수정하거나, scheduleJob 내에서 익명 함수를 사용하여 auctionedOff 함수를 호출하는 방식으로 변경할 필요가 있습니다.

또한, require로 모듈을 불러올 때 올바르게 참조하지 못하는 문제로 보입니다. auctionedOff 함수를 단일 함수로 export하지 않고 다른 함수들과 함께 객체 내에서 export한 경우, 다음과 같이 함수를 사용해야 합니다:

const { auctionedOff } = require('../services');

위의 방식으로 코드를 수정하여 문제가 해결되는지 확인해 보시기 바랍니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.