안녕하세요
업보처럼 차곡차곡 수업 내용 복습을 미루다가 더 늦기전에 후다닥 하는 중입니다ㅠ
스프링 부트 설치는 그다지 어렵지 않고, 그 이후에 정확히 원리를 알고 이용하는 것이 중요하기 때문에
설치 후에 내용을 다뤄보도록 하겠습니다!
0. Spring Boot 설치 후 이 파일들 뭔데?
1) src/main/java
저희가 추후 작성할 대부분의 자바 파일들이 들어갈 공간입니다.
컨트롤러, DTO, 데이터 베이스 처리를 위한 엔티티, 서비스 파일 등 다양한 파일이 들어갑니다.
2) src/main/resource
여기는 자바 파일을 제외한 환경 설정 및 HTML, CSS, 정적 파일 등이 들어갈 공간입니다.
이 resource파일 내에 다양한 하위 디렉토리들이 또 있는데요,
- static : 정적인 파일들이 들어갑니다. js,image, html, css 등이 들어갑니다!
- templates: HTML 파일의 형태로 자바의 객체와 연동되는 파일이라고 합니다. 저희 수업에서는 thymeleaf(타임리프)라는 서버 사이드 템플릿 엔진을 사용하고 있습니다.
- application.properties: 해당 애플리케이션의 설정을 정의하는데 사용되는 프로퍼티 파일입니다. 예시로는, 서버 포트를 설정하고, 데이터베이스 연결을 설정하거나 이름 설정 등 다양한 설정을 정의할 수 있습니다.
3) build.gradle
이 파일은 그레이들이라는 아이가 사용하는 환경 파일입니다.
(gradle은 Groovy 언어를 기반으로 한 빌드 도구로, 프로젝트를 구성하고 빌드 스크립트를 작성합니다!
Maven과 Ant 보다 좋다고 하네요)
대부분의 출저: https://wikidocs.net/160947
2-01 스프링부트 프로젝트의 구조
현재 SBB 프로젝트는 HelloController.java와 HelloLombok.java 파일만 생성한 상태다. 그런데 이보다 규모를 갖춘 프로젝트를 만들고자 한다면 프로젝트…
wikidocs.net
2. DTO, VO
자바를 배운지 얼마 되지 않았지만, REST API를 배우니 자주 사용하는 단어가 등장합니다.
DTO와 VO라는 단어입니다.
1) DTO

로직을 갖고 있지 않은 순수한 데이터 객체를 나타냅니다.
데이터 "전달"의 용도로 사용하기 때문에 getter와 setter를 제외한 타 로직은 들어가지 않습니다.
저는 밑에서 배우는 VO는 객체에 있는 데이터 그 자체를 갖고 가는 느낌이고,
DTO는 객체 상자여서 그걸 나를 수 있는 형태? 같은 느낌이었습니다.
(아닐수도,,,)

깔끔하고 간단하쥬?
2) VO
DTO와 달리 값 오브젝트를 지닌 데이터 값 그 자체입니다.
VO는 setter가 없고, getter만 있기 때문에, 값 변경이 불가능하고 데이터 불변성을 보장한다고 합니다.
그래서 Read-Only 속성을 가지고 있습니다.
따라서, 값 자체가 의미가 있는 게 VO 이고, 이 전달될 데이터들이 잘 보존되는 걸 보장하는 게 DTO라고 하네요
이 DTO와 VO가 따로 뭐 모듈이나, 어떤 새로운 무언가인 줄 알았는데,
그저 이런 속성을 가진 데이터를 정의하고, 저희가 이걸 사회적으로 각각 DTO, VO라고 부르기로 한거라네요!
3) api를 해보자
수업 시간에 api를 배우면서 간단한 구조를 만들고, get, post 등을 mapping하는 것을 배웠었습니다.

폴더 구조를 본다면 java파일 안에 controller와 배웠던 dto, vo 패키지가 있습니다.
resources 패키지 안에는 static패키지와 templates패키지가 있습니다.
이 controller 중, HelloController를 보도록 하겠습니다.
@Controller
public class HelloController {
@GetMapping("hi")
public String getHi(Model model) {
Hello hello = new Hello(99);
int age = 17;
List<String> names = Arrays.asList("Kim", "Lee", "Hong", "Park");
model.addAttribute("hello", "Spring World");
model.addAttribute("uText", "<onstrong>Hello</strg>");
model.addAttribute("value", "이름을 입력하세요!");
model.addAttribute("WithValue", "hello");
model.addAttribute("link", "hi");
model.addAttribute("imgSrc", "cutecat.jpg");
model.addAttribute("userRole", "admin");
model.addAttribute("isAdmin", false);
model.addAttribute("names", names);
model.addAttribute("classHello", hello);
return "hi";
}
class Hello {
private int age;
public Hello(int age) {this.age = age;}
public int getAge() {return age;}
}
}
- @Controller 어노테이션: 해당 클래스가 컨트롤러임을 알려줌
- @GetMapping("hi"): /hi 경로로 들어오는 GET 요청에 대한 처리를 담당하는 메소드
- Model model: Thymeleaf 템플릿에 데이터를 전달하기 위한 모델 객체
- Hello 클래스: 데이터 객체
이 Controller를 통해 이런 Thymeleaf로 view를 보여줄 수 있게 됩니다.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 타임리프 표현식과 문법 -->
<p th:text="'안녕하세요. ' + ${hello}"></p>
<p th:utext="${uText}"></p>
<input type="text" th:value="${value}">
<p th:with="temp=${withValue}" th:text="${temp}"></p>
<a th:href="@{/{id}(id=${link})}">Link 1 (hi)</a> <br />
<a th:href="@{/hi}">Link 2 (hi)</a> <br />
<a th:href="@{https://www.google.com}">Google</a> <br />
<img th:src="${imgSrc}">
<div th:switch="${userRole}">
<p th:case="'admin'">Hello, Admin</p>
<p th:case="'user'">Hello, User</p>
<p th:case="*">Hello, Unknown</p>
</div>
<div>
<p th:if="${isAdmin}">Welcome, Admin</p>
<p th:unless="${isAdmin}">Welcome, Admin</p>
</div>
<ul>
<li th:each="name:${names}" th:text="${name}"></li>
</ul>
<div th:text="${classHello.getAge()}"></div>
</body>
</html>

보시면, 순서에 따라 제가 Controller에서 addAttribute한 name값과 일치한다면,
Thymeleaf에서 잘 표현되는 것을 볼 수 있습니다.
4) GetMapping을 다양하게 사용하는 법
아는 분들은 아시다시피, Get을 사용하는 것은 단순히 url에 따른 html을 로드하는 것도 있지만
url의 뒤에 query를 사용하여 사용자값에 따라 다른 url을 보여주기도 합니다.
하여, 알아보도록 하겠습니다!
// case 1.
@GetMapping("/get/response1")
public String getResponse1(@RequestParam(value="name") String name, @RequestParam(value="age") String age, Model model) {
model.addAttribute("name", name);
return "response";
}
case1.
@RequestParam을 이용하여 value에는 key값을, 그 다음에는 value값을 받습니다.
이 경우에는, required=true가 디폴트로 되어있어,
만약의 뒤에 값이 없다면 이 controller를 찾지 못해서 400 error가 뜨게 됩니다.
// case 2.
@GetMapping("/get/response2")
public String getResponse2(@RequestParam(value="name", required = false) String name, Model model) {
model.addAttribute("name", name);
return "response";
}
case2.
이 경우는 case1과 같지만, 이번에는 required=false를 설정하여
뒤의 값이 없어도 400에러가 뜨지 않습니다.
// case 3.
@GetMapping("/get/response3/{name}/{age}")
public String getResponse3(@PathVariable(value="name") String n,
@PathVariable String age, Model model) {
model.addAttribute("name", n);
model.addAttribute("age", "13");
return "response";
}
case3.
이 경우는 url에 RequestParam대신에 @PathVariable을 사용하여
url에 있는 여러개의 변수를 받아올 수 있습니다.

// case 4.
@GetMapping({"/get/response4/{name}", "/get/response4/{name}/{age}"})
public String getResponse4(@PathVariable(value="name") String n,
@PathVariable(required = false) String age, Model model) {
model.addAttribute("name", n);
model.addAttribute("age", age);
return "response";
}
case4.
여러개의 url패턴을 하나의 메소드에서 처리할 수 있는 경우입니다.
age값이 false라서, name값이 있는 url과 name값과 age값 전부 있는 url을 유연하게 처리할 수 있습니다.


5) Post도 알아보자
// case 1.
@PostMapping("/post/response1")
public String postResponse1(@RequestParam(value="name") String name, Model model) {
model.addAttribute("name", name);
return "response";
}
case 1.
Post 전송은 @PostMapping 어노테이션을 활용하며, 전달되는 값은 @RequestParam을 사용해서 전달합니다.
HTML 폼으로 POST 요청을 보낼 때는 @RequestParam을 사용하여 쿼리 파라미터를 읽어올 수 있습니다.
name이라는 key값이 담아서 name이라는 value로 전달하며, response라는 html을 반환하는 것입니다.
// case 2.
@PostMapping("/post/response2")
public String postResponse2(@RequestParam(value="name", required = false) String name, Model model) {
model.addAttribute("name", name);
return "response";
}
case 2.
이 케이스는 1과 동일한데, name값에 required = false가 추가 되었습니다.
name값에 해당하는 키가 없어도 실행은 된다~ 이 말입니다.
// case 3.
@PostMapping("/post/response3")
@ResponseBody
public String postResponse3(@RequestParam(value="name", required = false) String name, Model model) {
model.addAttribute("name", name);
return name;
}
case 3.
저희가 앞선 모든 케이스에서는 return을 template 내 html파일로 진행했었는데요,
post로 리턴하는 문자열의 그대로 값을 전달할 수 있습니다.
@ResponseBody 어노테이션을 사용한다면 html파일을 불러오지 않고, 값을 send할 수 있습니다!
5) post의 값을 객체로 전달할 때
저희가 post의 값을 받아올 때 늘 하나하나 받아오면 좋겠지만,
그걸 객체로 처리해야할 때가 있잖아요?
그 경우입니다.
@GetMapping("/dto/response1")
@ResponseBody
public String dtoResponse1(@ModelAttribute UserDTO userDTO) {
String msg = "이름 : " + userDTO.getName() + ", 나이 : " + userDTO.getAge();
return msg;
}
@ModelAttribute로 이 들어오는 데이터가 UserDTO라는 객체인 것을 알려주고 매핑해줍니다!
그래서 이 userDTO의 getter로 데이터에 접근할 수 있습니다.
그리고 이 @ModelAttribute는 생략이 가능합니다.
@PostMapping("/dto/response3")
@ResponseBody
public String dtoResponse3(@RequestBody UserDTO userDTO) {
String msg = "이름 : " + userDTO.getName() + ", 나이 : " + userDTO.getAge();
return msg;
}
이 코드는 위에서 @ModelAttribute를 사용했느냐 @RequestBody를 사용했느냐의 차이인데요,
제가 값을 입력한 곳은 일반 폼 전송으로, www-x-form-urlencoded의 형태로 데이터를 전송하는데
이 형태로는 RequestBody를 받을 수 없습니다.
오로지 json형태와 xml 형태로 데이터가 들어와야만 RequestBody 어노테이션을 사용할 수 있습니다.
6) axios와 DTO, VO를 사용할 때
기나긴 여정이 될 것입니다..
DTO를 axios와 쓸 때 5가지 경우를 보겠습니다.
1. Get + DTO
function dtoResponse1() {
var form = document.getElementById('form_dto1');
axios.get(`/axios/response1?name=${form.name.value}&age=${form.age.value}`)
.then((res)=>{
console.log( res );
console.log( 'dtoResponse1 : ', res.data );
});
}
function dtoResponse2() {
var form = document.getElementById('form_dto1');
axios.get(`/axios/response2?name=${form.name.value}&age=${form.age.value}`)
.then((res)=>{
console.log( res );
console.log( 'dtoResponse2 : ', res.data );
});
}
@GetMapping("/axios/response1") // 정상
@ResponseBody
public String axiosAPI1(@RequestParam(value = "name") String name, @RequestParam(value = "age") String age){
String msg = "이름 : " + name + "\n나이 :" + age;
return msg;
}
@GetMapping("/axios/response2") // 정상
@ResponseBody
public String axiosAPI2(UserDTO userDTO){
String msg = "이름 : " + userDTO.getName() + "\n나이 :" + userDTO.getAge();
return msg;
}
- 데이터가 URL에 노출되어 전송되기 때문에 일반적인 GET 요청에 사용 가능합니다.
- @RequestParam은 각각의 파라미터를 직접 받아옵니다.
2. POST + DTO
function dtoResponse3() {
var form = document.getElementById('form_dto2');
axios.post(`/axios/response3`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'dtoResponse3 : ', res.data );
});
}
function dtoResponse4() {
var form = document.getElementById('form_dto2');
axios.post(`/axios/response4`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'dtoResponse4 : ', res.data );
});
}
function dtoResponse5() {
var form = document.getElementById('form_dto2');
axios.post(`/axios/response5`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'dtoResponse5 : ', res.data );
});
}
// case 1.
@PostMapping("/axios/response3") // error
@ResponseBody
public String axiosAPI3(@RequestParam(value = "name", required = false) String name, @RequestParam(value = "age", required = false) String age){
String msg = "이름 : " + name + "\n나이 :" + age;
return msg;
}
// case 2.
@PostMapping("/axios/response4") // null
@ResponseBody
public String axiosAPI4(UserDTO userDTO){
String msg = "이름 : " + userDTO.getName() + "\n나이 :" + userDTO.getAge();
return msg;
}
// case 3.
@PostMapping("/axios/response5") // 정상
@ResponseBody
public String axiosAPI5(@RequestBody UserDTO userDTO){
String msg = "이름 : " + userDTO.getName() + "\n나이 :" + userDTO.getAge();
return msg;
}
post는 get보다 약간 더 까다롭습니다.
- case1: @RequestParam은 주로 URL의 쿼리 파라미터를 받아오는 데 사용되므로, POST 요청의 본문에 담긴 데이터는 읽을 수 없습니다.
- case2: @ModelAttribute는 주로 HTML 폼 데이터를 받아오는 데 사용되지만, JSON 형식의 데이터를 처리하기 어렵습니다.
- case3: @RequestBody는 POST 요청의 본문(body)에 담긴 데이터를 읽어와 객체로 변환합니다.
- JSON 형식의 데이터를 처리할 때 주로 사용되며, 이 경우에는 axios.post 메소드로 JSON 데이터를 전송하고, 서버에서 @RequestBody로 받아 처리합니다
- 결론: POST + DTO에는 @RequestBody를 사용하여 데이트를 받아와야한다!!!!!!!!!!!!!
3. GET + VO
function voResponse1() {
var form = document.getElementById('form_vo1');
axios.get(`/axios/vo/response1?name=${form.name.value}&age=${form.age.value}`)
.then((res)=>{
console.log( res );
console.log( 'dtoResponse1 : ', res.data );
});
}
function voResponse2() {
var form = document.getElementById('form_vo1');
axios.get(`/axios/vo/response2?name=${form.name.value}&age=${form.age.value}`)
.then((res)=>{
console.log( res );
console.log( 'voResponse2 : ', res.data );
});
}
// case 1.
@GetMapping("/axios/vo/response1")
@ResponseBody
public String axiosVOAPI1(@RequestParam(value = "name") String name, @RequestParam(value = "age") String age){
String msg = "이름 : " + name + "\n나이 :" + age;
return msg;
}
// case 2.
@GetMapping("/axios/vo/response2")
@ResponseBody
public String axiosVOAPI2(UserVo userVO){
String msg = "이름 : " + userVO.getName() + "\n나이 :" + userVO.getAge();
return msg;
}
- 보이는 두가지의 경우 전부 잘 되고 가능합니다.
- @RequestParam으로 받아올 수도 있고 @ModelAttribute에서 UserVo에 mapping하여 받아오기도 가능합니다.
4. POST + VO
function voResponse3() {
var form = document.getElementById('form_vo2');
axios.post(`/axios/vo/response3`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'voResponse3 : ', res.data );
});
}
function voResponse4() {
var form = document.getElementById('form_vo2');
axios.post(`/axios/vo/response4`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'voResponse4 : ', res.data );
});
}
function voResponse5() {
var form = document.getElementById('form_vo2');
axios.post(`/axios/vo/response5`, {name: form.name.value, age: form.age.value})
.then((res)=>{
console.log( res );
console.log( 'voResponse5 : ', res.data );
});
}
// case 1.
@PostMapping("/axios/vo/response3")
@ResponseBody
public String axiosVOAPI3(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "age", required = false) String age){
String msg = "이름 : " + name + "\n나이 :" + age;
return msg;
}
// case 2.
@PostMapping("/axios/vo/response4")
@ResponseBody
public String axiosVOAPI4(UserVo userVO){
String msg = "이름 : " + userVO.getName() + "\n나이 :" + userVO.getAge();
return msg;
}
// case 3.
@PostMapping("/axios/vo/response5")
@ResponseBody
public String axiosVOAPI5(@RequestBody UserVo userVO){
String msg = "이름 : " + userVO.getName() + "\n나이 :" + userVO.getAge();
return msg;
}
- case1: @RequestParam을 사용하여 POST 요청의 본문(body)에서 데이터를 읽어오는 방식입니다. 하지만 이 방식은 일반적으로 HTML 폼 데이터를 처리할 때 사용되므로, JSON 형식의 데이터를 처리하기 어렵습니다.
- case2: UserVo 클래스를 사용하여 객체로 바로 매핑하는 방식입니다. 하지만 @ModelAttribute는 JSON 형식의 데이터를 처리하기 어렵습니다.
- case3: @RequestBody를 사용하여 JSON 형식의 데이터를 읽어오는 방식입니다. Axios에서 POST 요청을 보낼 때 JSON 형식의 데이터를 본문에 담아 전송하고, 서버에서는 @RequestBody를 통해 해당 데이터를 UserVo 객체로 변환하여 사용합니다.
- 결론: POST+ VO에서는 (폼 데이터가 아니라면) RequestBody로 보낸다!!!!!!!!!!!!!
'개발 공부한 것들 > SeSSAC 웹 풀스텍 과정 회고록' 카테고리의 다른 글
| [코딩온] 웹 풀스택 영등포 5기 과정_프로젝트 회고록 (0) | 2023.12.31 |
|---|---|
| [코딩온] 웹 풀스택 영등포 5기 과정_next.js(1) (1) | 2023.12.17 |
| [SeSSAC] 웹 풀스택 영등포 5기 과정_Java(Class) (2) | 2023.11.25 |
| [SeSSAC] 웹 풀스택 영등포 5기 과정_socket (1) | 2023.11.17 |
| SeSSAC 웹 풀스텍 과정 회고록[SeSSAC] 웹 풀스택 영등포 5기 과정_2차 프로젝트 회고록 (0) | 2023.11.14 |