반응형

출처: https://kisukpark.wordpress.com/2013/08/29/spring-mvc-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90/


Spring의 등장 배경

Java는 처음 Enterprise용 소프트웨어를 목적으로 개발되었다. 그래서 자바로 만들어지는 소프트웨어들은 개인이 만드는 것 보다 크고 복잡했으며 또한 무거웠다. 이러한 자바 프로그램의 복잡도를 줄이고자 Spring 프레임워크가 등장하였다.

Spring

Spring은 Java 기반의 프레임워크로서 여러 모듈로 구성되어있다. 하지만 Spring은 단순 프레임워크를 넘어 자신만의 철학을 지닌다. 즉, 프레임워크와 철학을 합쳐 Spring이라 부른다.

Spring은 일종의 Container 역할을 하는 프레임워크로 사용자가 필요한 모듈을 꽂아 사용할 수 있게 해준다. “틀이 되어 주겠다.”

  • Spring은 Java 객체를 담고 있는 경량 컨테이너이다. 이들 Java 객체의 생성, 소멸과 같은 라이프 사이클을 관리하고 Spring으로 부터 필요한 객체를 가져와 사용 할 수 있다.

Spring의 철학

철학설명
IoC(Inversion of Control)‘제어의 역전’으로서 기존에 어플리케이션이 프레임워크의 서비스를 콜하는 대신에 프레임워크가 어플리케이션의 각 컴포넌트들을 부르는 형태이다. 제어이 어플리케이션 그 자체에서 프레임워크로 넘어갔다고 하여 제어의 역전이라 한다.
DI(Dependency Injection)IoC를 실제로 구현하는 방법으로서 의존성있는 커포넌트들 간의 관계를 개발자가 직접 코드로 명시하지 않고 컨테이너인 Spring이 런타임에 찾아서 연결해주게 하는 것이다.

Spring IoC, DI 예제

Spring MVC Module

Spring 의 여러 module 중에서 웹에도 기존 소프트웨어에서 사용되는 MVC 패턴을 적용해 웹 어플리케이션을 만들고자 등장한게 Spring MVC module 이다.

기존에 servlet 원래의 spec에 맞춰 servlet을 개발하려고 하면, doGet(), doPost() 등의 함수를 생성해 주는 등의 공통적이고 반복적인 작업을 많이 해야했다.

그리하여 개발자가 직접 모든 웹 컴포넌트를 부르는 대신 그것보다 상위의 framework 가 있어 이러한 컴포넌트들을 자동으로 불러주고, 반복적인 작업을 줄여주기위해 Spring MVC Module이 등장하였다. Spring MVC Module 로 만들어진 프로그램들은 그 Spring MVC Module 내부에 Servlet(Dispatcher Servlet)을 포함하고 개발시 이전에는 일일이 해야했던 공통된 부분을 따로 하지 않아도 자동으로 되었다. 즉, Spring MVC을 통해 만들어진 프로그램은 그 내부에 Servlet을 포함하고 그 자체가 Tomcat과 같은 Servlet Container 위에 올라가 동작하는 Servlet 역할을 한다.

왜 Spring MVC가 필요한가?

ex) http://kisukworld.com/notice 라는 URL이 서버에 요청되었을 때

MVC가 적용되기 전의 웹 코드
package mypkg;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloServlet extends HttpServlet {
    /* DB에 접근할 Data Access Object(DAO) */
    NoticeDB db = new NoticeDB();

    @Override
    public void doGet(HttpServletRequest request, 
                      HttpServletResponse response)
        throws IOException, ServletException {

        //response.setContentType("text/html;charset=UTF-8");
        //PrintWriter out = response.getWriter();

	/* db 라는 DAO를 직접 사용해 데이터를 가져온다. */
	List<Notice> notices = new List<Notice>;
	notices = db.getNotices();

	/* URL 마다 분기점을 만들어 처리해 주어야 한다. */
  	if (request.getRequestURL().equals("/notices") {
   	    try {
		/* 직접 View 코드를 만들어 준다. */
		out.println("<!DOCTYPE html>");
   	        out.println("<html>");
   	        out.println("<body>");
       	  	out.println("<h1>Hello, world!</h1>");
       		out.println("</body>");
       		out.println("</html>");
	    } 
            finally {
		out.close();
    	    }
	}
    }
}

문제)

  • URL 마다 분기 점을 만들어 줘야 하므로 처리가 느리다.
  • 직접 NoticeDB 클래스의 객체를 사용하기 때문에 DB가 바뀌거나 하면 수정이 어렵다.
  • Model, View, Controller가 다 섞여 있다. -> 유지 보수가 어렵다.
Spring MVC가 적용된 웹 코드
// NoticeDAO.java

public interface NoticeDAO
{
    public List<Notice> getNotices();
}
  • NoticeDAO 라는 interface를 만들어 준다.
  • DAO를 interface로 만드는 이유는 DB마다 공통된 인터페이스를 적용하고 유지 보수를 쉽게 하기 위해서이다.
//NoticeDAOImpl.java

@Repository
public class NoticeDAOImpl implements NoticeDAO
{
    public List<Notice> getNotices()
    {
	//some code
    }
}
  • @Repository : 이 클래스는 DAO를 구현한 클래스임을 나타내는 Annotation.
//NoticeController1.java

public class NoticeController1()
{
    @Autowired
    private NoticeDAO noticeDAO;

    @RequestMapping("/notice")
    public List<Notice> getNotice()
    {
	List<Notice> notices = new List<Notice>;
	notices = noticeDAO.getNotices();

	//...

	return notices;
    }
}
  • @Autowired : 특정 클래스를 명시하지 않아도 Spring이 자동으로 NoticeDAO를 구현한 클래스를 연결해 준다.
  • @RequestMapping : 통해 분기문 없이 특정 URL의 요청이 왔을 때 Spring이 자동으로 이 메소드를 호출해준다. 통상적으로 한 URL에 대해 RequestMapping된 메소드는 한 개.
  • 메소드 안에서는 noticeDAO라는 객체에 대해 알 필요없이 interface가 제공하는 메소드를 사용하면된다.
//NoticeController2.java

public class noticeController2()
{
    @Autowired
    private NoticeDAO noticeDAO;

    @RequestMapping("/notice/{user_id}")
    public List<Notice> getNotice(
        @RequestParam("key1")String val1, 
        @PathVariable("user_id")Integer id)
    {

    }

    /* Notice에 관심있는 다른 메소드 */
    public void removeNotice(String title)
    {

    }
}
@RequestMapping(“/notice”) , @RequestMapping(“/notice/{user_id}”)  에서 두개의 URL은 다른 것이므로 각각이 붙은 메소드가 동시에 존재 가능하다. 동일한 URL을 다른 메소드에 매핑한다면 매핑된 모든 메소드가 실행된다. 단, 순서는 랜덤(비추)

Spring MVC Servlet 이 포함된 웹 서버의 구동방식

Image

Spring MVC가 없는 Servlet과 비교해서 원래의 Servlet 위치에 Spring MVC Module을 이용해 만들어진 프로그램이 하나 이상의 Servlet으로 위치해 있다. 아래의 그림은 두 개의 서블릿(Dispatcher Servlet)이 있고 Servlet Container(ex. Tomcat) 가 URL에 맞게 각 서블릿으로 요청을 보내는 것을 보여준다.

Image

일단 Servlet Container가 URL 요청에 맞게 Dispatcher Servlet을 골라 요청을 보내준 후(“/notice”는 위쪽 서블릿으로, “/board”는 아랫 쪽 서블릿으로) 에는 Spring Container 내부에서 아래의 작업이 진행된다.

Spring MVC module 내부의 작동

  1. 서블릿이 요청을 받는다(앞에서 Servlet Container가 적절한 서블릿으로 보낸준 것)
  2. Handler Mapping 을 통해서 요청을 처리할 Controller를 찾는다.
  3. 해당 Controller로 요청을 보낸다.
  4. Controller 로 부터 ModelAndView를 반환 받는다.
  5. 반환받은 View Name 으로 View Resolver를 통해 View를 찾는다.
  6. 찾은 View 파일에 Controller가 만들었던 Model을 주어 View를 완성하게 한다.
  7. 이 완성된 뷰가 Response가 되어 Client로 전해 진다.

Spring Container(=Application Container) 의 역할

  • Singleton 의 bean들을 관리한다.(Handler Mapper, Controller, View Resolver, View) : 요청 마다 매번 새로 객체를 만드는 것 보다 Singleton으로 하나 만들어 두고 재 사용하는게 성능에 도움이 된다.
  • 개발자가 직접 Dispatcher Servlet과 각 bean 들 사이의 의존성을 명시적으로 코드로 나타내지 않아도 Dependency Injection(DI) 으로 각 컴포넌트 사이의 연결을 만들어 준다.


반응형

+ Recent posts