HTTP 프로토콜 서비스를 지원하는 javax.servlet.http.HttpServlet 클래스를 상속
urlPatterns으로 지정한 /hello의 URL이 호출되면 service 메서드가 실행
HttpServletRequest을 통해 HTTP 요청 정보를 꺼내서 사용
HttpServletResponse객체에 HTTP 응답 정보를 입력
서블릿 특징
클라이어트의 요청에 대해 동적으로 작동하는 웹 애플리케이션 컴포넌트이다.
HTML을 사용하여 요청에 응답한다.
자바 Thread를 이용하여 동작한다.
MVC 패턴에서 컨트롤러로 이용된다.
UDP보다 처리 속도가 느리다.
HTML 변경 시 서블릿을 재컴파일해야 하는 단점이 있다.
서블릿 컨테이너(Servlet Container)
톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너이라고 부른다. 서블릿 컨테이너란 서블릿 객체를 자동으로 생성하고, 호출하고, 서블릿의 생명주기까지 다 관리해주는 컨테이너다. 서블릿 컨테이너는 서블릿 객체를 싱글톤으로 관리하며, 최초 로딩 시점에 서블릿 객체를 미리 만들어두고 재활용한다. 하지만 Request,(요청) Response(응답) 객체는 요청마다 새로 생성된다.
주요 기능
서블릿 생명주기 관리
웹 서버와의 통신 지원
동시 요청을 위한 멀티 쓰레드 처리 지원 및 관리
선언적인 보안 관리
서블릿 동작 방식
클라이언트가 URL을 입력하면 HTTP 요청이Servlet Catainer로 전송한다.
서블릿 컨테이너는 관련된 Servlet을 메모리에 올린다.
서블릿 컨테이너는 web.xml을 참조하여 해당 Servlet에 대한 Thread를 생성하고, HttpServletRequest(요청 정보가 저장된), HttpServletResponse(비어 있는) 객체를 생성하여 전달한다.
Thread는 Servlet의 service() 메서드를 호출한다.
service() 메서드는 요청에 맞게 doGet() 또는 doPost() 메서드를 호출한다.
doGet() 또는 doPost() 메서드는 인자에 맞게 생성된 동적 페이지를HttpServletResponse객체에 담아 응답을 보낸다.
응답을 처리하면 생성된 Thread를 종료하고, HttpServletRequest와 HttpServletResponse 객체를 소멸시킨다.
DiscountService는 Map으로 모든 DiscountPolicy 타입의 빈들을 주입받는다.
discount() 메서드는 discountCode로 스프링 빈 이름이 넘어오면 map에서 해당 이름을 가진 스프링 빈을 찾아서 실행한다.
주입 분석
Map<String, DiscountPolicy>: map의 키에 스프링 빈의 이름을 넣어주고, 그 값으로 DIscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
List<DiscountPolicy>: DiscountPolicy 타입으로 조회한 모든 스프링 빈을 담아준다.
만약 해당하는 타입의 스프링 빈이 없으면, 빈 컬렉션이나 Map을 주입한다.
참고. 위의 코드에서 생성자는 딱 1개이므로 @Autowired를 생략해서 의존관계가 자동 주입된다.
3. 자동, 수동의 올바른 실무 운영 기준
편리한 자동 기능을 기본으로 사용하자!
그렇다면 수동 등록할 때는 언제일까?
직접 기술 지원 객체를 등록할 때
애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 설정 정보에 바로 보이게 하는 것이 유지보수 하기 좋다.
비지니스 로직 중에서 다형성을 적극 활용할 때
위에서 조회한 빈이 모두 필요할 때, List, Map을 보면, DiscountService는 의존관계 자동 주입으로 DiscountPolicy 타입의 모든 빈을 주입받는다. 이런 경우 DiscountPolicy 의 구현 빈들만 따로 수동 등록하면, 설정 정보만 봐도 한 눈에 어떤 빈들이 주입될지 파악할 수 있다.
할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수도 있다. (미확정)
2. 개발
순수하게 자바 코드만 사용해서 개발
역할(인터페이스)과 구현(구현체)을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계했다. 덕분에 회원 저장소는 물론이고, 할인 정책도 유연하게 변경할 수 있다.
새로운 할인 정책 개발
유연한 설계가 가능하도록 객체지향 설계 원칙을 준수하며 개발했다.
기존의 할인 정책인 FixDiscountPolicy 대신 새로운 할인 정책 RateDiscountPolicy로 변경하면 된다.
publicclassOrderServiceImplimplementsOrderService{
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();privatefinal DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
문제점 발견
우리는 역할과 구현을 충실하게 분리했다. -> OK
다형성도 활용하고, 인터페이스와 구현 객체를 분리했다. -> OK
OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수했다 -> NO
DIP 위반: 추상(인터페이스) 뿐만 아니라 구체(구현) 클래스에도 의존하고 있다.
OCP 위반: 지금 코드는 기능을 확장해서 변경하면, 클라이언트 코드에 영향을 준다!
의존관계
기대했던 의존관계실제 의존관계
클라이언트인 OrderServiceImpl 이 DiscountPolicy 인터페이스 뿐만 아니라 FixDiscountPolicy 인 구체 클래스도 함께 의존하고 있다. 실제 코드를 보면 의존하고 있다! DIP 위반
정책 변경
FixDiscountPolicy 를 RateDiscountPolicy 로 변경하는 순간 OrderServiceImpl 의 소스 코드도 함께 변경해야 한다! OCP 위반
문제 해결
DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.
publicclassOrderServiceImplimplementsOrderService{
//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();private DiscountPolicy discountPolicy;
}
인터페이스에만 의존하도록 설계와 코드를 변경했다.
❌ 또 다른 문제 발생! -> 구현체가 없어서 실행을 해보면 NPE(null pointer exception)가 발생한다.