Spring을 새롭게 공부하게 되었는데 이를 복습하면서 기록하기 위해 이 글을 작성하게 되었다.
[수업 목표]
1. 스프링 3계층 Controller, Service, Repository 각 역할을 이해한다.
2. 스프링 MVC를 이해하고 사용법을 익힌다.
3. 스프링 IoC 컨테이너 / DI 의 핵심개념을 이해한다.
1) ItelliJ IDEA Ultimate
2) Java 8
3) Advanced REST Client - UI 없이 API 확인
1) IntelliJ 실행
2) New Project 클릭
3) 왼쪽 메뉴에서 "Spring Initializer" 클릭 후 "Next" 클릭
4) [중요] 확인 사항
5) 검색창에 다음 5가지 검색 후 엔터
환경설정 -> "auto import" -> Insert imports on paste: Always / Add unambiguous imports on the fly 체크
1) H2 웹콘솔 설정
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:springcoredb
2) SpringcoreApplication.java 파일 실행
3) H2 console URL(http://localhost:8080/h2-console) 접속해서, "JDBC URL"에 위에서 설정한 URL(jdbc:h2:mem:springcoredb) 입력 후 Connect 버튼 클릭
"스프링 서버 구현 시 왜 꼭 Controller, Service, Repository 사용해야 할까?
일단, Servlet 사용해보자!
Servlet(서블릿)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말함.
출처: 위키백과
@ServletComponentScan : ItemSearchServlet 클래스의 @WebServlet 어노테이션이 동작하게 함
-> @WebServlet(urlPatterns = "/api/search")까지 보고 ItemSearchServlet에 들어옴 -> GET 요청에 대한 처리가 ItemSearchServlet에서 처리됨
모든 서버의 요청들은 요청(HttpServletRequest) 과 응답(HttpServletResponse)로 되어있음
ItemSerachServlet 클래스
// 1. API Request 의 파라미터 값에서 검색어 추출 -> query 변수
String query = request.getParameter("query");
// 2. 네이버 쇼핑 API 호출에 필요한 Header, Body 정리
RestTemplate rest = new RestTemplate(); // 서버에서 또 다른 서버 호출 시 사용
HttpHeaders headers = new HttpHeaders();
headers.add("X-Naver-Client-Id", "");
headers.add("X-Naver-Client-Secret", "");
String body = "";
HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);
// 3. 네이버 쇼핑 API 호출 결과 -> naverApiResponseJson (JSON 형태)
ResponseEntity<String> responseEntity = rest.exchange("https://openapi.naver.com/v1/search/shop.json?query=" + query, HttpMethod.GET, requestEntity, String.class);
String naverApiResponseJson = responseEntity.getBody();
// 4. naverApiResponseJson (JSON 형태) -> itemDtoList (자바 객체 형태)
// - naverApiResponseJson 에서 우리가 사용할 데이터만 추출 -> List<ItemDto> 객체로 변환
ObjectMapper objectMapper = new ObjectMapper() // 응답 JSON을 자바 객체로 만들어 주는 것!
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JsonNode itemsNode = objectMapper.readTree(naverApiResponseJson).get("items");
List<ItemDto> itemDtoList = objectMapper
.readerFor(new TypeReference<List<ItemDto>>() {})
.readValue(itemsNode);
// 5. API Response 보내기
// 5.1) response 의 header 설정
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
// 5.2) response 의 body 설정
PrintWriter out = response.getWriter();
// - itemDtoList (자바 객체 형태) -> itemDtoListJson (JSON 형태)
String itemDtoListJson = objectMapper.writeValueAsString(itemDtoList);
out.print(itemDtoListJson);
out.flush();
"상품 검색 API" 동작 순서
ARS에서 API 동작 검증
http://localhost:8080/api/search?query=노트북
1) Intellij - New project 생성
2) HelloResponseController.java
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/hello/response")
public class HelloResponseController {
// [Response header]
// Location: http://localhost:8080/hello.html
@GetMapping("/html/redirect")
public String htmlFile() {
return "redirect:/hello.html";
}
// [Response header]
// Content-Type: text/html
// [Response body]
// * resources/templates/hello.html 의 text 내용
// <!DOCTYPE html>
// <html>
// <head><meta charset="UTF-8"><title>By Templates engine</title></head>
// <body>Hello, Spring 정적 웹 페이지!!</body>
// </html>
@GetMapping("/html/templates")
public String htmlTemplates() {
return "hello";
}
// [Response header]
// Content-Type: text/html
// [Response body]
// <!DOCTYPE html>
// <html>
// <head><meta charset="UTF-8"><title>By @ResponseBody</title></head>
// <body>Hello, Spring 정적 웹 페이지!!</body>
// </html>
@GetMapping("/body/html")
@ResponseBody
public String helloStringHTML() {
return "<!DOCTYPE html>" +
"<html>" +
"<head><meta charset=\"UTF-8\"><title>By @ResponseBody</title></head>" +
"<body> Hello, 정적 웹 페이지!!</body>" +
"</html>";
}
// [Response header]
// Content-Type: text/html
// [Response body]
// * resources/templates/hello-visit.html 의 text 내용
@GetMapping("/html/dynamic")
public String helloHtmlFile(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
// resources/templates/hello-visit.html
return "hello-visit";
}
private static long visitCount = 0;
// [Response header]
// Content-Type: text/html
// [Response body]
// {"name":"BTS","age":28}
@GetMapping("/json/string")
@ResponseBody
public String helloStringJson() {
return "{\"name\":\"BTS\",\"age\":28}";
}
// [Response header]
// Content-Type: application/json
// [Response body]
// {"name":"BTS","age":28}
@GetMapping("/json/class")
@ResponseBody
public Star helloJson() {
return new Star("BTS", 28);
}
}
GET naver.com HTTP/1.1
application/x-www-form-urlencoded
application/json
name=홍길동&age=20
text/html
application/json
= @Controller + @ResponseBody
@Controller는 스프링 서버 개발자 입장에서는 시작점과 끝점으로 보이지만, 사실 많은 부분을 보이지 않게 처리해 주고 있다.
@Controller
public class ItemSearchController {
@GetMapping("/api/search")
@ResponseBody
public List<ItemDto> getItems(@RequestParam String query) {
// ...
}
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/hello/request")
public class HelloRequestController {
@GetMapping("/form/html")
public String helloForm() {
return "hello-request-form";
}
// [Request sample]
// GET http://localhost:8080/hello/request/star/BTS/age/28
@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);
}
// [Request sample]
// GET http://localhost:8080/hello/request/form/param?name=BTS&age=28
@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);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/param
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=BTS&age=28
@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);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/model
// Header
// Content type: application/x-www-form-urlencoded
// Body
// name=BTS&age=28
@PostMapping("/form/model")
@ResponseBody
public String helloRequestBodyForm(@ModelAttribute Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
// [Request sample]
// POST http://localhost:8080/hello/request/form/json
// Header
// Content type: application/json
// Body
// {"name":"BTS","age":"28"}
@PostMapping("/form/json")
@ResponseBody
public String helloPostRequestJson(@RequestBody Star star) {
return String.format("Hello, @RequestBody.<br> (name = %s, age = %d) ", star.name, star.age);
}
}
[웹 서비스 구성도]
1. Controller
2. Service
3. Repository
// 멤버 변수 선언
private final ProductRepository productRepository;
// 생성자: ProductService() 가 생성될 때 호출됨
public ProductService() {
// 멤버 변수 생성
this.productRepository = new ProductRepository();
}
"제어의 역전 (IoC: Inversion of Control)"
프로그램의 제어 흐름이 뒤바뀜
DI 사용을 위해서는 객체 생성이 우선 되어야 하는데 어디서 객체 생성?
-> 스프링 프레임워크가 필요한 객체를 생성하여 관리!!!
빈 (Bean): 스프링이 관리하는 객체
스프링 IoC 컨테이너: '빈'을 모아둔 통
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfiguration {
@Bean
public ProductRepository productRepository() {
String dbUrl = "jdbc:h2:mem:springcoredb";
String dbId = "sa";
String dbPassword = "";
return new ProductRepository(dbUrl, dbId, dbPassword);
}
}
새로 배우는 내용이라 복습하면서 자세하게 적어보았다. 아즈아😬
출처: 스파르타 코딩 클럽