[SPRING] WIL - 3

진규빈·2024년 10월 5일
0

스프링 WIL

목록 보기
3/8

1. 스터디 복습

빈 스코프 (Bean Scope)

• 생성된 빈 인스턴스가 존재할 수 있는 컨테이너 영역, 객체가 생성되고 존재하는 범위
• 빈 생성 및 소멸하는 방법 결정하여, 이를 통해 메모리 관리와 빈의 생성 시점 등을 세밀하게 컨트롤 가능

<기본적인 빈 스코프>

Singlton Scope

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

Prototype Scope

• 다른 빈에 주입되거나 해당 빈 호출 시 스프링 컨테이너는 getBean() 메서드 호출 => 이때 캐시된 인스턴스 가져오는 싱글톤과 달리 프로토타입 스코프로 정의된 빈은 매번 새로운 객체 생성
• 일반적으로 상태 가진(stateful) 빈은 프로토타입 스코프 사용, 상태 없는(stateless) 빈은 싱글톤 스코프 사용
• 다른 스코프와 달리 프로토타입은 스프링 컨테이너가 모든 생명주기를 관리하지는 않음 => 호출에 의해 빈 생성되면 컨테이너에서 빈에 대한 기록 유지하지 않음

@Component
class ClientBean {
	@Autowired
    private ApplicationContext ac; // ApplicationContext 스프링 컨테이너 주입
    
    public int logic() {
    	PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class); // getBean() 통해 직접 요청
        prototypeBean.addCount();
        return prototypeBean.getCount();
    }
}
@Autowired
private ObjectProvider<PrototypeBean> prototypeBeanProvider; // ObjectProvider 타입 필드 선언

public int logic() {
	PrototypeBean prototypeBean = prototypeBeanProvider.getObject(); // getObject() 통해 프로토타입 빈 return
    prototypeBean.addCount();
    int count = prototypeBean.getCount();
    return count;
}

request, session, application, websocket 스코프는 web-aware Spring ApplicationContext 구현체 사용할 때만 쓸 수 있음
단, Spring Web MVC에서 사용 시 DispatcherServlet이 요청과 관련된 정보들을 전역적으로 사용할 수 있게 해주므로 추가 설정 필요 없음
+) DispatcherServlet: http 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러

Request Scope
• 모든 로그인 http 요청마다 LoginAction 빈에 대한 새로운 인스턴스 만들어 request 처리
• 각 인스턴스가 별개로 존재 => 객체 내부의 속성 값 등 변경해도 다른 인스턴스에 영향 X
• 각 객체는 request processing 끝날 경우 소멸

Session Scope
• 생성된 객체는 http session 만료되기 전까지 메모리 차지
• 웹 애플리케이션 세션에 대한 데이터 유지 및 관리할 때 사용

Application Scope
• ServletContext 레벨에서 유일한 인스턴스 생성
• http 요청 들어오면 웹 서버는 요청 처리하기 위해 새로운 스레드와 서블릿 생성
• 스레드마다 별도의 인스턴스 생성되어 사용 => 스레드 환경에서 안전하게 빈 사용 가능

Websocket Scope
• http와 다른 프로토콜 사용하여 클라이언트와 서버 간의 양방향 통신 가능
• 각 웹소켓 세션마다 별도의 빈 인스턴스 생성 => 웹소켓 세션의 생명주기와 함께 관리
@WebSocketScope는 스프링의 웹소켓 구현체인 Spring Websocket에서만 사용 가능

빈 생명주기 콜백

<스프링 컨테이너가 빈 관리할 때 호출되는 일련의 메서드들>

  1. 빈 생성 - 스프링 컨테이너가 빈 생성
  2. 의존성 주입 - 생성된 빈에 필요한 의존성 주입
  3. 초기화 콜백 - 초기화 작업 할 수 있도록 특정 메서드 호출
  4. 빈 사용 - 정상 초기화 후 애플리케이션에서 빈 사용
  5. 소멸 콜백 - 리소스 해제, 연결 종료 등의 작업 수행

<스프링의 빈 생명주기 콜백 관리>

인터페이스

public class ExampleBean implements InitializingBean, DisposableBean {
	@Override
    public void afterPropertiesSet() throws Exception {
    	// 초기화 콜백 (의존관계 주입 끝나면 호출)
    }
    
    @Override
    public void destroy() throws Exception {
    	// 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}

InitializingBean & DisoisableBean => 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 P
	...
    @PostConstruct
    public void init() {
    	System.out.println("NetworkClient.init");
        connect();
        call("초기화 연결 메세지");
        
    @PreDestroy
    public void close() {
		System.out.println("Network.Client.close");
        disconnect();
    }
}

• 장점) 최신 스프링에서 가장 권장, 편리, Spring에 종속적이지 않은 자바 표준 어노테이션이므로 스프링 아닌 다른 컨테이너에서도 동작
• 단점) 커스터마이징 불가능한 외부 라이브러리에서 적용 불가

2. 과제


User 클래스 생성
사용자 기본 정보(id, name) 저장하는 객체 구현

UserRepository 인터페이스 생성
주어진 id로 사용자 찾는 메서드 & 주어진 사용자 정보 저장하는 메서드 구현


UserService 클래스 생성
주어진 id & name으로 새로운 User 객체 생성 및 저장 => 싱글톤 스코프
"Prototype User"라는 이름의 새로운 사용자 생성 및 저장 => 프로토타입 스코프
서비스 초기화될 때 @PostConstruct 호출, 서비스 종료될 때 @PreDestroy 호출

InMemoryUserRepository 클래스 생성
UserRepository 구현한 클래스로, 메모리(HashMap)에 사용자 정보 저장 및 관리
findUserById(int id): userStorage 맵에서 주어진 id로 사용자를 찾아 반환 / 사용자 존재하지 않으면 null 반환
saveUser(User user): 주어진 User 객체를 userStorage 맵에 저장 / 저장하려는 id 이미 존재하면 기존 데이터 업데이트하고 메시지 출력
userStorage.put(user.getId(), user)로 사용자 추가
userStorage.containsKey(user.getId())로 중복된 id 확인 후 경고 메시지 출력 및 기존 정보 업데이트

3. 김영한 스프링 입문 강의

8강. 비즈니스 요구사항 정리

<일반적인 웹 애플리케이션 계층 구조>

• 컨트롤러: 웹 MVC의 컨트롤러 역할
• 서비스: 핵심 비즈니스 로직 구현
• 리포지토리: DB에 접근, 도메인 객체를 DB에 저장하고 관리
• 도메인: 비즈니스 도메인 객체

<클래스 의존 관계>

• 아직 데이터 저장소 선정되지 않아 우선 인터페이스로 구현 클래스 변경할 수 있도록 설계
• 데이터 저장소는 RDB, NoSQL 등 다양한 저장소 고민 중인 상황으로 가정
• 개발 진행하기 위해 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용

9강. 회원 도메인과 리포지토리 만들기


src/main/java/study.hello_spring에 domain 패키지 생성
domain 패키지에 Member 클래스 생성

src/main/java/study.hello_spring에 repository 패키지 생성
repository 패키지에 MemberRepository 인터페이스 생성
• 회원 객체 저장하는 저장소


repository 패키지에 MemoryMemberRepository 클래스 생성

10강. 회원 도메인과 리포지토리 테스트

• 개발한 기능 실행해서 테스트 할 때 자바의 main 메서드 통해 실행하거나 웹 애플리케이션의 컨트롤러 통해 실행
• 이러한 방법은 준비하고 실행하는 데 오래 걸리고, 반복 실행하기 어려우며, 여러 테스트 한번에 실행하기 어려움
• 자바는 JUnit이라는 프레임워크로 테스트 실행해서 이러한 문제 해결



test/java/study.hello_spring에 repository 패키지 생성
repository 패키지에 MemoryMemberRepositoryTest 클래스 생성
@Test 어노테이션 => 실행 가능하게 해줌
@AfterEach 어노테이션 => 테스트 메서드 종료 후 정리 작업 수행 (한번에 여러 테스트를 실행하면 메모리 DB에 직전 테스트의 결과가 남게 되면서 이전 테스트 때문에 다음 테스트가 실패할 가능성이 있기 때문) => 여기서는 메모리 DB에 저장된 데이터 삭제

0개의 댓글