1) 블랙박스 테스트 : 사용자의 입장에서 동작을 검사 (많은 값을 넣어보기)
2) 개발자 테스트 : 개발자가 직접 테스트 코드를 작성
장점
- 빠르고 정확한 테스트가 가능합니다. (예상 동작 VS 실제 동작)
- 테스트 자동화가 가능합니다.
- 배포 절차 시 테스트 코드가 수행되어 동작 검증
- 리팩토링이나 기능 추가를 할 때 더욱 편리합니다.
단점
- 개발 시간이 오래 걸림
- 테스트 코드를 유지보수하는 비용

테스트 코드의 위치는 test 폴더 내에 있다
작성 중인 코드에서 alt + insert > test 하면 자동으로 테스트를 만들어준다
테스트 코드는 main메소드가 없다 >> JUnit은 따로 main 실행하지 않아도 각각의 메서드, 기능별로 테스트 코드를 작성하여 실행시킬 수 있다.
test1, test2,.. 등의 메소드를 각각 실행시키면 된다.
이전 팀 프로젝트에서 Main 완성 이전에 테스트해보기 어렵다고 했던 팀원이 있었는데, 이 방법을 이용하면 테스트할 수 있겠다!
Assertions.assertEquals(10, result);
내가 예상한 값은 10이고, result (return될 값)이랑 동일할 경우 Test Passed가 된다.

server.port = 8081 등, 기본으로 설정된 포트를 바꿀 수도 있다.
동적 웹 페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술

약속된 HTTP의 규격 (어떤 브라우저에서 요청해도 구조는 동일함)을 맞추면서 쉽게 HTTP에 담긴 데이터를 사용하기 위한 객체
요청이 들어오면 답변 객체가 바로 만들어진다
web.xml에서 찾는다. 우리가 찾는 것은 아님!
Request에 담긴 GET,PUT,POST,... 에 해당하는 메소드 호출
API가 여러개 있을 때 각각에 매칭이 되는 Servlet 객체를 구현, 요청 메소드에 맞는 메소드를 호출 등등,... 이것들 다 개발자가 작성할 필요가 없도록 하는 서블릿
Front Controller 패턴 방식으로 처리하고 있다

GET /api/hello → 'HelloController' 의 **hello() 함수**
@RestController
public class HelloController {
@GetMapping("/api/hello") // Get 방식의 ~ 경로로 들어오면
public String hello() { // 이 함수를 호출할 것이다
return "Hello World!";
}
}@GetMapping("/api/hello")이후 더 복잡한 처리를 할 때는 Controller 내에서도 게층을 분리한다 (3 Layered..)
API마다 파일을 만들 필요가 없다.
@Controller
public class HelloController {
@GetMapping("/api/hello")
public String hello() {
return "Hello World!";
}
}
이때 String 타입의 return은 해당 String 이름의 HTML 파일이 있는지 확인해서 HTML을 반환한다 = View name의 정보를 반환해준다.
만약 실제 문자열을 return하고 싶다면 hello()위에 @ResponseBody 를 작성해야 한다
ex) Get으로 작성해두고 POST로 호출

@GetMapping("/api/hello")
@ResponseBody
public String get() {
return "GET Method 요청";
}
@PostMapping("/api/hello")
@ResponseBody
public String post() {
return "POST Method 요청";
}
/api로 시작하는 요청은 RequestMapping 내의 메소드로 들어오고, 이후 경로를 찾는다.
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login() { // 메서드명은 매핑에 관련 없으나 중복될 수 없다.
// ...
}
@GetMapping("/logout")
public String logout() {
// ...
}
@GetMapping("/signup")
public String signup() {
// ...
}
@PostMapping("/signup")
public String registerUser(SignupRequestDto requestDto) {
// ...
}
}


1. 직접 접근
변경할 내용 없이 이미 완성된 페이지만을 출력할 것이기 때문에 굳이 Controller을 거칠 필요는 없다.
http://localhost:8080/hello.html
2. Controller을 거쳐서 반환
우선 html 파일을 반환하도록 한다.
@GetMapping("/static-hello")
public String hello() {
return "hello.html";
}
thymeleaf를 추가하면 기본적으로 HTML 페이지를 templates 폴더 내에서 찾게 되어있다.
만약 다른 폴더에서 찾고 싶으면 themeleaf를 주석 처리하고, 직접 경로로 작성한다.
http://localhost:8080/폴더명-hello.html
3. Redirect
= 재호출. 직접 접근하는 경로로 다시 한 번 접근해라 (돌아서 접근하기)
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
http://localhost:8080/html/redirect
redirect 내에서 hello.html을 호출하는 것이므로 실질적으로는 두 번 페이지를 읽는다
4. Template engine에 view 전달
HTML 파일 이름 전체가 아닌 .html 앞까지만 작성하면 된다
@GetMapping("/html/templates")
public String htmlTemplates() {
return "hello";
}
http://localhost:8080/html/templates
_(1).png?id=60354f85-b3b2-47a4-9e90-8f5ca06f3bb6&table=block&spaceId=83c75a39-3aba-4ba4-a792-7aefe4b07895&width=1810&userId=&cache=v2)
반환할 데이터는 Model에 담고, Model이 적용될 View Main에 작업을 전달하면, ViewResolver을 통해서 그 Model의 데이터가 View에 적용이 되고 완성된 페이지를 클라이언트에 반환한다.
private static long visitCount = 0; // 객체 생성시마다 값이 변하지 않도록 함
...
@GetMapping("/html/dynamic")
public String htmlDynamic(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
return "hello-visit";
}
// 방문 한 번 할 때마다 방문 카운트를 +1 한다
여기서 매개변수로 쓰인 Model은 springframework.ui의 것이다.
Model에 변동이 일어난 데이터를 넣어주기
model.addAttribute("visits", visitCount);
HTML 파일에서 방문 카운트 출력하는 부분은 아래로 작성되어 있다
(방문자 수: <span th:text="${visits}"></span>)
동적 페이지 처리 과정
위 같은 html/css/js 파일 자체를 반환하는 방식은 요즘 선호되지 않는다.
최초 요청 때는 html을 반환해야 한다! 이때는 View를 반환해야 한다
프론트엔드와 백엔드가 각각 따로 발전하게 되면서, 느슨하게 결합하는 방식을 더 많이 채택하게 되었고, 최근에는 서버가 직접 뷰(html/css/js)를 반환하기 보다는 요청에 맞는 특정한 정보만 반환하는 것을 조금 더 선호한다.
주로 서버에서는 데이터 교환 포맷 중 아래와 같은 JSON 형태로 데이터를 반환한다.

R Studio 써서 다뤄 봤던 데이터다..!
기본적인 html 파일 요청을 제외하고는 JSON 데이터를 요청하는 API를 통해 브라우저에서 html을 조작하여 반영하는 방식
// [Response header]
// Content-Type: application/json
// [Response body]
// {"name":"Robbie","age":95}
@GetMapping("/json/string")
@ResponseBody
public String helloStringJson() {
return "{\"name\":\"Robbie\",\"age\":95}";
}
http://localhost:8080/response/json/string
// [Response header]
// Content-Type: application/json
// [Response body]
// {"name":"Robbie","age":95}
@GetMapping("/response/json/class")
@ResponseBody
public Star helloClassJson() {
return new Star("Robbie", 95);
}
http://localhost:8080/response/json/class
Star 객체에는 String name, int age 필드가 있다.
스프링에서 자동으로 자바 객체를 JSON으로 변환해준다.
즉, 클라이언트에 JSON 형태를 리턴하게 된다.
필드명이 Key, 우리가 삽입한 데이터가 Value가 된다.
= @Controller + @ResponseBody
@RestController
@RequestMapping("/response/rest")
public class ResponseRestController {
@GetMapping("/json/string")
public String helloStringJson() {
return "{\"name\":\"Robbie\",\"age\":95}";
}
}
우리 강의 중에는 배우는 단계이기 때문에 html까지 반환한다.
JSON 데이터 구조를 처리해주는 라이브러리
JSON 타입의 String으로 변환JSON 타입의 String을 Object로 변환ObjectMapper 클래스를 사용한다.@Test
@DisplayName("Object To JSON : get Method 필요")
void test1() throws JsonProcessingException {
Star star = new Star("Robbie", 95);
ObjectMapper objectMapper = new ObjectMapper(); // Jackson 라이브러리의 ObjectMapper
String json = objectMapper.writeValueAsString(star);
// json 형태를 읽을 수 없기 때문에 String으로 변환시켜 콘솔에 찍는다.
System.out.println("json = " + json);
}
@Test
@DisplayName("JSON To Object : 기본 생성자 & (get OR set) Method 필요")
void test2() throws JsonProcessingException {
String json = "{\"name\":\"Robbie\",\"age\":95}"; // JSON 타입의 String 생성
ObjectMapper objectMapper = new ObjectMapper();
// Jackson 라이브러리의 ObjectMapper
Star star = objectMapper.readValue(json, Star.class);
System.out.println("star.getName() = " + star.getName());
// 실제로 star 객체에 값이 들어갔는지 확인하기 위해
}
Object로 변환 시 기본 생성자, getter나 setter 중 하나가 필요하다.
이때 변환시킬 객체의 필드명 = json의 key값 이어야 한다.
objectMapper.readValue() : 매개변수는 (json, 어떤 객체로 만들 건지)
이때 객체 부분은클래스명.class를 사용해야 한다.
클라이언트에서 서버로 HTTP 요청을 보낼 때 데이터를 함께 보낸다. 이때 서버에서는 이 데이터를 받아서 사용해야 하는데, 데이터를 보내는 방식이 여러 가지가 있기 때문에 모든 방식에 대해 처리 방법을 학습해야 한다.
서버에 보내려고 하는 데이터를 URL 데이터를 경로에 추가한다
GET http://localhost:8080/hello/request/star/Robbie/age/95
// [Request sample]
@GetMapping("/star/{name}/age/{age}") // 필요한 데이터 부분에 {변수}
@ResponseBody
public String helloRequestPath(@PathVariable String name, @PathVariable int age)
// 중괄호 내의 변수 값을 넣어서 함수 실행
{
return String.format("Hello, @PathVariable.<br> name = %s, age = %d", name, age);
// 문자열을 리턴하여 화면에 찍는다
}

데이터를 보낼 때 key=value&key=value,... 형태로 보낸다
GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
// [Request sample]name=Robbie&age=95
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam String name, @RequestParam int age) {
return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
<form method="POST" action="/hello/request/form/model">
<div>
이름: <input name="name" type="text">
</div>
<div>
나이: <input name="age" type="text">
</div>
<button>전송</button>
</form>
태그 내에 form 태그를 작성
method : 전송 방식 / action : URL
POST http://localhost:8080/hello/request/form/param
// [Request sample]
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=Robbie&age=95
@PostMapping("/form/param")
@ResponseBody
public String helloPostRequestParam(@RequestParam String name, @RequestParam int age) {
return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
생략해도 프로그램 실행에 무방하다
GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
// [Request sample]
@GetMapping("/form/param")
@ResponseBody
public String helloGetRequestParam(@RequestParam(required = false) String name, int age) {
return String.format("Hello, @RequestParam.<br> name = %s, age = %d", name, age);
}
@RequestParam(required = false)@PathVariable(required = false) 도 해당 옵션이 존재합니다..png?id=06cefd7c-7041-4013-a10d-2864774d42c4&table=block&spaceId=83c75a39-3aba-4ba4-a792-7aefe4b07895&width=1810&userId=&cache=v2)
@RequestParam처럼 일일히 받아오기 힘들 경우 Java 객체로 받아올 수 있어 편리하다.
key-value
POST http://localhost:8080/hello/request/form/model
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=Robbie&age=95
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age); //star 객체의 값을 리턴
}
HTML의 form 태그를 사용하여 POST 방식으로 HTTP 요청을 전송
Jackson 라이브러리와 유사
이때 객체의 필드명과 @ 뒤의 필드 값이 같아야 한다. 다를 경우 값이 넘어오지 않는다
GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95
@GetMapping("/form/param/model")
@ResponseBody
public String helloRequestParam(@ModelAttribute Star star) {
return String.format("Hello, @ModelAttribute.<br> (name = %s, age = %d) ", star.name, star.age);
}
@ModelAttribuute는 생략이 가능하다.
이때 @RequestParam의 생략과는 어떻게 구분할까?
@RequestParam은 SimpleValueType 앞이고, 아닐 때는 Model이라고 판단한다
이때 Simple~은 원시 타입, Wrapper 타입, date등의 타입이다
HTTP Body에 JSON 데이터를 담아 서버에 전달할 때 해당 Body 데이터를 Java의 객체로 전달 받을 수 있다.
이때 받아 올 객체에 Getter / Setter / 값을 받아올 생성자가 필요하다.
POST http://localhost:8080/hello/request/form/json
// Header
// Content type: application/json
// Body
// {"name":"Robbie","age":"95"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
입력받아온 데이터를 처리하기 위한 객체 앞에 @RequestBody를 작성한다.
⚠️ 데이터를 Java의 객체로 받아올 때 주의할 점이 있습니다.
- 해당 객체의 필드에 데이터를 넣어주기 위해 set or get 메서드 또는 오버로딩된 생성자가 필요합니다.
- 예를 들어 @ModelAttribute 사용하여 데이터를 받아올 때 해당 객체에 set 메서드 혹은 오버로딩된 생성자가 없다면 받아온 데이터를 해당 객체의 필드에 담을 수 없습니다.
- 이처럼 객체로 데이터를 받아올 때 데이터가 제대로 들어오지 않는다면 우선 해당 객체의 set or get 메서드 또는 오버로딩된 생성자의 유무를 확인하시면 좋습니다.

데이터 전송 및 이동을 위해 생성되는 객체

대부분 일반 객체와 동일한 형태,
객체명 + ResponseDto / RequestDto 라는 이름의 클래스
List<MemoResponseDto> responseList =
memoList.values().stream().map(MemoResponseDto::new).toList();
for 문을을 돌면서 변수의 값을 하나씩 추출한다
변수의 값을 객체 Map으로 변환한다
앞 객체의 생성자가 실행된다. 이때 매개변수는 stream()에서 리턴받은 Memo 객체이다.
~를 List 형태로 변환한다