본문 바로가기
JAVA

싱글톤

by 다미르 2019. 4. 15.

요즘 전 포스팅에서 언급한 sns 로그인을 계속 진행하고 있다.(아직도...)

우리 사이트는 회원가입이 발생하는 곳이 총 4군데기에(프로모터, 일반회원 etc..)

4곳의 비지니스 로직에서 활용할 수 있는 공통 유효성 검증 라이브러리가 필요했다.

막상 다 만들고나니.. 이것을 클라이언트에서 어떻게 사용하게 만들까 무척 고심했는데

그 고민을 하며 얻은 것들을 간략히 정리해보려고 한다.


유효성 검증 라이브러리는 자주 사용하면서 추가적인 확장이 필요없는 utility성 메서드들이다.

개떡같은 확장, 구현 이런것과 상관없이 단발성으로 검사해서 true/false를 반환하면 그만이기 때문이다.

다 좋은데

 

싹다 정적 메서드로 만들자니 호출되는 빈도가 비지니스 로직 전반에 걸친 것은 아니고...

생성자를 제공하자니.. 유효성 검증 라이브러리는 결국 정규표현식이다.

자바의 정규표현식 객체 Pattern은 자원 소모가 심한 편이고,

클라이언트 코드에서 이런 자원 소모가 심한 객체를 사용하는 라이브러리가

실수로라도 과다 생성되는 것이 우려되었다.

 

물론 JVM의 gc의 성능이 많이 좋아져서, 왠만하면 괜찮다는 글이 많았지만

그래도 뭐... 혹시아나


이럴 때 사용하는 것이 어플리케이션에서 유일한 객체를 보장하는 싱글톤이다.

사실 백엔드로 스프링을 사용하긴 하는데,

내가 개발한 부분은 스프링에 의존적이지 않은 자바 라이브러리가 목적이었기에

@component와 같은 것은 사용할 수가 없었고

간만에 직접 싱글톤을 구현해보았다.

 

싱글톤을 적용하는 방법은 꽤 많지만 개념은 똑같다.

1. 생성자를 private로 만들어 new를 통한 인스턴스를 막는다.

2. 최초 한 번 호출시 생성되는 static 참조변수를 선언하고

3. 정적 메서드(보통 getInstance()가 보편적이다)를 통해서 static 참조변수를 반환한다.

 

위의 개념을 생각하며 정말 심플하게 짜보면(아마 싱글톤을 처음 보는 사람들이 많이 보는 예제일듯)

Validation.getInstance()가 호출되는 시점에 최초로 생성되며(lazy instance),

그 이후로는 생성될 수 없는 어플리케이션 전역에서 유일무이한 객체다.

가장 심플하면서, 싱글톤의 개념이 무엇인가 잘 보여주는 예제라고 생각한다.

 

하지만 조금더 나아가서 생각해보면.. 아직 문제가 남아있다.

바로 멀티 스레드

이 염병할 스레드 때문에 데인 적이 한두번이 아닌데 여기서도 마찬가지..

내가 작성한 의도는 유일무이한 객체를 생성하는 것이지만,

멀티 스레드가 개입되면 유일무이 하지 않을 수도 있다.

그런게 바로 스레드의 매직 아니겠는가.


자바에서는 이런 멀티 스레드 환경에서 안정성을 보장하기 위해

synchronized를 제공한다.

근데 이게 사실 더 문제다.

동기화가 굉장히 자원소모가 심하기에 getInstance()가 지속적으로 호출되는 상황이 오면

저 synchronized 키워드 하나가 어플리케이션 전반적으로 속도 문제를 일으킨다.

뭐 사실.. 내가 개발 중인 사이트는 회원가입은 무슨 개뿔

개미새끼 한 마리 안보여서 아무 상관없긴 하지만

일반적인 상황에서는 그렇다.

속도차이를 직접 보여주는 테스트 코드를 짜려다 귀찮아서 패쓰


어쨋든 멀티 스레드에서 유일성도 문제니 동기화는 해야겠고 막 쓰자니 속도가 걱정되고

이를 보완한 코드는 간단하다.

getInstance()에서 체크를 하는 것이다.

이미 인스턴스가 존재하면 참조변수를 그대로 반환하고,

없으면 동기화가 필요한 시점에 인스턴스를 확인해서 생성하면 될 것 아닌가.

의도는 명확하다 생각하기에 설명은 생략

속도 문제도 어느 정도 해결하고, 유일성을 보장하는 싱글톤 방식이다.

하지만 더 나은 방법이 있다.


사실 나도 이건 처음본다.

보면서 오 신기해.. 다들 너무 똑똑해 하면서 봤는데..

 

JVM의 클래스 로딩 방식과 static을 활용한 방식이다.

밑의 코드를 보면 이해가 될 것

1. 이너 클래스 Holder는 getInstance()가 호출되기 전에는 참조되지 않는다.

2. 최초getInstance()호출될 때 클래스 로더에 의해서 Validation 객체가 생성된다(lazy instance)

3. INSTANCE 변수는 static이기에 클래스 로더에 의해서 단 한 번만 생성된다. 즉 싱글톤이다.

4. 클래스 로딩은 한 번만 일어나기에 멀티 스레드에서 안전하다.

5. final 이기에 값이 재할당 되지 않는다.

 

요즘 가장 많이 사용하는 방식이다 카더라.

신박하다.

 

 

 

 

 

'JAVA' 카테고리의 다른 글

컴포지션(Composition)  (0) 2019.04.28
빌더 패턴!  (0) 2019.04.08
정적 팩토리 메서드의 장단점  (0) 2019.04.07