함수형도 싱글톤 되잖아요? 그럼에도 클래스를 택한 이유
“함수형도 싱글톤 만들 수 있는데요?”
맞습니다. 기술적으로 가능합니다.
하지만 저는 단순히 ‘싱글 인스턴스’를 만드는 것만으로는 부족했습니다.
본 글은 지난 “왜 클래스 기반 ZebraLabelService를 선택했는가” 글에 이은 후속 글 입니다.
함수형도 싱글톤이 될 수 있습니다
사실 함수형으로도 싱글톤은 만들 수 있습니다.
1 | // 함수형 싱글톤 패턴 (즉시 실행 함수) |
이렇게 구현하면 모듈 자체가 싱글 인스턴스처럼 동작하므로 기본적인 싱글톤 조건은 충족합니다.
하지만 ‘기술적으로 된다’와 ‘현실에 적합하다’는 다릅니다.
함수형 싱글톤도 구현은 되지만, 프린터와 같은 하드웨어 통합 환경에서는 아래와 같은 이유로 클래스 기반 설꼐가 더 적합했습니다.
클래스 기반이 더 적합했던 이유
1. 상태 은닉과 캡슐화의 명확성
- 함수형에서는 상태가 클로저 안에 숨어있고, 테스트나 디버깅 시 추적이 어렵습니다.
- 클래스에서는 상태가 명시적으로 필드로 존재하며, 추적과 주입이 쉽습니다.
1 | // 클래스 |
→ 코드를 보는 순간, 어떤 상태를 갖고 있는지 명확합니다.
함수 vs 클래스
2. 의존성 주입 시점 통제
1 | // 클래스에서는 메서드 단위 주입 가능 |
- 생성자나 메서드에서 외부 객체를 주입받을 수 있어 Mock 테스트가 수월
- 함수형은 클로저 내에서 의존성을 고정하거나 외부에서 따로 관리해야 함 → 유연성 떨어짐
3. 초기화 중복 방지(Promise 캐싱) 구조가 깔끔함
1 | // 클래스 예시 |
- 클래스에서는 비동기 초기화 중복 호출을 방지하는 로직을 인스턴스 내부에 안전하게 구현할 수 있음
- 함수형은 별도 상태 관리 또는 모듈 스코프 변수가 필요 → 명확한 책임 분리가 어려움
4. 명확한 수명 주기 관리
1 | // 클래스 기반 수명 주기 |
- 클래스에서는 수명 주기 메서드가 한 객체 안에서 명확하게 정의되어 있어,
- 사용하는 쪽에서는 사용 규약을 명확히 따를 수 있음
함수형 모듈은 내부에 상태를 숨기다 보면 초기화/해제 로직이 분산되기 쉽습니다.
5. 확장성과 인터페이스 구현의 유리함
- 클래스 기반 서비스는
IPrinterService
등의 인터페이스 구현이 자연스럽고 - 테스트 시 Mock 클래스로 교체하기도 용이합니다.
1 | class MockZebraLabelService implements IPrinterService { |
→ 구조적 유연성에서 함수형보다 객체지향 방식이 확장에 유리
함수형 싱글톤은 ‘가능’, 클래스 기반은 ‘현실적’
비교 항목 | 함수형 싱글톤 | 클래스 기반 싱글톤 |
---|---|---|
싱글 인스턴스 보장 | 가능 (모듈/클로저로) | 가능 (static 필드) |
상태 관리 | 클로저 기반, 추적 어려움 | 명시적 필드, 디버깅 용이 |
초기화/해제 관리 | 외부 분산될 수 있음 | 라이프사이클 메서드로 일관 |
의존성 주입 | 어려움 (클로저 바깥 주입 필요) | 용이 (생성자/메서드 주입) |
테스트/Mock 용이성 | 낮음 | 높음 |
설계 유연성/확장성 | 낮음 | 높음 |
결론
React 기반에서도 대부분 함수형으로 충분합니다.
하지만, 프린터처럼 하드웨어 리소스 통합과 상태 일관성이 중요한 환경에서는 단순히 ‘싱글 인스턴스를 만들 수 있다’는 수준의 함수형 접근은 한계가 있습니다.
클래스 기반 싱글톤은 기능뿐 아니라 책임, 수명 주기, 상태, 의존성까지 안전하게 감싸는 구조입니다.
이러한 이유로 우리는 함수형 싱글톤이 가능함에도 불구하고,
클래스 기반 싱글톤을 선택했습니다.
기억해두면 좋은 체크리스트
- 함수형이 싱글톤 구현이 가능한 건 맞다
- 하지만 클래스는 캡슐화, 라이프사이클, 테스트성까지 아우른다
- 하드웨어 통합에서는 초기화/상태/의존성 관리가 핵심이다
- 클래스 기반은 책임 분리가 명확하여 협업/유지보수에 유리하다
함수형도 싱글톤 되잖아요? 그럼에도 클래스를 택한 이유
https://devch.co.kr/categories/웹앱/React/react-why_use_class-2-250828/