일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 자바의 정석 기초편 ch3
- 자바의 정석 기초편 ch1
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch9
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch12
- 2024 정보처리기사 수제비 실기
- 게시글 목록 api
- 자바의 정석 기초편 ch14
- 자바의 정석 기초편 ch13
- @Aspect
- 자바의 정석 기초편 ch2
- 스프링 입문(무료)
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch6
- 스프링 db2 - 데이터 접근 기술
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch4
- 자바의 정석 기초편 ch7
- 스프링 mvc2 - 검증
- 자바의 정석 기초편 ch11
- jpa - 객체지향 쿼리 언어
- 스프링 mvc1 - 스프링 mvc
- 타임리프 - 기본기능
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch8
- 스프링 mvc1 - 서블릿
- 코드로 시작하는 자바 첫걸음
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch5
- Today
- Total
나구리의 개발공부기록
서블릿, 프로젝트 생성, Hello서블릿, HttpServletRequest(개요/기본사용법), HTTP요청 데이터(개요/GET 쿼리 파라미터/POST HTML Form,API메세지바디(텍스트/JSON)), HttpServletResponse, HTTP 응답데이터(텍스트/HTML/API JSON) 본문
서블릿, 프로젝트 생성, Hello서블릿, HttpServletRequest(개요/기본사용법), HTTP요청 데이터(개요/GET 쿼리 파라미터/POST HTML Form,API메세지바디(텍스트/JSON)), HttpServletResponse, HTTP 응답데이터(텍스트/HTML/API JSON)
소소한나구리 2024. 2. 13. 15:52 출처 : 인프런 - 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 (유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 프로젝트 생성
1) 스프링 부트 스타터
(1) 프로젝트 설정
- Gradle - Groovy
- Java: 21
- Spring Boot: 3.x.x
(2) Metadata
- Group: hello
- Artifact: servlet
- Packaging : War (Jar가 아닌 War에 주의 - JSP를 실행하기 위함)
(3) Dependencies
- Spring Web
- Lombok
(2) Postman 설치
2. Hello 서블릿
** 참고
- 서블릿은 톰캣 같은 WAS를 직접 설치하고 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음 톰캣 서버를 실행하면 됨
- 그러나 해당 과정이 매우 번거로워서 스프링 부트로 프로젝트를 생성하여 톰캣 서버를 별도설치 없이 서블릿 코드를 실행
1) 스프링 부트 서블릿 환경 구성
(1) ServletApplication - 수정
- 스프링 부트 애플리케이션인 ServletApplication에 애노테이션 적용
- @ServletComponentScan: 스프링 부트가 지원하는 서블릿 자동등록 애노테이션
@ServletComponentScan // 서블릿 자동 등록
@SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
(2) HelloServlet
- basic 패키지를 생성 후 실제 동작하는 서블릿 코드 등록
- @WebServlet(name: 서블릿 이름, urlPatterns: URL 매핑)
- HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너가 오버라이딩 된 메서드를 실행
@WebServlet(name="helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet { // 서블릿은 HttpServlet을 상속 받아야 함
@Override // ctrl + o 로 메서드 오버라이딩(자물쇠 잠겨있는 메서드 = protected)
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet.service");
System.out.println("request = " + request); // HttpServletRequest 구현체 확인
System.out.println("response = " + response); // HttpServletResponse 구현체 확인
String username = request.getParameter("username"); // 쿼리파라미터 조회(?username=guri)
System.out.println("username = " + username);
// 응답메세지 보내보기
// HTTP 헤더 정보
response.setContentType("text/plain"); // 콘텐츠타입
response.setCharacterEncoding("utf-8"); // 문자인코딩(옛날시스템이 아니면 대부분 utf-8)
// HTTP 바디 메세지
response.getWriter().write("hello "+username);
}
}
(3) HTTP 요청 메세지 로그 확인하기
- ~/application.properties 경로에 아래 코드 입력 후 서버 재시작 및 재요청을 보내보면 HTTP 요청 메시지를 로그로 출력하는 것을 확인할 수 있음
- 운영서버에 이렇게 모든 요청 정보를 다 남기면 성능저하가 발생할 수 있으므로 개발 단계에서만 적용할 것
logging.level.org.apache.coyote.http11=debug
- 강의에 있는 HTML 코드는 생략
3. HttpServletRequest
1) 개요
(1) HttpServletRequest역할
- HTTP요청 메시지를 개발자가 직접 파싱해서 사용해도 되지만 매우 불편함
- 서블릿은 개발자가 HTTP요청 메시지를 편리하게 사용할 수 있도록 개발자 대신하여 HTTP 요청메시지를 파싱 후 그 결과를HttpServletRequest객체에 담아서 제공함
(2) HTTP 요청메시지
- HttpServletRequest를 사용하면 아래의 HTTP 요청 메시지를 편리하게 조회할 수 있으며 추가로 여러가지 부가 기능도 함께 제공함
- START LINE: HTTP메서드, URL, 쿼리 스트링, 스키마, 프로토콜
- 헤더: 헤더 조회
- 바디: form 파라미터 형식조회, message body 데이터 직접 조회
POST /save HTTP/1.1 //STARTLINE : HTTP메소드, URL, 스키마, 프로토콜, 쿼리스트링 정보
Host: localhost:8080 // 헤더 : 헤더정보(Host, 콘텐츠타입 등)
Content-Type: application/x-www-form-urlencoded
username=kim&age=20 // 바디 : form파라미터 형식 조회, message body데이터 직접 조회, JSON 형식 등
(3) HttpServletRequest객체의 여러 부가기능
- 임시 저장소 기능 : 해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능
- 저장 : request.setAttribute(name, value)
- 조회 : request.getAttribute(name)
- 세션 관리 기능
- requst.getSession(create: true)
** 중요!
- HttpServletRequest, HttpServletResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 것임
- 이 기능에 대해 깊이있는 이해를 하려면 HTTP스펙이 제공하는 요청,응답 메시지 자체를 이해해야함.
2) 기본 사용법
(1) RequestHeaderServlet
- basic 하위에 request 패키지를 생성 후 작성
- startline정보, 헤더정보, 기타 정보드를 확인하는 방법들
** 참고
- 로컬에서 테스트를 하면 IPv6 정보가 나오는데 IPv4 정보를 보고 싶으면 -Djava.net.preferIPv4Stack=true 옵션을 VM options에 넣어주면 됨
package hello.servlet.basic.request;
@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//START LINE 정보 출력
printStartLine(request);
//Header 모든 정보
printHeaders(request);
//Header 편리한 조회
printHeaderUtils(request);
// 기타 정보 - HTTP 메세지의 정보가 아닌 네트워크의 커넥션 정보
printEtc(request);
}
private static 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 + ": " + request.getHeader(headerName));
}
*/
// 요즘 찍는 문법
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + ": "
+ request.getHeader(headerName)));
System.out.println("--- Headers - end ---");
System.out.println();
}
private static void printStartLine(HttpServletRequest request) {
System.out.println("--- REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod());
System.out.println("request.getProtocol() = " + request.getProtocol());
System.out.println("request.getScheme() = " + request.getScheme());
System.out.println("request.getRequestURL() = " + request.getRequestURL());
System.out.println("request.getRequestURI() = " + request.getRequestURI());
System.out.println("request.getQueryString() = " + request.getQueryString());
System.out.println("request.isSecure() = " + request.isSecure());
System.out.println("--- REQUEST-LINE - 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();
}
}
4. HTTP요청 데이터
1) 개요
- 주로 3가지 방법으로 데이터를 전달
(1) GET - 쿼리 파라미터
- 메시지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달
- 예)검색, 필터, 페이징등에서 많이 사용하는 방식
- /url**?username=hello&age=20*** 의 형태
(2) POST - HTML Form
- 메시지 바디에 쿼리 파라미터와 비슷한 형식으로 전달, username=hello&age=20
- 조회하는 방법이 쿼리파라미터와 호환이 됨
- 예)회원 가입, 상품 주문, HTML Form 사용
- Content-Type : application/x-www-form-urlencoded
(3) HTTP message body에 데이터 직접 담아서 요청
- HTTP API에서 주로 사용
- 데이터형식은 주로 JSON을 사용하며 XML, TEXT도 가능
- POST, PUT, PATCH 등 사용가능
2) GET 쿼리 파라미터
(1) 특징
- 메시지 바디 없이 URL의 쿼리 파라미터를 사용해서 데이터를 전달
- 예) 검색, 필터, 페이징등에서 많이 사용하는 방식
- 예) http://localhost:8080/request-param?username=hello&age=20&username=hello2...
(2) 쿼리 파라미터 조회 메서드
String username = request.getParameter("username"); //단일 파라미터 조회
Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들 모두 조회
Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map으로 조회
String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회
(3) RequestParamServlet
- 파라미터 이름은 하나이고 값이 중복인 경우 getParameter로 조회하면 첫번째 값만 반환하므로 getParameterValues로 조회
package hello.servlet.basic.request;
@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");
// 모든 요청의 파라미터를 조회
// Iterator 사용방식으로 조회 ParamName = key, request.getParameter(paramName) = value
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("name = " + name);
}
}
}
3) HTTP요청 데이터 - POST HTML Form
(1) 특징
- 주로 회원가입, 상품주문 등에서 사용하는 방식
- 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달, username=hello&age=20
- content-type: application /x-www-form-urlencoded
(2) HTML Form을 생성하여 테스트
- src/main/webapp/basic/hello-form.html 생성
- 작성된 html폼에 데이터를 입력하면 localhost:8080/request-param, POST로 데이터가 전달
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-param" method="post">
username: <input type="text" name="username"/>
age: <input type="text" name="age"/>
<button type="submit">전송</button>
</form>
</body>
</html>
(3) POST HTML Form 전송
- application /x-www-form-urlencoded 형식과 쿼리 파라미터 형식이 동일 하므로 쿼리 파라미터 조회 메서드를 그대로 사용 가능함
- 클라이언트(웹 브라우저)입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로 request.getParameter()로 편리하게 구분없이 조회가 가능
** 참고
- GET URL 쿼리 파라미터 형식으로 데이터 전달 시에는 메시지 바디가 없으므로 content-type이 없으나 POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 데이터를 포함하여 전송하기 때문에 content-type을 꼭 지정 해야함
(4) Postman으로 테스트
- 이렇게 간단한 테스트는 포스트맨을 활용하면 좋음
- HTTP 메소드: POST
- URL 작성: http://localhost:8080/request-param
- Body -> x-www-form-urlencoded 선택하면 Herder에서 자동으로 content-type이 설정이 되지만 혹시 모르니 꼭 확인 후 전송할 것
4) HTTP 요청데이터 - API 메시지바디(단순 텍스트)
(1) 특징
- HTTP message body에 데이터를 직접 담아서 요청
- HTTP API에서 주로 사용하며 JSON, XML, TEXT 사용 - 주로 JSON사용(사실상 표준)
- POST, PUT, PATCH 등 사용
(2) RequestBodyStringServlet
- InputStream을 사용해서 HTTP 메시지 바디의 데이터를 읽을 수 있음
- inputStream은 바이트코드를 반환
- 바이트 코드를 스트링으로 변환하려면 Charset을 지정해 주어야함
package hello.servlet.basic.request;
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 메시디 바디의 내용을 바이트 코드로 얻음
ServletInputStream inputStream = req.getInputStream();
// StreamUtils: 스프링이 제공하는 유틸(바이트코드를 스트링으로 변환), 바이트코드를 변환시에는 항상 인코딩 정보를 알려주어야 함
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
resp.getWriter().write("ok");
}
}
(3) Postman으로 테스트
- Postman세팅 : Body - raw - text 선택
- 보내기전 Headers의 Content-Type이 text/plain으로 바뀌었는지 확인
- 텍스트 입력 후 Send하면 ok의 응답 메시지가 뜨고, IDE의 로그에 입력한 메시지가 출력된 것을 확인할 수 있음
5) HTTP 요청데이터 - API 메시지바디(JSON)
(1) JSON 형식 데이터 전송
- POST http://localhost:8080/request-body-json
- content-type: application/json
- message body: {"username": "hello", "age":20} 전송
- 결과: message body = {"username": "hello", "age":20}
(2) HelloData
- JSON 형식으로 파싱할 수 있도록 객체 생성
- lombok라이브러리를 사용하여 간단히 애노테이션으로 Getter, Setter를 생성
package hello.servlet.basic;
@Getter @Setter //lombok 라이브러리 사용으로 Getter Setter 자동으로 생성
public class HelloData {
private String username;
private int age;
}
(3) RequestBodyJsonServlet
- 스프링 부트가 제공하는 Jackson 라이브러리인 ObjectMapper를 생성하여 messageBody에 담긴 JSON문자열을 HelloData의 형식으로 파싱
package hello.servlet.basic.request;
import java.nio.charset.StandardCharsets;
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.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());
resp.getWriter().write("ok");
}
}
(3) Postman 테스트
- POST / http://localhost:8080/request-body-json으로 메시지 바디에 JSON형태로 전송하면 ok응답과 함께 IDE의 로그에 전송한 데이터가 출력되는 것을 확인할 수 있음
** 참고
- JSON결과를 파싱해서 사용하기위해 자바객체로 변환하려면 Jackson, Gson 같은 라이브러리를 추가해서 사용해야함
- 스프링부트로 Spring MVC를 선택하면 기본 Jackson 라이브러리 ObjectMapper가 제공됨
- HTML Form의 데이터도 메시지 바디를 통해 전송되므로 이렇게 직접 읽을 수 있음
- 그러나 편리한 파라미터 조회 기능(request.getParameter(...))을 이미 제공하기 때문에 제공된 기능을 사용하는 것이 편리함
5. HttpServletResponse - 기본사용법
1) HttpServletResponse의 역할
(1) HTTP 응답 메시지 생성
- HTTP 응답코드 지정
- 헤더 생성
- 바디 생성
(2) 편의 기능 제공
- Content-Type, 쿠키, Redirect
(3) ResponseHeaderServlet
- response 패키지를 생성 후 작성
- 작성 후 애플리케이션을 실행하여 매핑된 url로 접속해보면 /basic/gello-form.html로 리다이렉트 되고, 개발자 도구에서 네트워크 상태를 보면 response-header에 응답정보가 있는 것을 알 수 있음
package hello.servlet.basic.response;
@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); // 200
//[response-headers]
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"); // 임의의 헤더를 생성
//[message-body]
PrintWriter writer = response.getWriter();
writer.println("성공");
//[Header 편의 메서드]
content(response);
cookie(response);
redirect(response);
}
private static void content(HttpServletResponse response) {
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
}
private static void cookie(HttpServletResponse response) {
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600);
response.addCookie(cookie);
}
private static void redirect(HttpServletResponse response) throws IOException {
response.sendRedirect("/basic/hello-form.html");
}
}
6. HTTP 응답데이터
1) 단순 텍스트,HTML
(1) 단순 텍스트 응답
- writer.println("ok"); 기존 테스트로 응답 메세지 작성했던 방식
(2) HTML응답
- HTTP API - messageBody JSON응답
(3) ResponseHtmlServlet
- 코드를 작성 후 매핑된 url로 접속하면 작성한 html이 렌더링 되어 화면에 출력된 모습을 확인할 수 있음
- 페이지 소스 보기를 사용하면 결과 HTML도 확인할 수 있음
package hello.servlet.basic.response;
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕하세요</div>");
writer.println("</body>");
writer.println("</html>");
}
}
2) HTTP 응답데이터 - API JSON
(1) ResponseJsonServlet
- HTTP응답으로 JSON을 반환할 때는 conten-type을 application/json으로 지정해야함
- Jackson라이브러리가 제공하는 objectMapper.writeValueAsString()메서드를 활용하여 객체를 JSON문자로 변경할 수 있음
- 실행 후 매핑된 url로 접속하면 {"username":"kim","age":38}과 같이 JSON 형태로 응답값이 출력됨
package hello.servlet.basic.response;
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type","application/json");
resp.setCharacterEncoding("UTF-8");
HelloData data = new HelloData();
data.setUsername("kim");
data.setAge(38);
// {"username":"kim","age":38}
String result = objectMapper.writeValueAsString(data);
resp.getWriter().write(result);
}
}
** 참고
- application/json은 스펙상 utf-8 형식을 사용하도록 정의되어 있어서 charset=utf-8과 같은 추가 파라미터를 지원하지 않기때문에 application/json만 입력해도 됨
- response.getWriter()를 사용하면 추가파라미터를 자동으로 추가하는데 이럴때는 response.getOutputStream()으로 출력하면 추가 파라미터 없이 출력됨