Tedious: ์š”์ฒญ์€ SentClientRequest ์ƒํƒœ๊ฐ€ ์•„๋‹Œ LoggedIn ์ƒํƒœ์—์„œ๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ฝ”๋“œ: 'EINVALIDSTATE').

์— ๋งŒ๋“  2016๋…„ 09์›” 28์ผ  ยท  23์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: tediousjs/tedious

์•„๋ž˜ ์˜ˆ์ œ์—์„œ๋Š” ํ•จ์ˆ˜ ๋ฐฐ์—ด์„ ์ง๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์š”์ฒญ์€ LoggedIn ์ƒํƒœ์—์„œ๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ connection.state !== connection.STATE.LOGGED_IN ๋ฅผ ํ™•์ธํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์˜ ์ฝ๊ธฐ ๊ธฐ๋Šฅ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ์˜ค๋ฅ˜๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด ์š”์ฒญ์„ ๋‹ค์‹œ ๋Œ€๊ธฐ์—ด์— ๋„ฃ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

{ [RequestError: Requests can only be made in the LoggedIn state, not the SentClientRequest state]
  message: 'Requests can only be made in the LoggedIn state, not the SentClientRequest state',
  code: 'EINVALIDSTATE' }

์ด๊ฒƒ์„ ๋‹ฌ์„ฑํ•˜๋Š” ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ #19 ๋ฐ #355 ์ด์ „์— ์ œ๊ธฐ๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ์—์„œ ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ์‹คํ–‰ํ•˜๋Š” ๊ถŒ์žฅ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•”ํ˜ธ:

var Connection = require('tedious').Connection;  
var Request = require('tedious').Request;  
var TYPES = require('tedious').TYPES;  
var async = require('async');

// Create connection to database
var config = {
        userName: 'mylogin',  
        password: 'mypassword',  
        server: 'localhost',
        options: {
            // encrypt: true, /*If you are connecting to a Microsoft Azure SQL database, you will need this*/
            database: 'testdb'
        }
    }
var connection = new Connection(config);  

// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {  
    if (err) {
        console.log(err);
    }  
    else    {
        console.log("Connected");

        // Execute all functions in the array serially
        async.waterfall([
            function Start(callback){
                console.log("Starting...");
                callback(null, 'Jake', 'United States');
            },
            function Insert(name, location, callback){
                console.log("Inserting into Table...");

                request = new Request("INSERT dbo.employees (name, location) OUTPUT INSERTED.id VALUES (<strong i="14">@Name</strong>, @Location);", function(err){  
                     if (err) {
                            console.log(err);
                        }  
                    }
                );  
                request.addParameter('Name', TYPES.NVarChar, name);  
                request.addParameter('Location', TYPES.NVarChar, location);  

                request.on('row', function(columns) {  
                    columns.forEach(function(column) {  
                      if (column.value === null) {  
                        console.log('NULL');  
                      } else {  
                        console.log("Employee id inserted is " + column.value);  
                      }  
                    });  
                }); 

                // Check how many rows were inserted
                request.on('doneInProc', function(rowCount, more) {  
                    console.log(rowCount + ' row(s) inserted');  
                    callback(null, 'Jared');
                });             

                connection.execSql(request);  
            },
            function Read(callback){
                // Requests can only be made in the LoggedIn state, so check for that
                if (connection.state !== connection.STATE.LOGGED_IN) {
                    // Put the request back on the dispatcher if connection is not in LoggedIn state
                    setTimeout(Read, 0, callback);
                    return;
                }

                console.log("Reading rows from the Table...");

                // Create the request to read from table
                request = new Request("SELECT * FROM dbo.employees;", function(err) {  
                    if (err) {
                        console.log(err);
                    }  
                });  

                // Output the number of rows read 
                request.on('doneInProc', function (rowCount, more, rows) {  
                    console.log(rowCount + ' row(s) returned');  
                    callback(null);
                });  

                connection.execSql(request);  
            }],
            function Complete(err, result) {
                if(err) {
                    console.log("Error:", err);
                }
                else {
                    console.log("Done!");
                }
            }
        )
    }
});

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

Google์—์„œ ์—ฌ๊ธฐ๋กœ ์˜ค๋Š” ์‚ฌ๋žŒ๋“ค(์ €์ฒ˜๋Ÿผ): tedious-connection-pool ์€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. pool.acquire((err, connection) => ...) ๋ฐ connection.release() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์ค‘ ์—ฐ๊ฒฐ์„ ์›ํ•˜์ง€ ์•Š์œผ๋ฉด {min: 1, max: 1} ๋กœ ํ’€์„ ๊ตฌ์„ฑํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ง๋ ฌํ™”ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  23 ๋Œ“๊ธ€

์š”์ฒญ์— ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” '์ฝœ๋ฐฑ' ์†์„ฑ์ด ์žˆ๊ณ  ์š”์ฒญ์— 'requestCompleted' ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋‘˜ ๋‹ค ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์‹œ๋„ํ•˜์ง€ ์•Š์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค :)

์ด์— ๋Œ€ํ•œ ์ •๋ฆฌ ๋ฐ ๋ฌธ์„œํ™”๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์‹œ๋„ํ•˜๊ณ  ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.

'์ฝœ๋ฐฑ' ์‚ฌ์šฉ:

  request.callback = function (err, rowCount, rows) {
      // rows is not being set but rowCount is. May be a bug.
      if (err) {
        // Error handling.
      } else {
        // Next SQL statement.
      }
  };

'์š”์ฒญ ์™„๋ฃŒ' ์‚ฌ์šฉ:

  request.on('requestCompleted', function () {
      // Next SQL statement.
  });

์‹ค์ œ๋กœ ์ด ๋…ธํŠธ๋ฅผ https://github.com/tediousjs/tedious/issues/61 ์— ๋„ฃ๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ์ด๋ฏ€๋กœ ์ด ๋…ธํŠธ๋ฅผ ๋‹ซ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ์—ฝ๋‹ˆ๋‹ค. https://github.com/tediousjs/tedious/issues/61 ์€ ์ „ํ˜€ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. @SaloniSonpal ์ด ์–ธ๊ธ‰ํ•œ ๋ฌธ์ œ๋Š” ์‹ค์ œ๋กœ ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ถ”์ ์„ ์œ„ํ•ด ์ด๊ฒƒ์„ ๋‹ค์‹œ ์—ฝ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์•ฝ๊ฐ„์˜ ์ •๋ฆฌ์™€ ๋ฌธ์„œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

@tvrprasad ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ๊ณ  ํ›จ์”ฌ ๋” ๊นจ๋—ํ•ฉ๋‹ˆ๋‹ค. callback ์ ‘๊ทผ ๋ฐฉ์‹์€ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ์ด์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Event: 'requestCompleted' ๋Š” ๋ฐ˜๋“œ์‹œ ๋ฌธ์„œํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

(ํ•ธ๋“œํฐ์œผ๋กœ ๋ณด๋‚ด๋‹ˆ ๋ง์ด ๊ธธ์–ด์ง€๋Š” ์  ์–‘ํ•ด ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.)

callback ์†์„ฑ์„ ์„ค์ •ํ•˜๋ฉด ์ „๋‹ฌํ•œ ์ฝœ๋ฐฑ์„ ๋ฎ์–ด์”๋‹ˆ๋‹ค.
์š”์ฒญ ์ƒ์„ฑ์ž์—. ๋Œ€์‹  ๋‹ค์Œ์—์„œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
Request ์ƒ์„ฑ์ž์— ์ „๋‹ฌ๋œ ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ async. ์ด๋ ‡๊ฒŒ ๋‹น์‹ ์€
์˜ค๋ฅ˜๋ฅผ ๋น„๋™๊ธฐ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„, ๊ทธ ์ฝœ๋ฐฑ์ด ์ „๋‹ฌ๋œ ์ฝœ๋ฐฑ์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋†“์ณค์Šต๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  request = new Request(sqlQuery, function (err, rowCount, rows) {
    if (err) {
      // Error handling.
    } else {
      // Next SQL statement.
    }
  });

@arthurschreiber 'requestCompleted' ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์ค‘๋ณต๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์•Œ์•„์ฐจ๋ฆฐ ๋˜ ๋‹ค๋ฅธ ์‚ฌ์‹ค์€ ์ฝœ๋ฐฑ์— ์ „๋‹ฌ๋œ ํ–‰ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•ญ์ƒ ๋น„์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ์•Œ์•„์ฐจ๋ฆฐ ๋˜ ๋‹ค๋ฅธ ์‚ฌ์‹ค์€ ์ฝœ๋ฐฑ์— ์ „๋‹ฌ๋œ ํ–‰ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ํ•ญ์ƒ ๋น„์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์—ฐ๊ฒฐํ•  ๋•Œ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” rowCollectionOnRequestCompletion ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ „์—ญ ์˜ต์…˜์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์•ฝ๊ฐ„ ์ด์ƒํ•ฉ๋‹ˆ๋‹ค. ์š”์ฒญ๋ณ„๋กœ ์ด๊ฒƒ์„ ๊ฐ–๋Š” ๊ฒƒ์ด ๋” ํ•ฉ๋ฆฌ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ์ด๋ฉด์˜ ์ฃผ์š” ์•„์ด๋””์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. rowCollectionOnRequestCompletion ์˜ต์…˜์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ํ–‰์€ ํ•ด๋‹น Request ๊ฐ€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค(ํ˜„์žฌ๋กœ์„œ๋Š” Request ๋Š” gc'๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค). ์ž‘์€ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์˜ ๊ฒฝ์šฐ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์ง€๋งŒ ํฐ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์˜ ๊ฒฝ์šฐ ์„ฑ๋Šฅ ์— ๋งŽ์€ ๋ถ€์ •์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ ํ˜ธ๋˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์€ rows ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ ๋ชจ๋“  ํ–‰์„ ๋ฐฐ์—ด๋กœ ์ˆ˜์ง‘ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์ง์ ‘ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ตœ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ๋‹น ๋Œ€์‹  ์š”์ฒญ๋ณ„๋กœ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Request ์ƒ์„ฑ์ž๋Š” ์•„์ง ์˜ต์…˜์„ ์ทจํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฉ”์„œ๋“œ ์„œ๋ช…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

'requestCompleted' ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ง€๋ฃจํ•œ ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

rowCollectionOnRequestCompletion ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด #459๋ฅผ ์—ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•œ๋‹ค
์ด์ „ ๋ฒ„์ „๊ณผ ํ˜ธํ™˜๋˜๋Š” ๋ฐฉ์‹์œผ๋กœ.

callback ์†์„ฑ์„ ์„ค์ •ํ•˜๋ฉด ์ „๋‹ฌํ•œ ์ฝœ๋ฐฑ์„ ๋ฎ์–ด์”๋‹ˆ๋‹ค.
์š”์ฒญ ์ƒ์„ฑ์ž์—. ๋Œ€์‹  ๋‹ค์Œ์—์„œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
Request ์ƒ์„ฑ์ž์— ์ „๋‹ฌ๋œ ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ async. ์ด๋ ‡๊ฒŒ ๋‹น์‹ ์€
์˜ค๋ฅ˜๋ฅผ ๋น„๋™๊ธฐ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

@arthurschreiber ๋งž์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ๊ฒฐ๊ณผ/์˜ค๋ฅ˜๋ฅผ ๋ฌด์‹œํ•˜์ง€ ์•Š๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค :-)

Event: 'requestCompleted' ์— ๋Œ€ํ•œ ๋ฌธ์„œ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด #464๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

Google์—์„œ ์—ฌ๊ธฐ๋กœ ์˜ค๋Š” ์‚ฌ๋žŒ๋“ค(์ €์ฒ˜๋Ÿผ): tedious-connection-pool ์€ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. pool.acquire((err, connection) => ...) ๋ฐ connection.release() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์ค‘ ์—ฐ๊ฒฐ์„ ์›ํ•˜์ง€ ์•Š์œผ๋ฉด {min: 1, max: 1} ๋กœ ํ’€์„ ๊ตฌ์„ฑํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ง๋ ฌํ™”ํ•ฉ๋‹ˆ๋‹ค.

๋ˆ„๊ตฌ๋“ ์ง€ ์ œ์•ˆํ•˜์‹ญ์‹œ์˜ค. Windows ์ธ์ฆ ๋ชจ๋“œ๋Š” nodejs2Sql ์—ฐ๊ฒฐ์— ๋Œ€ํ•ด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@wafaabbass tedious ๋Š” ์•„์ง Windows ํ†ตํ•ฉ ์ธ์ฆ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ž‘์—… ์ค‘์ด๋ฉฐ #415์—์„œ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„, ์ €๋„ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋””๋ฒ„๊ทธ:

์š”์ฒญ์€ SentClientRequest ์ƒํƒœ๊ฐ€ ์•„๋‹Œ LoggedIn ์ƒํƒœ์—์„œ๋งŒ ์ด๋ฃจ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
(node:5180) UnhandledPromiseRejectionWarning: Unhandled Promise Rejection (rejection id: 1): RequestError: SentClientRequest ์ƒํƒœ๊ฐ€ ์•„๋‹Œ LoggedIn ์ƒํƒœ์—์„œ๋งŒ ์š”์ฒญ ๊ฐ€๋Šฅ
(node:5180) [DEP0018] DeprecationWarning: ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ์•ฝ์† ๊ฑฐ๋ถ€๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•ž์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์€ ์•ฝ์† ๊ฑฐ๋ถ€๋Š” 0์ด ์•„๋‹Œ ์ข…๋ฃŒ ์ฝ”๋“œ๋กœ Node.js ํ”„๋กœ์„ธ์Šค๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

์•„์ง ํ•ด๊ฒฐ๋˜์—ˆ๋‚˜์š”? ์ด๊ฒƒ์€ ๋‚ด ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

function createAsPromiseWithTransaction(callback, data, connection) {

  const transactionReady = (err) => {
    if (err) {
      return callback(err)
    }
    return callback(null)
  }

  const transactionFailed = (originalErr) =>
    (err) => {
      if (err) {
        return callback(err)
      }
      return callback(originalErr)
    }

  const willIGetArea = new Promise(
    (resolve, reject) => {
      const create = function(data, connection) {
        let areaIdInserted = null
        const request = new Request(CONSTANTS.SPNames.Area,
          (err, rowCount, rows) => err ? reject(err) : resolve(areaIdInserted))

        request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
        request.addParameter('ParentArea', TYPES.Int, data.parentArea)
        request.addParameter('Name', TYPES.VarChar, data.nameArea)
        request.addParameter('ShortName', TYPES.VarChar, data.shortNameArea)
        request.addParameter('Description', TYPES.VarChar, data.descriptionArea)
        request.addParameter('Gender', TYPES.Bit, data.gender)
        request.addParameter('Colour', TYPES.VarChar, data.colour)
        request.addParameter('X', TYPES.Int, data.x)
        request.addParameter('Y', TYPES.Int, data.y)
        request.addParameter('Active', TYPES.Int, data.active)

        request.on('row', function(columns) {
          areaIdInserted = columns[1].value
        });

        connection.callProcedure(request)
      }

      const transactionStarted = function(err) {
        if (err) return callback(err)
        create(data, connection)
      }

      connection.beginTransaction(transactionStarted)
    })

  const willIGetSymptom = function(areaId) {

    return new Promise(function(resolve, reject) {

      const create = function(data, connection, areaId) {
        let symptomIdInserted = null
        const request = new Request(CONSTANTS.SPNames.Symptom,
          (err, rowCount, rows) => err ? reject(err) : resolve(symptomIdInserted))

        request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
        request.addParameter('AreaId', TYPES.BigInt, areaId)
        request.addParameter('Code', TYPES.VarChar, data.code)
        request.addParameter('Name', TYPES.VarChar, data.nameSymptom)
        request.addParameter('ShortName', TYPES.VarChar, data.shortNameSymptom)
        request.addParameter('Description', TYPES.VarChar, data.descriptionSymptom)
        request.addParameter('Gender', TYPES.Bit, data.gender)
        request.addParameter('Active', TYPES.Bit, data.active)

        request.on('row', function(columns) {
          symptomIdInserted = columns[1].value
        });


        connection.callProcedure(request)
      }

      create(data, connection, areaId)

    })
  }

  const willIGetModifier = new Promise(function(resolve, reject) {

    const request = new Request(
      CONSTANTS.SPNames.Modifier,
      function(err, rowCount, rows) {
        if (err) {
          reject(err)
        } else {
          resolve(rowCount,rows)
        }
      })

    request.addParameter('IUser', TYPES.UniqueIdentifier, data.iUser)
    request.addParameter('Name', TYPES.VarChar, data.nameModifier)
    request.addParameter('Order', TYPES.SmallInt, data.order)
    request.addParameter('Active', TYPES.Bit, data.active)

    connection.callProcedure(request)

  })

  const runPromise = () =>
    willIGetArea
    .then( (areaId) =>
      willIGetSymptom(areaId)
      .then( (symptomId) =>
        willIGetModifier
        .then((rowCount,rows) => connection.commitTransaction(transactionReady))
        .catch((err) => connection.rollbackTransaction(transactionFailed(err)))
      )
      .catch((err) => connection.rollbackTransaction(transactionFailed(err)))
    )it 
    .catch((err) => connection.rollbackTransaction(transactionFailed(err)))

  runPromise()

}

willIGetArea ๋ฐ willIGetSymptom์„ ํ˜ธ์ถœํ•˜๋ฉด ์ด ๋ชจ๋“  ๊ฒƒ์ด ์ž‘๋™ํ•˜์ง€๋งŒ willGetModifier๋กœ ํ˜ธ์ถœํ•˜๋ฉด ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Google์ด ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•ด ์ด ํŽ˜์ด์ง€๋ฅผ ๋จผ์ € ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ˆ„๊ตฐ๊ฐ€ ์œ ์šฉํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ.
๋‹จ์ผ ์—ฐ๊ฒฐ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์—์„œ ์—ฐ๊ฒฐ์„ ์œ„ํ•œ Promise ๊ธฐ๋ฐ˜ ๋ž˜ํผ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝํ—˜ ๋งŽ์€ js ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜์ ์œผ๋กœ ์ข‹์€ ์†”๋ฃจ์…˜์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

class PromisedConnection {
    constructor(config) {
        // this.connection always points to a promise resolving after the last assigned request
        // initial setting is either resolved once a new connection is established or rejected if an error occurs
        this.connection = new Promise((resolve, reject) => {
            const dbConnection = new Connection(config);
            dbConnection.on("connect", function (err) {
                if (err) {
                    reject(err);
                }
                else {
                    resolve(dbConnection);
                }
            });
        });
    }

    execute(request) {
        const nextConnection = new Promise((resolve, reject) => {
            // after scheduling new request this.connection should be reassigned to be the last in promise queue
            this.connection
                .catch( (reason) => {
                    reject(reason);
                } )
                .then( (dbConnection) => { // a new request can be executed only within the connection is free again (resolved after the last request)
                    request.on("requestCompleted", () => { // add an additional event listener in order to release connection after the request is done
                        resolve(dbConnection);
                    });
                    dbConnection.execSql(request);
                });
        } );
        this.connection = nextConnection;
    }
}

๊ทธ๋ฆฌ๊ณ  ์ฟผ๋ฆฌ ์ž์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const dbConnection = new PromisedConnection(config);

function getObjList(query, parameters = []) {
    return new Promise((resolve, reject) => {
        const objList = [];
        let request = new Request(
            query,
            function (err, rowCount, rows) {
                if (err) {
                    reject(err);
                } else if (rowCount < 1) {
                    reject(new Error("0 rows returned from DB"));
                }
            }
        );
        for (const {name, type, value} of parameters) {
            request.addParameter(name, type, value);
        };

        request.on("row", (columns) => {
            objList.push(Obj.fromColumns(columns)); // here I just make a specific object from each row
        });

        request.on("requestCompleted", () => {
            resolve(objList);
        });

        dbConnection.execute(request);
    });
}

๋”ฐ๋ผ์„œ getObjList ๋Š” ์ด์ œ ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์ง€ ์•Š๊ณ  ์–ธ์ œ๋“ ์ง€ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์— ๋Œ€ํ•œ ์šฐ๋ ค๋Š” ์ข‹์Šต๋‹ˆ๋‹ค. ๋‚ด ์ฝ”๋“œ์—์„œ๋Š” ์—ฐ๊ฒฐ ์š”์ฒญ, SQL ์š”์ฒญ, ์‚ฝ์ž… ์š”์ฒญ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. SQL ์š”์ฒญ ํ›„ requestCompleted ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ ํ›„์—๋„ SQL๊ณผ ์‚ฝ์ž… ์š”์ฒญ ์‚ฌ์ด์— ๋‹ค๋ฅธ ์—ฐ๊ฒฐ ์š”์ฒญ์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ์‚ฝ์ž… ์š”์ฒญ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด ์˜ค๋ฅ˜์˜ ์ผ๋ฐ˜์ ์ธ ์ด์œ ๋Š” _ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ์—์„œ ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋งŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์š”์ฒญ์„ ํ•˜๊ธฐ ์ „์— ์˜ค๋ฅ˜๋‚˜ ๊ฒฐ๊ณผ์™€ ํ•จ๊ป˜ ์š”์ฒญ ์ฝœ๋ฐฑ์ด ์‹คํ–‰๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ FAQ ํŽ˜์ด์ง€๋ฅผ ๋ฐฉ๋ฌธํ•˜์‹ญ์‹œ์˜ค.

์ด ํฌ๋Ÿผ์—๋Š” ๋‹ค์–‘ํ•œ ๋ฌธ์ œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. Requests can only be made in the LoggedIn state, not the SentClientRequest state ์˜ค๋ฅ˜๋Š” ๋‹ค์–‘ํ•œ ์ด์œ ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  FAQ ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋‹ค์Œ ์ •๋ณด๋กœ ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ๋ณด๋‹ค ์ •ํ™•ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜์‹ญ์‹œ์˜ค.

  • ์ง€๋ฃจํ•œ ๋ฒ„์ „
  • ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ๊ตฌ์„ฑ
  • ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ์Šคํฌ๋ฆฝํŠธ

๊ฐ์‚ฌ ํ•ด์š”! ๐Ÿ˜„

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ๋ฌธ์ œ๋Š” ๋‚ด ํŽธ์ด์—ˆ์œผ๋ฉฐ ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

@mgarf ๋‹น์‹ ์˜ ์†”๋ฃจ์…˜์€ ๋ฌด์—‡์ด์—ˆ์Šต๋‹ˆ๊นŒ? ๋˜ํ•œ ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๋ชฐ๋ž์Šต๋‹ˆ๋‹ค. ์ด ํ† ๋ก ์—์„œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๊ณผ ๊ฐ™์€ ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@mikebutak ๊ฐ ์š”์ฒญ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ์—ฐ๊ฒฐ์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๋งŒ ์žˆ์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ์—์„œ ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด ์™„๋ฃŒ๋œ ํ›„ ์ด์ „ ์š”์ฒญ์˜ ์ฝœ๋ฐฑ์—์„œ ์ƒˆ ์š”์ฒญ์„ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด,

function executeStatement() {
  request = new Request("select 42, 'hello world'", function(err, rowCount) {
    if (err) {
      console.log(err);
    } else {
      console.log(rowCount + ' rows');
    }

    connection.execSql(new Request("select 42, 'hello world'", function(err, rowCount) {
      if (err) {
        console.log(err);
      } else {
        console.log(rowCount + ' rows');
      }
      connection.close();
    }))
  });
  connection.execSql(request);

์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์ฒด ์†”๋ฃจ์…˜์„ ๊ฒŒ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๊ณ  Google์„ ๋ณผ ๋•Œ ์—ฌ๊ธฐ์—์„œ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋งค์ดˆ 1 http ํ˜ธ์ถœ์„ ์‹คํ–‰ํ•˜๊ณ  ๋ฌธ์ œ ์—†์ด 0.5์ดˆ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋กœ ์ด ์ฝ”๋“œ๋ฅผ ๋กœ๋“œ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

const {Connection, Request} = require("tedious");

const executeSQL = (sql, callback) => {
  let connection = new Connection({
    "authentication": {
      "options": {
        "userName": "USERNAME",
        "password": "PASSWORD"
      },
      "type": "default"
    },
    "server": "SERVER",
    "options": {
      "validateBulkLoadParameters": false,
      "rowCollectionOnRequestCompletion": true,
      "database": "DATABASE",
      "encrypt": true
    }
  });

  connection.connect((err) => {
    if (err)
      return callback(err, null);

    const request = new Request(sql, (err, rowCount, rows) => {
      connection.close();

      if (err)
        return callback(err, null);

      callback(null, {rowCount, rows});
    });

    connection.execSql(request);
  });
};

executeSQL("SELECT * FROM users", (err, data) => {
  if (err)
    console.error(err);

  console.log(data.rowCount);
});

//or

executeSQL("SELECT * FROM users", (err, {rowCount, rows}) => {
  if (err)
    console.error(err);

  console.log(rowCount);
});

์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์ฒด ์†”๋ฃจ์…˜์„ ๊ฒŒ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๊ณ  Google์„ ๋ณผ ๋•Œ ์—ฌ๊ธฐ์—์„œ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋งค์ดˆ 1 http ํ˜ธ์ถœ์„ ์‹คํ–‰ํ•˜๊ณ  ๋ฌธ์ œ ์—†์ด 0.5์ดˆ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋กœ ์ด ์ฝ”๋“œ๋ฅผ ๋กœ๋“œ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

const {Connection, Request} = require("tedious");

const executeSQL = (sql, callback) => {
  let connection = new Connection({
    "authentication": {
      "options": {
        "userName": "USERNAME",
        "password": "PASSWORD"
      },
      "type": "default"
    },
    "server": "SERVER",
    "options": {
      "validateBulkLoadParameters": false,
      "rowCollectionOnRequestCompletion": true,
      "database": "DATABASE",
      "encrypt": true
    }
  });

  connection.connect((err) => {
    if (err)
      return callback(err, null);

    const request = new Request(sql, (err, rowCount, rows) => {
      connection.close();

      if (err)
        return callback(err, null);

      callback(null, {rowCount, rows});
    });

    connection.execSql(request);
  });
};

executeSQL("SELECT * FROM users", (err, data) => {
  if (err)
    console.error(err);

  console.log(data.rowCount);
});

//or

executeSQL("SELECT * FROM users", (err, {rowCount, rows}) => {
  if (err)
    console.error(err);

  console.log(rowCount);
});

์ด ์†”๋ฃจ์…˜์€ ์ž‘๋™ํ•˜์ง€๋งŒ ์œ ํšจํ•œ ๊ตฌํ˜„์ž…๋‹ˆ๊นŒ? ์ˆ˜์‹ญ๋งŒ ๊ฐœ์˜ ์‚ฝ์ž…/์„ ํƒ์ด ์žˆ๊ณ  ๊ฐ๊ฐ์— ๋Œ€ํ•œ ์—ฐ๊ฒฐ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ๊ณ ๋ คํ•  ๋•Œ ๊ทธ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์‹คํ˜„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๊นŒ? ์†”๋ฃจ์…˜ btw์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์†”๋ฃจ์…˜์ธ์ง€ ์ดํ•ดํ•˜๋ ค๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ Azure Function์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ?

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰