Spring Framework 소개

김수민·2023년 4월 2일
0

백엔드 부트캠프

목록 보기
46/52

Framework란?

Framework란

Ralph Johnson: "소프트웨어의 구체적인 부분에 해당하는 설계와 구현ㅇ르 재사용이 가능하게끔 일련의 협업화된 형태로 클래스들을 제공하는 것"
어떤 대상의 큰 틀이나 외형적인 구조를 의미.

frame의 예시

웹의 초창기 시절에는 HTML 문서를 구성하는 태그 중에서 frame이라는 태그가 존재했었음.

<framset cols = "33%,*,33%">
  <frame name="left" src="/left_menu"/>
  <frame name="center" src="/context"/>
  <frame name="right" src="/right_menu"/>
</frameset>

Frameset과 frame 태그를 이용해서 HTML 문서의 틀만 구성하고 있음. 예제 코드에는 나와있지 않지만 HTML 문서의 세부 내용들은 별도의 html 파일에 구성되어 있음.

이미 배웠던 Java에서 framework의 의미를 찾아볼 수 있는데, 바로 collection framework임. Java에서 자주 사용하는 map이나 set, list 등의 Collection들은 데이터를 저장하기 위해 널리 알려져 있는 자료구조를 바탕으로 비슷한 유형의 데이터들을 가공 및 처리하기 쉽도록 표준화 된 방법을 제공하는 클래스의 집합임. Java 클래스의 유형 중에서 기본적인 뼈대로만 구성되어 있는 것은 바로 추상 메서드만 정의되어 있는 인터페이스(interface)임. 그리고 Java에서의 collection은 바로 map, set, list같은 인터페이스와 그 인터페이스들을 구현한 구현체들의 집합임. 결론적으로 프로그래밍상에서의 framework는 기본적으로 프로그래밍을 하기 위한 어떠한 틀이나 구조를 제공한다는 것을 알 수 있음.

Framework를 사용하는 장점

  • 효율적으로 코드를 작성할 수 있음

개발하고자 하는 애플리케이션을 그 밑바닥부터 일일이 전부 개발하는 것이 아니라 서로 다른 애플리케이션 간의 통신이나 데이터를 데이터 저장소에 저장하는 등의 다양한 긴으들 역시 framework이 라이브러리 형태로 제공함으로써 개발자가 애플리케이션의 핵심 로직을 개발하는 것에 집중할 수 있도록 해줌.

  • 정해진 규약이 있어 애플리케이션을 효율적으로 관리할 수 있음

우리가 사용하는 framework의 규약에 맞게 코드를 작성하기 때문에 유지보수가 필요한 경우 더 빠르고 쉽게 문제점을 파악해 수정할 수 있음. 동시에 내가 작업했던 코드를 다른 사람이 수정할 경우에도 이미 framework의 규약에 맞게 작성된 코드이기 때문에 빠르게 코드를 파악하고 수정하기 용이함. 이는 곧 유지보수 이외에도 비슷한 기능을 개발할 때 코드의 재사용이 용이하고 기능의 확장또한 쉽게 확장 가능함.

Framework를 사용하는 단점

  • 내가 사용하고자 하는 Framework에 대한 학습이 필요함

Framework에서 정한 규약들을 학습할 시간이 필요하다는 뜻임. Spring의 경우 Java언어에 대한 이해도 필요하지만 추가로 Spring이라는 framework에 대한 학습이 필요함.

  • 자유롭고 유연한 개발이 어려움

우리가 사용하는 framework의 규약을 벗어나기가 어려움. 이미 만들어진 애플리케이션에서 framework를 변경하거나 유연한 개발을 위해 framwork를 사용하지 않게 변경할 경우 정말 많은 시간과 노력이 필요함.

Framework와 Library의 차이

Library는 애플리케이션을 개발하는데 사용되는 일련의 데이터 및 프로그래밍 코드. / 애플리케이션을 개발할 때 필요한 기능을 미리 구현해놓은 집합체

실생활을 통한 예시

차체를 구성하는 frame과 바퀴 혹은 핸들, 엔진과 같은 다양한 부품들이 모여서 하나의 자동차를 이름. 이 자동차를 구성하고 있는 요소에서 framework는 자동차의 뼈대, 즉 frame을 의미함. 그리고 library는 자동차에서 다양한 기능을 제공하는 부품을 의미함. 자동차가 나아가기 위한 바퀴, 엔진, 또는 밤에 운전하기 위한 라이트, 비가 올 때 필요한 와이퍼 등의 부품에 해당함.

자동차를 구매 후, 부품을 교체할 때 frame은 쉽게 교체할 수 없음. 하지만 와이퍼, 라이트는 언제든지 쉽게 교체가능함. 소프트웨어 관점에서 보면, 한번 정해진 framework를 교체하는 일은 어렵지만, library는 쉽게 교체가 가능하며 필요한 library들을 선택적으로 사용할 수 있다는 의미임. 이를 더 명료하게 표현한다면 애플리케이션에 대한 제어권의 차이가 있음.

코드를 통한 예시

@SpringBootApplication
@RestController
@RequestMapping(path = "/v1/message")
public class SampleApplication {
	@GetMapping
    public String getMessage() { //(2)
    	String message = "hello world";
        return StringUtils.upperCase(message); //(1)
    }
    
    public static void main(String[] args) {
    	SpringApplication.run(SampleApplication.class, args);
    }
}

명확하게 library를 사용하는 부분은 (1)의 StringUtils.uppercase(message)임. StringUtils 클래스는 Apache Common Lang3 라이브러리의 유틸리티 클래스 중 하나인데 애플리케이션이 동작하는 중에 이 SpringUtils 클래스의 upperCase() 메서드의 파라미터로 전달하는 문자열(message 변수)을 대문자로 변환하고 있음.

애플리케이션 코드는 애플리케이션을 개발하는 사람, 즉 개발자가 작성할 것. 이렇게 개발자가 짜 놓은 코드 내에서 필요한 기능이 있으면 해당 라이브러리를 호출해서 사용하는 것이 바로 library임. 즉, 애플리케이션 흐름의 주도권이 개발자에게 있는 것.

main() 메서드 내에 SpringApplication.run() 메서드는 Spring Framework에서 지원하는 기능들인데 이러한 기능들은 라이브러리와는 다르게 코드 상에는 보이지 않는 상당히 많은 일들을 함. (2)의 getMessage() 메서드 내부의 코드처럼 개발자가 ㅔ서드 내에 코드를 작성해두면 Spring Framework에서 개발자가 작성한 코드를 사용해서 애플리케이션의 흐름을 만들어냄. 즉, 애플리케이션 흐름이 주도권이 개발자가 아닌 Framework에 있는 것임.

--> IoC(Inversion of Control, 제어의 역전)

Spring Framework

Spring Framework란?

Spring Framework는 웹 애플리케이션을 개발하기 위한 여러 Framework 중 하나.

Spring Framework의 장점

  • POJO(Plan Old Java Object) 기반의 구성
  • DI(Dependency Injection) 지원
  • AOP(Aspect Oriented Programming, 관점지향 프로그래밍) 지원
  • Java 언어를 사용함으로써 얻는 장점

대부분의 기업들이 기업용 엔터프라이즈 시스템용 애플리케이션 개발에 있어 Framework를 선택할 때 개발 생산성을 높이고 어떻게 하면 애플리케이션의 유지 보수를 좀 더 용이하게 할 것인가에 많은 초점을 맞춤.

기업용 엔터프라이즈 시스템: 기업의 업무(기업 자체 조직의 업무, 고객을 위한 서비스 등)를 처리해주는 시스템. 기업용 엔터프라이즈 시스템은 대량의 사용자 요청을 처리해야하기 때문에 서버의 자원 효율성, 보안성, 시스템의 안정성이나 확장성 등을 충분히 고려해서 시스템을 구축하는것이 일반적

Spring Framework는 개발 생상성을 향상시키고 애플리케이션의 유지 보수를 용이하게 하는 Framework의 기본 목적 그 이상을 달성하게 해줌.

결론: 객체 지양 설게 원칙에 잘 맞는 재사용과 확장이 가능한 애플리케이션 개발 스킬을 향상시킬 수 있음. 보다 나은 성능과 서비스의 안정성이 필요한 복잡한 기업용 엔터프라이즈 시스템을 제대로 구축하기 위한 능력을 기를 수 있음.

Spring Framework를 배워하는 이유

JSP를 이용한 애플리케이션

JSP는 Java Server Page의 약자로 초창기 Java기반의 웹 애플리케이션 개발은 JSP를 통해 이루어짐. JSP 개발 방식은 사용자에게 보여지는 View 페이지쪽 코드와 사용자의 요청을 처리하는 서버쪽 코드가 섞여있는 형태의 개발방식임. 쉽게 말하자면 웹 브라우저를 통해 사용자에게 보여지는 클라이언트 측 html/JavaScript 코드와 사용자의 요청을 처리하는 서버 측 Java 코드가 뒤섞여 있는 방식임.

  • JSP 방식 예제 코드
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<!-- (1) 시작 -->
<%
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");

    System.out.println("Hello Servlet doPost!");

    String todoName = request.getParameter("todoName");
    String todoDate = request.getParameter("todoDate");

    ToDo.todoList.add(new ToDo(todoName, todoDate));

    RequestDispatcher dispatcher = request.getRequestDispatcher("/todo_model1.jsp");
    request.setAttribute("todoList", ToDo.todoList);

    dispatcher.forward(request, response);
%>
<!-- (1)-->
<html>
<head>
    <meta http-equiv="Content-Language" content="ko"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

    <title>TODO 등록</title>
    <style>
        #todoList {
            border: 1px solid #8F8F8F;
            width: 500px;
            border-collapse: collapse;
        }

        th, td {
            padding: 5px;
            border: 1px solid #8F8F8F;
        }
    </style>
    <script>
        function registerTodo(){
            var todoName = document.getElementById("todoName").value;
            var todoDate = document.getElementById("todoDate").value;

            if(!todoName){
                alert("할일을 입력해주세요..");
                return false;
            }
            if(!todoDate){
                alert("날짜를 입력해주세요.");
                return false;
            }

            var form = document.getElementById("todoForm");
            form.submit();

        }
    </script>
</head>
<body>
    <h3>TO DO 등록</h3>
    <div>
        <form id="todoForm" method="POST" action="/todo_model1.jsp">
            <input type="text" name="todoName" id="todoName" value=""/>
            <input type="date" name="todoDate" id="todoDate" value=""/>
            <input type="button" id="btnReg" value="등록" onclick="registerTodo()"/>
        </form>
    </div>
    <div>
        <h4>TO DO List</h4>
        <table id="todoList">
            <thead>
                <tr>
                    <td align="center">todo name</td><td align="center">todo date</td>
                </tr>
            </thead>
						<!-- (2) 시작 --->
            <tbody>
                <c:choose>
                    <c:when test="${fn:length(todoList) == 0}">
                        <tr>
                            <td align="center" colspan="2">할 일이 없습니다.</td>
                        </tr>
                    </c:when>
                    <c:otherwise>
                        <c:forEach items="${todoList}" var="todo">
                            <tr>
                                <td>${todo.todoName}</td><td align="center">${todo.todoDate}</td>
                            </tr>
                        </c:forEach>
                    </c:otherwise>
                </c:choose>
            </tbody>
						<!-- (2)-->
        </table>
    </div>
</body>
</html>

(1) 영역은 클라이언트이 요청을 처리하는 서버쪽 코드.
(2) 영역은 서버로부터 전달받은 응답을 화면에 표시하기 위한 JSP에서 지원하는 jstl 태그 영역.
(1),(2) 영역 이외의 나머지 구현 코드들은 사용자에게 보여지는 화면을 구성하는 html 태그 및 css 스타일 코드와 할일 등록시 유효성 검사를 실시하는 Javascript 코드 즉, 클라이언트 측에서 사용하는 기술들에 해당한느 코드.

코드 자체가 너무 길어서 가독성도 떨어지고 너무 복잡함 -> 애플리케이션 유지 보수 측면에서 최악의 방식임. 웹 디자이너와 html 퍼블리셔, 자바스크립트 개발자 및 자바 개발자 간에 효율적으로 협엽하는 것이 거의 불가능한 수준임.

서블릿(Servlet)을 이용한 애플리케이션

앞에서 설명한 JSP 방식 역시 내부적으로는 Servlet방식을 사용함. Servlet은 클라이언트 웹 요청 처리에 특화된 Java 클래스의 일종인데, Spring을 사용한 웹 요청을 처리할 때에도 내부적으로는 Servlet을 사용함. 여기서 의미하는 Servlet을 이용한다라는 의미는 Servlet을 위한 Java 코드가 클라이언트 측 코드에서 분리되어 별도의 Java클래스로 관리된다는 것을 의미함.

  • 서블릿 방식 예제 코드
@WebServlet(name = "TodoServlet")
public class TodoServlet extends HttpServlet {
    // (1) Database를 대신한다.
    private List<ToDo> todoList;

    @Override
    public void init() throws ServletException {
        super.init();
        this.todoList = new ArrayList<>();
    }

		// (2)
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        String todoName = request.getParameter("todoName");
        String todoDate = request.getParameter("todoDate");

        todoList.add(new ToDo(todoName, todoDate));

        RequestDispatcher dispatcher = 
                request.getRequestDispatcher("/todo.jsp");
        request.setAttribute("todoList", todoList);

        dispatcher.forward(request, response);
    }

		// (3)
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Hello Servlet doGet!");

        RequestDispatcher dispatcher = 
                request.getRequestDispatcher("/todo.jsp");
        dispatcher.forward(request, response);
    }
}

위의 코드는 jSP 예제 코드에서 Java 코드만 별도의 서블릿 클래스로 분리된 모습임. 클라이언트 측의 JSP 코드에서 서버 측 Java코든느 서블릿 클래스로 분리했기 때문에 클라이언트와 서버 간에 어느정도 역할이 분리되었다고 볼 수 있음.

(2)의 경우 클라이언트 측에서 등록할 할일 데이터를 전송하면 이 요청을 받아서 데이터 저장소에 등록해주는 역할을 하는데, 위의 코드에서는 데이터베이스같은 별도의 저장소를 사용하지 않고, (1)과 같이 Java의 List에 추가하였음.

뭔가 아주 많은 일을 할 것 같지만 데이터를 가공하는 비즈니스 로직이 있는 것도 아니고, 가공된 데이터를 데이터베이스에 저장하는 등의 데이터 엑세스 로직 역시 존재하지 않는데도 불구하고 코드 자체가 너무 긺.

Spring MVC를 이용한 애플리케이션

  • Spring MVC 방식 예제코드
@Controller
public class ToDoController {
    @RequestMapping(value = "/todo", method = RequestMethod.POST)
    @ResponseBody
    public List<ToDo> todo(@RequestParam("todoName")String todoName,
                               @RequestParam("todoDate")String todoDate) {
        ToDo.todoList.add(new ToDo(todoName, todoDate));
        return ToDo.todoList;
    }

    @RequestMapping(value = "/todo", method = RequestMethod.GET)
    @ResponseBody
    public List<ToDo> todoList() {
        return ToDo.todoList;
    }
}

위의 코드는 서블릿 방식의 코드를 Spring MVC 방식의 예제 코드로 변경한 것.

서블릿 방식의 코드에서는 클라이언트의 요청에 담긴 데이터를 꺼내오는 작업을 개발자가 직접 코드로 작성해야하고, 캐릭터셋도 지정해주어야하는 반면, Spring MVC 방식의 코드에서는 눈에 보이지 않지만 그런 작업들을 Spring에서 알아서 처리해줌.

Spring의 이런 편리함과 간결함에도 불구하고 Spring 기반의 애플리케이션의 기본 구조를 잡는 설정 작업이 여전히 불편하다는 단점이 존재함.

  • Spring MVC 설정 파일 예제 코드
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-config/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-config/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>CORSFilter</filter-name>
        <filter-class>com.codestates.filter.CORSFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CORSFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

위 코드는 Spring MVC 방식 예제코드처럼 구현한 Spring애플리케이션을 정상적으로 구동하기 위한 설정 파일들의 일부임. web.xml이라는 설정 파일 이외에 다른 설정 파일들도 필요로 함. 이러한 복잡한 설정때문에 입문자들의 진입장벽이 너무 높아 포기하는 사람들이 많아짐. -> Srping boot가 탄생함.

Spring Boot을 이용한 애플리케이션

  • Spring Boot 기반의 예제 코드
@RestController
public class TodoController {
    private TodoRepository todoRepository;

    @Autowired
    TodoController(TodoRepository todoRepository) {
        this.todoRepository = todoRepository;
    }

    @PostMapping(value = "/todo/register")
    @ResponseBody
    public Todo register(Todo todo){ // (1)
        todoRepository.save(todo); // (2)
        return todo;
    }

    @GetMapping(value = "/todo/list")
    @ResponseBody
    public List<Todo> getTodoList(){
        return todoRepository.findAll(); // (3)
    }
}

위의 코드는 Spring MVC 기반의 코드를 Spring Boot 기반에서 조금 더 개선한 모습. 이번에는 클라이언트 측에서 전달한 요청 데이터를 (1)과 같이 Todo라는 클래스에 담아서 한번에 전달받을 수 있도록 함. 요청 데이터가 Todo 객체로 변경되는 것은 Spring이 알아서 해줌. 이전 방식까지는 클라이언트가 전달한 요청 데이터를 테스트 목적으로 단순히 List에 담았는데 이번에는 (2), (3)과 같이 데이터베이스에 저장해서 데이터 액세스 처리까지 하도록 함. 이렇게 데이터를 실제로 저장하는 기능을 추가했는데도 불구하고 코드의 길이는 크게 바뀌지 않았고 깔끔해짐.

spring.h2.console.enabled=true
spring.h2.console.path=/console
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true

위의 코드는 Spring Boot에서의 구성(설정) 파일임. Spring Boot를 사용하지 않는 Spring MVC 방식에서 겪어야 했던 설정의 복잡함을 Spring Boot에서는 찾아볼 수 없음. 심지어 데이터베이스를 연동하지 않았다면 위에 입력한 4줄이 코드도 필요하지 않음.

Spring의 복잡한 설정마저도 Spring이 대신 처리를 해주기 때문에 개발자는 애플리케이션의 핵심 비즈니스 로직에만 집중할 수 있게 됨.

0개의 댓글