본문 바로가기

Spring/스프링 입문

01. 스프링 웹 개발 기초 - 동작 방식 3가지

💡 본 게시글은 김영한님의 인프런(Inflearn) 강의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술에 대해 공부하고, 정리한 내용입니다.

들어가며

(1) 웹 개발이란?
 사용자들에게 정보와 서비스를 제공하기 위해 웹의 공간을 설계하고 제작하는 전체의 과정을 의미합니다. 이렇게 제작된 웹을 통해 사용자들은 다양한 서비스를 이용하고, 다른 사용자들과 상호작용을 할 수 있습니다.

(2) 웹 개발을 잘하려면 어떤 것들을 공부해야 할까요?
 우선 웹 개발을 하려면 다양한 언어들이 필요합니다. 프론트엔드에는 HTML, CSS, JavaScript 등이 있고, 백엔드에서는 Java, Python, PHP 등이 있으며, 데이터베이스 관리 시스템도 필요합니다. 그렇다면 웹 개발을 잘하려면 이 언어들을 사용하면서 웹이 어떻게 동작하는지, 클라이언트와 서버가 어떻게 소통하는지, 데이터가 어떻게 전송되고 저장되는지에 대한 깊은 이해가 필요합니다.

(3) 이렇게 많은 언어들 중에서, 왜 하필 Spring을 공부해야 하는가?
 물론 프로그래밍 언어와 플랫폼은 수 없이 많지만, 왜 특히 Spring을 선택해야 할까요? Spring Framework는 Java를 기반으로 하는데, Java는 전 세계적으로 널리 사용되는 안정적인 언어이기 때문입니다. 이로 인해 Spring은 기업 환경에서 강력한 웹 애플리케이션과 서비스를 구축하는 데 매우 효과적입니다.

Spring의 가장 큰 장점 중 하나는 그 유연성입니다. 다양한 모듈과 라이브러리를 통해 개발자는 복잡한 요구사항에 맞춘 맞춤형 솔루션을 쉽게 개발할 수 있습니다. 예를 들어, 데이터베이스 연결, 보안 관리, 웹 서비스 구축 등 다양한 개발 과제를 쉽게 해결할 수 있습니다.

또한, Spring은 '의존성 주입(Dependency Injection)'과 같은 프로그래밍 패러다임을 채택하여, 애플리케이션의 유지보수성과 확장성을 향상시키고 코드의 재사용을 촉진합니다. 이런 기능들은 개발자가 더욱 효율적으로 코드를 작성하도록 도와줍니다.

결국, Spring을 공부하는 것은 현대 웹 개발에서 중요한 역량을 쌓고, 다양한 기업의 기술 요구를 충족시킬 수 있는 기반을 마련하는 데 큰 도움이 됩니다. 이러한 이유로 많은 개발자와 기업들이 Spring을 선호하고, 그에 따라 시장에서도 Spring을 활용할 수 있는 개발자에 대한 수요가 매우 높습니다.

=> 그렇다면 웹을 개발하는 다양한 방법들을 공부해보도록 하겠습니다. (스프링 입문 파트는 스프링을 어떻게 사용해야 하는 지에 초점이 잡혀있습니다.)


1) 웹을 개발하는 세 가지 방법

 웹을 개발하는 방법에는 다양한 방법들이 존재하며, 각기 다른 상황에 따라 적합한 방법을 선택하게 됩니다. 여기서는 그 중에서도 가장 대표적인 세 가지 방법을 살펴보겠습니다. 바로 정적 컨텐츠, MVC와 템플릿 엔진, 그리고 API입니다.

  • 정적 페이지: 정적 페이지는 서버에서 추가적인 작업 없이 그대로 웹 브라우저에 표시됩니다. 즉, 서버에 저장된 파일이 그대로 사용자에게 전달되는 방식입니다.
  • MVC & 템플릿 엔진: JSP나 PHP와 같은 서버 사이드 언어를 사용하여 동적인 페이지를 생성하는 방식입니다. 여기서는 MVC(Model, View, Controller) 패턴이 사용되며, 데이터 처리(Model), 사용자에게 처리 결과 보여주기(View), 사용자 요청 처리 및 결과 표시(Controller)의 역할을 하는 MVC 패턴을 사용합니다.
  • API: API는 Application Programming Interface의 약자로, 서로 다른 소프트웨어 간에 상호작용을 가능하게 하는 방법입니다. 대부분의 경우, JSON 포맷을 사용하여 클라이언트에게 데이터를 전달합니다. 이 방법은 주로 ViewJS, ReactJS 등의 클라이언트 사이드 프레임워크에서 사용합니다. 이런 방식을 사용하면, 서버와 클라이언트의 개발을 분리할 수 있어 효율적인 개발이 가능해집니다.

(1) 정적 컨텐츠 (Static Contents)

정적 컨텐츠란?

7.1.5. Static Content
 기본적으로 스프링 부트는 클래스 경로의 /static(또는 /public 또는 /resources 또는 /META-INF/resources)이라는 디렉터리나 ServletContext의 루트에서 정적 콘텐츠를 제공한다. 스프링 MVC의 ResourceHttpRequestHandler를 사용하므로 자신의 WebMvcConfigurer를 추가하고 addResourceHandler 메서드를 재정의하여 해당 동작을 수정할 수 있다.

 웹 서버가 클라이언트에게 파일을 그대로 전달하는 웹 컨텐츠를 말합니다. HTML, CSS, JavaScript, 이미지 파일 등이 이에 해당하며, 이러한 파일들은 서버에 미리 저장되어 있어, 클라이언트의 요청이 있을 때마다 그대로 전달되게 됩니다. 이렇게 전달된 파일들은 웹 브라우저에서 해석되어 표시됩니다.

※ 작동 원리 
(1) 먼저, '/resources/static' 경로 안에 'hello-static.html'라는 파일을 생성합니다. 이 파일은 위의 코드처럼 웹 페이지를 구현하는 HTML 코드를 포함하고 있습니다. 그리고 이 상태에서 웹 서버를 실행합니다.

<!DOCTYPE HTML>
<html>
<head>
    <title>static content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	정적 컨텐츠 입니다.
</body>
</html>

 

(2) 웹 서버가 실행된 후에는 'http://localhost:8080/hello-static.html'로 접속하면 'hello-static.html'에서 구현한 웹 페이지 화면을 볼 수 있습니다.

- 이 과정에서 컨트롤러에 코드를 별도로 작성해주지 않아도 자동으로 매핑이 이루어집니다. 이는 톰켓 서버의 작동 방식 때문입니다.
먼저, 웹 브라우저에서 'http://localhost:8080/hello-static.html' 이라는 주소를 통해 접속을 시도합니다.
이 요청은 톰캣 서버로 전달되며, 서버는 해당 주소와 연결된 컨트롤러를 찾기 시작합니다.
만약 서버가 해당 주소와 연결된 컨트롤러를 찾지 못한다면, 다음 단계로 넘어갑니다.
이후, 서버는 'static'이라는 디렉터리로 이동하여 'hello-static.html'이라는 이름의 파일을 찾기 시작합니다.
만약 'hello-static.html'이라는 파일이 존재한다면, 해당 파일을 찾아 반환해줍니다. 이렇게 되면 사용자는 웹 브라우저를 통해 해당 페이지를 볼 수 있게 됩니다.

※ 정적 컨텐츠의 장·단점
 정적 컨텐츠는 서버에 미리 저장된 파일 그대로를 클라이언트에게 전달하기 때문에, 웹 페이지를 빠르게 로딩할 수 있는 장점이 있습니다. 그러나 한번 저장된 파일은 변경되지 않기 때문에, 동적인 데이터를 표시하려면 다른 방법을 사용해야 하는 단점이 있습니다.


(2) MVC (Model, View, Controller) 와 템플릿 엔진

※ MVC로 개발하게 된 배경
(1) 웹 애플리케이션의 동작방식
 웹 애플리케이션은 보통 사용자가 브라우저를 통해 서비스를 요청하면, 요청된 데이터를 기반으로 서비스가 처리되고, 이에 대한 결과를 반환하여 사용자의 화면에 보여줍니다.

(2) 초기 웹 애플리케이션 개발 방식
 초기 웹 개발에서 이러한 서비스의 모든 코드들이 뒤섞여 개발되었습니다. 이렇게 개발하면 코드는 복잡해지고, 추후 변경사항이 생긴다면 수정하기도 어렵습니다.

그래서 개발자들은 관심사를 분리하여 개발하게 되었습니다. 

(2) 관심사의 분리란?
 관심사의 분리(Separation of Concerns, SoC)는 소프트웨어 공학의 중요한 원칙 중 하나로, 복잡한 프로그램을 관리하기 쉽게 분해하여 설계하는 철학을 말합니다. 개발에서 관심사의 분리는 코드를 여러 부분으로 나누어 각 부분이 특정 기능을 갖도록 하는 것 입니다.  

그렇다면 웹 애플리케이션을 개발할 때, 어떻게 관심사를 분리하게 되었는지를 설명하겠습니다.

(3) MVC 패턴이란?
 위 내용에서 관심사의 분리를 통해 애플리케이션을 개발할 때, 세 가지의 주요 구성요소로 분리하여 개발을 진행하게 되었습니다.

  • 사용자가 브라우저를 통해 서비스를 요청: 사용자가 데이터를 입력하고 서비스를 요청할 때, 이 데이터는 Model에 반영됩니다.
  • 사용자의 브라우저를 통해 서비스 결과를 출력: 사용자에게 보여지는 UI 부분은 View가 담당하게 됩니다.
  • 이 사이의 중간 처리 과정: 사용자가 서비스를 요청하면, 그에 따라 데이터를 어떻게 처리할 지 Controller가 결정하게 됩니다.

=> 즉, 사용자가 서비스를 요청할 때, 데이터는 Model을 통해 관리가 되며, 요청을 처리하는 과정을 Controller가 판단하게 됩니다. 처리가 완료되면, View를 통해 결과가 출력됩니다. 

이러한 방식을 MVC 패턴이라고 합니다. 이렇게 개발을 진행하면, 이전의 뒤섞인 코드보다 체계적이고 효율적으로 개발할 수 있습니다.

 MVC 패턴의 정의
 복잡한 문제를 해결하기 위해 서로 다른 부분으로 나누어 각 부분이 자신의 일에만 집중할 수 있도록 하는 개념입니다. 쉽게 말해, 큰 문제를 작은 조각으로 나누고, 각 조각이 하나의 특정한 작업에만 집중하게 함으로써 전체적으로 문제를 더 쉽게 관리하고 해결할 수 있도록 하는 것입니다.  

※ MVC의 장점
(1) MVC는 애플리케이션의 관심사를 명확히 분리하여 코드의 유지보수가 용이하게 만듭니다.

  • View는 사용자 인터페이스에 집중하게 됩니다.
  • Model은 비즈니스 로직과 데이터 처리에 집중하게 됩니다.

=> 이런 방식으로 코드의 재사용성이 향상되며, 개발자는 각 부분을 독립적으로 개선하거나 변경할 수 있습니다.

(2) 스프링 프레임워크에서는 이 MVC 패턴을 스프링 MVC라는 모듈을 통해 지원합니다.

=> 웹 애플리케이션을 구축할 때 사용되며 애플리케이션의 각 부분을 잘 정의된 역할로 분리하여 개발의 복잡성을 줄여줍니다.

※ 작동 원리
(1)  MVC가 어떻게 작동되는 지, 원리를 알기 위해서 우선 controller를 제작합니다.

// MVC 방식을 사용하는 간단한 컨트롤러의 예시입니다.
@GetMapping("hello")
public String hello(Model model){
    model.addAttribute("data", "hello!!");
    return "hello"; // 반환 값은 'hello' 템플릿과 매핑되어 뷰를 생성합니다.
}
// 'hello-mvc'라는 경로로 GET 요청을 받는 메서드입니다.
@GetMapping("hello-mvc")
public String helloMvc(

	// 'name' 파라미터는 필수가 아니며, 값이 없을 경우에도 메서드는 동작합니다.
    @RequestParam(name = "name", required = false) String name, Model model){
        
   	// 'name' 값을 모델에 추가하여 뷰에서 사용할 수 있게 합니다.
    model.addAttribute("data", "hello " + name + "!!"); 
    
    // 반환 값은 'hello' 템플릿과 매핑되어 뷰를 생성합니다.
    return "hello"; 
}

(2) 그리고 'resources/templates'에 아래의 코드를 사용해 'hello.html' 파일을 생성합니다.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
      	<title>Hello</title>
      	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
        <p th:text="'안녕하세요. ' + ${data}" >안녕하세요.</p>
    </body>
</html>

(3) 이렇게 설정하면 다음과 같이 동작하게 됩니다.

① 웹 브라우저에서 `localhost:8080/hello-mvc` 주소로 요청을 합니다.
② 내장 톰캣 서버가 이 요청을 받고, 스프링 애플리케이션에 해당 경로와 연결된 컨트롤러를 찾도록 요청을 전달합니다.
③ 스프링 애플리케이션 내의 `helloController`에서 `@GetMapping` 어노테이션을 통해 `hello-mvc` 경로가 매핑된 메서드를 확인하고 실행합니다.
④ 해당 메서드는 `Model` 객체에 `(key: name, value: "spring")`을 삽입하고, "hello-template"이라는 뷰 이름을 반환하여 스프링에게 전달합니다.
⑤ 스프링의 `viewResolver`가 동작하여 `resources/templates/hello-template.html` 파일을 찾고, Thymeleaf 템플릿 엔진에게 렌더링을 요청합니다.
⑥ Thymeleaf 템플릿 엔진은 `Model` 객체에서 받은 데이터를 사용하여 HTML을 생성하고, 이를 웹 브라우저로 반환합니다. 여기서 `Model` 객체의 `name` 키를 찾아 그 값으로 "spring"을 치환합니다.

URL 파라미터를 통한 데이터 처리:

  • @RequestParam을 컨트롤러에서 사용하여 URL에서 파라미터를 받음.
  • required = false 설정으로 필수가 아닌 파라미터도 처리 가능.
  • 예: `localhost:8080/hello-mvc?name=spring` 접속 시, "hello spring" 출력.

(3) API (Application Programming Interface)

※ API의 정의 
 이전 MVC 방식은 View와 템플릿을 조작하여 화면 랜더링 후 웹 브라우저에 전달하였습니다. 반면, API 방식은 웹 브라우저에 데이터를 직접 전달합니다. API는 크게 문자 전달 방식과 JSON 방식(키 : 값)을 활용합니다.

 API 방식은 서버 간의 통신을 위해 주로 사용되며, 웹이나 앱 어플리케이션에서도 활용됩니다. 이 방식은 컨트롤러에서 직접 응답을 반환하는 형태를 띠게 됩니다. 이를 통해 클라이언트와 서버 간의 효율적인 데이터 교환을 가능하게 합니다.

  • API는 "Application Programming Interface"의 줄임말로, 서로 다른 소프트웨어 간의 상호작용을 가능하게 하는 규약입니다.
  • 시스템 간의 통신을 위한 도구로, 함수, 메소드, 프로토콜 등 다양한 형태를 가질 수 있습니다. 이 형태는 사용하는 시스템이나 목적에 따라 변화합니다.
  • 웹 개발에서 흔히 볼 수 있는 API는 HTTP를 이용하여 데이터를 주고 받는 웹 API입니다.
  • 웹 API는 대개 REST(REpresentational State Transfer)SOAP(Simple Object Access Protocol) 아키텍처를 따르며, 클라이언트와 서버 간의 데이터 교환을 가능하게 합니다.
더보기

Rest API (Json)

  • REST API는 REpresentational State Transfer의 약자입니다.
  • 웹 서비스를 설계하는 아키텍처 스타일 중 하나입니다.
  • REST는 자원을 이름(또는 URI, Uniform Resource Identifier)으로 구분하고, 해당 자원의 상태를 주고 받는 것을 기본으로 합니다.

 

  • REST API는 HTTP 메서드를 사용하여 CRUD(Create, Read, Update, Delete) 연산을 수행합니다:
    • GET: 지정된 자원을 조회합니다. 서버에 변경을 주지 않습니다.
    • POST: 새로운 자원을 생성합니다.
    • PUT: 자원의 전체를 갱신합니다.
    • PATCH: 자원의 일부를 갱신합니다.
    • DELETE: 지정된 자원을 제거합니다.

 

더보기

SOAP(Simple Object Access Protocol)

 SOAP는 Simple Object Access Protocol의 약자로, 네트워크에서 웹 서비스 간의 정보 교환을 위한 통신 프로토콜입니다.

  • SOAP는 XML 기반의 메시지 형식을 사용하여 애플리케이션 간의 데이터를 전송합니다. 이 프로토콜은 HTTP, SMTP, TCP, UDP 등 다양한 통신 프로토콜 위에서 동작할 수 있으며, 이로 인해 네트워크의 다른 부분과 통신하는 데 많은 유연성을 제공합니다.
  • SOAP는 뛰어난 확장성과 강력한 타입 체계, 범용성을 제공하지만, 복잡성과 높은 오버헤드로 인해 최근에는 JSON 기반의 RESTful API가 더 널리 사용되고 있습니다.
  • 스프링 프레임워크에서는 Spring Web Services 프로젝트를 통해 SOAP 기반의 웹 서비스를 개발하는 데 필요한 도구를 제공합니다. 이를 이용하면 SOAP 요청을 처리하는 웹 서비스를 구현하거나, 클라이언트에서 SOAP 요청을 보내는 코드를 작성할 수 있습니다.

 

  • SOAP 메시지는 보통 요청/응답 쌍으로 이루어져 있으며, 각 메시지는 다음 세 가지 주요 부분으로 구성됩니다:
    1. Envelope: 모든 SOAP 메시지를 감싸는 루트 요소로, 메시지가 SOAP 규약을 따른다는 것을 나타냅니다.
    2. Header: 선택적 요소로, 메시지의 속성(예: 인증, 트랜잭션 등)을 포함할 수 있습니다.
    3. Body: 실제 메시지 내용을 포함하는 요소로, 웹 서비스 요청 및 응답 정보를 포함합니다.

※ 단순 문자열로 변환
아래와 같이 컨트롤러에 코드를 작성합니다.

@GetMapping("hello-string")
@ResponseBody 
public String helloString(@RequestParam("name") String name){
    return "hello " + name; 
}

 

 웹 브라우저에서 hello-string으로 name 값을 넣고 접속하면, 바로 웹 브라우저에 데이터가 출력됩니다. 정적 페이지, MVC를 사용할 때와는 다르게 html 의 title 부분이 단순히 페이지 주소로 나오는 것을 확인할 수 있습니다.

 여기서 MVC와 차이점은 @ResponseBody가 붙어있다는 점입니다. 이는 응답메세지의 바디 부분에 반환 값을 포함하겠다는 의미입니다. 또한, 모델이 없고, 반환값을 템플릿으로 하지 않습니다.

 @RequestParam("name")은 'name'이라는 쿼리를 받아, 그 값을 'name'이라는 변수에 저장한다는 의미입니다. MVC에서도 사용 가능합니다.

이 경우, HttpMessageConverter가 반환 값을 받아 HTTP 메시지로 변환합니다. 단순 문자열을 반환하는 경우, 여러 컨버터 중 StringConverter가 작동합니다.

※ 객체 (Json) 반환
이번에는 controller에 아래와 같이 코드를 작성해줍니다.

// 데이터 전달 : 객체를 전달하는 경우
@GetMapping("hello-json") 
@ResponseBody 
public hellojson hello_json(@RequestParam("name") String name, String id, String passwd){
    hellojson hellojson = new hellojson();

    hellojson.setName(name);
    hellojson.setId(id);
    hellojson.setPasswd(passwd);

    return hellojson; 
}

static class hellojson{
    private String name;
    private String id;
    private String passwd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

이번에는 해당 웹 브라우저에 접속하면 n ame, id, passwd 파라미터에 각각 넣은 값들이 JSON 형식으로 출력되는 것을 확인할 수 있습니다. 하지만 만약에 잘못된 값으로 파라미터에 넣게 되면 400 에러가 발생됩니다. 왜냐하면, @RequestParam("name") 안에 있는 name 파라미터를 생략했기 때문입니다. @RequestParam 안에 들어가는 값은 기본적으로 "필수로 넘어와야하는 값" 입니다.

※ 작동 원리
그렇다면 위 코드의 전체 작동 과정을 알아보겠습니다.

웹 브라우저에서 사용자는 `http://localhost:8080/hello-api`라는 URI로 요청을 보냅니다.
요청을 받은 서버는 `helloController`라는 컨트롤러를 찾아서 이 요청을 처리하도록 넘깁니다.
`helloController` 내부의 메서드에는 `@ResponseBody`의 데이터(`hello(name:spring)`)를 HTTP 응답의 본문으로 직접 전송하도록 지시합니다.
컨트롤러에서 반환된 데이터는 `HttpMessageConverter`를 통해 클라이언트에 전송될 형태로 변환됩니다. 

   - JsonConverter: 객체를 JSON 형식의 문자열로 변환합니다.
   - StringConverter: 단순 문자열 데이터로 변환합니다.

변환된 데이터는 최종적으로 웹 브라우저로 응답되며, 여기서는 JSON 객체 (`{name: spring}`)의 형태로 전달됩니다.

 이 과정을 통해, 클라이언트는 서버로부터 데이터를 JSON 형식 또는 문자열로 받게 되며, 이 정보를 사용하여 사용자에게 결과를 표시하거나 다른 처리를 할 수 있습니다.