반복주기2
학습내용
동적인 HTML 웹 페이지 개발할 수 있다.
Spring MVC의 Model, View, Controller 에 대한 이해를 한다.
+ 강의 상 약간의 오타!
form.html에서 E-mail을 입력받을 수 있는 input 태그를 추가하면서 <label for=“email”>로 해줘야 한다! 지난 번 강의와 html이 약간 차이가 난다. 이러한 부분이 신경쓰인다면 강의 github에 가서 clone 으로 내려받은 다음 이번 반복주기를 들어가자.
강의들을 때는 몰랐는데, 혼자 복습 과정 중 '삽질'했던 부분
강의를 들을 때는 ‘음, 당연하지, 그렇지.' 하면서 쉽다고 느껴졌던 부분이 혼자 새하얀 에디터에서 부터 시작하려니 막막한 부분이 없지 않아 있었다. 일단 @Controller이라는 애노테이션으로 클래스를 컨트롤러로 만들어줘야 하는데, 일반 클래스로 왜 요청을 받지 못하는거지? 이러면서 삽질을 했다… 또 Controller 내부에서 url 경로(?) Path를 설정하는 부분이 GetMapping, PostMapping 애노테이션 부분과 메소드에서 String 값으로 반환해주는 부분 두 부분이 있는데, 이 부분에 대한 개념이 명확하지 않아 계속 삽질을 해댔다. Servlet과 JSP로 간단한 프레임워크를 만들어본 경험 덕분에 Spirng MVC가 내부적으로 어떻게 돌아가는지에 대한 이해가 조금 있기에 망정이지 한참 해맬뻔했다.
강의 내용 정리
템플릿 엔진이란 무엇인가
동적은 웹 페이지를 생성하기 위한 것으로 웹 템플릿들과 웹 컨텐츠 정보를 처리하기 위해 설계된 소프트웨어를 말한다. 프로그램 로직과 프레젠테이션 계층을 분리하기 위한 수단으로 사용된다. Controller로부터 전달받는 Data를 표현하는 역할을 하는 것이다. 강의에서 사용하게될 mustache 이란 템플릿 엔진 이외에도 많은 템플릿 엔진이 존재한다. ex) jstl, velocity, Freemarker
+ MVC 구조에 대한 기초적인 내용을 우선적으로 한 번 보고 들어가면 Spring MVC가 작동하는 원리를 이해하는데 도움이 될 듯하다.
2-1. mustache를 활용한 동적인 HTML만들어 보기
우선 사용자로부터의 요청을 받아들일 컨트롤러를 만들어야 한다. 클래스를 생성하자. 그리고 우리는 스프링에게 생성한 클래스가 컨트롤러라는 것을 애노테이션(어노테이션)을 이용해서 알려준다. 알려주는 순간, 우리가 생성한 자바 클래스는 단순한 클래스가 아닌 웹 애플리케이션 구조에서 컨트롤러라는 역할을 수행하게 되는 것이다.
프로젝트를 생성할 때 mustache를 이미 의존성 라이브러리에 삽입해주었기 때문에 mustache 템플릿 엔진을 사용하기 위한 다른 설정은 필요하지 않다. 이제 mustache를 이용해서 템플릿을 만들자. html 확장자를 그대로 따르고 있으며, 스프링은 static 폴더와 templates 폴더를 나누어 HTML 파일과 템플릿 엔진 파일을 구분하게 된다. 그렇기 때문에 우리는 templates 폴더 아래 html 파일을 생성해주면 된다.
Mustache 파일을 호출하기 위해서는 컨트롤러를 통해서 호출해야 한다. 컨트롤러로부터 데이터를 받아야 표현하기로한 데이터 부분을 표현할 수 있기 때문이다. 컨트롤러 클래스에 welcome이라는 메서드를 생성하고(여기에서 메서드 명은 중요하지 않다.), return 값으로 mustache 템플릿의 파일 명만 적어준다. 스프링이 파일명에 자동으로 확장자를 붙여 templates 폴더에서 해당하는 파일을 찾는다.
브라우저에서 어떤 URL을 통해서 이 페이지에 접근할 것인가?
@GetMapping 애노테이션을 통해서 URL을 지정해주면, 지정된 URL로 접속했을 때, 해당하는 메서드의 반환값에 따라 템플릿 파일을 불러오는 것이다. Servlet으로 웹 애플리케이션을 구축해본 경험이 있다면 이해가 쉬울 것이다. pagecontroller에게 @Component 애노테이션을 통해서 URL을 지정해준 원리인 것이다.
code>
1 2 3 4 | @GetMapping(“/welcome”) // welcome 이라는 url로부터 오는 get 요청을 수행한다. public String welcome( ){ // 요청이 들어오면 이 메소드가 실행된다. return “welcome”; // welcome 이라는 파일을 templates에서 찾는다. } | cs |
그렇다면 템플릿에서는 어떻게 값을 보여주는가?
사용자로부터 넘어온 데이터는 컨트롤러에서 모델에 key-value 형식으로 저장한다. 모델? 우리는 모델을 만든 적 없다. 하지만 이미 스프링이 만들어 둔 모델이 존재한다. 우린 그 모델을 컨트롤러의 메소드 인자로 전달만 해주면 되는 것이다. 클라이언트가 get 방식으로든, post 방식으로든 요청을 보내올 것이다. 우리는 컨트롤러에서, 보내오는 정보를 메소드의 인자로 설정해두어 받을 준비를 하면 된다. 그리고 메소드 구현부에서 클라이언트로부터 넘어오는 정보들을 모델에 넣어 주기만 하면 된다. 기본적으로 모델에는 key-value 형식으로 데이터가 저장된다. 컨트롤러에서 이렇게 모델에 데이터를 저장해두면 템플릿에서는 모델에 접근하여 key 값을 통해 값을 get 할 수 있는 것이다.
code>
1 2 3 4 5 | @PostMapping(“/create”) // 이번엔 post 방식으로 데이터를 보내왔다. public String create(Model model, String data){ // 클라이언트로부터 넘어온 데이터를 data라 하자 model.addAttribute(“data”, data); // 모델에 “data"라는 key 값으로 data 값을 저장해둔다. return “welcome”; // welcome.html이라는 템플릿 엔진으로 요청에 응답한다. } | cs |
정리.
Controller에게 요청을 한다. 지정해준 URL 로 요청을 보내면 애노테이션이 적용된 메서드가 호출되고, 메서드의 반환 값에 따른 파일에 해당하는 템플릿을 보여주게 되는 것이다. 템플릿을 보여줄 때, 템플릿 엔진에 설정된 데이터들을 모델에서 찾아 화면에 표시하게 된다.
2-2. 회원 가입 기능 구현
우선 form.html 파일 수정을 조금 수정한다. 사용자는 우리가 제공하는 input 태그를 이용해 서버로 정보를 보낼 것이다. 이 input 태그들을 둘러싸고 있는 form 태그에 action 옵션 추가해준다. 이 action 옵션은 form 태그 안에 있는 submit 타입의 button을 클릭했을 때 실행시킬 action을 설정해주는 것이다. 이 action 옵션에 url을 지정해두어 사용자로부터 오는 정보를 컨트롤러에게 보낼 수 있는 것이다.
1 2 3 | <form action=“/create”> <button type=“submit”>Submit</button> </form> | cs |
그리고 input 태그에 id 속성을 name으로 변경한다. 컨트롤러 역시 input 태그로부터 오는 정보들을 key-value 형식으로 받아들이게 되는데 그 때 key 역할을 하는 것이 input 태그의 속성 중 name 이라는 속성이기 때문이다.
1 | <input type=“text” name=“userId”> | cs |
한 가지 더 해줄 것이 있는데, form 태그에 method 옵션을 추가하는 것이다. 아무 설정도 하지 않으면 default method 방식은 get 방식으로 설정된다. get 방식으로 설정되면 사용자가 전달하려는 데이터들이 url을 통해 쿼리 스트링으로 전달된다. 이 부분은 보안적인 부분에서 매우 취약할 수 있다. 그렇기 때문에 우리는 method 옵션을 post 방식으로 변경해주자.
1 | <form action=“/create” method=“post”> | cs |
cf> get 방식은 무엇이고, post 방식은 무엇인가?
get, post 이외에도 다른 방식이 존재한다. get방식은 주로 데이터를 요청하거나 가져올 때 사용하는 방식이고, post 방식은 새로운 데이터를 추가하거나 수정할 때 사용하는 방식이다.
사용자가 한 번에 여러 input 태그로부터 데이터를 보내오다보니 컨트롤러에서 이 요청을 받아들이기 위한 메서드가 뚱뚱해졌다. ( 받아야 할 인자가 많아졌음을 의미한다! ) 정보가 한 꺼번에 온다는 것은 그 데이터들을 한 객체로 묶을 수 있다는 뜻 아니겠는가! 그래서 우리는 클래스를 하나 더 만들어서 사용자로부터 오는 정보들을 그 클래스를 기반으로 생성되는 인스턴스에 set해주자는 기가 막힌 생각을 하게 된다. 그러면 메소드에서 인스턴스 하나만 인자로 받으면 되니 메서드가 깔끔해지기 때문이다. 이러한 클래스를 VO(Value Object)라고 한다. 이 클래스의 역할은 그저 데이터를 운반해주는 역할을 한다.
이 때 set 메서드의 이름이 중요하다. 우리는 사용자로부터 받는 데이터들을 이 클래스에 담아둔다고 어디에도 명시한 적 없다. 스프링이 요청에 해당하는 메소드의 인자를 확인하고 '아 이 VO 객체에 데이터를 담으면 되겠구나!' 하고 인자로 설정된 클래스를 찾아가기 때문에 set 메소드를 정확히 입력해줘야 한다. 즉, 아까 위에서 언급한 key에 해당하는 부분을 기준으로 convention에 잘 맞게 set 메소드 명을 작성해야 한다는 것이다. 예를 들면 userId 라는 name 의 데이터를 받아온다면 setUserId로 메소드명을 지정해줘야 한다는 것이다! 이렇게 하게 되면 post로 넘겨진 값들이 인자로 전달되는 User 객체에 담기게 된다.
Tip> VO 클래스의 Field에 인자들을 적고, [option] + [command] + [s] 단축키를 누르면 여러 옵션들이 나오는 데 그 중 getter, setter 메소드를 생성할 수 있는 옵션이 있다. (toString 도 있다.)
2-3. 사용자 목록 페이지 구현
redirect 설정하기!
post 요청에 대한 응답을 수행하고, return 값으로 어떤 값을 주는게 좋을까에 대한 부분이다. 일반적으로 글을 작성하면 작성을 마친 다음 완료를 누르면 게시글 형태로 올라온 자신의 글이 보인다. 뭐 이런 것처럼 사용자에 요청에 대한 작업을 마치고 적당한 응답을 해줄 때 사용하는 것이 redirect 이다. 우리가 설정한 페이지로 이동을 시킬 수 있는 것이다.
code>
1 2 3 4 5 | @PostMapping(“/create”) public String create(User user){ users.add(user); // users는 user 인스턴스들을 모아둘 ArrayList 객체이다. return “redirect:/list”; // list 템플릿 엔진으로 redirect 해준다. } | cs |
mustache 에서 for문 사용하여 user list 출력하기!
바로 위 작업에서 템플릿 엔진으로 요청을 보냈으므로 그 list 요청에 해당하는 컨트롤러가 필요하다. 또한 user의 값을 저장하고 있는 ArrayList도 넘겨줘야 한다. 그렇기 때문에 같은 컨트롤러에 하나의 메서드를 더 만들어줌으로써 이를 해결하자.
code>
1 2 3 4 5 | @GetMapping(“/list”) public String list(Model model){ // 템플릿 엔진에게 데이터를 전달해주기 위해 model 을 인자로 받는다. model.addAttribute(“users”, users); // users라는 key 값으로 users라는 value를 전달하자. return “list”; // 데이터가 준비됬으니 템플릿 엔진을 통해 post 요청에 대한 응답을 마무리 하자. } | cs |
위에 링크된 mustache 기본문법 페이지를 살펴보면 반복 작업에 대한 문법적인 내용을 볼 수 있다. 이 부분에서도 태그의 위치를 알맞게 설정해줘야 하는데, 잘못 설정하여 값을 제대로 출력하지 못했었다.
++오늘 배운 Linux 명령어 & Command LIne 명령어++
프로세스 중 java 로 돌아가는 프로세스 확인 |
ps -ef | grep java |
Git에서 강제로 push 하기 |
git push --force |
항상 좋은 강의 해주셔서 감사합니다!
'Dev.BackEnd > Spring Boot' 카테고리의 다른 글
#SLiPP Spring boot, JPA 강의 - 반복주기 5 (0) | 2016.10.23 |
---|---|
#SLiPP Spring boot, JPA 강의 - 반복주기 4 (2) | 2016.10.17 |
[Spring boot] Spring-Boot에서 JDBC Driver 설정하고 사용하기 (0) | 2016.10.12 |
#SLiPP Spring boot, JPA 강의 - 반복주기 3 (0) | 2016.10.10 |
#SLiPP Spring Boot, JPA 강의 - 반복주기 1 (1) | 2016.09.26 |