출처 : http://wangmul.egloos.com/1566763

 

싱글턴패턴의 남용

어디서나 접근 가능하다는 장점은 프로그래밍에서 큰 잇점을 주게 된다.
가장 일반적으로 이용할 수 있는 것은 프로그램 전체적으로 영향을 주는 설정값들 ( 게임서버의 전체/현재 접속자 수, 현재 실행되어 있는 곳의IP등)을 위치에 관계없이 접근할 수 있다는 것은 그 누구도 부정할 수 없는 멋진 장점이다.

하지만 여기에서부터 문제는 발생한다.
어디서나 접근이 가능하기에 각종 매니저 클레스(주1)를 싱글턴으로 사용하게 된다. 매니저를 싱글턴으로 사용하는 방식 자체에 문제가 있다고 볼수는 없다. 코드를 파악하기도 편하고 새로운 기능을 만들어 넣을때도 쉽게 만들어 넣을 수 있다. 그러나매니저클래스를 싱글턴으로 사용하기 시작하면 로직은 흐트러질 수 밖에없다.

싱글턴이 흐트러뜨리는 로직

매니저클래스에서 로직을 사용하게 되었을 때 문제를 발생시키는 것은 싱글쓰레드일 때보다 멀티 쓰레드일때 문제가 더 크게 발생한다.

void A클레스::유저정보조회 ( 유저아이디 )
{
    if ( DB.유저정보조회 (유저아이디) == false )
    {
        세션관리자.GetInstance().세션삭제 (유저아이디)
    }
}

void B클레스::패킷처리 ( 패킷데이터 )
{
    파싱완료데이터 = 패킷파싱 (패킷데이터 )
    switch ( 파싱완료데이터.패킷타입가져오기() )
    {
    case 로그인 :
        ...
        break;
    case 로그아웃 :
        세션관리자.GetInstance().세션삭제 (유저아이디)
        break;
    }
}

멀티 쓰레드에서 이런식으로 많은 곳에서 세션삭제를 요청하게 된다면 세션삭제에 대한 흐름이 흐트러져 여러곳에서 세션 삭제를 동시에 요청할 수도있다. 그리고 더욱 문제가 되는 것은 세션 삭제에서 오류가 발생했을 때 세션 삭제요청이 어디서 왜 호출되었는지 알기 어려워진다. 그렇기때문에세션 삭제에 대해서 중복 삭제 요청에 대한 추가적인 처리가 들어가야 하고 삭제안에서 여러가지 조건을 검사해 줘야 한다.

싱글턴을 사용하지 않는다면

그렇다면 싱글턴을 사용하지 않는다면 어떻게 바뀔까?
일단, 세션관리자를 마음껏 호출할 수 없기 때문에 위의 두 메소드는 다른 방식으로 만들어져야 한다.

bool A클레스::유저정보조회 ( 유저아이디 )
{
    if ( DB.유저정보조회 (유저아이디) == false )
    {
        return false;
    }
}

파싱완료데이터 B클레스::패킷처리 ( 패킷데이터 )
{
    파싱완료데이터 = 패킷파싱 (패킷데이터 )
    return 파싱완료데이터
}

이렇게 바꾼 뒤에 메인쓰레드에서 다음과같이 확인을 하는 식으로 변경한다.

메인스레드()
{
    패킷 = 패킷수신()
    파싱완료데이터 = 패킷처리 ( 패킷 )
    switch ( 파싱완료데이터.패킷타입가져오기() )
    {
    case 로그인:
    {
        if ( A클레스.유저정보조회( 아이디 ) == true )
        {
            ...
        }
        else
        {
           세션관리자.GetInstance().세션삭제( 아이디 )
        }
    }
    break;
    case 로그아웃:
        세션관리자.GetInstance().세션삭제( 아이다 )
    break;
    }
}

위의 코드로 바뀌면서 얻을 수 있는 것은 예측가능한 코딩을 할 수 있다는 것이다.
세션관리자를 호출할 수 있는 곳은 메인쓰레드뿐이고 그렇기에 세션을 삭제하려면 모든 로직이 메인쓰레드로 들어와야 한다.
세션삭제가 이루어지는 곳이 명확해지기 때문에 오류가 발생할 가능성도 적어지고 로직이 하나의 흐름을 가질 수 있게 된다.

싱글턴은 전역 설정값을 저장할 때만

싱글턴 패턴은 데이터에만 사용되어야 하고 로직에 사용되어선 안된다는것은 내가 가지고 있는 프로그래밍 법칙중에 하나이다. 전역적으로 접근이필요하고 변화가 적은 데이터를 저장하고 접근할 때에만 싱글턴을 사용한다면 그것은 큰 힘이 되어 줄 수 있다.
하지만 어디서든 접근이 가능하다는 것을 로직에까지 이용하는 것은 goto문과 비슷한 문제를 만들어낸다.  갑자기 점프해버리기 때문에코드의 가독성을 해치고 예측 불가능한 호출을 만들어낸다.

솔직하게 고백하면 나 역시도 실무에서 싱글턴의 유혹에 벗어나지 못하곤 한다. 싱글턴의 마약같은 매력에서 벗어나지 못하고 정말 힘들고 어려울 때에싱글턴을 선택하곤 한다.

하지만 한가지만은 분명하게 기억하고 있다. 싱글턴은 프로그래머에게 양날의 검이다.
이 양날의 검은 편하기에 자꾸 쓰고 싶어지게 하지만 함부로 사용하게 되면 자신을 상하게 만든다.
싱글턴을 사용하고 싶다는 생각이 들 때 정말 써야만 하는 것인지 한번 더 고민하고 생각한 뒤에 사용하는 것 만으로도 앞으로 생겨나게 될 큰문제들을 피해갈 수 있는 훨씬 빠른 지름길을 찾는 것이라는 것. 이 사실만 잊지 않는다면 난 언젠가 싱글턴의 유혹에서 벗어날 수 있을 것이라고 생각한다.


-- 아래의 용어들은 제가 임의로 정의한 용어들입니다. --
(주1) 매니저 클래스라는 것은 접속되어 있는 유저들의 세션을 관리하거나 게임방들을 관리하는 것과 같이 공통의 성격을 가지고 있는 인스턴스를관리해주는 클레스로 어디서든 매니저 클래스를 불러와 조건을 넣어주면 조건에 맞는 클래스를 리턴해주는 클래스를 말한다. 일반적으로 아래와 같은모습을 가지고 있다.

세션 세션관리자.GetSession ( 유저아이디 )
{
    ...
    if ( 유저아이디=세션에 저장된 아이디 )
        return 세션
    return NULL
}

 

'Development > 패턴자료' 카테고리의 다른 글

[펌] Some Background on Design Patterns  (0) 2011.08.13
[펌] Creational Patterns  (0) 2011.08.13
[펌] Behavioral Patterns  (0) 2011.08.13
[펌] AbstractFactory  (0) 2011.08.13
[펌] 디자인패턴이란?  (0) 2011.08.13
안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스
,