조합하기
- 코드 합치기
- UI 렌더링 요청
- API 요청
- HTTP 클라이언트로 통신 (axios 이용)
- app.sample.messages 패키지
통합 작업
- 같은 프로젝트에 프런트엔드 코드와 백엔드 코드 추가
- 메시지를 가져와 새로운 메시지를 저장하는 API를 백엔드 코드에 추가
- 백은데 코드와 통신하는 기능을 프런트엔드 코드에 추가
코드 합치기
Vue.js를 통해 httlp-server로 만든 index.html 파일과 정적.js 에셋 파일
-> 통합완료시 임베디드 톰캣 서버가 파일들을 서비스한다.
작성한 프런트엔드 코드를 백엔드 코드에 옮기는 방식으로 통합한다.
처리 과정
#요청이 들어오면 Filter와 DispatcherServlet 객체를 거쳐 Controller 객체에 도달한다.
설계한 코드의 요청 유형
- UI 렌더링
- JSON 메시지를 가져와 새로운 메시지 저장하는 API
UI 렌더링
1. MessageController에 index() 핸들러 메서드 추가
2. 프런트 엔드 코드인 index.html를 resources/templates/index.html에 위치
3. 정적 .js 에셋들은 resources/static/ 에 위치시키면, vue.js 페이지 요청시 스프링이 해당 파일을 제공한다.
API 추가하기
messages/ 페이지에서 UI 제공하므로, API는 경로를 분리한다.
->/api/아래에 API 요청 위치시킨다.
- MessageController 변경 : /api/ 경로 변경, getMessages 추가
- MessageService 변경 : getMessages 추가
- MessageRepository 변경 : Message 객체 리스트를 가져오는 HQL 실행
1. MessageController 변경
- 새 메시지 저장하는 post 메서드 /messages의 위치를 /api/messages로
- 메시지 가져오는 get 메서드 /api/messages 추가
- MessageController에서 @RequestMapping 어노테이션을 제거한다. (/api/로 URL을 변경하므로)
package app.sample.messages;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@Controller
//@RequestMapping("/messages")
public class MessageController {
private MessageService messageService;
public MessageController(MessageService messageService) {
this.messageService = messageService;
}
//index 페이지 매핑 -> index 핸들러 메소드로 뷰 이름 반환
@GetMapping("/messages")
public String index(){
return "index";
}
@GetMapping("/welcome")
//리턴 값을 Http 응답본문으로 처리한다는 뜻 -> 타임리프로 대체
//: 스프링이 핸들러 반환 값을 뷰 이름으로 사용해 타임리프로 응답 생성
//@ResponseBody
public String welcome(Model model){
//public ModelAndView welcome(){
//1. 볼드체 직접 사용
//return "<strong>Hello, Welcome to Spring Boot!</strong>";
//2. Model 인스턴스를 이용해 String 반환
//메소드에 스프링이 생성하는 Model 인스턴스 전달 -> 템플릿의 키와 일치하는 메시지를 속성으로 추가
model.addAttribute("message", "Hello, Welcome to Spring Boot!");
return "welcome";
//3. ModelAndView 인스턴스 생성
// ModelAndView mv = new ModelAndView("welcome");
// mv.addObject("message", "Hello, Welcome to Spring Boot!");
//return mv;
}
//get 매핑 수행
@GetMapping("/api/messages")
@ResponseBody
public ResponseEntity<List<Message>> getMessages() {
List<Message> messages=messageService.getMessages();
return ResponseEntity.ok(messages);
}
//Post 매핑 수행
@PostMapping("/api/messages")
@ResponseBody
public ResponseEntity<Message> saveMessage(@RequestBody MessageData data) {
Message saved = messageService.save(data.getText());
if (saved == null) {
return ResponseEntity.status(500).build();
}
return ResponseEntity.ok(saved);
}
}
2. MessageService 변경
-> getMessages() 메소드 추가
//메시지 가져오기는 항상 읽기만 하므로
@Transactional(readOnly = true)
public List<Message> getMessages() {
return repository.getMessages();
}
3. MessageRepository 변경
(1) getMessages() 메소드에서 하이버네이트 세션을 얻어서 메시지를 가져오는 HQL(Hibernate Query Langaguage) 생성
(2) 하이버네이트가 생성한 SQL 출력하기 위해 application.properties 변경 : logging.level.org.hibernate.SQL=DEBUG
(3) HQL의 실행을 위해 하이버네이트 Query 객체 생성 -> 쿼리 실행해 테이블의 레코드 반환을 위해 .list() 메소드 호출
-> 하이버네이트가 객체 관계 매핑을 관리하고 Message 객체 리스트를 제공한다.
//HQL 실행하도록 하이버네이트 Query 객체 생성해 추가
public List<Message> getMessages() {
Session session = sessionFactory.getCurrentSession();
String hql = "from Message";
Query<Message> query = session.createQuery(hql, Message.class);
return query.list();
}
HTTP 클라이언트로 통신 (axios 이용)
HTTP 클라이언트로 백엔드와 통신하는 작업 수행을 위해 API 기반 프로미스 axios 사용
-> CDN에서 가져와 /resources/static에 추가
https://unpkg.com/browse/axios@0.2.1/dist/axios.min.js
1. index.html 변경
-> 메시지 페이지를 열었을 때 메시지를 가져오는 로직을 Vue 인스턴스의 created() 라이플 사이클 훅에 추가해 빠르게 보여주도록 한다.
변경 전 Index.html
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
</head>
<body>
<div id="app" v-cloak>
<message-list :items="messages" @delete="deleteMessage"></message-list>
<form @submit.prevent="addMessage">
<textarea id="newMessage" v-model="newMessage" placeholder="Leave a message" v-focus></textarea>
<div><button :disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
<script src="../vue.js"></script>
<script type="module">
import MessageList from '../static/components/MessageList.js'
import LifecycleLogger from '../static/plugins/lifecycle-logger.plugin.js'
import '../static/directives/focus.directive.js'
import '../static/filters/datetime.filter.js'
Vue.use(LifecycleLogger, {beforeMount: false})
window.vm = new Vue({
el: '#app',
name: 'MessagesApp',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
methods: {
addMessage (event) {
if (!this.newMessage) return
let now = new Date()
this.messages.push({id: now.getTime(), text: this.newMessage, createdAt: now})
this.newMessage = ''
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
</body>
</html>
추가할 메서드
created () {
axios.get('api/messages?_=' + new Date().getTime())
.then((response) => {
this.messages = response.data
})
.catch((error) => {
console.log('Failed to get messages' + error);
});
},
methods: {
addMessage (event) {
if (!this.newMessage) return
axios.post('api/messages', {text: this.newMessage})
.then((response) => {
this.messages.push(response.data)
this.newMessage = ''
})
.catch((error) => {
console.log(error);
});
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
변경 후 Index.html
<!DOCTYPE html>
<html>
<head>
<title>Messages App</title>
<style>
[v-cloak] {display: none;}
body > div {width: 500px; margin: 0 auto;}
textarea {width: 100%;}
ul {padding: 0 15px;}
</style>
</head>
<body>
<div id="app" v-cloak>
<message-list :items="messages" @delete="deleteMessage"></message-list>
<form @submit.prevent="addMessage">
<textarea id="newMessage" v-model="newMessage" placeholder="Leave a message" v-focus></textarea>
<div><button :disabled="addDisabled" type="submit">Add</button></div>
</form>
</div>
<script src="../vue.js"></script>
<script src="../axios.v0.18.0.min.js"></script>
<script type="module">
import MessageList from './components/MessageList.js'
import LifecycleLogger from './plugins/lifecycle-logger.plugin.js'
import './directives/focus.directive.js'
import './filters/datetime.filter.js'
Vue.use(LifecycleLogger, {beforeMount: false})
window.vm = new Vue({
el: '#app',
name: 'MessagesApp',
data: {
messages: [],
newMessage: ''
},
computed: {
addDisabled () {
return this.messages.length >= 10 || this.newMessage.length > 50
}
},
components: {
MessageList
},
created () {
axios.get('api/messages?_=' + new Date().getTime())
.then((response) => {
this.messages = response.data
})
.catch((error) => {
console.log('Failed to get messages' + error);
});
},
methods: {
addMessage (event) {
if (!this.newMessage) return
axios.post('api/messages', {text: this.newMessage})
.then((response) => {
this.messages.push(response.data)
this.newMessage = ''
})
.catch((error) => {
console.log(error);
});
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
})
</script>
</body>
</html>
(1) Script에 axios CDN 추가
<script src="../axios.v0.18.0.min.js"></script>
(2)created() 라이프 사이클 훅 내부에 메시지 가져오는 로직 추가
(3) addMessage() 메소드에서 axios.post()로 새 메시지를 백엔드에 보낸다.
-> 요청 완료시 저장된 메시지를 메시지 리스트에 추가
2. addMessages() 메소드 수정
created () {
axios.get('api/messages?_=' + new Date().getTime())
.then((response) => {
this.messages = response.data
})
.catch((error) => {
console.log('Failed to get messages' + error);
});
},
methods: {
addMessage (event) {
if (!this.newMessage) return
axios.post('api/messages', {text: this.newMessage})
.then((response) => {
this.messages.push(response.data)
this.newMessage = ''
})
.catch((error) => {
console.log(error);
});
},
deleteMessage (message) {
this.messages.splice(this.messages.indexOf(message), 1)
}
}
'Server Programming > Spring Boot 2 Full-Stack Programming' 카테고리의 다른 글
[작업 관리 애플리케이션 개발] 3. 스프링 5 -> 스프링 부트 + MySQL (0) | 2022.11.22 |
---|---|
[작업 관리 애플리케이션 개발] 2. Vue.js 2 (0) | 2022.11.22 |
[작업 관리 애플리케이션 개발] 1. 자바 개발자 관점의 자바스크립트 (0) | 2022.11.22 |