스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 - 웹 개발 기초

jkky98·2024년 6월 18일
0

Spring

목록 보기
3/77

SpringBoot env

SpringBoot = 3.0.0 이상
Java 17 version
Gradle dependencies : starter-thymeleaf, starter-web

Welcome Page

// resources/static
<!DOCTYPE HTML>
<html>
<head>
    <title>Hello</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
Hello<a href="/hello">hello</a>
</body>
</html>

static 디렉터리에 index.html올려두면 스프링이 알아서 첫 화면으로 index.html을 띄운다.

웹 개발 방식

정적 컨텐츠

정적 컨텐츠는 HTML 파일과 같은 정적 파일을 브라우저 요청에 대해 그대로 응답해주는 방식이다. 이러한 정적 컨텐츠 방식은 동적인 처리가 어려워 이를 보완하기 위해 웹 프레임워크가 등장하였다.

정적 컨텐츠 요청 처리 과정

웹 브라우저는 baseUrl/hello-static.html로 요청을 보낸다. 요청은 스프링 애플리케이션 내장 톰캣 서버에서 처리되며, 아래와 같은 흐름으로 진행된다.

  1. 내장 톰캣 서버가 요청을 받아 스프링으로 전달한다.
  2. 스프링은 요청 경로에 해당하는 hello-static 관련 컨트롤러가 있는지 확인한다.
  3. 해당 경로에 대한 컨트롤러가 없음을 확인한 후, resources 디렉토리에서 정적 파일을 찾는다.
  4. static/hello-static.html 파일을 찾아 이를 브라우저에 응답으로 전달한다.

한계점

정적 컨텐츠는 단순한 파일 제공에는 적합하지만, 사용자 요청에 따라 동적으로 변하는 데이터를 처리하거나 제공하기에는 한계가 있다. 이러한 문제를 해결하기 위해 웹 프레임워크가 사용된다.

MVC와 템플릿 엔진

main/java/hello/hello_spring/ 경로에 controller 디렉토리를 만든다. 그 안에 HelloController 자바 클래스를 만든다.

클래스 내부에 다음을 작성한다.

@GetMapping("hello-mvc")
    public String helloMvc(@RequestParam("name") String name, Model model) {
        model.addAttribute("name", name);
        return "hello-template";
    }

MVC 방식의 요청 처리 과정

@GetMapping을 사용하여 baseUrl/hello-mvc URL에 매핑할 수 있다. 요청은 다음과 같은 방식으로 처리된다.

  1. helloMvc 메서드는 URL의 param에서 전달받은 name 값을 읽는다.
  2. 스프링은 제공된 Model model 객체에 name 값을 key-value 형식으로 담는다.
    • 예: model.addAttribute("name", name)
  3. 메서드는 "hello-template"을 반환한다.
    • 이때, "hello-template"templates 디렉토리에 위치한 HTML 파일 이름이다.

템플릿 엔진 처리

hello-template.html 파일은 Thymeleaf와 같은 템플릿 엔진을 사용하여, 컨트롤러에서 전달한 name 값을 활용할 수 있다.
템플릿 엔진은 HTML 내에서 동적으로 데이터를 바인딩하여 사용자에게 렌더링된 결과를 제공한다.

// hello-template.html

<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>

HTML 파일에서 <p> 태그의 th 속성은 우리가 의존성으로 설치한 Thymeleaf 템플릿 엔진을 활용하는 것이다. 예를 들어, 브라우저에서 BaseUrl/hello-mvc?name=spring으로 요청을 보내면, URL의 name 파라미터 값인 spring이 컨트롤러의 name 변수에 배치된다. 컨트롤러는 이 값을 "name": "spring" 형태로 Model에 담아 템플릿(html)으로 전달하며, Thymeleaf 템플릿 엔진은 해당 데이터를 활용해 HTML에 동적으로 반영하고 화면에 렌더링한다.

컨트롤러에서 전달한 name 값은 스프링이 자동으로 ViewResolver를 통해 템플릿 경로의 hello-template(리턴된 문자열)과 연결한다. 이후, ViewResolver는 해당 템플릿 파일을 찾아 Thymeleaf 템플릿 엔진을 사용해 HTML을 동적으로 변경하고, 최종 결과를 브라우저로 응답한다.

API

현재 가장 많이 사용되는 방식은 순수하게 데이터만 보내는 역할을 하는 것이다. MVC 방식에서는 스프링부트 개발자가 템플릿 연결과 같은 프론트엔드까지 고려해야 했으나, API 방식은 단순히 JSON 형식으로 요청받은 데이터를 응답으로 보내주는 역할만 수행한다.

	@GetMapping("hello-api")
    @ResponseBody
    public Hello helloApi(@RequestParam("name") String name) {
        Hello hello = new Hello();
        hello.setAge(15);
        hello.setName(name);
        hello.setNext(new Hello());
        hello.getNext().setName(name);
        hello.getNext().setAge(16);

        return hello;
    }

    static class Hello {
        private String name;
        private int age;
        private Hello next;

        public String getName() {
            return name;
        }

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

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Hello getNext() {
            return next;
        }

        public void setNext(Hello next) {
            this.next = next;
        }
    }

컨트롤러 메서드를 조금 더 복잡하게 설계한 경우, @ResponseBody가 추가되었다는 점과 리턴 타입이 String이 아닌 커스텀 클래스 타입으로 변경되었다는 것이 주요 차이점이다. 이러한 경우, 스프링은 기존의 ViewResolver가 아닌, HttpMessageConverter를 활용하여 리턴된 객체를 JSON 또는 XML 등의 포맷으로 변환한 뒤 응답으로 처리한다.

이때 HttpMessageConverter가 사용되는 이유는, 템플릿 렌더링이 아닌 데이터 자체를 응답 본문에 포함하기 때문이다. ViewResolver는 문자열을 템플릿 파일 경로로 해석하고 HTML 뷰를 렌더링하는 역할을 하지만, @ResponseBody가 적용된 경우에는 데이터만 반환하므로 HTML 뷰가 필요하지 않다. 따라서 스프링은 템플릿 처리를 건너뛰고, HttpMessageConverter를 통해 객체를 HTTP 응답에 맞는 포맷(JSON, XML 등)으로 변환한다.

예를 들어, 리턴 타입으로 커스텀 클래스 User가 있을 경우, 스프링은 HTTP 요청의 Accept 헤더를 확인하여 클라이언트가 원하는 데이터 포맷(JSON, XML 등)을 파악한다. 그런 다음, 해당 포맷에 맞는 HttpMessageConverter(예: MappingJackson2HttpMessageConverter는 JSON 변환에 사용됨)를 사용해 객체를 직렬화(Serialization)하여 응답 본문에 담는다.

즉, HttpMessageConverter가 사용되는 이유는 데이터 응답 방식의 전환 때문이다. 템플릿 뷰 대신 JSON이나 XML 포맷의 순수 데이터를 클라이언트에 전달해야 하는 경우, 이를 처리할 적합한 변환기가 필요하며, 이 역할을 HttpMessageConverter가 담당한다.

컨트롤러 메서드에서 객체를 반환할 경우, 스프링은 JsonConverter를 통해 객체를 JSON 형식으로 변환한다. 현재 반환하는 객체의 구조는 내부 속성으로 String nameint age 같은 값을 포함하거나, 객체 내부에 또 다른 객체를 담고 있는 형태일 수 있다.

url에 http://localhost:8080/hello-api?name=spring!!!!!!!!! 위와 같이 접속할 경우 name에 spring!!!!!!!이 전달될 것이고 이것은 객체 내부의 name에 저장된다. 객체는 다음과 같이 json화 해서 펼쳐진다.

객체를 중첩(필드로 또 다른 객체가 존재)시켜도 JsonConverter가 잘 작동하여, 객체 내부의 객체까지 자동으로 직렬화해주는 모습을 확인할 수 있다. 이는 스프링이 JsonConverter를 통해 중첩된 객체 구조를 JSON 형식으로 변환하고 응답하는 과정에서 이루어진다.

이와 같은 방식으로 API 방식은 순수하게 JSON 데이터만 전달하는 역할을 한다. 전달된 JSON 데이터는 프론트엔드 프레임워크(예: React, Vue, Angular 등)가 이를 해석하여 화면의 동작이나 상태를 구성하고, 사용자와의 상호작용을 처리한다. 이러한 구조는 백엔드와 프론트엔드의 역할을 명확히 분리하여 각각의 개발과 유지보수를 효율적으로 진행할 수 있게 한다.

profile
자바집사의 거북이 수련법

0개의 댓글