*김영한 | 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 참조한 글입니다.
지난 시간에 다운받은 hello-spring 프로젝트에 대해 살펴보자.
'src - main - java - hellospring.hellospring - HelloSpringApplication'에서 코드를 실행시켜 보면, 다음과 같이 작동한다.
TomcatWebServer에서 8080(http)를 확인할 수 있다.
웹브라우저에서 localhost:8080 을 입력하면, 다음과 같은 페이지가 뜬다.
(만약 '사이트에 연결할 수 없음'페이지가 뜬다면 무언가 잘못된 것이다.)
에러 페이지지만, 정상적으로 작동한 것이다!
우린 아직 아무것도 작성하지 않았기에, 에러 페이지가 뜬 것이다.
웹 개발 방법에는 크게 세 가지 방법이 있다.
- 정적 컨텐츠 : 서버에서의 변형 없이, 파일을 똑같이 '전달'해주는 방식.
- MVC와 템플릿 엔진 : 서버에서의 변형이 있다. 템플릿 엔진 서버에서 html을 동적으로 바꾼다.
- API : JSPM 포맷(데이터 구조 포맷의 일환)으로 데이터를 클라이언트에게 전달한다.
1. 정적 컨텐츠 (Welcome Page 만들기)
도메인네임을 치고 들어왔을 때 첫 화면인 Welcome Page를 만들어보자.
'src - main - resources - static' 패키지에 index.html 파일을 추가해주고 다음의 코드를 붙여넣는다.
<!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>
이렇게 입력해주면 된다.
<실행 결과> 도메인네임 : localhost:8080
나의 작고 허접한 Welcome Page가 완성되었다.
2. MVC와 템플릿 엔진
도메인네임 입력 후 서버 접속 원리를 간단하게 설명하면 다음과 같다.
1. 사용자가 웹브라우저에 도메인네임 입력
2. 1을 통해 요청받은 내장 톰캣 서버가 스프링 컨테이너에서 해당 Controller가 있는지 확인한다.
즉, Controller 확인이 static보다 우선순위이다!!!
3. Controller가 없다면, static에서 확인한다.
4. 웹브라우저에 해당 내용을 반환한다.
# Controller가 뭔데?
- Model : 데이터와 관련된 부분들(정보라고 생각하면 된다.)
Model은 View와 Controller에 의존하지 않아야 한다. = Model 내부에는 View와 Controller 관련된 코드가 없어야 한다.
- View : 사용자(클라이언트)에게 보이는 부분들. html / css / java 가 관련 기술이다. 클라이언트 기술이라 부른다.
View는 Model에만 의존한다.
- Controller : Model과 View를 이어주는 부분.
Controller는 View와 Model에 의존할 수 있다.
ex. 사용자가 구글에 치킨이라고 검색
-> Controller 발동 : Model에게 치킨 정보 요청
-> Model로부터 받은 정보를 controller가 View에게 전달
-> View가 사용자에게 전달
이제 진짜 MVC에 대해 다뤄보자!
MVC = Model View Controller 의 약자이다.
과거의 'Model 1'방식에서는 View와 Controller가 통합되어 있어, View에서 모든 접근이 이루어졌다.
그러나 View가 사용자에게 화면을 전달하는 역할에만 집중하기 위해, 작업을 나누어 수행하게 되었다. => MVC
컨트롤러를 활용해 간단한 예제를 진행해보자.
2-1-1. 'src - main - java - hellospring.hellospring - controller' 에 HelloController class를 생성한 뒤 다음의 코드를 붙여넣는다.
package hellospring.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
}
<코드 설명>
@GetMapping("hello")
localhost:8080/hello 로 요청될 경우
public String hello(Model model) {
model.addAttribute("data", "hello!!");
모델에 data의 데이터 값(=hello!!)을 넣어서 화면을 띄어라. (data는 2-1-2번 과정의 hello.html 에 등장한다.)
return "hello";
}
resources/templates/hello.html을 화면에 불러와라. (hello.html은 2-1-2번에서 만들 것이다.)
즉, 컨트롤러에서 리턴 값으로 문자를 반환하면 = return "hello";
viewResolver가 화면을 찾아 처리한다. = resources/templates/ + ViewName + .html 이 화면에 나타난다.
더보기 : no usages 가 나오는 경우
* no usages 가 나오는 경우 없애는 방법
나같은 경우, 강의 속 화면과 달리
인텔리제이 최근 버젼 기능에 의해 no usages(메서드나 필드를 선언해놓았지만 사용중이지 않음)가 표기되었다.
마우스 우클릭 - Hide 'Code Vision: Usages' Inlay Hints 를 누름으로써 끌 수 있다.
이렇게 입력해주면 된다.
2-1-2. 'src - 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>
이렇게 입력해주면 된다.
2-1-3. 웹브라우저에 localhost:8080/hello 로 들어가 확인해보면, 다음과 같은 화면이 뜬다.
2-1-4. 빌드하고 실행하기
ide가 다 알아서 해주긴 하지만 혹시 모를 필요를 위해 수동적으로 빌드하고 실행하는 방법에 대해 배워보자.
강사님은 mac을 사용하시는데, 나는 window라 강의 영상 속 설명이 굉장히 축약되어 있어 꽤 헤맸던 부분이다.
먼저 hello-spring 폴더에서 우클릭해 주소를 따온다.
이때 주의할 점은!! 위의 캡처 화면처럼 .gradle / .idea .... 등등의 바로 상위버젼 파일의 주소를 따야한다는 것이다.
이 상태에서 주소 복사하면 안된다!!
(정확히는 안 되는 것은 아니고, 명령프롬프트에서 한 단계 입력을 더 해줘야한다.)
그리고 명령 프롬프트를 불러와서 cd + 복사한 주소 입력한다.
그다음 gradlew 를 입력한다. *잘 안된다면 gradlew clean build 를 입력한다.
성공적으로 빌드했다.
*JAVA_HOME 이 유효하지 않은 디렉토리에 세팅되어있다며 에러가 뜰 수도 있는데 그런 경우 환경변수를 올바르게 다시 설정해주면된다. JAVA 환경변수 설정에 대해서는 이전 게시물에 자세히 나와있다.
'hello-spring - build - libs' 폴더에 가면 다음과 같은 파일이 만들어져있는 것을 확인할 수 있다.
이 파일을 실행시켜 성공적으로 빌드했음을 확인할 수 있다.
방금 만든 컨트롤러 예제를 조금 더 발전시켜보자.
2-2-1. HelloController class에 다음의 코드를 붙여넣는다.
package hellospring.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
//여기서부터
@GetMapping("hello-mvc")
public String helloMvs(@RequestParam("name") String name, Model model) {
model.addAttribute( "name", name);
return "hello-template";
}
//여기까지가 추가된 부분
}
이렇게 입력해주면 된다.
<코드 설명>
@GetMapping("hello-mvc")
localhost:8080/hello-mvc 로 요청될 경우
public String helloMvs(@RequestParam("name") String name, Model model) {
model.addAttribute( "name", name);
localhost:8080/hello-mvc?name=spring! 으로 요청될 경우템플릿 엔진을 사용하여 모델에 name이라는 데이터(이경우는 'spring!')를 넣어서 화면에 띄워라.
*query인자를 필수로 추가해서 요청해야할지의 여부는 @RequestParam("name", required=false)로 설정할 수 있다.
Default값은 true이다.
return "hello-template";
}
이 말은 resources/templates/hello-template.html 로 반환한단 말이니까,
이제 templates 패키지 밑에 hello-template.html을 만들어줘야겠지?
2-2-2. 'src - resources - templates' 패키지에 hello-template.html 파일을 생성하고, 다음의 코드를 붙여넣는다.
<html xmlns:th="http://www.thymeleaf.org">
<body>
<p th:text="'hello ' + ${name}">hello! empty</p>
</body>
</html>
이렇게 입력해주면 된다.
<실행 결과> 도메인네임 : localhost:8080/hello-mvc?name=spring!
도메인 네임이 localhost:8080/hello-mvc?name=spring! 이기에 hello spring!이라고 화면에 출력됐다.
3. API
api란, view 없이 string이나 json 형태로 반환해주는 방식이다. (요즘은 json으로의 반환이 주로 사용된다.)
HelloController class에 다음의 코드를 붙여넣는다.
package hellospring.hellospring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "hello!!");
return "hello";
}
@GetMapping("hello-mvc")
public String helloMvs(@RequestParam("name") String name, Model model) {
model.addAttribute( "name", name);
return "hello-template";
}
//여기서부터
@GetMapping("hello-string")
@ResponseBody
public String helloString(@RequestParam("name") String name) {
return "hello" + name;
}
@GetMapping("hello-api")
@ResponseBody
public Hello helloAPi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello;
}
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//여기까지가 추가된 부분
}
*Code Completion : https://www.jetbrains.com/help/idea/auto-completing-code.html
<코드 설명>
// 문자열로 내보내기
@GetMapping("hello-string")
@ResponseBody // 템플릿과 달리 viewResolver를 쓰지 않고 HttpMessageConverter가 동작함. http body에 담아서 return함.
public String helloString(@RequestParam("name") String name) {
return "hello" + name; // 이 문자가 그대로 http body에 옮겨져 출력됨
}
* HttpMessageConverter 동작
- return 값이 문자 -> StringHttpMessageConverter 동작 -> 문자 그대로 http body에 옮겨 반환
- return 값이 객체 -> MappingJackson2HttpMessageConverter 동작 -> 객체를 json구조로 반환
(jackson : 객체를 json구조로 바꾸는 대표적인 라이브러리 중 하나)
<실행 결과> 도메인네임 : http://localhost:8080/hello-mvc?name=spring!
오잉? 다른 게 없는데?? 할 수 있지만.. 마우스 우클릭을 해 소스를 확인해보면,
내가 쓴 문자 그대로~~ 출력했음을 알 수 있다.
//객체로 내보내기
@GetMapping("hello-api")
@ResponseBody
public Hello helloAPi(@RequestParam("name") String name) {
Hello hello = new Hello();
hello.setName(name);
return hello; // @ResponseBody 사용시 객체가 들어오면 json구조로 반환함
}
// getter setter를 가진 클래스 생성하기
static class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
* json 구조
{"name":"들어온 인지값"}처럼 key : value 구조를 뜻한다.
*window기준 단축키 alt + insert -> getter setter를 통해 코드를 빠르게 불러올 수 있다.
* getter setter / property 접근 방식
private필드로 정의되면 완전 비공개를 통해 외부에 공개하지 않는다. 즉, 직접적인 접근이 불가하다.
그러므로 인가된 사람이 접근하고 싶다면 우회적으로 접근해야 하는데, 이때 필요한 것이 getter setter 메소드이다.
- getter() : private 필드를 우회적으로 접근할 수 있다. get은 사용자 입장에선 '얻다'이지만 컴퓨터 입장에선 '주다'니까..
getter 부분은 public을 통해 접근제한자 설정을 해주어야한다.
- setter() : private 필드를 우회적으로 변경할 수 있다.
<실행 결과> 도메인네임 : http://localhost:8080/hello-api?name=spring!
<소스 보기>
이번 글에서는 스프링 웹 개발의 세 가지 방식 - 정적 컨텐츠, MVC와 템플릿 엔진, API에 대해 배워봤다.
다음 글에서는 '회원 관리 예제'를 통해 스프링 웹 개발에 대해 더 자세히 공부해보도록 하겠다.