본문 바로가기
Spring

[토비의 스프링] 스프링의 이해와 원리

by 슈슈슉민 2024. 3. 23.

스프링이란 어떻게 오브젝트가 설계되고, 만들어지고, 어떻게 관계를 맺고 사용되는지에 관심을 갖는 프레임워크 - 143p

 

프로그래밍 모델

스프링이 제공하는 편리함은 객체지향 설계 원칙과 디자인 패턴의 핵심 원리를 갖고 있는 IoC/DI에서 온다. 또한 AOP를 이용하여 독립적으로 모듈화 할 수 있다.

 

왜 스프링이여야만 할까

싱글톤의 장점을 빼내오고자

추상 클래스의 중복 멤버 통합과 인터페이스의 다중 상속기능을 동시에 이용할 수 있다. 이 을 같이 사용하는 여러가지 코드 패턴들이 나왔고, 이것이 디자인 패턴의 근간이 될 수 있다.

 

1. 싱글톤 패턴 

메모리 절약을 위해, 인스턴스가 필요할 때 똑같은 인스턴스를 새로 만들지 않고 기존의 인스턴스를 가져와 활용하는 기법을 말한다. 싱글콘 클래스는 고정된 메모리 영역을 가지고 하나의 인스턴스만 사용하기 때문에 메모리 낭비 방지를 할 수 있으며 DataBase Connection Pool처럼 공통된 객체를 여러개 생성해야 하는 상황에서 많이 사용된다.

 그러나 인터페이스가 아닌 클래스의 객체를 미리 생성하고 정적 메소드를 사용하기에 의존성이 높다. TDD 단위 테스트에 애로사항이 있다. 단위테스트 문자 그대로 단위별로 구분되어야 하는데 자원을 공유하고 있기에 테스트마다 매번 인스턴스의 상태를 초기화해야 한다. Mock(테스트용 가짜)객체를 생성할 때 주로 상속관계를 이용하기에 테스트가 어렵다.

결과적으로 유연성이 많이 떨어지는 패턴이라고 할 수 있다.

스프링에서는 클래스 제어를 IoC(Inversion Of Control) 방식의 컨테이너에게 넘겨 관리하며 싱글톤의 장점을 가져온다

 

제어의 역전(Inversion of Control)

프로그램의 제어 흐름 구조가 뒤바뀌는 것이다.

일반적인 프로그램은 오브젝트 -> 메소드 로 제어권이 이동한다. 모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정하고, 언제 어떻게 그 오브젝트를 만들지 결정한다.

 제어의 역전은 이 과정을 뒤집는다. 모든 제어 권한을 자신이 아닌 다른 대상에게 위임한다. 예로 서블릿을 들 수 있는데 서블릿을 개발해서 서버에 배포할 수는 있지만 그 실행을 개발자가 직접 제어할 수 있는 방법은 없다. 대신 서블릿에 대한 제어 권한을 가진 컨테이너가 적절한 시점에 서블릿 클래스의 오브젝트를 만들고 그 안의 메소드를 호출한다. 프레임 워크도 이와 같이 제어의 역전이 있다. 여기서 잠깐

 프레임워크와 라이브러리의 차이는 무엇일까. 

라이브러리를 사용하는 어플리케이션 코드는 어플리케이션 흐름을 직접 제어한다. 반면에 프레임워크는 거꾸로 어플리케이션 코드가 프레임워크에 의해 사용된다.

프레임워크 에 개발한 클래스를 등록하고, 프레임워크가 흐름을 주도하는 중에 개발자가 만든 어플리케이션 코드를 사용하도록 만드는 방식이다. 

 

스프링의 IoC

스프링에서는 스프링이 제어권을 가지고 직접 만들고 관계를 부여하는 오브젝트를 빈(콩)이라고 한다. 오브젝트 단위의 어플리케이션 컴포넌트이다. 동시에 스프링 빈은 스프링 컨테이너가 생성과 관계설정, 사용 등을 제어해주는 제어의 역적이 적용된 오브젝트를 가리킨다.

 빈의 생성과 관계설정 같은 것을 담당하는것은 빈 팩토리라고 부른다. 이를 좀 더 확정한 개념을 어플리케이션 컨텍스트라고 부른다. 

 어플리케이션컨텍스트는 @ Bean 이 붙은 메소드의 이름을 가져와 빈 목록을 만들어둔다. Cient가 어플리케이션 컨텍스트의 getBean()을 호출하면 자신의 빈목록에서 찾아 메소드를 호출해서 오브젝트를 생성시킨후 클라이언트에게 돌려준다.

 

 어플리케이션 컨텍스트 오브젝트가 만들어지는 방식, 시점과 전략을 커스텀 할 수 있고, 자동생성, 오브젝트에 대한 후처리, 정보의 조합, 설정 방식의 다변화, 인터셉팅 등 오브젝트를 효과적으로 활용할 수 있다.

 

서버 어플리케이션과 싱글톤

 스프링이 싱글톤 패턴을 이용하는 이유는 서버환경 때문이다. 스프링이 처음 설계됐던 대규모의 엔터프라이즈 서버환경은 서버 하나당 최대로 초당 수십에서 수백 번씩 브라우저나 여타 시스템으로부터의 요청을 받아 처리할 수 있는 높은 성능이 요구되는 환경이었다. 매번 클라이언트엥서 요청이 올 때 마다 각 로직을 담당하는 오브젝트를 새로 만들어서 사용하게 되면 서버가 과부화가 걸린다. 

 예로 초당 n개의 요청 등어오면 n개의 오브젝트가 생성된다. n을 5개라고 가정하면 60초에 15*10^4개 36000초에 9*10^6개의 오브젝트가 생성된다. 자바의 GarbageCollectioin 스레드 종료 작업(Stop The World)가 자주 시행됨에 따라 과도한 오버헤드가 걸려 프로그램의 성능이 저하될 수 있다. 

 서블릿은 자바 엔터프리이즈 기술의 가장 기본이 되는 서비스 오브젝트이다. 서블릭은 대부분 멀티스레드 환경에서 싱글통으로 동작한다. 서브릿 클래스당 하나의 오브젝트만 만들어두고, 사용자의 요청을 담당하는 여러 스레드에서 하나의 오브젝트를 동시에 공유한다. 

매번 새 오브젝트를 만들지 않는다.

how to?

1. private : 다른 클래스는 생성 못하게 할 것

2. static : 딱 한번만 만들어 지고 또 전역화 하여 다른 클래스에서 사용할 수 있게 할 것

3. getInstance() : 만들어진 오브젝트(싱글톤)을 재사용할 것

 

그러나 각 번호에 따른 단점이 생긴다.

1. 상속불가, 테스트 힘듦

2.  객체 지향적이지 않는다.

3. 서버 환경에서는 여러 개의 JVM에 분산돼서 설치가 되는 경우 각각 독립적으로 오브젝트가 생기기에 싱글톤 패턴을 따르지 못한다.

 

싱글톤 레지스트리

 이를 극복하기 위해 스프링이 직접 싱글톤 형태의 오브젝트를 만들고 관리한다. public을 사용할 수 있게 하기 때문에 싱글톤 패턴을 지키면서 자유로히 오브젝트를 만들 수 있다.  

  허나 멀티 스레드 환경이라면 주의 할 것 들이 생긴다. 다중 사용자의 요청응ㄹ 한번에 처리하는 스레드들이 동시에 싱글통 오브젝트의 인스턴스 변수를 수정하는 것은 매우 위험하다. 따라서 상태정보를 내부에 갖고 있지 않은 무상태(stateless)방식으로 만들어 져야 한다. 그럼 db나 생성된 정보를 어떻게 다뤄야 할까? 이때는 메소드 파라미터와 메소드 내의 로컬변수를 사용하면 된다. 

private 변수타입 변수명;

public 메소드(){
	this.변수명...
}

 

DI(Depengency Injection)

스프링 IoC 기능의 대표적인 동작원리는 의존관계 주입이다. DI는 오브젝트가 다른 오브젝트에게 자신의 레퍼런스를 전달하는 것이다. 의존 관계는 모델 설계에서도 존재하지만 런타임시에도 존재한다. 런타임시 의존관계를 결정하고 만들려면 제 3의 존재가 필요하다. 이 제 3의 존재가 런타임 의존관계 결정 권한을 갖고있다. 코드에는 런타임 클래스에 대한 의존관계가 나타나지않고, 인터페이스를 통해 낮은 결합도를 지녀 다양한 확장을 가능케 한다.

 

의존관계 검색과 주입

의존관계 주입 외에도 의존 관계 검색이 있다. 자신의 엄마를 찾는 것 마냥 의존할 대상을 찾는 것이다. 의존 관계 검색은 런타임 시 의존관계를 맺을 오브젝트를 결정하는 것과 오브젝트의 생성 작업은 외부 컨테이너에게 IoC로 맡기지만, 이를 가져올 때는 메소드나 생성자를 통한 주입 대신 스스로 컨테이너에게 요청하는 방법을 사용한다. 따라서 자신이 스프링의 빈일 필요가 없다. 

 

 

'Spring' 카테고리의 다른 글

Spring Security와 JWT  (0) 2024.06.10
Spring Boot 와 Oauth2 구축  (2) 2024.06.10
Spring Security Architecture 이해하기  (2) 2024.06.10
static 과 Bean  (0) 2024.02.15