이번에 동아리에서 이 책을 이용하여 스터디를 진행하게 되었다. 

스터디를 진행하면서 책을 읽은 내용을 정리해보려고 한다. 

 

Chapter 1,2는 이미 간단하게 진행해서 추후에 내용을 다시 정리해서 작성하고, Chapter3 부터 정리한다. 

 

교재의 상세한 코드는 아래의 링크에서 확인 가능하다. 

https://github.com/thecodinglive/JPub-JavaWebService

 

GitHub - thecodinglive/JPub-JavaWebService: here is book example

here is book example. Contribute to thecodinglive/JPub-JavaWebService development by creating an account on GitHub.

github.com


Chapter 3 스프링 프레임워크 

1. 빈 + 컨테이너

스프링은 객체를 관리해주는 빈 컨테이너 프레임워크이다.

 

1) 웹 관리 컨데이너

- 웹 프로젝트에서 서버가 실행되는 동안 여러 가지 일을 개별적으로 처리하는 것은 힘듦. 

   -> 자바 빈을 대신 관리해주는 컨테이너를 찾음 

   -> EJB를 사용하게 됨 (웹 관리 컨테이너의 시작)

- 개발의 요구사항이 복잡해짐에 따라 경량화되고 간소화된 컨테어너 선호

   -> EJB가 필요한지 고민하게 됨

   -> POJO(일반 자바 클래스)를 사용하기 시작

 

2) 스프링

- 로드 존슨에 의해 개발됨

- 웹 애플리케이션 컨테이너와 상관없이 독립적으로 빈의 생명주기를 관리 가능

- Spring.IO에 스프링 기반으로 추가된 프로젝트들을 공개 중

- 다양한 모률로 확장 중 -> Spring Core Container를 통해 모듈 간의 의존도를 최소화했기 때문 

- 의존도를 낮추는 방법 중 하나가 IoC 패턴


2. IoC 패턴 활용 

- IoC(Inversion of Control) : 제어의 역전이라고 부름

- 프로그맴의 생명 주기에 대한 주도권이 웹 애플리케이션 컨테이너에 있기 때문에 IoC 패턴이 웹 개발에서 유용

- 웹 개발 시 데이터 베이스 처리, 파일 처리 인스턴스를 관리해야함

   -> 일괄적으로 인스턴스를 관리하기 위해 객체 생성을 관리하는 도구가 필요함 (DIP:의존 관계 역전 원칙)

 

의존관계 역천 원칙 DIP

  • high-level 모듈은 low-level 모듈에 의존하지 않고 인스턴스에 의존해야한다. 
  • 세부사항이 추상화에 의존해야한다. (그 반대는 안된다) -> 인터페이스 활용하여 결합도를 낮춘다.

의존성 주입 DI 

- 인터페이스를 사용하더라도 인스턴스화를 위해서는 객체 생성에 필요한 코드가 수반됨

   -> 결합도를 완전히 분리할 수 없음 

   -> 이 문제를 해결하는 것이 DI

- 대신 의존성을 주입해준다. 

 

1) 인터페이스와 스프링

인터페이스를 활용한 예제

Boss와 Employee라는 2개의 클래스는 WorkManager 인터페이스를 상속 받아 doIt 메서드를 가지고 있다. 

public class Boss implements WorkManager{
	@Override
    public String doIt(){
    	return "do boss";
    }
}
public class Employee implements WorkManager{
	@Override
    public String doIt(){
    	return "do work";
    }
}

 

Boss와 Employee 클래스가 일하는 WorkerService를 만든다. 

@Setter
public class WorkService{
    // 참고로 이런식으로 인스턴스 변수를 가지는 것을 디자인 패턴에서 strategy pattern이라고 한다.
	WorkManager workManager; 
    
    public void askWork(){
    	System.out.println(workManager.doIt());
    }
}
public class BasicApp{
	public static void main(String ar[]){
    	WorkService workService = new WorkService();
        WorkManager employee = new Employee();
        WorkMagaer boss = new Boss();
        
        workService.setWorkManager(employee);
        workService.askWork();
        
        workService.setWorkManager(boss);
        workService.askWork();
    }
}

실제로 실행해보면 workService의 askWork의 출력결과가 setter에 따라 다르다는 것을 확인할 수 있다. 

 

2) 스프링 xml 설정 

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="boss" class="basic.Boss" init-method="onCreated" destroy-method="onDestroyed"></bean>
    <bean id="employee" class="basic.Employee" init-method="onCreated" destroy-method="onDestroyed"></bean>

    <bean id="myWorkService" class="basic.WorkService">
        <property name="workManager">
            <ref bean="boss"/>
        </property>
    </bean>

    <bean id="yourWorkService" class="basic.WorkService">
        <property name="workManager">
            <ref bean="employee"/>
        </property>
    </bean>

</beans>

 

중요한 것은 스프링 applicationContext.xml에 bean 태그를 통해 boss, employee, myWorkService, yourWorkService를 빈으로 등록할 수 있다. 빈으로 등록한 객체에 대해서는 스프링이 대신 인스턴스를 생성하기 때문에 new 연산자를 통해 인스턴스를 생성하지 않아도 된다. 

 

public class BasicApp{
	public static void main(String ar[]){
    	GenericXmlApplicationContext context = new GenericXmlApplicationContex("classpath:applicationContext.xml");
        
    	WorkService yourWorkService = context.getBean("yourWorkService", WorkService.class); // xml 상에서 구현체를 employ로 명시함
        yourWorkService.askWork();
        
    	WorkService WorkService = context.getBean("myWorkService", WorkService.class); // xml 상에서 구현체를 boss로 명시함
        WorkService.askWork();
    }
}

개별적으로 new를 사용하여 객체를 생성하지 않고 스프링에서 bean으로 등록한 다음 참조하여 사용한다. 

 

XML 설정시 빈 생명주기 제어

스프링에서 객체를 빈으로 등록하거나 제거할 때 콜백 메서드를 등록할 수 있다. 

  • init-method : 스프링으로 클래스가 최기화되면 호출될 메서드를 추가한다. 설정할 클래스에 onCreated 메서드를 추가하고, xml파일에서 init-method="onCreated"를 추가한다. 
  • destory-method : 클래스 소멸시에 호출되는 메서드를 추가한다. 설정할 클래스에 onDestroyed 메서드를 추가하고, xml 파일에서 destroy-method="onDestroyed"를 추가한다. 

 

3) 스프링 JavaConfig 설정

@Configuration 어노테이션을 클래스 상단에 추가하고, @Bean 태그를 통해 매서드를 추가할 수 있다. 

@Configuration
@Import(CompanyConfig.class)
public class BeanConfig {
    @Bean
    public WorkManager employee() {
        return new Employee();
    }

    @Bean
    public WorkManager boss() {
        return new Boss();
    }

    @Bean
    public WorkService yourWorkService() {
        WorkService workService = new WorkService();
        workService.setWorkManager(employee());
        return workService;
    }

    @Bean
    public WorkService myWorkService() {
        WorkService workService = new WorkService();
        workService.setWorkManager(boss());
        return workService;
    }
}
public class JavaConfigSpringApp {
    public static void main(String ar[]){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(BeanConfig.class);
        context.refresh();

        WorkService yourWorkService = context.getBean("yourWorkService", WorkService.class);
        yourWorkService.askWork();

        WorkService myWorkService = context.getBean("myWorkService", WorkService.class);
        myWorkService.askWork();

        context.close();
    }
}

 

AnnotationConfigApplicationContext의 인스턴스를 사용하여 빈을 로드하는 것 외에 다른 부분은 동일하다. 

 

@Import

@Import 어노테이션을 사용하여 설정 내용을 파일별로 분리하여 사용할 수 있다. 

예를 들어 CompanyConfig.java도 bean 설정 파일로 사용하는 경우,

@Import(CompanyConfig.class)를 BeanConfig 클래스 상단에 추가하면 된다. 

 

어노테이션 설정 시 생명주기 제어

  • @PostConstruct : 빈이 초기화될 때 호출된다. 추가할 클래스의 메서드 상단에 어노테이션을 추가한다. 
  • @PreDestroy : 종료될 때 호출된다. 마찬가지로 메서드 상단에 어노테이션을 추가하여 설정한다. 

3. Spring MVC

1) 스프링 MVC 구조 

스프링 MVC 구조는 DispatcherServlet, View Resolver, Interceptor, Handler, View 등으로 구성됨

 

사진 출처 - [Spring] Spring의 MVC 패턴과 MVC1과 MVC2 비교 ChanBLOG (chanhuiseok.github.io)

 

전체적인 흐름

DispatcherServlet이 요청을 받으면 그 요청를 처리할 수 있는 Handler의 이름을 HandlerMapping에 물어봄 

-> HandlerMapping은 요청 URL을 보고 Handler를 판단하고 Handler 실행 전에 전처리, 후처리로 실행할 인터셉터 목록을 정함 

-> DispatcherServlet은 제어권을 Handler로 전달

-> Handler는 응답에 필요한 서비스 호출, 렌더링할 ViewName을 ViewResolver에 전달

-> ViewResoler는 응답에 필요한 View 생성

-> VIiew에 Model과 컨트롤러를 전달하여 응답 생성 

-> 응답을 클라이언트에 반환

 

스프링 MVC에서는 DispaterServlet을 web.xml에 등록하고 요청받은 루트 url를 매핑하는 것만으로 해당 역할 대체 가능 

 

2) 스프링 MVC 설정 및 DispatcherServlet 설정 

(자세한 코드는 분석하지 않고 넘어간다. )

  1. web.xml에 DispatcherServlet을 설정한다. 
  2. dispatcher-servlet.xml 파일에 인터셉터, 컨트롤러 설정을 bean으로 지정 
  3. xml 스키마 정의 파일에 대한 정보도 지정함.
  4. view이름을 처리할 viewResolver를 bean으로 등록함 

 

3) 컨트롤러와 뷰

@Controller
public class IndexController {

	@RequestMapping("/")
    public ModelAndView home(){
       // return new ModelAndView("home");
        ModelAndView mv=new ModelAndView("home");
        mv.addObject("title", "Jpub Spring WEB");
        mv.addObject("today", new Date().toString());

        return mv;
    }
}

어노테이션을 통해 Controller로 설정한다. 

@RequestMapping을 통해 controller가 실행될 url을 정의할 수 있다. 

예를 들어 @RequestMapping("home")은 localhost/home에 접속할 경우 호출된다.

이 예제에서는 루트 즉 localhost 접속시 해당 컨트롤러가 호출 된다. 

 

ModelAndView를 통해 데이터에 해당하는 모델과 view를 지정할 수 있다. 

이 예제에서는 호출할 뷰 이름을 home으로 지정하여 localhost에 접속할 경우 home.jsp의 뷰가 출력될 것이다. 

 

addObject를 통해 모델의 이름과 데이터를 뷰에 전달할 수 있다. 

map과 유사하게 (key, value) 형태로 사용할 수 있다. 

이 예제의 경우 view에서 title 모델을 사용하면 Jpub Spring WEB이 출력되고, 

today 모델을 사용하면 today의 값인 현재 시간이 출력된다. 

즉 view에서 key 값을 통해 value를 사용할 수 있다. 

 

4) 인터셉터 

- 인터 셉터를 이용하여 컨트롤러가 요청을 처리하기 전과 후의 로직을 추가할 수 있다. 

- dispatcher-servlet.xml에 인터셉터 태그를 추가하여 설정할 수 있다. 

- mapping 태그에 요청받을 경로를 속정으로 정의할 수 있다. 

'Spring > 스프링 부트로 배우는 자바 웹 개발' 카테고리의 다른 글

Chapter 4  (0) 2021.11.07

+ Recent posts