[STUDY] 스프링 입문 기록(1) - 코드로 배우는 스프링부트, 웹 MVC, DB 접근 기술
01. Spring 웹 개발 기초
웹 개발 시엔 크게 3가지 방법이 있다.
(1) 정적 컨텐츠
- 서버에서 하는 것 없이 파일을 그대로 웹브라우저에 띄우는 것
- 웹 브라우저에서 loalhost:8080/hello-static.html을 입력하면, 내장 톰캣 서버가 요청을 받는다.
- 내장 톰캣 서버가 hello-static.html을 스프링에게 넘기면 스프링은 컨트롤러(Controller)를 우선순위로 두고 hello-static이라는 컨트롤러가 있는지 확인 한다.
- 해당 컨트롤러가 없을 경우 Stpring Boot는 resources내부에 있는 static/hello-static.html 을 찾은 후 웹브라우저에 반환하는 과정으로 동작이 이루어진다.
hello-static.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) MVC와 템플릿 엔진
- MVC: Model, View, Controller
- Model: 컨트롤러로 처리된 후, 화면에 필요한 것들을 담아서 화면 쪽으로 보내주는 것
- View: 화면을 그리는 데에 역량을 집중하는 것
- Controller: 비즈니스 로직이나 내부적인 것을 처리하는 데에 집중하는 것
- 템플릿 엔진: JSP, PHP와 같이 HTML을 그냥 주는 것이 아닌 서버에서 프로그래밍 해서 HTML을 동적으로 바꿔서 내리는 것
Controller
@GetMapping("hello-mvc")
public String helloMvc(@RequestParam("name") String name, Model model) {
model.addAttribute("name", name);
return "hello-templates";
}
View
hello-templates.html
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>
Solution: Controller(hello-mvc)의 name 파라미터에 jeonniu!를 추가
ex) http://localhost:8080/hello-mvc?name=jeonniu!
- 컨트롤러에서 name은 jeonniu!로 바뀌고 Model에 담긴 후, hello-templates.html에 넘어가면 Model KEY 값인 name에 jeonniu!를 출력하는 형식
- 웹 브라우저에서 localhost:8080/hello-mvc를 띄우면 내장 톰캣 서버가 hello-mvc를 스프링에 넘긴다.
- helloController에 hello-mvc가 매핑 된 것을 확인 후 호출한다. (해당 메서드를 return 할 때는 hello-templates, Model에는 key는 name, 값은 jeonniu!로 넣어준다.)
- 화면 해결자인 viewResolver가 templates 파일 내부에 hello-templates라는 return의 String name과 똑같은 것을 찾으면, 템플릿 엔진이 변환한 html을 웹브라우저에 반환한다.
- 정적 컨텐츠와의 차이: 정적 컨텐츠는 파일을 그대로 웹브라우저에 띄우는 방식의 웹개발이지만, MVC는 서버에서 HTML을 변환해서 내려주는 방식이라는 점에서 차이가 있다.
(3) API
- Json 데이터 구조 포맷으로 클라이언트에게 데이터를 전달해주는 방식
- API로 데이터만 내려주면, 화면은 클라이언트가 알아서 그리고 정리하는 방식을 사용
- 서버끼리 통신할 경우엔 HTML을 내릴 필요가 없이 데이터의 흐름을 아는 것이 중요하기 때문에 주로 사용
(1) 문자를 받는 방식
Controller
@GetMapping("hello-string")
@ResponseBody // http의 Response body부에 해당 내용을 직접 넣어주겠다는 의미
public String helloString(@RequestParam("name") String name) {
return "hello" + name; // "hello (요청한 내용)"
}
@ResponseBody 를 사용하면 뷰 리졸버( viewResolver )를 사용하지 않는다.
또한 HTTP의 BODY에 문자 내용을 직접 반환한다는 의미를 가지고 있다.
해당 템플릿 엔진은 데이터를 그대로 내려주는 방식이기 때문에 페이지의 소스를 보면 HTML 태그가 없는 것을 확인할 수 있다.
반면 MVC 방식은 View라는 템플릿이 있는 상태에서 조작하는 방식으로 페이지 소스를 확인하면 HTML 태그가 있는 것을 확인할 수 있다.
(2) 객체를 받는 방식
Controller
// Spring에서 객체를 반환하고 ResponseBody를 사용할 경우, json으로 반환하는 것이 기본적
// @ResponseBody 를 사용하면 뷰 리졸버( viewResolver )를 사용하지 않음
@GetMapping("hello-api")
@ResponseBody
public Hello helloApi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}
public class Hello {
private String name;
// getter setter window 단축키: alt+insert
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Getter/Setter 방식 사용하는 근본적인 이유는 '객체지향'에서 말하는 캡슐화(encapsulation)를 달성하기 위함이다.
캡슐화란 서로 관련이 있는 데이터와 그 데이터를 다루는 메서드를 하나의 클래스로 묶는 것을 의미하는데, 캡슐화의 가장 큰 장점은 다른 객체에서 자신의 정보를 은닉하고, 오직 연산을 통해서만 접근할 수 있는 정보은닉(Information Hiding)이 가능하다는 것이다.
Getter
- 서로 다른 객체 A, B가 있다고 가정할 때, 객체 A는 객체 B의 변수 x를 참조하고 있다.
public class A {
B B = new B();
int x = B.x;
}
public class B {
int x;
}
여기서 객체 B의 변수명인 x를 x1으로 수정하고 싶다면? 객체 A는 x1을 사용할 수 없을 것이다.
이는 캡슐화가 제대로 이루어지지 않았다고 할 수 있다.
이 때 Getter 방식을 사용해보자.
public class A {
B B = new B();
int x = B.getX();
}
public class B {
int x1;
public int getX() {
return x1;
}
}
객체 B의 변수명 x가 x1으로 객체 A에는 아무런 영향 없이 기존의 x 값을 사용할 수 있게 된다.
Setter
- 서로 다른 객체 A, B가 있다고 가정할 때, 객체 A의 x 값을 객체 B의 x 값으로 전달하고 싶다.
public class A {
int x = 0;
B B = new B();
B.x = x;
}
public class B {
int x;
}
하지만 위의 방식으로 값을 전달할 경우, 객체 B의 x에 직접 선언해주는 것이 되어 캡슐화를 제대로 이룰 수 없다.
이 때 Setter 방식을 사용해보자.
public class A {
int x = 0;
B B = new B();
B.setX(x);
}
public class B {
int x;
public void setX(int x) {
this.x = x;
}
}
이렇게 하면 간접적으로 객체 A의 x 값을 객체 B의 x값으로 전달할 수 있게 되어
캡슐화의 목적을 달성할 수 있다.
KEY=VALUE 형태로 이루어져 있는 것을 확인할 수 있다.
- 웹브라우저에서 localhost8080//hello-api 를 검색할 경우 톰캣 내장 서버에서 hello-api가 왔음을 Spring에 던진다.
- Spring은 hello-api를 찾는다.
- 이 때, @ResponseBody가 붙어 있을 경우 뷰 리졸버(viewResolver)에게 던지지 않고 http 응답에 그대로 데이터를 넘긴다.
- 하지만 보낼 데이터가 문자가 아닌 객체이기 때문에 Json 방식으로 변형한 객체를 http 응답에 반환한다. (Spring의 디폴트 값)
@ResponseBody 를 사용할 경우
- HTTP의 BODY에 문자 내용을 직접 반환
- viewResolver 대신에 HttpMessageConverter 가 동작
- 기본 문자처리: StringHttpMessageConverter
- 기본 객체처리: MappingJackson2HttpMessageConverter
- 만약 해당 데이터가 단순 문자라면 SpringConverter가 동작, 객체라면 JsonConverter가 동작한다는 뜻
- byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있다.
해당 내용들을 정리하면 다음과 같다.
웹 개발의 3가지 방법
01. 정적컨텐츠
- 서버에서 하는 것 없이 파일 그대로 웹 브라우저에 띄우는 방식
02. MVC와 템플릿 엔진
- 서버에서 뷰 리졸버(viewResolver)를 통해 HTML을 변환해서 웹 브라우저에 내려주는 방식
03. API
- @ResponseBody 를 사용한다.
- 크게 문자/객체를 받을 두가지 상황으로 나눠져 있다.
- 문자를 받는 경우: SpringConverter가 동작하여 HTTP의 BODY에 문자 내용을 직접 반환하는 방식
- 객체를 받는 경우: JsonConverter가 동작하여 Json 방식으로 변형한 객체를 HTTP 응답에 반환하는 방식
참고: 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (https://inf.run/PWvM)