→ Model, View, Controller 디자인 패턴
Servlet
Tomcat
Servlet & MVC
예시 코드(수업)[url : /course/body] →
`@WebServlet(name = "courseServletBody", urlPatterns = "/course/body")`
코드
@WebServlet(name = "courseServletBody", urlPatterns = "/course/body")
public class CourseServletBody extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
service 메서드 : HttpServlet의 기본 메서드
HttpServletRequest와 HttpServletResponse 객체는 Java Servlet 스펙에 정의되어 있는 인터페이스
코드
ServletInputStream inputStream = req.getInputStream();
// HTTP Body에서 가져온 데이터를 바이트코드로 변환
String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
// 바이트코드를 String 으로 변환
req.getInputStream() : HttpServletRequest 객체에서 클라이언트로부터 전송된 HTTP 요청 본문의 데이터를 읽어오기 위한 메서드
ServletInputStream : Servlet 환경에서 클라이언트로부터 전송된 요청 본문의 이진 데이터를 읽기 위한 메서드를 제공
즉, HttpServletRequest req를 통해 Client에서 받아온 정보를 req.getInputStream()을 읽어오는 메서드
StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8): Spring Framework에서 제공하는 유틸리티 클래스인 StreamUtils를 사용하여 ServletInputStream에서 읽어온 데이터를 문자열로 변환하는 코드
→ StandardCharsets.UTF_8: 문자열의 인코딩 방식을 지정하는 상수
코드
// JSON To Object
Course course = new ObjectMapper().readValue(body, Course.class);
System.out.println("course.getTitle() = " + course.getTitle());
System.out.println("course.getInstructor() = " + course.getInstructor());
System.out.println("course.getCost() = " + course.getCost());
// 가격 수정
course.setCost(9999999);
new ObjectMapper().readValue(body, Course.class)
: Jackson 라이브러리를 사용하여 요청 본문의 문자열 데이터를 Java 객체로 변환하는 코드
- ObjectMapper
: Jackson 라이브러리에서 제공하는 JSON 데이터를 Java 객체로 변환하거나, Java 객체를 JSON 데이터로 변환하기 위한 클래스
- readValue(): 메소드는 JSON 문자열을 Java 객체로 변환하는 메소드
→ 즉, 첫 번째 인자로는 JSON 문자열 데이터가 들어있는 문자열(**`body`**변수)을 전달하고, 두 번째 인자로는 변환하고자 하는 Java 객체의 클래스(**`Course.class`**변수)를 전달
(Course.class는 뒤에 나온다)
- 결론 : **`body`**에 들어있는 JSON 문자열 데이터가 **`Course`**클래스의 객체로 변환되어 **`course`**변수에 할당
- 그렇게 위 코드를 보면 Course 클래스의 필드 값을 호출하거나 변경하고 있다.
코드
// 요청에 대한 결과 반환
res.setContentType("application/json");
res.setCharacterEncoding("utf-8");
res.setStatus(HttpStatus.CREATED.value());
res.setContentType("application/json"): HTTP 응답의 Content-Type 헤더를 설정
→ application/json: JSON 형식의 데이터를 의미하며, 이를 설정함으로써 클라이언트에게 전송되는 데이터가 JSON 형식임을 명시
res.setCharacterEncoding("utf-8"): HTTP 응답의 문자 인코딩을 설정하는 부분
res.setStatus(HttpStatus.CREATED.value()): HTTP 응답의 상태 코드를 설정하는 부분
→ HttpStatus.CREATED: HTTP 상태 코드 201(Created)을 의미하며, 이를 설정함으로써 클라이언트에게 요청이 성공적으로 처리되어 새로운 리소스가 생성되었음을 알림
→ value()메소드는 HttpStatus.CREATED상태 코드의 정수값인 201을 반환
위 코드를 사용해서 HTTP 응답의 Content-Type, 문자 인코딩, 상태 코드를 설정하여 클라이언트에게 적절한 응답을 반환할 수 있음
코드
// Object To JSON
res.getWriter().write(new ObjectMapper().writeValueAsString(course));
new ObjectMapper().writeValueAsString(course): course객체를 Jackson 라이브러리를 사용하여 JSON 형식의 문자열로 변환하는 부분
→ 위에서 설명한 readValue()와 반대로 writeValueAsString()메소드는 Java 객체를 JSON 형식의 문자열로 변환하여 반환
res.getWriter().write(...): 이 코드는 HTTP 응답의 body에 JSON 형식의 문자열을 작성하는 부분
→ res.getWriter()
: ServletResponse인터페이스에서 제공하는 메소드로, HTTP 응답의 출력 스트림을 반환
→write(...)메소드는 문자열을 출력 스트림에 작성하여 클라이언트로 전송
즉, 위 코드는 course객체를 JSON 형식의 문자열로 변환하여 HTTP 응답의 body에 작성하게 된다. → 클라이언트는 이 JSON 형식의 문자열을 받아 응답 데이터를 처리할 수 있다.
위 코드들을 모두 합쳐보면 HttpServletRequest 객체를 통해 HTTP Body에 있는 데이터를 읽어와서 String 형태로 변환하고, 이를 Jackson 라이브러리를 사용하여 Course 클래스 객체로 변환 그리고 Course 객체의 내용을 수정하고, HttpServletResponse 객체를 통해 JSON 형태로 변환하여 클라이언트로 응답을 보내는 로직이 구현!!
코드
private void printHttpInfo(HttpServletRequest req) {
System.out.println("req.getRequestURI() = " + req.getRequestURI());
System.out.println("req.getRequestURL() = " + req.getRequestURL());
System.out.println("req.getServerPort() = " + req.getServerPort());
System.out.println("req.getServletPath() = " + req.getServletPath());
System.out.println("req.getMethod() = " + req.getMethod());
}
req에는 위에 수행했던 코드들을 통해 변경된 내용이 req에 들어가게 된다.
printHttpInfo()메소드 : req객체를 사용하여 HTTP 요청의 다양한 정보를 출력
예시 코드(수업)[url : /course] →
courseServletBody 코드와 비슷하지만 courseServlet 코드는 단순히 req 정보들을 받아 확인 후 res를 통해 Client에게 전달해보는 코드이다.
코드
// 데이터 확인
String title = req.getParameter("title");
String instructor = req.getParameter("instructor");
double cost = Double.parseDouble(req.getParameter("cost"));
req.getParameter(): 클라이언트가 전송한 HTTP 요청의 파라미터 값을 문자열 형태로 반환
→ 예를 들어 url 이 아래와 같다고 치면,
http://example.com/course?title=Java&instructor=John&cost=99.99
title은 Java, instructor은 John, cost는 문자열을 double형으로 변환하여 99.99 로 출력
코드
// Object To JSON
Course course = new Course(title, instructor, cost);
res.getWriter().write(new ObjectMapper().writeValueAsString(course));
Course객체를 생성한 후, 해당 객체를 JSON 형식의 문자열로 변환하여 HTTP 응답에 작성하는 부분
Course클래스의 객체인 course는 title, instructor, cost라는 세 개의 파라미터를 가지고 있는 Course클래스의 생성자를 호출하여 객체를 생성 → 생성자 호출!!!
→ course는 각각의 필드에 전달된 값을 갖게 됨
다음은 앞서 설명한 것과 같이 res에 course객체를 JSON 형식의 문자열로 변환
마지막 코드는 JSON 형태의 문자열로 변환하는 과정으로 앞에 설명한 것과 같다.
CourseServlet 결론
courseServlet클래스의 service()메소드에서 req.getParameter()메소드를 사용하여 요청(request)의 파라미터 값을 추출 후 Course클래스의 생성자에 title, instructorcost값을 전달하여 새로운 Course객체를 생성 이 사이에 Object To Json 과정은 클라이언트에게 전송되는 응답의 형식과 문자 인코딩을 지정하는 것으로, 클라이언트가 서버에서 받는 응답의 형태와 인코딩을 제대로 인식할 수 있도록 도와줌CourseServletBody 결론
courseServlet클래스를 통해 course 객체의 필드 값이 들어갔다면, courseServletbody클래스를 통해 필드 값 변경을 해보는 것이다.전체 코드
CourseServlet
@WebServlet(name = "courseServlet", urlPatterns = "/course")
public class CourseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
//printHttpInfo(req);
// 데이터 확인
String title = req.getParameter("title");
String instructor = req.getParameter("instructor");
double cost = Double.parseDouble(req.getParameter("cost"));
// 요청에 대한 결과 반환
res.setContentType("application/json");
res.setCharacterEncoding("utf-8");
// Object To JSON
Course course = new Course(title, instructor, cost);
res.getWriter().write(new ObjectMapper().writeValueAsString(course));
}
private void printHttpInfo(HttpServletRequest req) {
System.out.println("req.getRequestURI() = " + req.getRequestURI());
System.out.println("req.getRequestURL() = " + req.getRequestURL());
System.out.println("req.getServerPort() = " + req.getServerPort());
System.out.println("req.getServletPath() = " + req.getServletPath());
System.out.println("req.getMethod() = " + req.getMethod());
}
}
CourseServletBody
@WebServlet(name = "courseServletBody", urlPatterns = "/course/body")
public class CourseServletBody extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// printHttpInfo(req);
// HTTP Body 정보 가져오기
ServletInputStream inputStream = req.getInputStream(); // HTTP Body에서 가져온 데이터를 바이트코드로 변환
String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 바이트코드를 String 으로 변환
System.out.println("body = " + body);
// JSON To Object
Course course = new ObjectMapper().readValue(body, Course.class);
System.out.println("course.getTitle() = " + course.getTitle());
System.out.println("course.getInstructor() = " + course.getInstructor());
System.out.println("course.getCost() = " + course.getCost());
// 가격 수정
course.setCost(9999999);
// 요청에 대한 결과 반환
res.setContentType("application/json");
res.setCharacterEncoding("utf-8");
res.setStatus(HttpStatus.CREATED.value());
// Object To JSON
res.getWriter().write(new ObjectMapper().writeValueAsString(course));
}
private void printHttpInfo(HttpServletRequest req) {
System.out.println("req.getRequestURI() = " + req.getRequestURI());
System.out.println("req.getRequestURL() = " + req.getRequestURL());
System.out.println("req.getServerPort() = " + req.getServerPort());
System.out.println("req.getServletPath() = " + req.getServletPath());
System.out.println("req.getMethod() = " + req.getMethod());
}
}
코드
@RestController
@RequestMapping("/controller/course")
public class CourseController {
@PostMapping("/body")
public ResponseEntity<?> courseBody(@RequestBody Course course) {
System.out.println("course.toString() = " + course.toString());
course.setCost(123456);
return ResponseEntity.ok(course);
}
위의 CourseServlet과 겹치지 않게 /controller/course경로로 들어오는 요청들은 CourseController클래스의 메소드들 중에서 해당 경로와 매핑되는 메소드로 처리
ResponseEntity<?>: 스프링 프레임워크에서 사용되는 HTTP 응답을 나타내는 클래스
→ <?>부분은 모든 타입을 나타내며, 원하는 타입으로 대체
→ ResponseEntity<?>를 사용하면 컨트롤러에서 HTTP 응답을 더욱 세밀하게 제어 가능
@RequestBody: 이 어노테이션은 POST 요청에서의 Request Body에 포함된 데이터를 객체로 변환하기 위해 사용
POST 요청의 Request Body에 포함된 데이터를 Course객체로 변환하여 처리하고, 이를 ResponseEntity에 담아 응답으로 반환
ok(): HTTP 상태 코드를 200 OK로 설정하고, 본문을 설정하지 않음
notFound(): HTTP 상태 코드를 404 Not Found로 설정하고, 본문을 설정 XbadRequest(): HTTP 상태 코드를 400 Bad Request로 설정하고, 본문을 설정 X“cost” = 99000 값을 넣어주었다면 setCost(12345)메소드를 호출 12345로 변경이 된다.
하지만, 이는 메모리 상에서만 해당 객체의 cost값을 변경한 것이고, HTTP 요청의 바디에 있는 데이터와는 무관하다.
만약, @RequestBody로 받아온 객체를 변경하여 HTTP 요청의 바디에 있는 데이터를 변경하고자 할 경우, 아래의 코드로 입력하면 된다.
Course updatedCourse = new Course(course.getTitle(), course.getInstructor(), 123456)
코드
@GetMapping("/query")
public ResponseEntity<?> courseQuery(@PathParam("title") String title, @PathParam("instructor") String instructor, @PathParam("cost") double cost) {
System.out.println("title = " + title);
System.out.println("instructor = " + instructor);
System.out.println("cost = " + cost);
return ResponseEntity.ok(new Course(title, instructor, cost));
}
@PathParam: URL 경로(Path)에 포함된 데이터를 파라미터로 받아오기 위해 사용
위의 코드는 입력받은 파라미터를 출력해 보고 Course 클래스에 해당 인자를 받는 로직
질문!!! → @PathParam 은 SpringMVC에서는 사용 안됨??Java API for RESTful Web Services 에서 사용됨(@PathVariable을 사용해야 함 → url : "/query/{title}/{instructor}/{cost}”) 근데 Controller는 Spring MVC???!!!!
코드
@GetMapping("/model-attribute")
public ResponseEntity<?> courseModelAttribute (@ModelAttribute Course course) {
System.out.println("course.toString() = " + course.toString());
return ResponseEntity.ok(course);
}
@ModelAttribute: 요청 파라미터들을 객체로 변환하기 위해 사용
→ /model-attribute엔드포인트로 들어오는 HTTP GET 요청의 요청 파라미터들이 Course
객체로 변환되어 처리됨 (이것도 질문!!!)
여기서 “/query”를 이용할 경우
http://localhost:8080/query?title={title}&instructor={instructor}&cost={cost}
위의 url로 명시한 후 사용을 한다.
“model-attribute”를 이용할 경우
http://localhost:8080/model-attribute?title={title}&instructor={instructor}&cost={cost}
위의 url을 사용하는데 query와 model-attribute의 url 명시가 비슷해 보인다.
여기서 ResponseEntity.ok(course); 와 ResponseEntity.ok(new Course(title, instructor, cost));의 차이
ResponseEntity.ok(course): 이미 생성된 Course객체 course를 HTTP 응답 본문으로 설정하여 200 OK 상태 코드를 가지는 ResponseEntity객체를 생성하는 것 → course객체는 이미 생성되어 있는 객체를 그대로 사용하는 것이기 때문에, 해당 객체에 이미 값이 설정되어 있어야 함ResponseEntity.ok(new Course(title, instructor, cost)) : 새로운 Course객체를 생성하여 해당 객체를 HTTP 응답 본문으로 설정하고, 200 OK 상태 코드를 가지는 ResponseEntity객체를 생성하는 것 → title, instructor, cost와 같은 파라미터를 받아서 새로운 Course객체를 생성하는 것이기 때문에, 해당 파라미터를 인자로 받아서 객체를 생성해야 함즉!! ResponseEntity.ok(course)는 이미 생성된 course객체를 사용하는 것이고, ResponseEntity.ok(new Course(title, instructor, cost))는 새로운 Course객체를 생성하여 사용하는 것
@RestController
@RequestMapping("/controller/course")
public class CourseController {
@PostMapping("/body")
public ResponseEntity<?> courseBody(@RequestBody Course course) {
System.out.println("course.toString() = " + course.toString());
course.setCost(123456);
return ResponseEntity.ok(course);
}
@GetMapping("/query")
public ResponseEntity<?> courseQuery(@PathParam("title") String title, @PathParam("instructor") String instructor, @PathParam("cost") double cost) {
System.out.println("title = " + title);
System.out.println("instructor = " + instructor);
System.out.println("cost = " + cost);
return ResponseEntity.ok(new Course(title, instructor, cost));
}
@GetMapping("/model-attribute")
public ResponseEntity<?> courseModelAttribute (@ModelAttribute Course course) {
System.out.println("course.toString() = " + course.toString());
return ResponseEntity.ok(course);
}
@GetMapping
public ResponseEntity<?> course(Course course) {
System.out.println("course.toString() = " + course.toString());
return ResponseEntity.ok(course);
}
}
@Getter
@ToString
@Setter
//@AllArgsConstructor
@NoArgsConstructor
public class Course {
private String title;
private String instructor;
private double cost;
public void setCost(double cost) {
this.cost = cost;
}
public Course(String title, String instructor, double cost) {
this.title = title;
this.instructor = instructor;
this.cost = cost;
}
}
courseServlet은 HttpServlet을 상속한 서블릿으로, Java Servlet 기술을 사용하여 HTTP 요청을 처리
web.xml파일이나 @WebServlet어노테이션 등을 통해 서블릿 매핑을 설정하고, 해당 서블릿이 담당할 URL 패턴을 지정하여 사용CourseServlet은 HttpServletRequest와 HttpServletResponse객체를 통해 클라이언트의 요청을 처리하고 응답을 생성CourseController는 Spring MVC의 컨트롤러로, Spring 프레임워크가 제공하는 기능을 사용하여 HTTP 요청을 처리
@RequestMapping, @GetMapping, @PostMapping등을 사용하여 URL 패턴과 HTTP 메소드에 대한 처리를 설정하고, 메소드 파라미터와 리턴 타입을 통해 요청과 응답 데이터를 처리ResponseEntity를 사용하여 응답을 생성하고 반환일반적으로는 Spring MVC의 @Controller을 사용하는 CourseController를 선호
그리고 만약, 파라미터 값을 안넣게 된다면 null 값을 호출하게 된다.