싱글톤으로 관리하고자 하던 객체가 뜻하지 않은 시점에 초기화되어, 잘 못 초기화된 객체가 싱글톤으로 관리되는 상황을 마주한 적이 있다.
정확한 상황은 아래와 같다.
websocket을 활용한 채팅 서버를 express로 구현하는 간단한 프로젝트를 진행해본 적이 있다.
socket.io 라이브러리를 사용했고, express-generator가 제시하는 형식을 따랐으며, socket.io와 관련된 로직과 변수들은 하나의 클래스에 모아두자는 발상을 했고, 이를 위해 SocketManager
이라는 클래스를 생성했다.
import { Server } from "socket.io";
export default class SocketManager {
static #instance;
constructor(server) {
if (Socket.#instance) return Socket.#instance;
Socket.#instance = this;
this.io = new Server(server);
... // 추가적인 초기화 작업
}
}
위 코드 예시와 같이 socket.io는 Server
라는 클래스를 제공하며, 이는 생성자 단에서 http 모듈의 http.createServer()
의 결과물을 받는다. 내가 만든 SocketManager은 생성자단에서 http 서버 인스턴스를 받고, socket.io의 Server 생성자 이용해 서버 인스턴스에 ws관련된 작업을 해준다. 그럴싸해보인다(지금 보면 이렇게 안했을 것 같다)
SocketManager는 내 애플리케이션의 전체 과정을 통틀어 하나면 충분했고, 설령 초기화가 다시 되어도 안됐기에, 싱글톤으로 처리해주려는 작업도 해두었다.
express-generator를 사용해보면 http 서버 인스턴스를 띄우는 작업은 /bin/www
에서 진행되고, express 모듈의 express()
함수가 반환하는 app에 대한 설정들 (ex) app.use따위로 미들웨어를 등록해주고, 라우터를 등록하고 하는 작업들)은 /app.js
에서 진행되었고, 관심사가 잘 나누어져있다고 생각했다. 따라서 이를 따라 SocketManager
의 생성자는 /bin/www
에서 http 서버 인스턴스가 생성된 후 호출해주었다.
결과적으로 의존 관계가 이런 셈
graph TD
/bin/www --> SocketManager
/bin/www --> /app.js
그러나 문제가 발생했다. 내 기억에는 클라이언트에서 ws 프로토콜로 보내지는 모든 요청에 404 Error가 발생했고, 도통 영문을 알 수 없었다.
이유는 간단했다. /bin/www
는 /app.js
에 의존하는데, app.js를 타고 가다보면 SocketManager 생성자가 호출되는 시점이 존재한다. 내가 의도한 바와 같이 /bin/www
에서 먼저 안전하게 인스턴스가 생성되기 전에 이상한 곳에서 인스턴스가 먼저 생성된 것이다.
당시에는 socket.io에서 제공하는 Server 클래스의 생성자를 호출하는 작업을 전부 뺐다. 그 후 SocketManager에 init()
이라는 메서드를 추가하고, 그 안에 해당 로직들을 전부 집어넣었다. 아래와 같이 말이다.
import { Server } from "socket.io";
export default class SocketManager {
static #instance;
constructor() {
if (Socket.#instance) return Socket.#instance;
Socket.#instance = this;
}
init(server){
this.io = new Server(server);
... // 추가적인 초기화 작업
}
}
따라서 어디서 얼마나 SocketManager의 생성자를 호출하든, 인스턴스는 하나만 생성될 것이고, 초기화 작업은 내가 init()
함수를 명시적으로 호출해서 진행할 수 있게 됐다.
문제는 많다. 일단 그나마 로직이 복잡해서 그랬지, 내가 init()
함수를 호출해야할 시점을 알기 힘들다면..? 내가 모든 인스턴스들의 생성 시점을 하나 하나 파악해서 인스턴스를 초기화 시킬 수는 없다 ㅠㅠ
그래서 nestJS가 생명주기, 의존성 관리를 다 해준다는 것에 너무 공부를 해보고 싶었다. 일단 현재 js만 써서 프로젝트를 하는 시점에서 java를 공부하는 것은 너무 부담이 커서, nestJS를 쓰며 이러한 것 들을 경험해보고 싶었다.
당장에 IoC 컨테이너가 어떤 원리와 알고리즘으로 의존성들을 관리하고 의존성을 주입해주는지는 모르더라도, 이러한 프레임워크가 개발자에게 요구하는 정보들, 요구하는 형식 등을 익혀보고 싶었다. 어찌 됐든 다른 프레임워크도 IoC 컨테이너를 제공한다면 비슷한 것들을 요구할 것 같았기에
물론 이 외에 그냥 내가 낯설어서 쓰려고 하는 것도 있다. 새로운 도구를 쓰는 것은, 이를 씀으로써 인식과 지평이 넓어진다. 도구를 항~~~상 자세하고 깊게 쓸 필요는 없지만, 토이프로젝트 해보는 것은 좋은 선택이라고 생각한다.