✍️
dodeon
  • 개발왕, 도던
  • 스프링 시큐리티 인 액션
    • 오늘날의 보안
    • 안녕! 스프링 시큐리티
    • 사용자 관리
    • 암호 처리
    • 인증 구현
    • 실전: 작고 안전한 웹 애플리케이션
    • 권한 부여 구성: 액세스 제한
    • 권한 부여 구성: 제한 적용
    • 필터 구현
    • CSRF 보호와 CORS 적용
    • 실전: 책임의 분리
    • OAuth 2가 동작하는 방법
    • OAuth 2: 권한 부여 서버 구현
  • 스프링 고급편
    • 스레드 로컬
    • 템플릿 메서드 패턴과 콜백 패턴
    • 프록시 패턴과 데코레이터 패턴
  • 스프링 입문
    • 프로젝트 환경설정
    • 스프링 웹 개발 기초
    • 회원 관리 예제 - 백엔드
    • 스프링 빈과 의존 관계
    • 회원 관리 예제 - MVC
    • 스프링 DB 접근 기술
      • JDBC
      • JPA
    • AOP
  • 스프링 핵심 원리
    • 객체 지향 설계와 스프링
      • 스프링의 탄생
      • 객체 지향 프로그래밍
      • 좋은 객체 지향 설계의 원칙
      • 객체 지향 설계와 스프링
    • 스프링 핵심 원리 이해
      • 회원 도메인 개발
      • 주문 도메인 개발
    • 객체 지향 원리 적용
      • 관심사의 분리
      • 새로운 구조와 정책 적용
      • 정리
      • IoC, DI, 컨테이너
      • 스프링으로 전환하기
    • 스프링 컨테이너와 스프링 빈
      • 스프링 빈 기본 조회
      • 동일 타입이 둘 이상일 때 조회
      • 상속일 때 조회
      • BeanFactory와 ApplicationContext
      • 다양한 설정 형식
      • 스프링 빈 설정 메타 데이터
    • 싱글턴 컨테이너
      • @Configuration과 싱글턴
    • 컴포넌트 스캔
      • 탐색 위치와 기본 탐색 대상
      • 필터와 중복 등록
    • 의존 관계 자동 주입
      • 롬복과 최신 트렌드
      • 조회 빈이 2개 이상일 때
      • 애너테이션 직접 만들기
      • 조회한 빈이 모두 필요할 때
      • 올바른 실무 운영 기준
    • 빈 생명 주기 콜백
      • 인터페이스 방식
      • 메서드 지정 방식
      • 애너테이션 방식
    • 빈 스코프
      • 프로토타입 스코프
      • Provider
      • 웹 스코프
  • 스프링 MVC
    • 웹 애플리케이션 이해
      • 서버
      • 서블릿
      • 멀티 스레드
      • HTML, HTTP API, CSR, SSR
      • 자바 백엔드 웹 기술 역사
    • 서블릿
      • HttpServletRequest
      • HTTP 요청 데이터
      • HttpServletResponse
      • HTTP 응답 데이터
    • 서블릿, JSP, MVC 패턴
      • 서블릿으로 만들기
      • JSP로 만들기
      • MVC 패턴
    • MVC 프레임워크 만들기
      • 프론트 컨트롤러 패턴
      • View 분리
      • Model 추가
      • 단순하고 실용적인 컨트롤러
      • 유연한 컨트롤러
      • 정리
    • 스프링 MVC의 구조 이해
      • 스프링 MVC 전체 구조
      • 핸들러 매핑과 핸들러 어댑터
      • 뷰 리졸버
      • 스프링 MVC 시작하기
      • 스프링 MVC 컨트롤러 통합
      • 스프링 MVC 실용적인 방식
    • 스프링 MVC 기본 기능
      • 프로젝트 생성
      • 로깅
      • 요청 매핑
      • HTTP 요청의 기본 및 헤더 조회
      • HTTP 요청 파라미터
      • HTTP 요청 메시지
      • HTTP 응답
      • HTTP 메시지 컨버터
      • 요청 매핑 핸들러 어댑터
    • 스프링 MVC 웹 페이지 만들기
    • 메시지, 국제화
      • 스프링 메시지 소스
    • Validation
      • BindingResult
      • FieldError, ObjectError
      • 오류 코드와 메시지 처리
      • Validator 분리
    • Bean Validation
      • Form 전송 객체 분리
      • HTTP 메시지 컨버터
    • 로그인
      • 쿠키
      • 세션
      • 서블릿 HTTP 세션
      • 서블릿 필터
      • 스프링 인터셉터
      • ArgumentResolver 활용
    • 예외 처리와 오류 페이지
      • 오류 화면 제공
      • 필터
      • 인터셉터
      • 스프링 부트 오류 페이지
    • API 예외 처리
      • 스프링 부트 기본 오류 처리
      • HandlerExceptionResolver
      • ExceptionResolver
      • ControllerAdvice
    • 스프링 타입 컨버터
      • Converter
      • ConversionService
      • 뷰 템플릿에 적용하기
      • Formatter
    • 파일 업로드
      • 서블릿과 파일 업로드
      • 스프링과 파일 업로드
      • 파일 업로드 및 다운로드 예제
  • 자바 ORM 표준 JPA 프로그래밍
    • JPA 소개
    • JPA 시작하기
    • 영속성 관리
      • 영속성 컨텍스트
      • 플러시
      • 준영속 상태
    • Entity 매핑
      • 객체와 테이블 매핑
      • 데이터베이스 스키마 자동 생성
      • 필드와 칼럼 매핑
      • 기본 키 매핑
      • 실전 예제
    • 연관 관계 매핑
      • 단방향 연관 관계
      • 양방향 연관 관계
      • 실전 예제
    • 다양한 연관 관계 매핑
      • 다대일
      • 일대다
      • 일대일
      • 다대다
      • 실전 예제
    • 고급 매핑
      • 상속 관계 매핑
      • 매핑 정보 상속
      • 실전 예제
    • 프록시와 연관관계 관리
      • 프록시
      • 즉시 로딩과 지연 로딩
      • 영속성 전이와 고아 객체
      • 실전 예제
    • 값 타입
      • 기본값 타입
      • 임베디드 타입
      • 값 타입과 불변 객체
      • 값 타입의 비교
      • 값 타입 컬렉션
      • 실전 예제
    • 객체 지향 쿼리 언어 - 기본
      • 기본 문법과 쿼리 API
      • 프로젝션
      • 페이징
      • 조인
      • 서브 쿼리
      • JPQL 타입 표현과 기타 식
      • 조건식
      • JPQL 함수
    • 객체 지향 쿼리 언어 - 중급
      • 경로 표현식
      • fetch join
      • 다형성 쿼리
      • Entity 직접 사용
      • Named 쿼리
      • 벌크 연산
  • 스프링 부트와 JPA 활용 - 웹 애플리케이션 개발
    • 프로젝트 환경설정
    • 도메인 분석 설계
      • 도메인 분석 설계
      • Entity 클래스 개발
      • Entity 설계 시 주의점
    • 애플리케이션 아키텍처
    • 회원 도메인 개발
    • 상품 도메인 개발
    • 주문 도메인 개발
      • Entity, 리포지토리, 서비스 개발
      • 주문 기능 테스트
      • 주문 검색 기능 개발
    • 웹 계층 개발
      • 변경 감지와 병합
  • 스프링 부트와 JPA 활용 - API 개발과 성능 최적화
    • API 개발 기본
      • 회원 등록 API
      • 회원 수정 API
      • 회원 조회 API
    • 지연 로딩과 조회 성능 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환
      • JPA에서 DTO 직접 조회
    • 컬렉션 조회 최적화
      • Entity 직접 노출
      • Entity를 DTO로 변환: 페치 조인
      • Entity를 DTO로 변환: 페이징과 한계 돌파
      • DTO 직접 조회
      • DTO 직접 조회: 컬렉션 조회 최적화
      • DTO 직접 조회: 플랫 데이터 최적화
      • 정리
    • OSIV와 성능 최적화
  • 스프링 데이터 JPA
    • 예제 도메인 모델
    • 공통 인터페이스 기능
      • 순수 JPA 기반 리포지토리
      • 공통 인터페이스 설정
    • 쿼리 메서드 기능
      • JPA Named Query
      • @Query
      • 파라미터 바인딩
      • 반환 타입
      • 페이징과 정렬
      • 벌크성 수정 쿼리
      • @EntityGraph
      • JPA Hint & Lock
    • 확장 기능
      • 사용자 정의 리포지토리
      • Auditing
      • Web 확장
    • 스프링 데이터 JPA 분석
    • 나머지 기능
      • Specifications
      • Query By Example
      • Projections
      • Native Query
  • Querydsl
    • 프로젝트 환경 설정
    • 예제 도메인 모델
    • 기본 문법
      • JPQL vs Querydsl
      • Q-Type 활용
      • 검색 조건
      • 결과 조회
      • 정렬
      • 페이징
      • 집합 함수
      • 조인
      • 서브 쿼리
      • Case 문
      • 상수, 문자 더하기
    • 중급 문법
      • 프로젝션과 결과 반환
      • 동적 쿼리
      • 수정, 삭제 벌크 연산
      • SQL Function 호출
    • 순수 JPA와 Querydsl
      • 순수 JPA 리포지토리와 Querydsl
      • 동적 쿼리와 성능 최적화 조회
      • 조회 API 컨트롤러 개발
    • 스프링 데이터 JPA와 Querydsl
      • 스프링 데이터 페이징 활용
      • 스프링 데이터 JPA가 제공하는 Querydsl 기능
  • 데이터 접근 핵심 원리
    • JDBC 이해
      • JDBC와 최신 데이터 접근 기술
      • 데이터베이스 연결
      • JDBC 개발
  • 백엔드 시스템 실무
    • CPU bound 애플리케이션
      • CPU를 극단적으로 사용하는 애플리케이션
      • 스트레스 테스트 툴로 성능 측정
      • Dockerized 애플리케이션 GCP 배포
      • Jenkins를 이용한 배포
    • CPU bound 애플리케이션 무중단 배포
      • nginx를 통한 로드밸런싱 구성
      • 서버를 늘려서 성능 측정
    • 배포 자동화와 협업을 위한 Git
      • GitHub Webhook과 jenkins로 배포 자동화
      • 머지할 때 발생하는 충돌 해결하기
      • 실무에서 유용한 Git 꿀팁
    • I/O bound 애플리케이션
    • Message Queue를 도입하여 데이터 유실 방지
      • 스트레스 테스트
    • 검색과 분석을 위한 저장소 ElasticSearch
    • Kubernetes
  • 모든 개발자를 위한 HTTP 웹 기본 지식
    • 인터넷 네트워크
      • IP
      • TCP, UDP
      • PORT
      • DNS
    • URI와 웹 브라우저 요청 흐름
    • HTTP 기본
      • 클라이언트-서버 구조
      • stateful, stateless
      • 비 연결성
      • HTTP 메시지
    • HTTP 메서드
    • HTTP 메서드 활용
    • HTTP 상태 코드
    • HTTP 헤더 - 일반
      • 표현
      • 콘텐츠 협상
      • 전송 방식
      • 정보
      • Authorization
      • 쿠키
    • HTTP 헤더 - 캐시
      • 검증 헤더와 조건부 요청
      • 조건부 요청 헤더
      • 프록시 캐시
      • 캐시 무효화
  • 김영한의 실전 자바
    • 제네릭
  • 예제로 배우는 스프링 입문
    • 예제로 배우는 스프링 입문(개정판)
      • PetClinic 예제
      • 스프링 IoC
      • 스프링 AOP
      • 스프링 PSA
  • 스프링 프레임워크 핵심 기술
    • 스프링 프레임워크 핵심 기술
      • IoC 컨테이너와 빈
        • 스프링 IoC 컨테이너와 빈
        • ApplicationContext와 빈 설정
        • @Autowired
        • @Component와 컴포넌트 스캔
        • 빈의 스코프
        • Environment
        • MessageSource
        • ApplicationEventPublisher
        • ResourceLoader
      • Resource/Validation
        • Resource 추상화
        • validation 추상화
      • 데이터 바인딩 추상화
      • SpEL
      • 스프링 AOP
      • Null-Safety
  • 스프링 부트 개념과 활용
    • 스프링 부트 원리
      • 자동 설정
      • 내장 서버
        • 컨테이너와 서버 포트
        • HTTPS와 HTTP2
      • 독립적으로 실행 가능한 JAR
    • 스프링 부트 활용
      • Spring Application
      • 외부 설정
      • 프로파일
      • 로깅
      • 테스트
      • Spring Boot Devtools
    • 스프링 웹 MVC
      • 소개
      • HttpMessageConverters
      • ViewResolver
      • 정적 리소스
      • 웹 JAR
      • index 페이지와 파비콘
      • ExceptionHandler
      • Spring HATEOAS
      • CORS
  • THE JAVA
    • JVM 이해하기
      • 자바, JVM, JDK, JRE
      • JVM 구조
      • 클래스 로더
      • Heap
      • Garbage Collector
    • 리플렉션
      • 클래스 정보 조회
  • The Java - Test
    • JUnit 5
      • JUnit 시작하기
      • JUnit 시작하기
    • Mockito
Powered by GitBook
On this page
  • 종류
  • request
  • session
  • application
  • websocket
  • 예제
  • 스코프와 Provider
  • 스코프와 프록시
  • 동작 원리
  • 정리
  • 주의점

Was this helpful?

  1. 스프링 핵심 원리
  2. 빈 스코프

웹 스코프

PreviousProviderNext웹 애플리케이션 이해

Last updated 4 years ago

Was this helpful?

웹 환경에서만 동작하는 스코프다. 프로토타입과 다르게 스프링이 해당 스코프의 종료 시점까지 관리한다. 따라서 종료 메서드가 호출된다.

종류

request

  • HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프

  • 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.

0.00001초 차이로 동시에 요청해도 클라이언트 A와 B는 각각 다른 스프링 빈이 생성된다.

session

  • HTTP Session과 동일한 생명 주기를 가지는 스코프

application

  • 서블릿 컨텍스트와 동일한 생명주기를 가지는 스코프

websocket

  • 웹 소켓과 동일한 생명 주기를 가지는 스코프

이 챕터에서는 request 스코프만 예제로 설명한다. 나머지는 범위만 다르고 동작 방식이 비슷하기 때문이다.

// web 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-web'

웹 스코프는 웹 환경에서만 동작하므로 위의 라이브러리를 추가하자.

Tomcat started on port(s): 8080 (http) with context path ''
Started CoreApplication in 0.914 seconds (JVM running for 1.528)

그럼 이렇게 톰캣이 띄워지게 된다. 이렇듯 spring-boot-starter-web 라이브러리를 추가하면 스프링 부트가 내장 톰캣 서버를 활용해 웹 서버와 함께 실행한다.

server.port=9090

만약 기본 포트인 8080을 다른 곳에서 사용중이라면 application.properties에서 포트를 변경한다.

스프링 부트는 웹 라이브러리가 없으면 지금까지 학습한 AnnotationConfigApplicationContext를 기반으로 애플리케이션을 구동한다.

반면, 웹 라이브러리가 추가되면 웹과 관련된 추가 설정과 환경들이 필요해 AnnotationConfigServletWebServerApplicationContext를 기반으로 구동한다.

예제

만약 동시에 여러 HTTP 요청이 오면, 정확히 어떤 요청이 남긴 로그인지 구분하기 어렵다. 이럴 때 사용하기 좋은 것이 request 스코프다.

[d06b992f...] request scope bean create
[d06b992f...] [http://localhost:8080/log-demo] controller test
[d06b992f...] [http://localhost:8080/log-demo] service id = testId
[d06b992f...] request scope bean close

UUID + 요청URL + 메시지 형태로 출력할 것이다. UUID로 HTTP 요청을 구분하고 어떤 URL을 요청해서 남은 로그인지 확인한다.

@Component
@Scope(value = "request")
public class MyLogger {

  private String uuid;
  private String requestURL;

  public void setRequestURL(String requestURL) {
    this.requestURL = requestURL;
  }

  public void log(String message) {
    System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message);
  }

  @PostConstruct
  public void init() {
    // 유니크한 아이디 생성
    uuid = UUID.randomUUID().toString();
    System.out.println("[" + uuid + "] request scope bean created: " + this);
  }

  @PreDestroy
  public void close() {
    System.out.println("[" + uuid + "] request scope bean closed: " + this);
  }
}

request 스코프이기 때문에 HTTP 요청마다 빈이 생성되고 소멸될 것이다. 빈이 생성되는 시점에는 @PostConstruct로 uuid를 생성해 저장해둔다. 빈이 요청 당 하나씩 생성되므로 uuid를 저장해두면 다른 요청과 구분할 수 있다.

빈이 소멸되는 시점에는 @PreDestroy를 사용해 종료 메시지를 남긴다. requestURL은 빈이 생성되는 시점에는 알 수 없어 외부에서 setter로 입력받는다.

@Controller
// 생성자에 자동으로 autowired로 자동 주입된다.
@RequiredArgsConstructor
public class LogDemoController {

  private final LogDemoService logDemoService;
  private final MyLogger myLogger;

  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    // 로그에 찍기 위해 URL을 받아온다.
    String requestURL = request.getRequestURL().toString();
    myLogger.setRequestURL(requestURL);

    myLogger.log("controller test");
    logDemoService.logic("testId");

    return "OK";
  }
}

로거를 테스트하기 위한 컨트롤러를 만든다. reqeustURL을 myLogger에 저장해두면 myLogger는 HTTP 요청 당 구분되므로 값이 섞이지 않게 된다.

참고로 이렇게 저장하는 부분은 컨트롤러 보다는 공통 처리가 가능한 스프링 인터셉터나 서블릿 필터를 이용하는 게 좋다. 여기서는 예제를 단순화하기 위해 이렇게 구현했다.

@Service
@RequiredArgsConstructor
public class LogDemoService {

  private final MyLogger myLogger;

  public void logic(String id) {
    myLogger.log("service id = " + id);
  }
}

서비스 계층에서도 로그를 찍어보자. request 스코프를 사용하지 않고 모든 정보를 서비스 계층에 넘긴다면 파라미터가 많아서 지저분해진다. requestURL처럼 웹과 관련된 정보가 웹과 관련없는 서비스 계층까지 넘어가는 문제도 있다. 웹과 관련된 부분은 컨트롤러까지만 사용하고 서비스 계층은 웹 기술에 종속되지 않아야 하므로 가급적 순수하게 유지하는 것이 유지 보수에 좋다.

reqeust 스코프의 MyLogger 덕분에 웹과 관련된 부분을 파라미터로 넘기지 않고 MyLogger의 멤버 변수에 저장해 코드와 계층을 깔끔하게 유지할 수 있다.

실행해보면 위와 같은 에러가 뜬다. LogDemoController가 만들어지려면 MyLogger를 주입받아야 하는데 MyLogger는 실제 HTTP 요청이 있을 때 빈을 생성하므로 실패한 것이다.

스코프와 Provider

첫번째 해결 방안은 Provider를 사용하는 것이다.

@Controller
@RequiredArgsConstructor
public class LogDemoController {

  private final LogDemoService logDemoService;
  // myLogger를 찾을 수 있는, DL할 수 있는 객체가 주입되어 주입 시점에 myLogger을 주입받을 수 있다.
  private final ObjectProvider<MyLogger> myLoggerProvider;

  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    // DL로 원하는 객체를 가져온다.
    MyLogger myLogger = myLoggerProvider.getObject();

    String requestURL = request.getRequestURL().toString();
    myLogger.setRequestURL(requestURL);

    myLogger.log("controller test");
    logDemoService.logic("testId");

    return "OK";
  }
}
@Service
@RequiredArgsConstructor
public class LogDemoService {

  private final ObjectProvider<MyLogger> myLoggerProvider;

  public void logic(String id) {
    MyLogger myLogger = myLoggerProvider.getObject();
    myLogger.log("service id = " + id);
  }
}

ObjectProvider 덕분에 ObjectProvider.getObject()를 호출할 때까지 request 스코프 빈을 생성해달라고 스프링 컨테이너에 요청하는 시점을 지연할 수 있다.

ObjectProvider.getObject()를 호출하는 시점에는 HTTP 요청이 진행 중이므로 request 스코프 빈이 생성된다.

이제 http://localhost:8080/log-demo에 접속하면 위와 같은 결과가 뜬다. 같은 요청이면 컨트롤러와 서비스 모두 같은 uuid가 출력된다.

한 번 더 요청하면 다른 uuid로 로그가 찍힌다.

스코프와 프록시

두 번째 해결 방안은 프록시 방식이다.

@Component
// 프록시 모드 추가
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
  ...
}
@Controller
@RequiredArgsConstructor
public class LogDemoController {

  private final LogDemoService logDemoService;
  // 원복한다.
  private final MyLogger myLogger;

  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    String requestURL = request.getRequestURL().toString();
    myLogger.setRequestURL(requestURL);

    myLogger.log("controller test");
    logDemoService.logic("testId");

    return "OK";
  }
}
@Service
@RequiredArgsConstructor
public class LogDemoService {

  // 원복한다.
  private final MyLogger myLogger;

  public void logic(String id) {
    myLogger.log("service id = " + id);
  }
}

proxyMode를 추가하면 MyLogger의 가짜 프록시 클래스를 만들어두고 HTTP request와 상관 없이 가짜 프록시 클래스를 다른 빈에 미리 주입한다.

적용 대상이 클래스면 TARGET_CLASS, 인터페이스면 INTERFACES를 선택한다.

Provider 사용 시와 같이 정상적으로 동작한다.

동작 원리

  • CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어 주입한다.

  • 이 가짜 프록시 객체는 실제 요청이 오면 그때 내부에서 실제 빈을 요청하는 위임 로직을 가진다.

  • 가짜 프록시 객체는 실제 request 스코프와는 관계가 없다. 어디든 사용할 수 있다.

    • 그냥 가짜이고, 내부에 단순한 위임 로직만 있다.

    • myLogger처럼 사용하는 객체가 간단하게 싱글턴처럼 동작한다.

@Controller
@RequiredArgsConstructor
public class LogDemoController {

  private final LogDemoService logDemoService;
  private final MyLogger myLogger;

  @RequestMapping("log-demo")
  @ResponseBody
  public String logDemo(HttpServletRequest request) {
    System.out.println("myLogger = " + myLogger.getClass());

    ...

    return "OK";
  }
}

myLogger를 찍어보면 이상한 값이 나왔다.

@Scope의 proxyMode = ScopedProxyMode.TARGET_CLASS를 설정하면 스프링 컨테이너는 CGLIB이라는 바이트 코드 조작 라이브러리로 MyLogger를 상속 받은 가짜 프록시 객체를 생성한다.

일단 스프링 컨테이너에 myLogger라는 이름의 가짜 프록시 객체를 넣어두고 실제 필요한 시점에 가져와서 동작하는 것이다. ac.Bean("myLoger", MyLogger.class)로 찍어봐도 프록시 객체가 조회된다. 그래서 의존 관계 주입도 이 가짜 프록시 객체로 주입된다.

가짜 프록시 객체는 요청이 오면 그때가 돼서야 내부에서 진짜 빈을 요청하는 위임 로직을 실행한다.

  1. 가짜 프록시 객체는 내부에 진짜 myLogger를 찾는 방법을 알고 있다.

  2. 클라이언트가 myLogger.logic()을 호출하면 가짜 프록시 객체의 메서드가 호출된다.

  3. 가짜 프록시 객체는 request 스코프의 진짜 myLogger.logic()을 호출한다.

  4. 가짜 프록시 객체는 원본 클래스를 상속받아 만들어졌기 때문에 사용하는 클라이언트 객체는 원본이든 아니든 다형성을 활용해 동일하게 사용한다.

정리

  • 프록시 객체 덕분에 클라이언트는 싱글턴 빈을 사용하듯 편리하게 request 스코프를 사용할 수 있다.

  • Provider와 프록시의 핵심은 진짜 객체 조회를 꼭 필요한 시점까지 지연 처리 한다는 점이다.

  • 애너테이션 설정 변경만으로 원본 객체를 프록시 객체로 대체할 수 있다.

    • 다형성과 DI가 가진 큰 강점이다.

  • 꼭 웹 스코프가 아니어도 프록시는 사용할 수 있다.

주의점

  • 싱글턴을 사용하는 것 같지만 다르게 동작하므로 주의해서 사용해야 한다.

  • 이런 특별한 scope는 꼭 필요한 곳에만 최소로 사용하자. 유지보수가 어려워질 수 있다.