소프트웨어를 Model, View, Controller로 구분하여 각각의 역할을 분리하는 디자인 패턴이다.
Spring MVC는 Servlet을 기반으로 HTTP 요청을 처리하고, MVC 디자인 패턴을 적용해 요청과 응답을 효율적으로 관리한다.
Java를 사용하여 웹 페이지를 동적으로 생성하는 서버 측 프로그램 또는 사양이다.
HttpServletRequest, HttpServletResponse 객체 생성service() 호출 → HTTP Method에 따라 doGet(), doPost() 등 호출HttpServletResponse에 담아 Client에 반환DispatcherServlet은 요청을 분석하고 적절한 Controller로 요청을 전달한다.
예를 들어 @GetMapping("/api/hello") 요청이 들어오면 해당 요청을 처리할 Controller의 메서드를 찾아 실행한다.
Handler Mapping을 통해 Controller에 요청 전달DispatcherServlet이 View에 Model을 적용하여 Client에 전달@Controller는 관련된 요청을 하나의 Controller에서 관리할 수 있도록 한다.
예를 들어 회원 관리 기능의 로그인, 회원가입, 로그아웃 등을 하나의 Controller에서 관리할 수 있다.
@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) {
// ...
}
}
HTTP Method가 다르면 같은 URL 경로를 사용할 수 있다.
@GetMapping("/api/hello")
@ResponseBody
public String hello() {
return "Hello World";
}
@PostMapping("/api/hello")
@ResponseBody
public String post() {
return "Hello World2";
}
공통되는 API 경로를 묶어서 관리할 수 있다.
@Controller
@RequestMapping("/api")
public class HelloController {
@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello World";
}
}
위와 같이 설정하면 실제 요청 경로는 /api/hello가 된다.
이미 완성된 HTML 파일을 그대로 반환하는 페이지다.
예를 들어 http://localhost:8080/hello.html을 요청하면 Controller를 거치지 않고 정적 리소스를 반환할 수 있다.
@GetMapping("/html/redirect")
public String htmlStatic() {
return "redirect:/hello.html";
}
redirect:를 사용하면 해당 URL로 다시 요청을 보낸다.
Controller에서 Model에 데이터를 담고, Template Engine이 HTML에 데이터를 적용하여 동적인 페이지를 생성한다.
@GetMapping("/html/dynamic")
public String htmlDynamic(Model model) {
visitCount++;
model.addAttribute("visits", visitCount);
return "hello-visit";
}
model.addAttribute()로 전달한 값은 HTML의 ${visits}와 같은 표현식에 적용된다.
대표적인 Template Engine으로 Thymeleaf, Groovy, FreeMarker, JSP 등이 있다.
서버에서 HTML 전체를 반환하는 대신 필요한 데이터를 JSON 형식으로 반환할 수 있다.
@GetMapping("/response/json/string")
@ResponseBody
public String helloStringJson() {
return "{\"name\":\"Robbie\",\"age\":95}";
}
@GetMapping("/response/json/class")
@ResponseBody
public Star helloClassJson() {
return new Star("Robbie", 95);
}
Spring은 Java 객체를 JSON으로 변환하여 반환할 수 있다.
@RestController는 @Controller와 @ResponseBody를 함께 사용하는 것과 같다.
@RestController
public class HelloController {
// ...
}
@ResponseBody를 사용하면 HTML View가 아닌 데이터를 응답으로 반환한다.
Jackson은 JSON 데이터를 처리하는 라이브러리다.
Spring은 Jackson과 관련된 기능을 제공하여 Java 객체를 JSON으로 변환할 수 있다.
@Test
@DisplayName("Object To JSON")
void test1() throws JsonProcessingException {
Star star = new Star("Robbie", 95);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(star);
System.out.println("json = " + json);
}
둘 다 HTTP 요청으로 데이터를 전달받는 방법이지만, 데이터가 URL에 표현되는 방식이 다르다.
URL 경로에 값을 포함하여 전달하는 방식이다.
// GET http://localhost:8080/hello/request/star/Robbie/age/95
@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
);
}
{name}, {age} 형태로 URL 경로에 변수 지정@PathVariable로 해당 값 전달받음Query String을 통해 데이터를 전달하는 방식이다.
// GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95
@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 설정 시 해당 값이 없어도 됨여러 데이터를 하나씩 @RequestParam으로 받는 대신 Java 객체로 한 번에 받을 수 있다.
// POST http://localhost:8080/hello/request/form/model
// 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
);
}
Query String에서도 사용할 수 있다.
// 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
);
}
@ModelAttribute는 생략할 수 있다.
JSON 형식으로 전달된 HTTP Body를 Java 객체로 받을 때 사용한다.
// POST http://localhost:8080/hello/request/form/json
// 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
);
}
CRUD는 데이터를 생성(Create), 조회(Read), 수정(Update), 삭제(Delete)하는 기본적인 데이터 처리 방식이다.
DTO(Data Transfer Object)는 데이터 전송 및 이동을 위해 사용하는 객체다.
@PostMapping("/memos")
public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// Memo Max ID Check
Long maxId = memoList.size() > 0
? Collections.max(memoList.keySet()) + 1
: 1;
memo.setId(maxId);
// DB 저장
memoList.put(memo.getId(), memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
@GetMapping("/memos")
public List<MemoResponseDto> getMemos() {
List<MemoResponseDto> responseList = memoList.values().stream()
.map(MemoResponseDto::new)
.toList();
return responseList;
}
@PutMapping("/memos/{id}")
public Long updateMemo(
@PathVariable Long id,
@RequestBody MemoRequestDto requestDto
) {
if (memoList.containsKey(id)) {
Memo memo = memoList.get(id);
memo.update(requestDto);
return memo.getId();
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
@DeleteMapping("/memos/{id}")
public Long deleteMemo(@PathVariable Long id) {
if (memoList.containsKey(id)) {
memoList.remove(id);
return id;
} else {
throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
}
}
Spring에서 데이터를 다루기 위해서는 애플리케이션뿐 아니라 데이터베이스의 기본 구조도 이해해야 한다.
RDBMS는 테이블을 기본 단위로 데이터를 관리한다.
| 아이디 | 이름 | 전화번호 |
|---|---|---|
| 1 | 홍길동 | 010-7777-7777 |
| 2 | 김철수 | 010-6666-6666 |
| 3 | 이가영 | 010-8888-8888 |
SQL(Structured Query Language)은 RDBMS에서 데이터를 조작하고 관리하기 위해 사용하는 언어다.
DDL(Data Definition Language)은 데이터베이스와 테이블의 구조를 정의하는 언어
CREATE DATABASE 데이터베이스이름;
CREATE TABLE 테이블이름 (
필드이름1 필드타입1,
필드이름2 필드타입2
);
ALTER TABLE 테이블이름 ADD 필드이름 필드타입;
ALTER TABLE 테이블이름 DROP 필드이름;
ALTER TABLE 테이블이름 MODIFY COLUMN 필드이름 필드타입;
데이터베이스 또는 테이블을 삭제한다.
DROP DATABASE 데이터베이스이름;
DROP TABLE 테이블이름;
테이블의 데이터를 삭제하고 테이블 구조는 유지한다.
TRUNCATE TABLE 테이블이름;
DML(Data Manipulation Language)은 데이터를 조작하는 언어
INSERT INTO 테이블이름(필드이름1, 필드이름2, 필드이름3)
VALUES(데이터값1, 데이터값2, 데이터값3);
SELECT 필드이름
FROM 테이블이름
WHERE 조건;
UPDATE 테이블이름
SET 필드이름1 = 데이터값1,
필드이름2 = 데이터값2
WHERE 필드이름 = 데이터값;
DELETE FROM 테이블이름
WHERE 필드이름 = 데이터값;
DCL(Data Control Language)은 데이터베이스의 권한을 제어하는 언어
사용자 또는 ROLE에 권한을 부여한다.
GRANT [객체권한명] (컬럼)
ON [객체명]
TO { 유저명 | 롤명 | PUBLIC } [WITH GRANT OPTION];
사용자 또는 ROLE에 부여한 권한을 회수한다.
REVOKE { 권한명 [, 권한명...] | ALL }
ON 객체명
FROM { 유저명 [, 유저명...] | 롤명 | PUBLIC };
JDBC(Java Database Connectivity)는 Java에서 데이터베이스에 접근할 수 있도록 제공하는 API다.
JDBC를 사용하면 애플리케이션의 DB 접근 로직을 특정 DB에 직접 종속시키는 것을 줄일 수 있다.
예를 들어 MySQL에서 PostgreSQL로 DB를 변경할 경우 DB 연결 및 요청 방식이 달라질 수 있다. JDBC는 이러한 DB 접근을 위한 표준화된 인터페이스를 제공한다.
JDBC를 사용할 때 반복되는 작업을 줄이기 위해 Spring에서는 JdbcTemplate을 제공한다.
등의 반복적인 작업을 대신 처리할 수 있다.
DispatcherServlet을 중심으로 HTTP 요청이 Controller까지 전달되는 흐름@RestController, @ResponseBody를 이용한 JSON 응답@PathVariable, @RequestParam, @ModelAttribute, @RequestBody의 차이JdbcTemplate의 역할