static 메서드는 객체 생성 없이 호출 가능하다. 그렇다면 원격 프로그램은 어떻게 실행할 수 있을까?
원격 프로그램의 실행
웹 브라우저 + WAS(톰캣) -> 원격 프로그램의 실행
@Controller // 1. 프로그램 등록
public class Hello {
@RequestMapping("/hello") // 2. URL과 main()을 연결
public void main() {
System.out.println("Hello");
}
}
원래 static이 없다면 객체를 생성해야 한다. 다만, @Controller 어노테이션이 붙은 프로그램은 스프링이 자동으로 객체를 생성해주기 때문에 인스턴스 메서드(static이 안 붙은 메서드)가 URL이 연결되어 다른 컴퓨터에서 호출될 수 있다.
** 인텔리제이 단축키
패키지를 선택한 상태에서 Command + N
해당 패키지에 파일 추가
** github
따라서, 프로젝트 전체에서 변경 사항을 커밋하려는 경우 'git add -A'를 사용하는 것이 더 안전하며, 특정 디렉토리에서만 작업하는 경우 'git add .'를 사용할 수 있다.
클라이언트와 서버는 역할로 구분한다.
서버의 종류
브라우저를 통해 사용하는 서비스들은 Web Server를 통해 이루어진다.
한 대의 컴퓨터에 서버 프로그램이 여러 개 설치되어 있다면 같은 IP 내에 서비스가 존재하기 때문에 포트가 필요하다. 서버 프로그램은 특정 포트와 연결되어 있다. Email Server는 25번 포트, File Server는 22번 포트, Web Server는 80번 포트를 기본적으로 사용한다.
WAS란?
Web Application을 서비스하는 서버이다.
클라이언트가 애플리케이션을 사용할 수 있게 해준다. 서버에 있는 프로그램을 클라이언트에게 서비스해주는 프로그램이다.
HTTP 요청과 응답
사용자는 브라우저에 URL을 입력하게 된다. 이때 DNS 서버가 도메인 이름(fastcampus.co.kr)이 IP 주소(111.222.33.44)를 알려준다. 해당 IP 주소가 있는 웹 서버에 요청이 간다.
HTTP 요청
HTTP 응답
HttpServletRequest
HttpServletRequest request
를 적어주면 메서드 안에서 해당 객체의 메서드를 사용할 수가 있다.위의 메서드를 통해 원하는 정보를 얻을 수 있다. URL을 잘라서 원하는 것을 얻을 필요가 없다.
String year = request.getParameter("year");
String month = request.getParameter("month");
String day = request.getParameter("day");
// Enumeration은 Iterator의 old version이다.
Enueration enum = request.getParameterNames();
Map paramMap = request.getParameterMap();
// 같은 name이 많을 때 배열로 반환한다.
// 만약 getParameter()를 사용 시 첫 번째 value만 반환된다.
/* ex) 체크박스의 경우
hobby=computer&hobby=drawing&hobby=music
이런 식으로 들어오게 된다. */
String[] yearArr = request.getParameterValues("year");
GET과 POST의 차이점
입력한 내용이 query string으로 URL뒤에 붙는다.
URL 뒤에 query string이 붙지 않고 별도로 전송이 된다.
HTTP 요청 방법
<a>
- GET<form>
- GET이 default, POST프로토콜이란?
서로 간의 통신을 위한 규칙
주고 받을 데이터에 대한 형식을 정의한 것
HTTP
Hyper Text Transfer Protocol
단순하고 읽기 쉽다.
텍스트 기반의 프로토콜
클라이언트가 브라우저에 URL을 입력하면 HTTP 요청 메시지를 만들어 서버에 요청 메시지를 보낸다. 서버는 이를 보고 HTTP 응답 메시지를 만들어 클라이언트에게 보여준다.
상태를 유지하지 않는다(stateless)
클라이언트 정보를 저장하지 않는다. 서버에 부담이 없어진다.
확장 가능하다.
커스텀 헤더 추가 가능
상태 코드
텍스트 파일과 바이너리 파일
정적 리소스와 동적 리소스
src/main/resources/static 폴더에 위치한 파일들은 정적 리소스로 취급된다.
이 파일들은 서버 측 처리 없이 그대로 클라이언트에 제공된다.
따라서 localhost:8080/index.html로 직접 접근할 수 있다.
src/main/resources/templates 폴더에 위치한 파일들은 동적 리소스로 취급된다.
이 파일들은 주로 템플릿 엔진(예: Thymeleaf)을 통해 처리되어야 한다.
직접 URL로 접근할 수 없으며, 컨트롤러를 통해 접근해야 한다.
templates 폴더의 index2.html에 접근하려면 컨트롤러를 만들어야 한다.
@Controller
public class HomeController {
@GetMapping("/index2")
public String showIndex2() {
return "index2";
}
}
이후 localhost:8080/index2로 접근하면 index2.html이 보일 것입니다.
만약, localhost:8080/index2.html로 접근하면 error 페이지가 나온다.
Spring Boot는 templates 폴더의 파일을 직접 URL로 접근할 수 있는 정적 리소스로 취급하지 않는다.
따라서 해당 URL에 맞는 컨트롤러 매핑이 없으면 404 에러가 발생하고, 기본 에러 페이지가 표시된다.
static 폴더
templates 폴더
이러한 구조는 보안과 유연성을 위해 설계되었다. 동적 콘텐츠는 서버 측 로직을 통해 제어되어야 하므로, 직접 접근을 제한하고 컨트롤러를 통해 접근하도록 한다.
HttpServletResponse로 응답하기
Tomcat이 response 객체를 생성해서 준다. 요청 시 사용할 수 있는 객체, 응답에 사용할 수 있는 객체가 있다.
응답에 필요한 출력 스트림을 담아서 준다.
response.getWriter()
response.setStatus(200);
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head></head>");
out.println("<body>Monday</body>);
out.println("</html>");
out.close();
톰캣은 응답의 헤더를 기본적으로 만들어준다. 헤더는 변경과 확장이 가능하다.
HttpServletResponse의 메서드
메서드 | 설명 |
---|---|
void addCookie(Cookie cookie) | 응답에 쿠키를 추가 |
void addHeader(String name, String value) | 응답 헤더를 추가 |
String getHeader(String name) void setHeader(String name, String value) | 지정된 이름의 헤더의 값을 반환 지정된 이름의 헤더의 값을 변경 |
void sendRedirect(String location) | 지정된 위치(URL)로 요청하라는 응답(3xx)를 전송 |
ServletOutputStream getOutputStream() PrintWriter getWriter() | 클라이언트(브라우저)와 연결된 출력 스트림을 반환 |
코드의 분리
OOP - 변경에 대비
객체 지향 설계 5대 원칙
Spring의 Reflection API
@Controller
public void date(@HttpservletRequest request) {
request.getParameter("year");
request.getParameter("month");
request.getParameter("day");
}
위의 코드를 아래처럼 바꿀 수 있다.
@Controller
public void date(int year, int month, int day) {
}
각 매개변수에는 @RequestParam이 생략되어 있다.
DispatcherServlet
request.getParameter()
컨트롤러 메서드의 반환타입
@GetMapping("/getYoil")
pubic String main(int year, int month, int day) {
return "yoil" // resources/templates/yoil.html
}
@GetMapping("/yoil") //resources/templates/yoil.html
public void main(int year, int month, int day) {
}
@RestContoller와 @Controller의 차이점은?
@RestController 어노테이션은 메소드의 반환값을 HTTP 응답 본문으로 직접 전송한다. Thymeleaf 템플릿을 사용하려면 @Controller 어노테이션을 사용해야 한다.
@ModelAttribute
- 적용 대상을 Model의 속성으로 자동 추가해준다.
- 반환 타입 또는 컨트롤러 메서드의 매개변수에 적용 가능
- Controller 메서드 파라미터에서 참조형 앞에 사용할 경우 어노테이션 생략 가능
참조형에만 사용 가능하다.
ex. int, char 등 사용 불가하다.
메서드 앞에 사용
@ModelAttribute("yoil")
private char getYoil(MyDate myDate) {
/*** 생략 ***/
char yoil = '일';
return yoil;
}
yoil
)을 "yoil"이라는 이름으로 모델에 자동으로 추가한다.char yoil = getYoil(myDate);
을 추가할 필요가 없다. 또한, main 메서드가 실행될 때 모델에는 이미 "yoil" 속성이 추가되어 있다.@Controller
public class YoilTeller {
@RequestMapping("/yoil")
public String getYoil(@ModelAttribute MyDate myDate, Model model) {
return "yoil";
}
model.addAttribute("myDate", date);
Model model
@Controller
public class YoilTeller {
@RequestMapping("/yoil")
public String main(@ModelAttribute MyDate date, Model model) throws IOException {
return "yoil";
}
@ModelAttribute("yoil")
private char getYoil(MyDate date) {
Calendar cal = Calendar.getInstance(); // 현재 날짜와 시간을 갖는 cal
cal.clear(); // cal의 모든 필드를 초기화
cal.set(date.getYear(), date.getMonth() - 1, date.getDay());
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
char yoil = "일월화수목금토".charAt(dayOfWeek - 1);
return yoil;
}
}
** 이때, 컨트롤러 메서드의 매개변수가 기본형일 경우 @RequestParam
이 생략된 것이며, 참조형일 경우 @ModelAttribute
가 생략된 것이다.
WebDataBinder의 역할
@RequestMapping("getYoil")
public String main(@ModelAttribute MyDate date, BindingResult result) {
}
URL -> getYoil?year=2023&month=1&day=2
name | value |
---|---|
"year" | "2023" |
"month" | "1" |
"day" | "2" |
year | month | day |
---|---|---|
2023 | 1 | 2 |
@RequestParam
- 요청의 파라미터를 연결할 때 매개변수에 붙이는 애너테이션
서블릿(Servlet)은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 HTTP 메시지를 대신 파싱한다.
이렇게 파싱된 메시지를 HttpServletRequest 객체에 담아서 제공하는 것이다.
즉, HttpServletRequest는 서블릿이 HTTP 요청 메시지를 파싱한 결과를 담은 객체이다.
HttpServletRequest를 사용하면 HTTP 요청 메시지를 편리하게 조회할 수 있게 된다.
@RequestMapping("/requestParam")
public void main(HttpServletRequest request) {
String year = request.getParameter("year");
}
@RequestMapping("/requestParam")
public void main(@RequestParam String year) {
}
request.getParameter()
필요 없어짐.@RequestParam(name="year", required=true)
@RequestMapping("/requestParam")
public void main(String year) {
}
required = false
브라우저를 통해서 요청받은 값이 실제 서버의 객체에 바인딩 될 때, 중간 역할을 한다.
1. 타입변환
2. 데이터 검증
➡️ 변환 결과나 에러는 BindingResult에 저장
@RequestMapping("/requestParam")
public void main(int year) {
}
required=false
이기에 client는 잘못한 것이 없다.@RequestMapping("/requestParam")
public void main(@RequestParam(required=false, defaultValue="1") int year) {
}
@RequestMapping, @GetMapping, @PostMapping
@Controller
@RequestMapping
public class LoginController {
@RequestMapping("/login/login", method = RequestMethod.GET)
public String login(){
return "login";
}
@Controller
@RequestMapping
public class LoginController {
@GetMapping("/login/login")
public String login(){
return "login";
}
두 코드는 동일하다. URL이 같아도 method가 다르면 서버가 요청을 구별할 수 있다.
만약, 하나의 메서드로 GET, POST를 둘 다 처리하는 경우에는 @RequestMapping으로 사용해야 한다.
@Controller
@RequestMapping
public class LoginController {
@RequestMapping("/login/login", method = {RequestMethod.GET, RequestMethod.POST})
public String login(){
return "login";
}
클래스에 붙이는 @RequestMapping
맵핑될 URL의 공통 부분을 @RequestMapping으로 클래스에 적용
@Controller
@RequestMapping("/login")
public class LoginController {
@PostMapping("/login")
public String login(){
return "login";
}
URL Encoding
URL에 포함된 non-ASCII 문자를 문자 코드(16진수) 문자열로 변환
String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다", "utf-8");
Redirect
@GetMapping
return "redirect:/login/login";
url 다시쓰기
- URL을 변경하는 것.
- 주로 URL에 쿼리 스트링을 추가
String msg = "id 또는 pwd가 일치하지 않습니다";
return "redirect:/login/login?msg="+msg;
ThymeLeaf 엔진
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="|id=${id}|">asdf</h1>
<h1 th:text="|pwd=${pwd}|">1234</h1>
</body>
</html>
json 이외의 형식이 들어가려면 | |
로 감싸주어야 한다.
<h1 th:text="${param.msg}"></h1>