geeone 스터디 블로그
[Spring] GDG 3주차 WIL 본문
스프링 세션 3주차 내용 복습
1. Bean?
Bean scope란?
Bean scope란 빈의 범위라는 뜻으로, 생성된 빈 인스턴스가 존재할 수 있는 컨테이너 영역
빈을 생성하고 소멸시키는 방법을 결정함 ( 메모리 관리와 빈의 생성 시점 등을 세밀하게 컨트롤 가능 )
스프링은 개발자가 빈을 생성하고 관리할 수 있도록 지원하는데, 이때 빈 객체의 스코프는 객체가 생성되고 존재하는 범위를 말함
기본적인 빈 스코프 종류
- singlton / prototype / request / session / application / websocket
Singlton scope

- 빈을 생성하고 그 범위가 싱글톤으로 지정되면 Spring IoC 컨테이너는 해당 빈으로 정의된 객체의 인스턴스를 정확히 단 한 개 생성
- 이 단일 인스턴스는 이러한 싱글톤 빈들의 캐시에 저장되며, 이후 동일한 이름의 빈에 대한 모든 요청 및 참조는 캐시된 객체를 반환
- 캐시 : 여러 요청이 있을 때마다 새로 빈 인스턴스를 생성하지 않고 이미 생성된 인스턴스를 재사용할 수 있도록 저장하는 역할을 하는 저장 공간
Prototype scope

- 다른 빈에 주입되거나 해당 빈이 호출될 때 스프링 컨테이너는 getBean() 메소드를 호출하는데, 이때 캐시된 인스턴스를 가져오는 싱글톤과 달리 프로토타입 스코프로 정의된 빈은 매번 새로운 객체를 생성
- 일반적으로 상태를 가진(stateful) 빈은 프로토타입 스코프를 사용
- 상태가 없는(stateless) 빈은 싱글톤 스코프를 사용
- 다른 스코프와 달리 프로토타입은 스프링 컨테이너가 모든 생명주기를 관리하지는 않음. 호출에 의해 빈이 생성되면 컨테이너에서는 빈에 대한 기록을 유지하지 않음
싱글톤 스코프로 정의된 빈 안에 프로토타입 스코프로 정의된 빈이 포함된다면?
포함된 프로토타입 빈은 싱글톤 빈의 생명주기 동안 동일한 인스턴스로 유지됨.
즉, 프로토타입 빈이 새로 생성되지 않고, 싱글톤 빈이 생성될 때 주입된 인스턴스를 계속 사용하게 됨
request, session, application, websocket 스코프는 web-aware Spring ApplicationContext 구현체를 사용할 때만 사용 가능
ApplicationContext : 스프링 애플리케이션에서 빈(bean)을 관리하고, 빈들 간의 의존성을 주입하는 중심 역할을 하는 인터페이스
web-aware ApplicationContext : HTTP 요청이나 세션, 서블릿 같은 웹 요소와 상호작용할 수 있도록 설계된 웹 전용 기능을 가진 ApplicationContext
Request scope
@RequestScope
@Component
public class LoginAction {
// ...
}
- 모든 로그인 Http 요청마다 LoginAction 빈에 대한 새로운 인스턴스를 만들어 request를 처리
- 각 인스턴스가 별개로 존재 -> 객체 내부의 속성 값 등을 변경해도 다른 인스턴스에는 영향 X
- 각 객체는 request processing이 끝날 경우 사라짐
Session scope
@SessionScope
@Component
public class UserPreferences {
// ...
}
- Session scope로 정의된 빈으로부터 생성된 객체는 http session이 만료되기 전까지 메모리를 차지
- 웹 어플리케이션 세션에 대한 데이터를 유지하고 관리할 때 사용됨
Application scope
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
- ServletContext 레벨에서 유일한 인스턴스를 생성
- HTTP 요청이 들어오면 웹 서버는 요청을 처리하기 위해 새로운 스레드와 서블릿을 생성
- 애플리케이션 스코프로 생성된 빈은 스레드마다 별도의 인스턴스가 생성되어 사용 -> 멀티 스레드 환경에서 안전하게 빈 사용 가능
websocket scope
@Component
@Scope(scopeName =
"
websocket"
, proxyMode
= ScopedProxyMode.TARGET_CLASS)
public class MyWebSocketHandler {
// ...
}
- 웹 소캣은 http와 다른 프로토콜을 사용함 / 클라인트와 서버 간 양방향 통신을 가능케 함
- 웹소캣 스코프를 사용하면 각 웹 소켓 세션마다 별도의 빈 인스턴스가 생성되며 웹 소캣 세션의 생명주기와 함께 관리됨.
- @WebSocketScope 는 Spring의 WebSocket 구현체인 Spring Websocket에서만 사용 가능.
2. 빈 생명주기 콜백
Spring 컨테이너가 빈을 관리할 때 호출되는 일련의 메서드들
#1 빈 생성
: 스프링 컨테이너가 빈을 생성
#2 의존성 주입
: 생성된 빈에 필요한 의존성을 주입
#3 초기화 콜백
: 초기화 작업을 할 수 있도록 특정 메서드가 호출
#4 빈 사용
: 정상 초기화 후 애플리케이션에서 빈을 사용
#5 소멸 콜백
: 리소스 해제나 연결 종료 등의 작업 수행
생성자 DI를 통해 객체 생성과 의존성 주입, 초기화 콜백을 한꺼번에 실행하면 안 될까?
-> 생성자에서 무거운 초기화 작업을 함께하는 것보다는 두 부분을 명확히 나누는 것이 유지 보수 관점에서 유리
스프링의 빈 생명주기 콜백 관리
-> 인터페이스(InitializingBean, DisposableBean)
-> 설정 정보를 통한 메소드 등록
-> @PostConstruct @PreDestroy 어노테이션
인터페이스
public class ExampleBean implements InitializingBean, DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
// 초기화 콜백 (의존관계 주입이 끝나면 호출)
}
@Override
public void destroy() throws Exception {
// 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
}
}
InitializingBean 과 DisposableBean 은 Spring에 의존적인 인터페이스로, 코드가 인터페이스에 의존
메소드를 오버라이드 하기 때문에 메소드명 변경 불가
소스코드를 수정할 수 없는 외부 라이브러리에 대해서는 적용 불가
설정 파일
public class ExampleBean {
public void initialize() throws Exception {
// 초기화 콜백 (의존관계 주입이 끝나면 호출)
}
public void close() throws Exception {
// 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
}
}
@Configuration
class LifeCycleConfig {
@Bean(initMethod ="initialize", destroyMethod ="close")
public ExampleBean exampleBean() {
}
}
장점 ㅣ 메소드 명 자유롭게 부여 가능 / Spring에 의존적이지 않음 / 외부 라이브러리에도 적용 가능
단점 ㅣ Bean 지정 시 initMethod와 destroyMethod를 직접 지정해야 하기에 번거로움
**@Bean 의 destroyMethod 는 기본값으로 close, shutdown 이라는 이름의 메소드가 종료 메소드라고 추론하여 자동 호출하므 로, 종료 메소드를 따로 부여하지 않더라도 작동
어노테이션
public class NetworkClient{
...
@PostConstruct
public void init() {
System.out.println("NetworkClient.init");
connect();
call("초기화 연결 메세지");
}
@PreDestroy
public void close() {
System.out.println("NetworkClient.close");
disconnect();
}
}
장점 ㅣ 최신 스프링에서 가장 권장하는 방법 / 어노테이션만 붙이면 되므로 편리 / Spring에 종속적이지 않은 자바 표준 어노테이션이므로 스프링이 아닌 다른 컨테이너에서도 동작
단점 ㅣ 커스터마이징이 불가능한 외부 라이브러리에서 적용 불가
-> 외부 라이브러리에서 초기화, 종료를 해야 할 경우 두 번째 방법 이용 권장
3주차 과제
Bean을 만들어 봅시다!
-> 커스텀 스코프와 빈 생명주기 콜백을 활용한 멀티 모듈 스프링 프로젝트 구현
User class, UserRepository Interface, UserService class 를 생성한 뒤 UserService class 에 임의의 비즈니스 로직(단순한 constructer 나 getter/setter 도 괜찮습니다.) 을 만들고 해당 로직에 커스텀 스코프(6가지 중 2개)를 적용하세요. 서비스가 특정 조건(예: 세션, 요청 등)에 따라 다르게 관리되도록 설정합니다. UserService 에 세 가지 방식 중 하나의 방식으로 생명주기 콜백을 작성해주세요. 초기화 콜백과 소멸 콜백을 작성하여 해당 콜백이 실행될 때 특정 작업(예 : 데이터베이스 연결과 해제 등) 이 실행될 수 있도록 해주세요
1. User class, UserRepository Interface, UserService class 생성
User.class
package User;
public class User {
private Long id;
private String name;
public User() {}
public User(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
UserRepository Interface
package User;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository{
User findById(Long id);
void save(User user);
}
UserService Class
package User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUser(Long id) {
return userRepository.findById(id);
}
public void saveUser(User user) {
userRepository.save(user);
}
}
2. 커스텀 스코프 적용
package User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedUserService extends UserService{
@Autowired
public RequestScopedUserService(UserRepository userRepository) {
super(userRepository);
System.out.println("RequestScopedUserService created");
}
}
package User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
@Service
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedUserService extends UserService {
@Autowired
public SessionScopedUserService(UserRepository userRepository) {
super(userRepository);
System.out.println("SessionScopedUserService created");
}
}
3. 생명주기 콜백 작성
package User;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class LifecycleUserService extends UserService {
@Autowired
public LifecycleUserService(UserRepository userRepository) {
super(userRepository);
System.out.println("LifecycleUserService created");
}
@PostConstruct
public void init(){
System.out.println("UserService initialized");
System.out.println("LifecycleUserService initialized");
}
@PreDestroy
public void destroy(){
System.out.println("UserService destroyed");
System.out.println("LifecycleUserService destroyed");
}
}
트러블 슈팅

-> 해결
@SpringBootApplication(scanBasePackageClasses = {UserRepository.class, UserSpringApplication.class})
https://velog.io/@oyoungsun/Spring-Failed-to-load-ApplicationContext-에러-해결
[Spring] Failed to load ApplicationContext 에러 해결
Junit5로 테스트를 진행하던 도중 Failed to load ApplicationContextjava.lang.IllegalStateException: Failed to load ApplicationContext에러가 발생해 해결 방법을 적어보았다. 계속 @Auto
velog.io
https://unhosted.tistory.com/77
Spring boot test 경로가 다른 패키지 테스트 시 오류
어느 날 마주한 에러 문구 Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @
unhosted.tistory.com
결과화면



'Spring' 카테고리의 다른 글
| [Spring] GDG 4주차 WIL : 관점 지향 프로그래밍(AoP) 및 커스텀 어노테이션 (0) | 2024.10.11 |
|---|---|
| [Spring] 강의 스터디 WIL 3주차 (4) | 2024.10.06 |
| [Spring] GDG 2주차 WIL (2) | 2024.09.27 |
| [Spring] 강의 스터디 WIL 2주차 (3) | 2024.09.27 |
| [Spring] GDG 1주차 WIL (1) | 2024.09.19 |