nodejs의 싱글 톤 패턴-필요합니까?


95

최근 에 Node.js에서 싱글 톤을 작성하는 방법에 대한 이 기사 를 보았습니다. 다음과 같은 require 상태 문서를 알고 있습니다 .

모듈은 처음로드 된 후 캐시됩니다. 를 여러 번 호출 require('foo')하면 모듈 코드가 여러 번 실행되지 않을 수 있습니다.

따라서 필요한 모든 모듈은 단일 상용구 코드없이 단일 항목으로 쉽게 사용할 수있는 것 같습니다.

질문:

위의 기사가 싱글 톤 생성에 대한 해결책을 제공합니까?


1
5 분입니다. 이 주제에 대한 설명 (v6 및 npm3 이후 작성) : medium.com/@lazlojuly/…
lazlojuly

답변:


56

이것은 기본적으로 nodejs 캐싱과 관련이 있습니다. 단순하고 단순합니다.

https://nodejs.org/api/modules.html#modules_caching

(v 6.3.1)

캐싱

모듈은 처음로드 된 후 캐시됩니다. 이것은 (무엇보다도) require ( 'foo')에 대한 모든 호출이 동일한 파일로 해석된다면 정확히 동일한 객체를 반환한다는 것을 의미합니다.

require ( 'foo')를 여러 번 호출하면 모듈 코드가 여러 번 실행되지 않을 수 있습니다. 이것은 중요한 기능입니다. 이를 통해 "부분적으로 수행 된"개체가 반환 될 수 있으므로주기를 유발하는 경우에도 전 이적 종속성을로드 할 수 있습니다.

모듈이 코드를 여러 번 실행하도록하려면 함수를 내보내고 해당 함수를 호출하십시오.

모듈 캐싱주의 사항

모듈은 확인 된 파일 이름을 기반으로 캐시됩니다. 모듈은 호출 모듈의 위치 (node_modules 폴더에서로드)에 따라 다른 파일 이름으로 해석 될 수 있으므로, require ( 'foo')가 다른 파일로 해석되는 경우 항상 똑같은 객체를 반환한다는 보장은 없습니다. .

또한 대소 문자를 구분하지 않는 파일 시스템 또는 운영 체제에서 확인 된 다른 파일 이름이 동일한 파일을 가리킬 수 있지만 캐시는 여전히 파일을 다른 모듈로 취급하고 파일을 여러 번 다시로드합니다. 예를 들어 require ( './ foo') 및 require ( './ FOO')는 ./foo 및 ./FOO가 동일한 파일인지 여부에 관계없이 두 개의 다른 객체를 반환합니다.

그래서 간단히 말해서.

싱글 톤을 원한다면; 개체를 내 보냅니다 .

싱글 톤을 원하지 않는다면; 함수를 내보내십시오 (그리고 그 함수에서 물건을 / 물건을 돌려 보내십시오 / 무엇이든).

명확하게 말하면 제대로 작동하면 https://stackoverflow.com/a/33746703/1137669 (Allen Luce의 답변)를 참조하십시오. 다르게 확인 된 파일 이름으로 인해 캐싱이 실패 할 때 발생하는 상황을 코드에서 설명합니다. 그러나 항상 동일한 파일 이름으로 확인하면 작동합니다.

2016 업데이트

es6 기호를 사용하여 node.js에서 진정한 싱글 톤 생성 또 다른 솔루션 : 이 링크에서

2020 업데이트

이 답변은 CommonJS (Node.js 자체 모듈 가져 오기 / 내보내기 방법)를 나타냅니다 . Node.js는 대부분 ECMAScript 모듈 로 전환 될 것입니다 : https://nodejs.org/api/esm.html (알지 못한 경우 ECMAScript는 JavaScript의 실제 이름입니다)

ECMAScript로 마이그레이션 할 때 지금은 https://nodejs.org/api/esm.html#esm_writing_dual_packages_while_avoiding_or_minimizing_hazards 를 읽으십시오.


3
싱글 톤을 원한다면; 객체를 수출 ... 그 도움 덕분에
danday74

1
이것은 일종의 나쁜 생각입니다-이 페이지의 다른 곳에서 제공된 여러 이유 때문에- 개념 은 본질적으로 유효합니다. 즉, 확립 된 명 목적 상황에서이 답변의 주장이 사실이라는 것입니다. 빠르고 더러운 싱글 톤을 원한다면 이것이 작동 할 것입니다. 코드로 셔틀을 시작하지 마십시오.
Adam Tolley

@AdamTolley "이 페이지의 다른 곳에서 여러 가지 이유로"동일한 캐시를 사용하지 않는 심볼릭 링크 파일 또는 잘못된 파일 이름을 언급하고 있습니까? 문서에 대소 문자를 구분하지 않는 파일 시스템 또는 운영 체제와 관련된 문제가 명시되어 있습니다. 심볼릭 링크에 관해서는 github.com/nodejs/node/issues/3402 에서 논의 된대로 여기에서 더 많은 것을 읽을 수 있습니다 . 또한 파일을 심볼릭 링크하고 있거나 OS와 노드를 제대로 이해하지 못한다면 항공 우주 공학 산업 근처에 있으면 안됩니다.;), 그러나 귀하의 요점을 이해합니다 ^^.
K-SO의 독성이 증가하고 있습니다.

@KarlMorrison-문서가 그것을 보장하지 않는다는 사실, 지정되지 않은 행동으로 보이는 사실 또는 언어 의이 특정 행동을 신뢰하지 않는 다른 합리적인 이유. 캐시가 다른 구현에서 다르게 작동하거나 REPL에서 작업하고 캐싱 기능을 완전히 파괴하는 것을 좋아할 수 있습니다. 내 요점은 캐시가 구현 세부 사항이며 단일 항목으로 사용하면 영리한 해킹입니다. 내가 영리 해킹을 사랑하지만 그들이 구별해야한다, 그게 다야 - (또한 아무도 노드 셔틀을 시작하지, 난 바보되는 WAS)
아담 톨리

136

위의 모든 것은 너무 복잡합니다. 디자인 패턴이 실제 언어의 결함을 보여주고 있다는 생각의 학교가 있습니다.

프로토 타입 기반 OOP (클래스리스)를 사용하는 언어에는 싱글 톤 패턴이 전혀 필요하지 않습니다. 즉석에서 단일 (톤) 개체를 만든 다음 사용하면됩니다.

노드의 모듈은 기본적으로 캐시되지만, 예를 들어 모듈 변경 사항의 핫 로딩을 원하는 경우 조정할 수 있습니다.

그러나 예, 공유 객체를 전체적으로 사용하려면 모듈 내보내기에 넣는 것이 좋습니다. "단일 패턴"으로 복잡하게 만들지 마십시오. JavaScript에서는 필요하지 않습니다.


27
아무도 upvotes를 얻지 않는 이상합니다 ... +1을 위해There is a school of thought which says design patterns are showing deficiencies of actual language.
Esailija

64
싱글 톤은 안티 패턴이 아닙니다.
wprl apr

4
@herby는 싱글 톤 패턴의 지나치게 구체적인 (따라서 부정확 한) 정의처럼 보입니다.
wprl

19
문서에 "require ( 'foo')를 여러 번 호출 하면 모듈 코드가 여러 번 실행 되지 않을 수 있습니다 ."라고 읽습니다 . "할 수 없다"라고 말하고 "그렇지 않을 것"이라고 말하지 않습니다. 따라서 모듈 인스턴스가 응용 프로그램에서 한 번만 생성되는지 확인하는 방법을 묻는 것은 제 관점에서 유효한 질문입니다.
xorcus 2014

9
이것이이 질문에 대한 정답이라는 것은 오해의 소지가 있습니다. @mike가 아래에서 지적했듯이 모듈이 두 번 이상로드되고 두 개의 인스턴스가있을 수 있습니다. 나는 Knockout의 복사본이 하나만 있지만 모듈이 두 번로드되기 때문에 두 개의 인스턴스가 생성되는 문제에 부딪 혔습니다.
dgaviola

26

아니요. Node의 모듈 캐싱이 실패하면 해당 싱글 톤 패턴이 실패합니다. OSX에서 의미있게 실행되도록 예제를 수정했습니다.

var sg = require("./singleton.js");
var sg2 = require("./singleton.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

이것은 작성자가 예상 한 결과를 제공합니다.

{ '1': 'test', '2': 'test2' } { '1': 'test', '2': 'test2' }

그러나 약간의 수정은 캐싱을 무효화합니다. OSX에서 다음을 수행하십시오.

var sg = require("./singleton.js");
var sg2 = require("./SINGLETON.js");
sg.add(1, "test");
sg2.add(2, "test2");

console.log(sg.getSocketList(), sg2.getSocketList());

또는 Linux에서 :

% ln singleton.js singleton2.js

그런 다음 sg2require 라인을 다음과 같이 변경하십시오 .

var sg2 = require("./singleton2.js");

그리고 bam , 싱글 톤이 패배합니다.

{ '1': 'test' } { '2': 'test2' }

이 문제를 해결할 수있는 방법을 모르겠습니다. 싱글 톤과 같은 것을 만들 필요가 있고 글로벌 네임 스페이스 (및 그로 인해 발생할 수있는 많은 문제)를 오염 시켜도 괜찮다면 작성자 getInstance()exports줄을 다음 과 같이 변경할 수 있습니다 .

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
}

module.exports = singleton.getInstance();

즉, 저는 이와 같은 작업을 수행해야하는 프로덕션 시스템에서 한 번도 발생하지 않았습니다. 또한 Javascript에서 싱글 톤 패턴을 사용할 필요성을 느끼지 못했습니다.


21

모듈 문서 의 모듈 캐싱주의 사항 을 조금 더 살펴 보겠습니다.

모듈은 확인 된 파일 이름을 기반으로 캐시됩니다. 모듈은 호출 모듈의 위치 (node_modules 폴더에서로드)에 따라 다른 파일 이름으로 해석 될 수 있으므로 require ( 'foo')가 다른 파일로 해석되는 경우 항상 똑같은 객체를 반환한다는 보장은 없습니다. .

따라서 모듈이 필요할 때 어디에 있는지에 따라 모듈의 다른 인스턴스를 얻을 수 있습니다.

모듈과 같은 소리는 싱글 톤 생성에 대한 간단한 해결책 이 아닙니다 .

편집 : 아니면 그들 이다 . @mkoryak과 마찬가지로 단일 파일이 (심볼릭 링크를 사용하지 않고) 다른 파일 이름으로 해석 될 수있는 경우를 생각 해낼 수 없습니다. 그러나 (@JohnnyHK 주석처럼) 서로 다른 node_modules디렉토리 에있는 파일의 여러 복사본 이 각각 개별적으로로드되고 저장됩니다.


좋아, 나는 그것을 세 번 읽었고 다른 파일 이름으로 해석되는 예를 여전히 생각할 수 없습니다. 도움?
mkoryak

1
@mkoryak 나는 그것이 당신이 요구하는 두 개의 다른 모듈을 가지고 node_modules있고 각각이 같은 모듈에 의존 하는 경우를 언급한다고 생각 node_modules합니다.
JohnnyHK

@mike 당신은 모듈이 다른 경로를 통해 참조 될 때 여러 번 인스턴스화되는 바로 여기에 있습니다. 서버 모듈에 대한 단위 테스트를 작성할 때 문제가 발생했습니다. 싱글 톤 인스턴스가 필요합니다. 그것을 달성하는 방법?
Sushil 2013-06-10

예를 들어 상대 경로가 있습니다. 예. 주어진 require('./db')두 개의 개별 파일에 db모듈 의 코드가 두 번 실행됩니다
willscripted dec

9
노드 모듈 시스템이 대소 문자를 구분하지 않기 때문에 방금 불쾌한 버그가 발생했습니다. 나는 require('../lib/myModule.js');한 파일과 require('../lib/mymodule.js');다른 파일을 호출 했지만 동일한 객체를 전달하지 않았습니다.
heyarne

18

node.js (또는 브라우저 JS)의 싱글 톤은 완전히 불필요합니다.

모듈은 캐시되고 상태 저장되므로 제공 한 링크에 제공된 예제를 훨씬 더 간단하게 쉽게 다시 작성할 수 있습니다.

var socketList = {};

exports.add = function (userId, socket) {
    if (!socketList[userId]) {
        socketList[userId] = socket;
    }
};

exports.remove = function (userId) {
    delete socketList[userId];
};

exports.getSocketList = function () {
    return socketList;
};
// or
// exports.socketList = socketList

5
문서는 말을 " 하지 않을 수 있습니다 여러 번 호출되고,이 코드가 다시 실행되면, socketList 빈 목록으로 재설정됩니다 가능성이 있으므로, 모듈의 코드를 여러 번 실행되도록"
조나단.

3
@홍옥. 컨텍스트 워드 프로세서에서 그 인용 주위에 꽤 설득력있는 사례 만들 것 하지 않을 수 있습니다 RFC를 스타일에 사용되는 NOT해야합니다 .
Michael

6
@Michael "may"는 그런 재미있는 단어입니다. 멋진 단어를 갖는 경우 중 하나 "아마도 없습니다"또는 "확실히".. 부정 수단
OJFord

1
may not적용 할 때 npm link개발하는 동안 다른 모듈. 따라서 eventBus와 같은 단일 인스턴스에 의존하는 모듈을 사용할 때는주의하십시오.
mediafreakch

10

ES6 클래스를 사용하는 유일한 답변

// SummaryModule.js
class Summary {

  init(summary) {
    this.summary = summary
  }

  anotherMethod() {
    // do something
  }
}

module.exports = new Summary()

다음과 함께이 싱글 톤이 필요합니다.

const summary = require('./SummaryModule')
summary.init(true)
summary.anotherMethod()

여기서 유일한 문제는 매개 변수를 클래스 생성자에 전달할 수 없지만 init메서드 를 수동으로 호출하여 피할 수 있다는 것입니다.


질문은하지 "어떻게 당신이 하나를 작성하려면 어떻게해야합니까" "필요한 싱글이다"입니다
mkoryak

@ danday74 summary다시 초기화하지 않고 다른 클래스에서 동일한 인스턴스 를 어떻게 사용할 수 있습니까?
M Faisal Hameed

1
Node.js에서는 다른 파일에서만 필요합니다 ... const summary = require ( './ SummaryModule') ... 그리고 동일한 인스턴스가됩니다. 멤버 변수를 생성하고이를 필요로하는 한 파일에서 해당 값을 설정 한 다음이를 필요로하는 다른 파일에서 해당 값을 가져 와서이를 테스트 할 수 있습니다. 설정된 값이어야합니다.
danday74

9

js에서 싱글 톤을 수행하기 위해 특별한 것이 필요하지 않습니다.이 기사의 코드는 다음과 같을 수도 있습니다.

var socketList = {};

module.exports = {
      add: function() {

      },

      ...
};

node.js 외부 (예 : 브라우저 js)에서는 래퍼 함수를 ​​수동으로 추가해야합니다 (node.js에서 자동으로 수행됨).

var singleton = function() {
    var socketList = {};
    return {
        add: function() {},
        ...
    };
}();

@Allen Luce가 지적했듯이 노드의 캐싱이 실패하면 싱글 톤 패턴도 실패합니다.
rakeen

6

싱글 톤은 JS에서 괜찮습니다. 그렇게 장황 할 필요는 없습니다.

예를 들어 서버 계층의 다양한 파일에서 동일한 ORM / DB 인스턴스를 사용하기 위해 싱글 톤이 필요한 경우 node에서 참조를 전역 변수에 채울 수 있습니다.

존재하지 않는 경우 전역 변수를 생성하는 모듈을 작성하고 그에 대한 참조를 반환합니다.

@ allen-luce는 여기에 복사 된 각주 코드 예제를 사용하여 제대로 작동했습니다.

singleton.getInstance = function(){
  if(global.singleton_instance === undefined)
    global.singleton_instance = new singleton();
  return global.singleton_instance;
};

module.exports = singleton.getInstance();

그러나 new키워드를 사용할 필요 가 없다는 점에 유의하는 것이 중요 합니다. 오래된 개체, 기능, iife 등이 작동합니다. 여기에서는 OOP 부두가 발생하지 않습니다.

참조를 반환하는 함수 내에서 일부 obj를 닫고 해당 함수를 전역으로 만들면 보너스 포인트가 있습니다. 전역 변수를 다시 할당해도 이미 생성 된 인스턴스가 방해를받지는 않습니다.


당신은 그 어떤 것도 필요하지 않습니다. 당신은 할 수 module.exports = new Foo()module.exports 다시 실행하지 않기 때문에 당신이 정말 멍청한 짓하지 않는 한,
mkoryak

구현 부작용에 절대 의존해서는 안됩니다. 단일 인스턴스가 필요한 경우 구현이 변경 될 경우를 대비하여이를 전역에 연결하십시오.
아담 톨리

위의 답변은 또한 'JS에서 싱글 톤을 사용해야합니까, 아니면 언어로 인해 불필요하게 만들까요?'라는 원래 질문에 대한 오해였으며 다른 많은 답변에서도 문제가되는 것 같습니다. 필자는 적절하고 명시적인 싱글 톤 구현을 대체하기 위해 require 구현을 사용하지 않는 것이 좋습니다.
Adam Tolley

1

간단하게.

foo.js

function foo() {

  bar: {
    doSomething: function(arg, callback) {
      return callback('Echo ' + arg);
    };
  }

  return bar;
};

module.exports = foo();

그럼 그냥

var foo = require(__dirname + 'foo');
foo.doSomething('Hello', function(result){ console.log(result); });

질문은하지 "어떻게 당신이 하나를 작성하려면 어떻게해야합니까" "필요한 싱글이다"입니다
mkoryak
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.