혹시 싱글톤이세요? 저는 벙글톤이에요 ㅋㅋㅋ

haero_kim·2021년 9월 7일
60

디자인 패턴

목록 보기
2/2

디자인 패턴 시리즈의 2번째 포스팅이다! (이전 포스팅 - 옵저버 패턴 개념 보러가기)

이번 포스팅에선 사람들에게 가장 많이 알려진 디자인 패턴 중 하나인 싱글톤 패턴의 개념에 대하여 쉽게 알아보고자 한다. 다른 패턴들과 마찬가지로, 어렵게 생각할 것 없으니 천천히 살펴보도록 하자.

1. 싱글톤 패턴이 뭐야? 🤔

싱글톤이 뭔지는 몰라도, 우선 '싱글'하면 떠오르는 갖가지 개념들이 있다. 보통 '혼자', '홀로' 라는 의미로 사용되곤 한다.

그치만 실망스럽게도 '벙글톤' 패턴은 없다. (ㅈㅅ)

필자는 개인적으로 디자인 패턴들의 이름들은 정말 직관적으로 잘 지었다는 생각이 든다.
한 번 'Singleton' 의 사전적 정의를 살펴보자.

'단독 개체' 라는 사전적 정의로, 싱글톤 패턴의 개념을 대략 유추해볼 수 있다.

쉽게 말해 프로그램 시작과 종료까지 클래스의 인스턴스를 단 한 번만 생성하여 사용하는 패턴을 의미한다. 그리고 프로그램 전역에서, 이 인스턴스를 공유하며 사용할 수 있게끔 하는 것이다.

그런데 이걸 왜 사용하는지 감이 잘 안 잡힐 것이다.


2. 왜 사용하는걸까?!

프로그램 전역에서 자주 사용되고 일관된, 정해진 동작을 하는 녀석이라면, 싱글톤 패턴으로 구현하는 게 훨씬 효율적일 수 밖에 없다. 어차피 같은 동작을 하는 건데, 계속하여 메모리에 할당하고 해제하며 인스턴스를 사용하기보다, 어디선가 이미 생성해둔 인스턴스를 사용하는 것이 효율적이지 않겠는가?

싱글톤 패턴으로 생성된 인스턴스는 고정된 메모리를 사용하기 때문에 필요 없는 메모리 낭비를 방지할 수 있고, 프로그램 전역에서 사용할 수 있는 인스턴스이다 보니 클래스간 데이터 공유도 쉬운 편이다.


3. 아하 그렇구나! 근데 어따 써요? 🙄

말만 들어보면 뭔가 효율적인 것 같긴 한데, 어떻게 사용하는 지 감이 안 잡힐 것이다. 예시를 들어보겠다.

예를 들어 안드로이드 앱이라고 쳤을 때, 서버와 네트워킹을 하여 데이터를 주고 받기 위해서는, HTTP 통신을 수행하는 객체를 생성하여 사용해야 한다. 편의상 HTTP 통신에 가장 많이 쓰이는 라이브러리인 Retrofit 를 예로 들겠다. 간단히 말해, Retrofit 객체는 쉽게 HTTP 메소드를 호출할 수 있도록 도와주는 녀석이다. 따라서 서버의 API 호출을 할 때, Retrofit 객체를 생성하여 이를 수행하면 매우 편리하다.

3 페이지로 구성된 안드로이드 앱이라고 가정해보자. 각 페이지에서는, 각기 다른 HTTP 통신을 수행하여 서버에서 데이터가져오거나, 보내는 동작을 수행한다. 그럼 각 페이지에서 Retrofit 객체를 생성하여 HTTP 통신을 하면 될 것이다. 그럼, 아래 그림처럼 동작할 것이다. 오른쪽은 메모리를 표현한 것이다.

뭔가 문제가 있어보인다. 안 될건 없지만, 중복된 동작을 하는 객체가 3번 생성되다 보니 메모리가 낭비되는 모습을 볼 수 있다. 굳이 이럴 필요가 있을까? 이럴 때 필요한 것이 바로 싱글톤 패턴이다.

아래처럼, 어디선가 최초로 한 번 생성해둔 Retrofit 객체를 사용하면, 어떤 페이지에서든 이를 활용하여 네트워킹 호출을 할 수 있을 것이다. 메모리 면에서 훨씬 효율을 취하고 있는 모습이다.

그럼 한 번, 이를 어떻게 구현하는지 살펴보자.


4. 자바에서의 싱글톤 패턴 구현하기

자바에서 싱글톤을 구현하는 방법은 여러가지이지만, 그 중 한 가지를 살펴보자. 우리가 지금까지 익힌 원리와 개념을 기반으로 쉽게 구현할 수 있다.

생성자를 private 처리하여 외부에서 객체를 생성할 수 없게 하고, 미리 생성해둔 객체를 외부 어디서든 반환받아 사용할 수 있도록 getInstance() 이라는 static 메소드를 만들어 준다.

public class SingletonObj {
    private static SingletonObj singletonObj = null;
    private SingletonObj() { }
    
    public static SingletonObj getInstance() {
        if (singletonObj == null) {
            singletonObj = new SingletonObj();
        }
        
        return singletonObj;
    }
}

이 때 생성자를 private 처리했으므로 객체를 직접 생성할 수 없기 때문에, 객체를 static 으로 정의하여 getInstance() 메소드를 사용할 수 있도록 해준다.

getInstance() 가 호출되면 singletonObj 에 새로운 객체를 할당해주게 되는데, 만약 이미 객체가 할당되어있다면 그것을 그대로 반환하게 된다. 이로써 싱글톤 패턴을 따를 수 있다.

🤚🏻 하지만 위험할 수 있다!

만약 멀티 쓰레딩 환경이라면, 여러 곳에서 동시에 getInstance() 를 호출하게 된다면, 본의 아니게 객체가 두 번 이상 생성될 수도 있기 마련이다. 따라서 synchronized 키워드를 붙여주도록 하자.

public class SingletonObj {
    private static SingletonObj singletonObj = null;
    private SingletonObj() { }

    public static synchronized SingletonObj getInstance() {
        if (singletonObj == null) {
            singletonObj = new SingletonObj();
        }

        return singletonObj;
    }
}

이렇게 위험을 방지했지만, 어차피 결국 여러 쓰레드에서 getInstance() 를 호출하게 되면 높은 비용이 발생하기 때문에 전반적으로 퍼포먼스 저하가 발생하게 된다.

뭐, 이러한 단점을 극복한 다양한 싱글톤 구현 방법들이 있다. 원리는 모두 동일하기 때문에, 싱글톤 패턴의 개념을 이해했다면 더 다양한 방법들을 따로 공부해보면 좋을 것이다. 어떤 방법이든 쉽게 적용해볼 수 있다!

그런데 어떤 방법이든간에 자바에서는 싱글톤 패턴을 구현하기 위해서는 따져야 할 조건들이 많아 불편하긴 하다. 이 틈을 타 또 코틀린의 위대함을 느끼고 가자 (이게 이렇게 된다고???)


5. 코틀린에서 싱글톤 패턴 구현하기

object SingletonObj {

}

이게 싱글톤 구현 끝이다!

코틀린에선 static 개념이 사라지면서 object 라는 개념이 등장한다. object 로 클래스를 정의하게 되면, 클래스를 정의하는 동시에 인스턴스를 생성한다. 이 때, 무조건 단일 인스턴스 생성을 보장한다.

이 클래스 안에 필요한 객체 (e.g. Retrofit 등) , 메소드 등을 정의해두면 프로그램 전역에서 이를 활용할 수 있다.

심지어는 다양한 제약 조건 (멀티 쓰레딩 환경 등) 을 고려해줄 필요 없이, Thread-safe 하고 Lazy 한 초기화를 언어 차원에서 지원해준다. 따라서 개발자는 이러한 것들을 신경쓰지 않아도, 알아서 안전하고 효율적이게 싱글톤이 구현된다.


6. 싱글톤 사용시 주의할 점

  • 자바에서의 싱글톤 설명 시 언급했듯이, 멀티 쓰레딩 환경에서 동기화 처리를 꼭 해줘야 한다.
    (물론 코틀린을 쓰면 이를 걱정할 필요는 없다 ㅎㅎ)

  • 싱글톤 객체가 하는 일이 방대하거나, 많은 데이터를 공유하게 된다면 본의 아니게 이를 사용하는 클래스들과의 결합도가 높아지기 때문에, 개방 폐쇄 원칙 (OCP) 을 위반하게 된다. (객체지향 설계원칙에 대해서도 한 번 포스팅을 다뤄보겠다)
    따라서 상황에 맞게 적절히 활용하도록 하자.


마치며

오늘은 싱글톤 패턴의 개념과, 이것의 장점 및 활용 방안 등에 대하여 알아보았다. 객체지향 설계원칙에 의거하여 이를 적절히 활용한다면, 특정한 상황에서 메모리를 효율적으로 활용할 수 있기 때문에 꼭 알아두는 것이 좋다. 또한 불필요한 코드 (생성자 등) 도 줄일 수 있어 편리하다.

profile
💻 안드로이드 앱 개발을 하는 대학생입니다

20개의 댓글

comment-user-thumbnail
2021년 9월 7일

이해가 잘 가는 글이군요^^

1개의 답글
comment-user-thumbnail
2021년 9월 7일

싱글 벙글에서 괜히 피식했네요ㅋㅋ 덕분에 이해가 잘갑니다😄

1개의 답글
comment-user-thumbnail
2021년 9월 8일

빵터지고 갑니다 ㅋㅋㅋㅋㅋㅋㅋㅋ

1개의 답글
comment-user-thumbnail
2021년 9월 8일

필력이 후덜덜 하시네요;; 문자 그대로 빨려 들어갔습니다.

왜? Why? 라는 말을 참 좋아하는데, 질답(Q&A)으로 이뤄진 서술 방식이 정말 마음에 드네요. 이런 저수준(Low-Level) 개그에 웃어서 솔직히 자존심 상하긴 해도... 인정할 수 밖에 없는, 유익한 글이네요.

바쁘신 와중에 시간내어 좋은 글 써 주셔서 감사합니다. 해로(H43RO) 선생님 덕분에 많은 걸 배우고 갑니다.

문연수 올림.

1개의 답글
comment-user-thumbnail
2021년 9월 9일

싱글벙글 꺄핰ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 넘 재밌어요~~~ 제 블로그에도 짤 퍼가도 될까요옹? ㅎㅎㅎ

1개의 답글
comment-user-thumbnail
2021년 9월 10일

싱글벙글ㅋㅋㅋ
글 재밌게 잘읽었어요!

그런데 궁금한게 있습니다
java-static 클래스에 Retrofit 인스턴스를 static 하게 생성하는 것과
kotlin-object 클래스에 Retrofit 인스턴스를 생성하는게 어떤 차이가 있을까요?

1개의 답글
comment-user-thumbnail
2021년 9월 10일

ㅋㅋㅋ벙글에서 피식하고 갑니다! 필력 되게 좋으신 거 같아요 ㅎㅎ

1개의 답글
comment-user-thumbnail
2021년 9월 11일

글이 술술 읽히네요

1개의 답글
comment-user-thumbnail
2021년 9월 14일

글 진짜 재밌게 잘쓰시네요 ㅎㅎㅎ

1개의 답글
comment-user-thumbnail
2021년 9월 16일

They tell me I'm inspirational and beautiful. It has been a pleasure working with Thrive. I appreciate your dedication to the projects that you and your team are on. It is nice from the customers stand. microneedling vaughan microneedling vaughan

답글 달기