스프링 기본편

한상우·2022년 8월 21일
0

spring

목록 보기
1/5
post-thumbnail

들어가기 전


자바는 객체 지향 언어, 객체 특징 중 다형성에 관해 설명

객체를 설계할 때 역할과 구현을 분리해야함

클라이언트(객체)는 대상의 역할만 알면 된다.

내부 구조에 대해서는 알 필요가 없다.

(예시 . 자동차 (GV80, 마티즈))

클라이언트는 자동차의 작동 방식만 알면 된다 (자동차 모는 법만 알면 GV80 타다가 마티즈 타도 상관없듯이)

인터페이스(역할) 만 만들고 클라이언트는 인터페이스에만 의존하도록 하기

대상은 인터페이스를 구현하면 된다. → 구현체를 언제든지 갈아끼울 수 있음

순수한 자바 코드로의 한계

MemberRepository repository = new MemoryMemberRepository();

이런 식으로 인터페이스에 의존하는 듯 하지만, 구현체에 의존하게 됨.

구현체에 의존하기 때문에, repository 객체 변경이 이루어질 때 클라이언트도 영향을 받게 된다.

이를 해결하기 위해 구현체에 의존하는 부분을 제거하고 생성자를 통해 외부에서 주입 받을 수 있도록 변경

  • 이를 의존성 주입 DI 라고 한다.

애플리케이션 실행 시점에 외부에서 실제 구현 객체를 만들어 클라이언트에 전달하고

클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입
객체를 생성하고 관리하면서 의존관계를 연결해 주는 것 → DI 컨테이너

추가


IoC컨테이너라고 하는데 보통 제어의 역전이라고 말들을 한다.

컨테이너를 사용하지 않는다면 생성을 순서대로 해야 하는데 (컨트롤러 만들고 서비스 만들고 … )

IoC 컨테이너에서는 반대로 생성한다. (서비스 만들고 컨트롤러 .. 그래야 주입할 수 있으니까)

스프링 컨테이너와 빈


ApplicationContext applicationContext

= new AnnotationConfigApplicationContext(AppConfig.class);
`` 스프링 컨테이너를 만드는 코드, ApplicationContext가 스프링 컨테이너 어노테이션 기반의 구현체에 AppConfig 클래스 정보를 넘겨준다.

스프링 컨테이너

@Configuration - 설정을 구성한다는 뜻의 어노테이션

@Bean - 스프링 컨테이너(DI 컨테이너)에 빈으로 등록함

스프링 컨테이너는 Configuration이 붙은 클래스를 설정 정보로 하여 내부에 @Bean 이 붙은걸 스프링 컨테이너에 등록한다

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MyMemberRepository();
    }
}

스프링 컨테이너는 파라미터로 넘어온 설정 정보를 사용해서 스프링 빈을 등록

빈 이름 (메서드 이름) | 빈 객체

memberService | MemberServiceImpl

빈 이름은 중복되어서는 안된다

빈이 등록된 이후에 의존관계 설정을 해준다

BeanFactory 스프링 컨테이너의 최상위 인터페이스

ApplicationContext가 BeanFactory기능을 상속받아 사용함

BeanDefinition : Bean에 대한 정보를 추상화(빈 설정 메타정보)

추상화에만 의존하도록 설계함

XML, 자바 코드를 읽어서 → BeanDefinition을 만들면 된다.

@Bean 하나씩 메타 정보가 생성된다

스프링 컨테이너는 이 메타정보를 가지고 스프링 빈을 생성한다.

Appconfig.class 읽어서 BeanDefinition ( 빈 메타정보 ) 을 만들어냄

영한쌤: ApplicationContext가 스프링 컨테이너다!!!

스프링 컨테이너를 생성할 때 -⇒ 구성 정보를 지정해주어야 한다!!

구성정보를 보고 빈 생성 여부를 결정

싱글톤 컨테이너


싱글톤 - 클래스의 인스턴스가 한개만 생성되도록 하는 디자인 패턴

싱글톤을 쓰게 되면 요청이 올 때마다 새로운 객체를 생성하지 않고 기존에 가지고 있는 객체를 사용한다

문제점

  • 순수 자바 코드로 싱글톤을 작성하면 코드가 길어진다
  • 클라이언트가 구체 클래스에 의존
  • 유연성이 떨어진다

스프링 컨테이너는 기본적으로 싱글톤 방식으로 Bean을 관리함

주의점

  • 싱글톤 객체는 stateless로 설계해야 한다

스프링 컨테이너는 싱글톤을 유지할 수 있을까??

@Configuration 이 그 역할을 해준다. @Configuration 어노테이션이 붙은 AppConfig 클래스 역시 빈으로 등록이 되는데, 이 때 AppConfig 는 스프링이 빈에 등록할 때 임의로 다른 클래스로 만들고 그 클래스를 빈 으로 등록한다. 덕분에 내부에서 싱글톤 조작을 할 수 있는거다. 만약 @Configuration 이 없으면 싱글톤으로 동작을 안한다.

스프링 설정 정보는 항상 @Configuration 을 사용하자

컴포넌트 스캔


빈을 등록할 때 직접 @Bean 을 붙여서 등록했지만 이런 방식은 규모가 커지면 불편해진다

스프링은 컴포넌트 스캔이라는 방식을 제공

설정 정보가 없어도 자동으로 빈을 등록할 수 있게 해주고 의존관계도 자동으로 주입해주는 @Autowired 등장

@Component 어노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록

생성자에 @Autowired 를 붙여주어 클래스 내에서 의존 관계 주입 - 스프링 컨테이너가 스프링 빈을 찾아서 주입한다.

스프링 부트의 시작 정보인 @SpringBootApplication 이 프로젝트 시작 루트에 있어 하위 패키지에서 컴포넌트 스캔을 진행한다. SpringBootApplication 어노테이션 안에 ComponentScan 어노테이션이 있다

스캔 대상

@Component : 컴포넌트 스캔에서 사용
@Controlller : 스프링 MVC 컨트롤러에서 사용
@Service : 스프링 비즈니스 로직에서 사용

@Repository : 스프링 데이터 접근 계층에서 사용 + 데이터 계층 예외를 스프링 예외로 변환
@Configuration : 스프링 설정 정보에서 사용

빈 이름 충돌나면 에러!!

의존관계 자동 주입


  1. 생성자 주입
  2. 수정자 주입
  3. 필드 주입
  4. 일반 메서드 주입

생성자 주입을 선택해라

대부분의 의존관계는 한 번 일어나면 애플리케이션 종료시까지 변경되지 않는다. (변경되면 안된다)

수정자 주입은 변경 메서드를 public 으로 둬서 다른 이가 변경할 수 있지만, 생성자 주입은 객체 생성시 한 번만 호출됨

필드에 final 키워드를 넣어줘 컴파일 시점에 생성자에 값이 설정되지 않는 오류를 막을 수 있다

롬복 사용


일일이 생성자에다가 @Autowired 해주는 작업이 불편해서 이를 단순화시킴

롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 컴파일 시점에 생성자를 자동으로 만들어준다

같은 타입의 빈이 2개 이상일 때 자동 주입


같은 타입의 빈이 2개 이상일 때 의존관계 자동 주입 설정을 하면 유니크 하지 않다는 오류가 발생

3가지 방법이 있다.

자동 수동 올바른 실무 운영


업무로직 (컨트롤러, 서비스, 레포지토리) 에는 자동 기능 적극 사용

기술로직 (AOP, DB 커넥션, 공통 로그 처리) 에는 수동 기능 사용하여 명확하게 드러나도록

애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 딱! 설정 정보에 바로
나타나게 하는 것이 유지보수 하기 좋다.
다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민

스프링 컨테이너

ServletContainer와 SpringContainer는 무엇이 다른가?

스프링부트는 어떻게 다중 유저 요청을 처리할까? (Tomcat9.0 Thread Pool)

정리

컨트롤러 1개는 어떻게 수십 만개의 요청을 처리하는가?

WAS 의 대표 - Tomcat

Tomcat은 다중 요청을 처리하기 위해서, 부팅할 때 스레드의 컬렉션인 Thread Pool을 생성

(default = 200)

  • 스레드 풀이 뭐야?
    • 스레드를 요청마다 생성하고 파괴한다면 OS와 JVM에 많은 부담, 동시에 여러 요청이 들어오면 너무 많은 스레드가 생겨 자원이 부족할 수 있음 → 서버의 다운 (톰캣 옛날 버전) 스레드를 제한된 수만큼 정해 놓고, 작업 큐에 들어오는 작업들은 스레드가 하나씩 맡도록 하는게 스레드 풀
  • 스레드 풀 플로우
    1. 코어 크기만큼 스레드를 생성
    2. 유저 요청이 들어오면 작업 큐에 담아둠
    3. 사용 가능한(idle) 스레드가 있다면 작업 큐에서 작업을 꺼내 스레드에 작업을 할당
      1. 스레드가 모두 쓰고 있다면 작업은 작업 큐에서 대기
      2. 작업 큐가 꽉 차게 된다면 새로운 스레드 생성
      3. 스레드가 최대 사이즈에 도달하고 작업큐도 꽉 찬다면 connection-refused 오류 반환
    4. 작업이 완료되면 스레드 원상태로 복귀,
      1. 코어 크기보다 스레드가 많이 생성되어 있다면 스레드를 파괴

톰캣은 HttpRequest 가 들어왔을 때 하나씩 스레드를 재사용 및 재배정 진행

Request 별로 스레드가 별도로 생성

  • ServeltContext가 뭐야??
    • 웹 애플리케이션 단위로 정보를 서버 쪽에 유지할 수 있는 방법으로 사용되는 인터페이스
    • 서블릿 컨테이너가 시작될 떄 웹 서버에 등록된 웹 애플리케이션 단위로 하나의 ServletContext 객체가 자동으로 생성된다. 웹 애플리케이션이 중지되면 소멸한다.
    • 톰캣 하나에 여러 웹 애플리케이션을 등록할 수 있는거구나..?
    • 웹 애플리케이션 단위로 컨텍스트가 생성되는 이유는 서블릿 컨테이너가 웹 애플리케이션 단위로 모든 자원을 관리할 수 있게 하기 위해서

컨테이너는 하나의 서블릿에 대해 다중 요청을 처리하기 위해 다중 스레드를 돌린다

이 스레드들이 하나의 Controller 객체를 공유하는 것이 가능한가??????

막 동기화 시켜야 하는거 아니여???

→ 컨트롤러 객체 하나를 생성하면 이는 Heap 에 생성되지만, 해당 class의 정보는 메소드 영역에 저장

모든 스레드가 객체의 정보를 공유할 수 있다 컨트롤러가 내부적으로 상태를 가지는게 없으니 그냥 메소드 호출만 하면 된다. 굳이 동기화할 필요가 없다!!!

서블릿 컨테이너와 스프링 컨테이너


서블릿 컨테이너

서블릿을 생성하고 관리하는 역할을 수행.

  1. 웹 브라우저에서 웹 서버에 HTTP request를 보내면 웹 서버는 받은 HTTP 요청을 WAS 서버의 웹 서버에 전달
  2. WAS 서버의 웹 서버는 HTTP 요청을 서블릿 컨테이너에 전달
  3. 서블릿 컨테이너는 HTTP 요청 처리에 대한 서블릿 인스턴스가 힙 메모리에 있는지 확인, 존재하지 않는다면 서블릿 인스턴스를 생성하고 이 서블릿 인스턴스의 init 메소드를 호출하여 초기화
  4. 서블릿 컨테이너는 서블릿 인스턴스의 service() 메소드를 호출하여 HTTP 요청을 처리, WAS 서버의 웹 서버에게 처리 결과를 전달
  5. WAS 서버의 웹 서버는 HTTP 응답을 웹 서버에게 전달, 웹 서버가 받은 HTTP응답을 웹 브라우저에 전달한다.

HttpRequest가 서블릿 컨테이너에 들어오면 서블릿 컨테이너는 HttpServletRequest, HttpServletResponse 두 객체를 생성한다. 이후 동적 페이지를 생성 후 HttpServletResponse 에 응답을 보낸다.

스프링 컨테이너

Bean 의 생명주기를 관리한다.

  1. 웹 애플리케이션이 실행되면 WAS에 의해 web.xml이 로드된다.

  2. web.xml에 등록되어 있는 ContextLoaderListener가 Java Class 파일로 생성된다.

    ContextLoaderListener가 ApplicationContext 를 생성해줌

    Servlet Context는 Spring Bean에 접근하려면 Application Context를 참조해야 한다

    ApplicationContext도 ServletContainer에 단 한 번만 초기화되는 Servlet이다

  3. 스프링 컨테이너가 구동 될 때 개발자가 작성한 비즈니스 로직과 dto, vo 등의 객체가 생성된다.

  4. 클라이언트로 부터 요청이 들어오면 Dispatcher Servlet 으로 들어가게 되는데, 스프링의 디스패처 서블릿도 서블릿이여서 한번만 생성되고, web.xml 에 서블릿이 등록되어 있으니 모든 요청이 오면 디스패처 서블릿으로 가라고 등록시켜둔다.

  5. 요청에 따라 핸들러 매핑을 통해 적절한 Controller 를 찾는다

Spring MVC 도 결국 서블릿 컨테이너가 관리하는 서블릿 한개

Spring MVC로 들어가는 모든 요청과 응답을 Dispatcher Servlet이 관리

Servlet의 종류에는 @WebServlet과 Spring @Bean이 있다. 어디서 관리하느냐에 차이다. Tomcat이 관리하냐? Spring이 관리하냐? Bean의 경우에는 POJO와 설정(Configuration Metadata)을 Spring이 제공하는 Container(DI Container, 또는 IoC Container)에 주입시키면 Bean으로 등록되고 사용이 가능하다. 결국 Spring을 쓴다는 것은 Spring으로 Servlet을 다루겠다는 뜻이다. Spring MVC 역시 Servlet Container가 관리하고 있는 Servlet이다.

profile
안녕하세요 ^^

0개의 댓글