Node.js 객체 사용하기

THXX·2023년 10월 18일
0

개발자를 했다면 JSON이라는 것은 누구나 다 들어봤을 것이라고 생각한다. JavaScript Object Notation, 즉 자바스크립트 객체 표기법이다. key-value 형식으로 된 객체를 표현한다.
시작은 {}로 시작하며, key를 추가하는 방법은 두 가지가 있다. key는 무조건 문자열 타입이다. Property (속성)라고도 한다.

const obj = {}; 로 시작하겠다.

  1. obj.aa = 3;
    위와 같이 key-value 쌍을 지정해줄 수 있다. 문제는 key가 숫자일 경우 사용할 수 없다. 즉, obj.3 = 5;과 같이 사용할 수 없다는 것을 의미한다.
    이는 아래와 같은 방법으로 해결될 수 있다.
  2. obj['aa'] = 3;
    이렇게 하면 aa라는 property가 obj 객체 안에 만들어지고 value가 값으로 들어간다. 이제 key 값이 숫자일 경우에도 가능하다.
    obj[3]=5;
    obj를 출력해보면 다음과 같이 출력된다.
    {'aa': 3, '3':5}

3은 정수로 입력되었지만 객체의 key값으로 들어가면서 문자열로 변환되었다. 모든 key값은 문자열이다.


나는 내부 데이터를 관리할 목적으로 Object를 활용하기 시작했다. 외부 데이터베이스를 쓰자니 네트워크 통신을 해야하고, 또 실시간으로 업데이트되고 삭제되는 데이터다보니 굳이 데이터베이스 체계를 사용하고 싶지 않았다. 프로그램 내 모든 내부 데이터를 Object로 관리하기로 결정했다.

Oject는 데이터베이스로 사용될 수 있다. 다만 빅데이터를 처리하기엔 큰 무리가 있을 것이다. 주 메모리에 적재되기 때문이다. 따라서 나의 경우와 같이 약 10MB~20MB 정도의 데이터만 사용할 것 같으면 Object를 사용하는데 큰 지장이 없을 것이다. 시간 복잡도의 경우 자세한 계산은 해보지 않았으나, 역시 이 부분에도 수지타산이 맞다. 20MB정도의 데이터라고 하면 8바이트 정수가 2,621,440개 밖에 안된다. O(n) 시간복잡도라고 해도 최신의 컴퓨터라면 빠르게 처리가 가능하다. 또 많은 수의 사용자를 예상하고 있지 않으므로, 진행하기로 했다.


데이터베이스에는 CRUD가 필요하다. 다음과 같이 치환된다.

  • CREATE : const db = {data:{}};
  • READ : db.data[key]
  • UPDATE : db.data[key] = updateval;
  • DELETE : delete db.data[key];

일반적인 Object 사용이다. 다만 CREATE에서 Object 최상단에 data를 넣고 그 다음 그 안에서 활동하는 모습을 볼 수가 있다. 문제가 생겼기 때문이다.


현재 나는 Node.js 기반 express 프레임워크로 백엔드 서버 스크립트를 작성 중에 있다. 이 때 많은 사람들이 파일을 나눠서 기능들을 모듈화한다. 나도 마찬가지였다.

모듈화 시 사용되는 문은 다음과 같다.

  • module.exports = somethingVar (or {somethingVar})
    somethingVar라는 변수를 모듈 밖으로 export하겠다는 뜻이다. 다른 js 파일에서 해당 변수를 사용할 수 있게 된다. 오른쪽 예와 같이 Object를 export해도 좋다.
  • require('asdf.js')
    asdf.js에서 module.exports로 export된 변수를 require로 불러올 수 있다. (import)

변수 하나만을 export한 경우, 보통 아래와 같이 import 한다.

const somethingVar = require('asdf.js');

Object를 export한 경우, 구조 분해 할당 문법을 이용하여 각각을 따로 불러올 수 있다.

example:
module.exports = {abc,def,ghi}; // in asdf.js
const {abc,def} = require('asdf.js'); // in 'req.js'

이렇게 export된 변수나 객체는 require로 import되었을 때, reference로 참조가 된다. 즉, 위 예시에서 req.js 에서 abc 변수를 변경한다고 하자. 그러면 asdf.js에서 export했던 변수의 값이 바뀌는 것이다.

문제는 객체를 export했을 경우이다. 아래는 그 예시이다.

asdf.js

var obj = {
  'a':3,
  'b':5,
  'c':6
};

function displayObj(){
	console.log(obj); 
}

function mergeObj(tobj){
  obj = {...obj,...tobj};
}

module.exports = {obj,mergeObj,displayObj};

req.js

var {obj,displayObj} = require('asdf.js');

function clearObj(){
	obj = {};
}

clearObj();
displayObj();
mergeObj({'d':9});
displayObj();
console.log(obj);

node로 req.js를 실행시켜 본 결과는 아래와 같다...

require()는 import할 때 reference를 가져온다고 했다. 이 점을 이용해서 해석을 해보자.

  1. req.js에서 선언된 clearObj를 이용해서 obj 변수에 {}를 할당했다. {}는 새로운 객체이므로, require로 가져왔던 reference가 더 이상 사용할 수가 없게 되었다.
  2. asdf.js에서 선언된 displayObj()를 require로 import하여 호출했다. 이 경우 displayObj()는 asdf.js의 메모리 공간을 참조하므로 asdf.js에 존재하는 obj 객체를 출력한다.
  3. asdf.js에서 선언된 mergeObj()를 require로 import하여 호출했다. 이 경우에도 asdf.js의 메모리 공간을 참조하고, asdf.js 내에서 obj 객체가 새로운 객체로 덮어씌어진다.
  4. displayObj()는 아직도 asdf.js의 메모리 공간에 있는 obj 객체를 참조하므로 덮어씌어진 새로운 객체를 출력하게 된다.
  5. req.js에서 console.log(obj)를 이용하여 req.js 내의 obj 객체를 출력한다. 이는 clearObj()에 의해 {}로 덮어씌어졌으므로 빈 객체인 {}를 출력하게 된다.

파일마다 각자의 메모리 공간이 있는 듯 보인다. 따라서 consistency, 일관성을 유지하기 위해서 다음과 같이 진행했다.

아예 obj 객체를 건들지 말고, obj 객체 내부에 있는 property를 건드는 것이다. 이렇게 하면 모든 모듈이 똑같은 객체를 참조하게 된다. 우리는 각 js 파일에 여러 개의 객체를 만들면 안된다. Singleton하게 객체를 다뤄야 한다. 데이터베이스로 이용할 것이기 때문이다.

{
  'data':{}
}

위와 같이 객체를 선언한다면, data property만 바꿔주면 된다. 값을 초기화하고 싶은가? obj.data={}; 로 초기화하면 된다. 이는 허용된다. obj={};는 절대 허용되지 않는다.


Node에 대한 철저한 공부가 부족했기 때문에 이런 일이 발생한 것 같다. 그냥 완전히 전역변수처럼 사용될 줄 알았는 데, 그렇지 않았다. 사실 이제 와서 보니 당연한 것 같기도 하다...

역시 철저한 준비태세가 중요하다.

~完~

profile
THXX FOR EVERYTHING

0개의 댓글