티스토리 뷰

스코프의 종류

스프링은 싱글톤, 프로토타입 외에 다음과 같은 네 가지 스코프를 기본적으로 제공한다.

  1. 요청(request)
  2. 세션(session)
  3. 글로벌세션(globalSession)
  4. 애플리케이션(application)

이 스코프는 모두 웹 환경에서만 의미 있다.

네 가지 스코프 중에서 application을 제외한 나머지 세 가지 스코프는 싱글톤과 다르게 독립적인 상태를 저장해두고 사용하는 데 필요하다.

서버에서 만들어지는 빈 오브젝트에 상태를 저장해둘 수 있는 이유는 사용자마다 빈이 만들어지는 덕분이다.

요청 스코프

요청 스코프 빈은 하나의 웹 요청 안에서 만들어지고 해당 요청이 끝날 때 제거된다.

각 요청별로 독립적인 빈이 만들어지기 때문에 빈 오브젝트 내에 상태 값을 저장해둬도 안전하다.

요청 스코프 빈은 DL을 사용하는 것이 편리하지만 원한다면 DI를 이용할 수도 있다.

하나의 웹 요청을 처리하는 동안에 참조하는 요청 스코프 빈은 항상 동일한 오브젝트임이 보장된다.

동시에 웹 요청이 달라지면 별도의 요청 스코프 빈이 만들어지기 때문에 여러 사용자가 많은 요청을 보내도 안전하다.

존재 범위와 특징은 도메인 오브젝트나 DTO와 비슷하다.

그렇기 때문에 애플리케이션 코드에서 요청 스코프의 빈을 사용할 이유가 많지는 않다.

하나의 요청 안에서 유지되는 정보는 메서드 호출이 일어나는 동안 파라미터와 리턴 값으로 오브젝트를 전달해주면 된다.

요청 스코프 빈의 주요 용도

  • 애플리케이션 코드에서 생성한 정보를 프레임워크 레벨의 서비스나 인터셉터 등에 전달할 때
  • 애플리케이션 코드가 호출되기 전에 프레임워크나 인터셉터 등에서 생성한 정보를 애플리케이션 코드에서 이용할 때

예시로, 보안 프레임워크에서 현재 요청에 관련된 권한 정보를 요청 스코프 빈에 저장해뒀다가 필요한 빈에서 참조할 수 있다.

하지만 과용하면 전역 변수를 사용하는 것처럼 코드를 이해하기가 힘들어지므로 주의해야 한다.

세션 스코프, 글로벌세션 스코프

HTTP 세션과 같은 존재 범위를 갖는 빈으로 만들어주는 스코프다.

HTTP 세션은 사용자별로 만들어지고 브라우저를 닫거나 세션 타임이 종료될 때까지 유지되기 때문에 로그인 정보나 사용자별 선택옵션 등을 저장해두기에 유용하다.

하지만 HTTP 세션을 직접 이용하는 건 매우 번거롭고, 웹 환경 정보에 접근할 수 있는 계층에서만 가능한 작업이다.

서비스 계층이나 데이터 엑세스 계층에서 HTTP 세션에 접근하려 한다면 문제가 된다.

결국 HTTP 세션에 저장되는 정보를 웹 기술에 종속적이지 않은 오브젝트에 담아서 HTTP 세션에 직접 접근 가능한 프레젠테이션 계층과 서비스 계층 사이에서 주고받아야 한다.

세션 스코프를 이용하면 이러한 번거로움 없이도 HTTP 세션에 저장되는 정보를 모든 계층에서 안전하게 이용할 수 있다.

웹 페이지가 바뀌고 여러 요청을 거치는 동안에도 세션 스코프 빈은 계속 유지된다.

HTTP 세션은 사용자별로 만들어지기 때문에 중복의 위험도 없다.

글로벌세션 스코프는 포틀릿에만 존재하는 글로벌 세션에 저장되는 빈이다.

애플리케이션 스코프

서블릿 컨텍스트에 저장되는 빈 오브젝트이다.

서블릿 컨텍스트는 웹 애플리케이션마다 만들어진다.

웹 애플리케이션마다 스프링의 애플리케이션 컨텍스트도 만들어진다.

따라서 애플리케이션 스코프는 컨텍스트가 존재하는 동안 유지되는 싱글톤 스코프와 비슷한 존재 범위를 갖는다.

🤔
싱글톤 스코프를 사용하면 되지 않나?

 

드물지만 웹 애플리케이션과 애플리케이션 컨텍스트의 존재 범위가 다른 경우가 있기 때문이다.

웹 애플리케이션 밖에서 더 오랫동안 존재하는 컨텍스트도 있고 더 짧게 존재하기도 하는 서블릿 레벨의 컨텍스트도 있기 때문이다.

애플리케이션 스코프는 싱글톤 스코프와 마찬가지로 상태를 갖지 않거나, 상태가 있다고 하더라도 읽기전용으로 만들거나, 멀티스레드 환경에서 안전하도록 만들어야 한다.

스코프 빈의 사용 방법

프로토타입 빈과 다르게 스프링이 생성부터 초기화, DI, DL 제거까지의 전 과정을 다 관리한다.

프로토타입과 다르게 컨테이너가 정확하게 언제 새로운 빈이 만들어지고 사용될지 파악할 수 있기 때문이다.

스코프 빈은 프로토타입 빈과 마찬가지로 Provider나 ObjectFactory 같은 DL 방식으로 사용해야 한다.

DI 하면 컨텍스트 초기화 중에 에러가 발생할 수 있다.

하지만 스코프 빈은 스프링이 제공하는 특별한 DI 방법을 이용하면 DI처럼 사용할 수 있다.

바로 스코프 빈에 대한 프록시를 DI 해주는 것이다.

그러면 평범한 싱글톤 빈을 이용하듯이 스코프 빈을 쓸 수 있다.

다음은 로그인한 사용자의 정보를 HTTP 세션 안에 유지하도록 하는 예제 코드다.

@Scope("session")
public class LoginUser {
    String loginId;
    String name;
    LocalDateTime loginTime;
}

다음 코드는 로그인을 처리하는 서비스 계층의 빈이다.

public class LoginService {
    // DL 방식
    @Autowired
    Provider<LoginUser> loginUserProvider;

    public void login(Login login) {
        // 로그인 처리
        LoginUser loginUser = loginUserProvider.get();
        loginUser.setLoginId();
        loginUser.setName();
        loginUser.setLoginTime();
    }
}

Provider를 통해 가져오는 LoginUser 오브젝트는 사용자 세션별로 다르게 만들어지기 때문에 정보를 수정해도 안전하다.

로그인 후 세션이 유지되는 동안에는 같은 오브젝트에 접근할 수 있으므로 어느 페이지에서든 로그인 정보를 참조할 수 있다.

스코프 빈을 DI 방식으로 사용하려면 프록시의 도움이 필요하다.

@Scope 어노테이션으로 스코프를 지정했다면 proxyMode 앨리먼트를 이용해서 프록시를 이용한 DI가 되도록 지정할 수 있다.

스코프 프록시는 각 요청에 연결된 HTTP 세션 정보를 참고해서 사용자마다 다른 LoginUser 오브젝트를 사용하게 해준다.

물론 스코프 프록시는 실제 스코프 오브젝트인 LoginUser를 상속하고 있어서 클라이언트인 LoginService에서는 평범한 LoginUser 타입의 오브젝트로 사용할 수 있다.

스코프 프록시는 프록시 패턴을 활용한 것이다.

 

프록시(Proxy) 패턴

‘대리인'이란 의미로, 일을 해야 할 본인을 대신(대리)하는 사람을 뜻한다. 본인이 아니라도 가능한 일을 맡기기 위해서 대리인을 세운다. 대리인은 어디까지나 대리에 지나지 않기 때문에 할

kkoon9.tistory.com

프록시 빈이 인터페이스를 구현하고 있고, 클라이언트에서 인터페이스로 DI 받는다면 proxyMode를 ScopedProxyMode.INTERFACES로 지정해준다.

프록시 빈 클래스를 직접 DI 한다면 proxyMode를 ScopedProxyMode.TARGET_CLASS로 지정해준다.

이번 예제에서는 LoginUser 클래스로 직접 DI 하므로 다음 코드와 같이 구성하면 된다.

@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class LoginUser {
}

이렇게 되면 LoginUser를 다음과 같이 사용할 수 있다.

public class LoginService {
    // DL 방식
    @Autowired
    LoginUser loginUser;

    public void login(Login login) {
        // 로그인 처리
        this.loginUser.setLoginId();
        this.loginUser.setName();
        this.loginUser.setLoginTime();
    }
}

프록시 방식의 DI를 적용하면 스코프 빈이지만 마치 싱글톤 빈을 사용하듯이 편하게 쓸 수 있다는 장점이 있다.

반면에 주입되는 빈의 스코프를 모르면 코드를 이해하기가 어려울수 있다는 단점이 있다.

싱글톤 빈이면서 인스턴스 변수로 저장된 오브젝트를 수정하는 코드는 서버에서 사용하는 데 문제가 있어 보이기 때문이다.

XML로 스코프 빈을 등록하고 DI에서 사용할 것이라면 다음 코드와 같이 <aop:scoped-proxy>를 넣어주면 된다.

<bean id="loginUser" class="..LoginUser" scope="session">
    <aop:scoped-proxy proxy-target-class="true"/>
</bean>

커스텀 스코프와 상태를 저장하는 빈 사용하기

스프링이 기본적으로 제공하는 스코프 외에도 임의의 스코프를 만들어 사용할 수 있다.

싱글톤 외의 스코프를 사용한다는 건 기본적으로 빈에 상태를 저장해두고 사용한다는 의미다.

사용자별로 일정한 작업 단위 동안 유지되는 정보를 저장해두는 용도로 사용하면 편리할 수 있다.

예로, 여러 페이지에 걸쳐서 완성하는 위저드 스타일의 폼 혹은 일정한 데이터 목록을 서버에 유지한 채로 편집하는 기능을 들 수 있다.

스프링에서는 Scope 인터페이스를 구현해서 새로운 스코프를 작성할 수 있다.

하지만 매우 복잡한 작업이다.

유연하게 정의할 수 있는 스코프를 이용하여 상태를 저장해두는 방식의 빈을 본격적으로 사용하려면 스프링 웹 플로우나 제이보스 Seam과 같은 프레임워크를 이용하면 된다.

두 프레임워크 모두 스프링의 Scope 인터페이스를 구현한 다양한 커스텀 스코프를 제공해준다.

빈에 상태를 저장해두는 방식을 선호하지 않는다면 상태를 갖지 않는 싱글톤 빈을 사용해야 한다.

여러 페이지를 거치는 동안 유지해야 할 상태정보는 URL 파라미터, 쿠키, 폼 히든 필드와 DB, HTTP 세션에 분산하여 저장해두고 코드로 관리해야 한다.

어떤 방식을 사용하든 더 이상 유지할 필요가 없는 상태정보는 가능한 한 빨리 제거해야 한다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함