일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Spring Data JPA
- Bean Validation
- 예제 도메인 모델
- 타임리프 문법
- API 개발 고급
- 스프링 mvc
- 스프링
- JPQL
- 불변 객체
- 페이징
- 값 타입 컬렉션
- 로그인
- 벌크 연산
- 컬렉션 조회 최적화
- 임베디드 타입
- 프로젝트 환경설정
- JPA 활용2
- 기본문법
- 트위터
- JPA
- 타임리프
- 김영한
- jpa 활용
- 일론머스크
- 스프링MVC
- QueryDSL
- 스프링 데이터 JPA
- 실무활용
- 검증 애노테이션
- JPA 활용 2
- Today
- Total
RE-Heat 개발자 일지
스프링 MVC 1편 - [2] 서블릿 본문
출처 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
아래 내용은 인프런 김영한님의 스프링 MVC 1편 강의를 듣고 정리한 내용입니다.
[1] 프로젝트 생성
① https://start.spring.io에서 스프링프로젝트 생성
② 스프링부트 버전 및 JAVA 버전 선택: JAVA 11버전을 사용하기 위해 2.7.12 버전 선택
참고 : 스프링부트 3.0버전 이상은 JAVA 17을 사용해야 함.
③ Packaging
JSP 사용 위해 WAR 선택. JSP는 무조건 WAR을 써야 함.
④ Dependencies
Spring Web : 스프링 기반 웹 애플리케이션 개발하는 데 필요한 라이브러리 자동 설정
Lombok : 반복되는 메소드를 어노테이션을 통해 자동 생성
ex) @Getter @Setter @ToString
@NoArgsConstructor(매개변수 X 기본 생성자)
@RequiredArgsConstructor(초기화되지 않는 모든 final필드 생성자 생성 의존성 주입)
@AllArgsConstructor(모든 필드에 대한 생성자 생성)
@EqualsAndHashCode 클래스에 대한 equals(Object other)와 hashCode()를 만든다.
@Data(끝판왕. @ToString @EqualsAndHashCode, @Getter @Setter @RequriedArgsConstructor 설정)
프로젝트 생성 도중 발생한 오류
Execution failed for task ':ServletApplication.main()'.
Process 'command 'C:/Program Files/Java/jdk-11.0.17/bin/java.exe'' finished with non-zero exit value 1
오류코드가 나와 File-Setting Gradle->IntelliJ로 바꾸는 방법을 써보는 등 고생했으나 알고 보니 port 8080을 같이 써서 생긴 오류였다.
해결책 : application.properties에서 server.port = 8081을 추가
[2] Hello Servlet
@ServletComponentScan
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
@ServeltComponentScan
스프링부트는 서블릿을 직접 등록해 사용할 수 있도록 위 어노테이션을 지원
=>@WebServlet이 달린 객체들을 싱글톤 객체로 등록시킴
@WebServlet(name="helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet.service");
System.out.println("request = " + request);
System.out.println("response = " + response);
String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello " + username);
}
}
① @WebServlet
name으로 싱글톤 객체 이름 등록.
urlPatterns을 통해 어떤 url이 들어오면 실행할지 지정 ex) localhost:8080/hello
② protected void service(HttpServletRequest request, HttpServletResponse response)
HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 service() 메서드를 실행
요청이 들어올 때마다 WAS가 request, response 객체 새롭게 만들어서 전송
③ getParameter()메소드로 파라미터 꺼내기
[GET방식] http://localhost:8081/hello?username=kim으로 보내면
response.getWriter().write("hello " + username);에 의해 브라우저에 hello kim이 출력
참고] POST 방식도 파라미터가 보내지는 형식은 같기 때문에 똑같이 getParameter()로 꺼낼 수 있음
참고 : HttpServlet을 상속하고 ctrl+o를 누르면 메소드를 쉽게 가져올 수 있음 [인텔리J 윈도우 기준]
서버가 받은 HTTP 요청메시지 출력하는 기능
application.properties에
logging.level.org.apache.coyote.http11=debug 추가하면 HTTP 요청메시지를 콘솔에 출력해 줌.
서블릿 컨테이너 동작 방식 설명
[3] HttpServletRequest 개요
서블릿이 HTTP 요청메시지를 개발자 대신 파싱하고 그 결과를 HttpServletRequest 객체 담아 제공
HttpServletRequest의 역할
① 요청 메시지 조회
StartLine : HTTP 메소드(POST), URL(/save), 쿼리 스트링, 스키마, 프로토콜
Header Line : 헤더 조회
Message Body : form 파라미터 형식 조회, message body 데이터 조회
② 임시 저장소 기능
해당 HTTP 요청이 들어온 시점부터, 끝나기 전까지 유지됨
request.setAttribute(name, value)으로 값을 저장
request.getAttribute(name)으로 조회
@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
printStartLine(request);
printHeaders(request);
printHeaderUtils(request);
printEtc(request);
}
//스타트 라인 정보
private static void printStartLine(HttpServletRequest request) {
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); //http
// http://localhost:8080/request-header
System.out.println("request.getRequestURL() = " + request.getRequestURL());
// /request-header
System.out.println("request.getRequestURI() = " + request.getRequestURI());
//username=hi
System.out.println("request.getQueryString() = " + request.getQueryString());
System.out.println("request.isSecure() = " + request.isSecure()); //https 사용 유무
System.out.println("--- REQUEST-LINE - end ---");
System.out.println();
}
//모든 헤더 정보
//Header 모든 정보
private void printHeaders(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
//옛날 스타일
// Enumeration<String> headerNames = request.getHeaderNames();
// while (headerNames.hasMoreElements()){
// String headerName = headerNames.nextElement();
// System.out.println(headerName + ": " + headerName);
//
// }
//요즘 쓰는 간결한 문법
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ":" + request.getHeader(headerName)));
//값 하나만 꺼낼 수 있음
request.getHeader("host");
System.out.println("--- Headers - end ---");
System.out.println();
}
private void printHeaderUtils(HttpServletRequest request) {
System.out.println("--- Header 편의 조회 start ---");
System.out.println("[Host 편의 조회]");
System.out.println("request.getServerName() = " + request.getServerName()); //Host 헤더
System.out.println("request.getServerPort() = " + request.getServerPort()); //Host 헤더
System.out.println();
System.out.println("[Accept-Language 편의 조회]");
request.getLocales().asIterator()
.forEachRemaining(locale -> System.out.println("locale = " + locale));
System.out.println("request.getLocale() = " + request.getLocale());
System.out.println();
System.out.println("[cookie 편의 조회]");
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
}
System.out.println();
System.out.println("[Content 편의 조회]");
System.out.println("request.getContentType() = " + request.getContentType());
System.out.println("request.getContentLength() = " + request.getContentLength());
System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());
System.out.println("--- Header 편의 조회 end ---");
System.out.println();
}
//기타 정보
private void printEtc(HttpServletRequest request) {
System.out.println("--- 기타 조회 start ---");
System.out.println("[Remote 정보]");
System.out.println("request.getRemoteHost() = " + request.getRemoteHost()); //
System.out.println("request.getRemoteAddr() = " + request.getRemoteAddr()); //
System.out.println("request.getRemotePort() = " + request.getRemotePort()); //
System.out.println();
System.out.println("[Local 정보]");
System.out.println("request.getLocalName() = " + request.getLocalName()); //
System.out.println("request.getLocalAddr() = " + request.getLocalAddr()); //
System.out.println("request.getLocalPort() = " + request.getLocalPort()); //
System.out.println("--- 기타 조회 end ---");
System.out.println();
}
}
① 스타트 라인 정보
Http Start line의 메서드, 프로토콜, 스키마(http, https), URL, URL, 쿼리 파라미터, https사용 여부 등이 출력
② 모든 헤더 정보
Enumeration(자바초기버전) -> Iterator(확장버전)
차이점
Enumeration은 Hashtable, Vector에서 사용가능
Iterator는 Collection인터페이스를 구현상속한 모든 컬렉션 클래스에서 사용가능
헤더 관련 모든 정보 출력.
③ 헤더 편의 조회
서버이름, 포트 번호, 선호 언어, 쿠키, 콘텐츠 등 조회
참고] 선호 문자는 Qualtiy Vlaues(q) 값을 활용해 우선순위를 매길 수 있음
ex) Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7 < 클수록 우선수위 높음
④ 기타 정보
remote는 클라이언트 정보, Local은 요청을 받은 서버에 관한 정보
클라이언트 이름, 클라이언트 IP, 클라이언트 포트 번호 조회
[4] HTTP 요청 데이터 - 개요
① GET - 쿼리 파라미터
형식 : /url?key1=value1&key2=value2
메시지 바디 없이, URL 쿼리 파라미터에 데이터를 포함해 전달
② POST - HTML Form
content-type: application/x-www-form-urlencoded html폼으로 데이터 전송
html <form></form> 안 내용을 메시지 바디에 쿼리 파라미터 형식으로 전달
참고 : HTML Form 데이터를 보낼 땐 POST만 사용 가능
③ HTTP message body
http message body에 데이터를 직접 담아서 요청하는 방식(HTTP API에서 주로 사용)
데이터 형식은 JSON, XML, TEXT 중 JSON을 주로 사용함.
POST, PUT, PATCH 등을 사용
[5] HTTP 요청 데이터 - GET 쿼리 파라미터
검색, 필터, 페이징에 많이 사용되는 방식
HttpServletRequest가 제공하는 getParameter메서드를 통해 파라미터 편리하게 조회 가능
/*
1. 파라미터 전송기능
http://localhost:8080/request-param?username=hello&age=20
* */
@WebServlet(name="requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[전체 파라미터 조회] - start");
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName))); //
System.out.println("[전체 파라미터 조회] - end");
System.out.println("[단일 파라미터 조회]");
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username = " + username);
System.out.println("age = " + age);
System.out.println("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name:usernames){
System.out.println("username = " + name);
}
response.getWriter().write("ok");
}
}
단일 vs 복수 파라미터 조회
단일 파라미터 : request.getParameter()
복수 파라미터 : request.getParameterValues() 사용
=>복수 파라미터일 때 getParamter()를 사용하면 values()의 첫 번째 값만 반환함
[6] HTTP 요청 데이터 - POST HTML Form
application/x-www-form-urlencoded 형식은 GET에서 살펴본 쿼리 파라미터와 형식이 같아 request.getParameter()로 구분 없이 조회 가능. 서버 입장에선 둘의 형식이 동일하다는 뜻.
[7] HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream(); //메시지 바디 내용을 바이트 코드로 가져옴
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 바이트 코드를 String으로 바꿈. 스프링 내장 StreamUtils로
System.out.println("messageBody = " + messageBody);
response.getWriter().write("ok");
}
}
[4]③ 내용과 동일, 주로 앱 to 서버, 웹 클라이언트 to 서버, 서버 to 서버에서 사용
[8] HTTP 요청 데이터 - API 메시지 바디 - JSON
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.getUsername() = " + helloData.getUsername());
System.out.println("helloData.getAge() = " + helloData.getAge());
}
}
JSON 결과를 파싱해서 자바 객체로 변환하려면 Jackson, Gson 같은 라이브러리를 사용해야 함.
스프링부트는 Jackson 라이브러리의 ObjectMapper를 사용.
[9] HttpServletResponse 기본 사용법
- HTTP 응답 메시지 생성
start line 세팅(HTTP 응답코드 지정)
헤더 생성
바디 생성
- 편의 기능 제공
Content-Type
쿠키
Redirect
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//[status-line]
response.setStatus(HttpServletResponse.SC_OK);
//[response-header]
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");//캐시 완전 무효화
response.setHeader("Pragma", "no-cache"); //과거 버전도 캐시 못하게
response.setHeader("my-header", "hello");
//[Header 편의 메서드] - 컨텐츠 타입 Header에 넣지 말고 이렇게 넣는 방법도 있군.
content(response);
cookie(response);
redirect(response);
//[메시지 바디]
PrintWriter writer = response.getWriter();
writer.print("ok");
}
private void content(HttpServletResponse response) {
//Content-Type: text/plain;charset=utf-8
//Content-Length: 2
//response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
//response.setContentLength(2); //(생략시 자동 생성)
}
private void cookie(HttpServletResponse response) {
//Set-Cookie: myCookie=good; Max-Age=600;
//response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600");
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600); //600초
response.addCookie(cookie);
}
private void redirect(HttpServletResponse response) throws IOException {
//Status Code 302
//Location: /basic/hello-form.html
//response.setStatus(HttpServletResponse.SC_FOUND); //302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");
}
}
[10] HTTP 응답 데이터 - 단순 텍스트, HTML
단순 텍스트는 앞서 writer.println("ok")로 확인
HTML 응답
@WebServlet(name="responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Cotent-Type : text/html;charset=utf-8
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕?</div>");
writer.println("</body>");
writer.println("</html>");
}
}
HTTP응답을 HTML으로 반환하려면 Content-type을 text/html로 지정해야 함.
utf-8로 인코딩하지 않으면 한글이 깨질 위험이 있음.
서블릿으로 HTML을 렌더링을 하려면 직접 작성해야 하는 번거로움이 있음.
[11] HTTP 응답 데이터 - API JSON
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//Content Type을 헤더에 넣기
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
//객체에 담기
HelloData helloData = new HelloData();
helloData.setUsername("kim");
helloData.setAge(20);
//객체 JSON으로 보내기
String result = objectMapper.writeValueAsString(helloData);
//화면에 찍기
response.getWriter().print(result);
}
}
JSON으로 반환 시 Content-type은 application/json으로 지정
objectMapper.writeValuesAsString()을 쓰면 객체를 JSON 문자로 변경가능
참고] application/json은 기본값이 utf-8이므로 application/json;charset=utf-8이라고 전달할 필요 X
'백엔드 > 스프링' 카테고리의 다른 글
스프링 MVC 1편 - [6] 기본 기능(상편) (0) | 2023.06.30 |
---|---|
스프링 MVC 1편 - [5] 스프링 MVC 구조 이해 (0) | 2023.06.29 |
스프링 MVC 1편 - [4] MVC 프레임워크 만들기 (0) | 2023.06.23 |
스프링 MVC 1편 - [3] 서블릿·JSP·MVC 패턴 (0) | 2023.06.22 |
스프링 MVC 1편 - [1] 웹 서버, 웹 애플리케이션 서버 (0) | 2023.06.21 |