<화면 출력 부분 관련>
👇1-1 NoticeController.java : ActionForward
public ActionForward execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {}
👇1-2 BoardController.java : String
public String execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {}
p.273
PrintWriter는 JS의 document라고 생각하면 됨
: 브라우저의 태그들을 기록함
: 단, PrintWriter 처리 주체는 톰캣(서버에서 이미 만들어져 내려옴). 따라서 document보다 빠름. 서버시점.
: document의 처리 주체는 브라우저. 브라우저에서 랜더링 될 때 출력됨. 클라이언트 시점.
8-9)
url은 first. 출력하는 건 second
<3가지 경우 나눠서 처리해보기>
redirect
forward
WEB-INF
: 있으면 redirect, forward. 없으면 WEB-INFreturn "redirect:/notice/noticeList.jsp"; //webapp
return "forward:/notice/noticeList.jsp"; //webapp - 요청이 유지되는 것으로 판단해서 서블릿이 쥐고 있는 값을 jsp에서도 사용할 수 있다.
return "/notice/noticeList"; //WEB-INF/jsp/notice 아래
p. 276
mime type. mime type으로 브라우저가 판단한다./ 슬래쉬로 나눔. 슬래쉬를 기준으로 리턴을 Array로 받겠다. workname. 예) notice, board...메소드 이름이면서 페이지 이름이다. viewNameMultipurpose Internet Mail Extensions의 약자로 파일 변환을 의미. 현재는 웹을 통해 여러 형태의 파일을 전달하는데 사용하고 있지만 이 용어가 생길 땐 이메일과 함께 동봉할 파일을 텍스트 문자로 전환해서 이메일 시스템을 통해 전달하기 위해 개발되어 Internet Mail Extensions라고 불리기 시작했다.
예전에는 텍스트 파일을 주고 받는데에 ASCII로 공통된 표준에 따르기만 하면 문제가 없었으나 네트워크를 통해 ASCII가 아닌 바이너리 파일을 보내는 경우가 생기게 되었다. 음악파일, 무비파일, 워드파일 등등 ASCII만으로는 전송이 안되기 때문에 기존 시스템에서 문제 없이 전달하기 위해서는 텍스트로의 변환이 필요했다.
텍스트 파일로 변환하는 것을 인코딩(Encoding), 텍스트 파일을 바이너리 파일로 변환하는 것을 디코딩(Decoding)이라고 한다.
MIME으로 인코딩한 파일은 Content-type정보를 앞부분에 담게되며 Content-type은 여러가지 타입이 있다.
웹 브라우저에서 서버에 접속하여 html 문서를 요청하면서 html문서에 있는 이미지 파일의 경로를 불러올 수 있다. 이러한 과정에서 이미지의 경로에 있는 파일이 웹브라우저에서 지원되는 MIME-Type이라면 웹브라우저를 이용하여 열어볼 수 있다.
바이너리파일(음악 파일, 무비 파일, 워드 파일 등) 또한 마찬가지 이다. 주로 쓰고 있는 대부분의 포맷인 .gif .jpg .mov 등등의 파일들은 웹 브라우저에서 무리없이 열리게 되는데 브라우저에서 지원하지 못하는 유형은 따로 지정해줘야 한다.
오디오 타입
Multipart 타입
TEXT 타입
Spring 버전이 높아지면서 달라진 점 : 의존관계에 대한 자동화
ModelAndViewModelAndView : 스코프는 String으로. 즉 select일 때 사용하겠다.request - setAttribute 사용(p.276)return "redirect:/notice/noticeList.jsp"; //webapp
return "forward:/notice/noticeList.jsp"; //webapp - 요청이 유지되는 것으로 판단해서 서블릿이 쥐고 있는 값을 jsp에서도 사용할 수 있다.
return "/notice/noticeList"; //WEB-INF/jsp/notice 아래
requestsessionapplication/JSON 형식으로 해야한다.JSP로 내보낼 필요가 없을 때는 html을 사용하지 않는다.
else if("jsonNoticeList2".equals(upmu[1])) {//select
logger.info("jsonNoticeList");
List<Map<String ,Object>> nList = null;
hmb.bind(pMap);
nList = nLogic.noticeList(pMap);
Gson g = new Gson();
String temp = g.toJson(nList);
res.setCharacterEncoding("utf-8");
res.setContentType("application/json");
PrintWriter out = res.getWriter();
out.print(temp);//[{"deptno":10, "dname":"영업무"}]
int end = path.toString().length();// -> notice/
path.delete(0, end);
path.append(temp);//url이 전달되는게 아니라 json형식 즉 문자열이 전달됨OOP(객체 지향 프로그래밍) 5대 설계 원칙 - SOLID
분리
1) 관심사 (입력 - 처리 - 출력)
2) 변하는 것, 변하지 않는 것 분리해 본다.
3) 공통(중복) 코드 분리
pageContext - 현재 페이지에서만 사용이 가능하다
: EL사용을 위해서만 사용될 뿐 사용되지 않는다
: 주소창이 바뀐다
request - 요청이 유지되는 동안에...
: 요청할 때 마다 생기고 서로 독립적이다. - 세션보다는 제한적
: 요청마다 1개씩 갖는다
: forward시에 사용이 가능함 - 주소창이 안바뀌는데 페이지는 바뀐다.
session - 사용자마다 1개씩 생기는 개별저장소 - 사용자 만명이면 만개가 생긴다.
: 그래서 서버 부담이 대단히 높다. 사용하기는 제일 편하다
: 로그인하면 생겼다가 로그아웃하면 사라진다.
: 메모리 부담이 제일 높다
: 잠깐 저장했다가 지우는 방법도 가능은 하다
: 주소창이 바뀌어서 기존에 요청이 끊어져도 유지된다 - 시간
application - 공통저장소이다. - 이부분이 세션과 다르다
: context마다 1개라서 어디서나 접근이 가능함
: 모든 클라이언트가 공유가능함 - 하나다
: Web Application의 시작부터 종료까지 계속 유지된다. - 독이다
사용방법은? - 저장소
저장할 때 -> setAttribute(이름,값);
읽어올 때 -> getAttribute(이름-String):값- Object;
지울 때 -> removeAttribute(이름);, 세션 session.invalidate();세션값 모두 삭제
2번과 3번은 스프링에서도 지원하기 때문에 주의 깊에 봐야한다. 1번과 4번은 별로...
XXXController : 서블릿 아니어도 괜찮아
: Controller 추가해 본다
: 누가 서블릿이지? - ActionServlet.java 추가 - FrontController(전처리 지원 : 관여) -> DispatcherServlet과 동급
doGet, doPost, doPut, doDelete : Restful API 지원하는 메소드를 오버라이딩 함
: 405번 발동하면 이 메소드 이름이 틀렸을 경우임
<form method="post" action="/notice/noticeInsert.gd"> -> BoardController로 갈지, QnAController로 갈지, MemberController로 갈지 결정해야 함
왜 한글이 깨지는 걸까?
Tomcat은 인코딩 타입이 8859_1 디폴트이다.
<connector URIEncoding=utf-8 -> URI한글포함될 때><!-- EncodingFilter 정의하기
아래 filter가 post방식에 대한 한글처리를 지원하는가? - 네
web.xml문서는 deployment descriptor - DD파일, 배포서술자(번역어)
-> @annotation으로 변경됨
-->
<filter>
<filter-name>CharacterEncoding</filter-name>
<filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
웹 서버와 웹 컨테이너의 기능을 모두 수행하는 프로그램(S/W)
웹 서버와 웹 컨테이너의 차이점 (둘을 분리해서 자세하게 알아둘 것)
옛날 ) Apache서버(웹서버 - 정적처리)와 Tomcat(웹서버 포함하는 컨테이너 - 동적처리) 분리 돼 있었음
엔터프라이즈 서버 : JBOSS, JEUS, 웹스피어 -AWS-

웹 서버
응답(Response)하는 쪽인 서버.
클라이언트의 요청에 응답(데이터)을 보내는 컴퓨터 프로그램.
데이터는 HTML문서, 컴퓨터에 저장된 리소스(자원)이다.
Apache, Nginx, Microsoft IIS 등이다.웹 컨테이너
클라이언트의 요청에 따른 데이터를 데이터베이스에서 전달받아 웹 서버로 전달하는 프로그램.
예) 패밀리 레스토랑에서 주문을 받아 주방에서 만든 음식을 서빙하는 종업원에게 전달하는 주방 보조가 웹 컨테이너, 음식을 서빙하는 종업원이 웹 서버
WAS 서버는 원래 웹 컨테이너 프로그램을 이르는 말이었으나 요즘은 웹 컨테이너 + 웹 서버 기능까지 내장하고 있다.
서버 규모가 커질 경우)
.classpath, .project : 로컬환경정보 - 이클립스가 생성해주는 파일
: workspace_jsp 배포 했을 때 -> nae2Gym 꺼내서 처리하면 에러가 뜨지 않음
: buildpath - 트러블 슈팅
: zulu11로 설치한 노트북 이슈
JDBC API -> myBatis(ORM 매핑오픈소스 - if문 지원함, 동적쿼리를 지원함 - SQL문 그대로 사용) -> Hibernate(ORM 프레임워크 - SQL문 없다. 그런데 조회됨)
JPA(DB연동 마지막 목표) : 좋다 나쁘다 문제 아님. 장단점이 분명하다. 단점) 튜닝 안 됨. 복잡한 계산식은 SQL문 사용이 유리함.
Lombok(롬복)은 Java 라이브러리로 반복되는 getter, setter, toString 등의 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리이다. 보통 Model 클래스나 Entity 같은 도메인 클래스 등에는 수많은 멤버변수가 있고 이에 대응되는 getter와 setter 그리고 toString() 메서드 그리고 때에 따라서는 멤버변수에 따른 여러개의 생성자를 만들어주게 되는데, 거의 대부분 이클립스같은 IDE의 힘만으로 생성한다고 하지만 이 역시도 번거로운 작업이 될 수 있다. 뿐만 아니라 코드 자체가 반복되는 메서드로 인해 매우 복잡해지게 된다.
Lombok은 여러가지 어노테이션을 제공하고 이를 기반으로 코드를 컴파일과정에서 생성해 주는 방식으로 동작하는 라이브러리이다. 즉 코딩 과정에서는 롬복과 관련된 어노테이션만 보이고 getter와 setter 메서드 등은 보이지 않지만 실제로 컴파일된 결과물(.class)에는 코드가 생성되어 있다는 뜻.
getter/setter메소드를 추가하지 않아도 괜찮다
전변을 private으로 선언하였다 : 캡슐레이션(고유한 정보들을 외부에서 직접 수정하는 걸 막는다)
👇BoardVO
package com.vo;
//JDBC API -> myBatis(ORM 매핑오픈소스 - if문 지원함, 동적쿼리를 지원함 - SQL문 그대로 사용) -> Hibernate(ORM 프레임워크 - SQL문 없다. 그런데 조회됨)
//JPA(DB연동 마지막 목표) : 좋다 나쁘다 문제 아님. 장단점이 분명하다. 단점) 튜닝 안 됨. 복잡한 계산식은 SQL문 사용이 유리함.
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
//왜 롬복을 사용하지?
//getter/setter메소드를 추가하지 않아도 괜찮다
//전변을 private으로 선언하였다 : 캡슐레이션(고유한 정보들을 외부에서 직접 수정하는 걸 막는다)
@Data //@Setter, @Getter
@NoArgsConstructor
public class BoardVO {
private int b_no =0;//
private String b_title =null;//
private String b_writer =null;//
private String b_content =null;//
private int b_hit =0;//
private String b_date =null;//
private String b_file =null;//
//Lombok에서 제공하는 @Builder를 사용하면 파라미터 갯수나 타입을 일일이 맞추지 않고도 자유롭게 사용 가능하다
//사용이란 생성자의 파라미터 값을 통한 전역변수들의 초기화 작업
@Builder
public BoardVO(int b_no, String b_title, String b_writer, String b_content, int b_hit, String b_date, String b_file) {
super(); //디폴트 생성자 호출 : 왜냐하면 파라미터 갖는 생성자가 하나라도 있으면 디폴트 생성자를 제공하지 않음
this.b_no = b_no;
this.b_title = b_title;
this.b_writer = b_writer;
this.b_content = b_content;
this.b_hit = b_hit;
this.b_date = b_date;
this.b_file = b_file;
}
}
👇BoardVOTest
public class BoardVOTest {
public static void main(String[] args) {
//lombok 적용의 좋은 점을 알아보자.
//lombok 적용되지 않은 경우) lombok 없으면 순서 지키지 않았을 때 에러 뜸.
EmpVO evo = new EmpVO("나잘난"); //public EmpVO(int empno) //lombok 없으면 순서 지키지 않았을 때 에러 뜸.
System.out.println("ename : "+evo.getEname());//나잘난
//lombok 적용된 경우) 순서, 개수 맞추지 않고 자유롭게 사용 가능
//BoardVO bvo = new BoardVO(5);
BoardVO bvo3 = new BoardVO();
bvo3.setB_title("제목333");
String title = bvo3.getB_title();
System.out.println(title);//제목333
BoardVO bvo = BoardVO.builder().b_no(3).build();
System.out.println(bvo.getB_no());//3
System.out.println(bvo.getB_title());//null or ""
BoardVO bvo2 = BoardVO.builder().b_no(30).b_title("제목1").build();
System.out.println(bvo.getB_no());//30
System.out.println(bvo.getB_title());//제목1
}
}
EmpVO는 파라미터로 "나잘난"을 받는 생성자를 사용하여 객체를 생성한다. Lombok을 적용하지 않은 경우, 순서나 개수를 지키지 않으면 에러가 발생한다. 그 후, getEname() 메서드를 통해 이름을 가져와 출력한다.
BoardVO는 Lombok의 @Builder 어노테이션을 사용하여 객체를 생성한다. 이를 통해 객체를 빌더 패턴을 사용하여 생성할 수 있다. 그 예로 bvo3는 b_title 속성만 설정하고, bvo는 b_no만 설정하며, bvo2는 b_no와 b_title을 설정한다.
각 객체의 값을 출력하여, Lombok이 적용된 경우 빌더 패턴을 사용하여 특정 필드만 설정하거나, 순서를 신경쓰지 않고도 객체를 생성할 수 있다. 이를 통해 Lombok을 사용하면 코드가 더 간결해지고 유연성이 증가한다는 것을 확인할 수 있다.
/webapp/board
/WEB-INF/jsp/board
ActionForward를 String으로 변경한다
ActionForward를 Object로 변경한다
@WebServlet으로 서블릿을 URL에 매핑할 때 사용 - 클래스 앞에
서블릿은 늦은 초기화를 사용한다
스프링은 이른 초기화를 사용한다. - 스프링은 서블릿을 발전시킨 것이다.
@WebServlet(urlPatterns={"/hello", "/hello/*"}, loadOnStartup=1)
미리 초기화를 해두고 싶은 서블릿에 붙일 수 있는 옵션임 - loadOnStartup
매핑 패턴 소개 - React Router사용하는 컨셉
아래 번호는 순번을 의미하므로 1번을 따져서 없으면 2번이 또 없으면 3번이 적용됨
exact mapping - /basic/hello.do -> http://localhost/basic/hello.do
path mapping - /basic/* ->
-> http://localhost/basic/hello.do
-> http://localhost/basic/hello
-> http://localhost/basic/
-> http://localhost/basic/test
extension mapping - .do, .gd
:확장자가 do로 끝나기만 한다면 내가 가로챌께
-> http://localhost/basic/hello.do
-> http://localhost/basic/login.do
-> http://localhost/basic/logout.do
default mapping - / - spring legacy 기본
:위에서 부터 따져보다가 어디에도 해당되지 않으면 디폴트가 적용됨
-> http://localhost/basic/hello.do
-> http://localhost/basic/hello
-> http://localhost/basic/
-> http://localhost/basic/test
스프링에서는 @RequestMapping이 제공된다 - 메소드 앞에온다
@Controller - 컨트롤계층의 역할을 맡게됨 - 어노테이션(@) - annotation
:자바에서는 Reflection API를 지원하고 있음
-> 힙 영역에 로드되어 있는 클래스 타입의 객체를 통해서 필드/메소드/생성자를
접근제어자와 상관없이 사용할 수 있도록 지원하는 API
-> 컴파일 시점이 아닌 런타임 시점에 동적으로 특정 클래스의 정보를
추출해 주는 프로그래밍 기법을 지원
-> 주로 프레임워크 또는 라이브러리 개발시 사용됨
예) Spring DI(dependency Injection), Test프레임워크(JUnit), JSON 라이브러리
package com.example.demo.pojo2;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import lombok.extern.slf4j.Slf4j;
//Restful API란 무엇인가?
//전송방식 : 바이너리 - UI솔루션이 지원하는 모드 중 한 가지
@SuppressWarnings("serial")
//@Slf4j : Logger를 쓰지 않고도 사용할 수 있다
public class ActionServlet extends HttpServlet {
Logger logger = Logger.getLogger(ActionServlet.class);
protected void doService(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
Controller controller = new BoardController();
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
doService(req, res);
}
//쿼리스트링, ?, 링크, header, 제한적임
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
doService(req, res);
}
//body, 서버인터셉트 안 당함, 무조건 서버전달, 제한이 없음 - 바이너리 타입(첨부파일)
//POST 방식 : enctype="multipart/form-data" - 바이너리 전달 - 문자+숫자 -
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
// TODO Auto-generated method stub
doService(req, res);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPut(req, resp);
}
}
package com.example.demo.pojo2;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.example.demo.pojo1.ActionForward;
import com.example.demo.pojo1.NoticeController;
import com.example.demo.pojo1.NoticeLogic;
/*
* upmu[] : 내려갈 때 -> ActionServlet -> BoardController로 연결될 때
* -> 개선점(1-3버전) -> spring -> XXXHandlerMapping -> BoardController 클래스에서부터 메소드를 쪼갤 수는 없나?(현재는 if문으로 되어 있어 가독성, 재사용성 떨어짐)
* pageMove[] : 올라올 때
*/
//@Controller : 스프링에서는 클래스 사이의 결합도를 낮추기 위해 상속(결합도가 높아지니까)을 포기하였다
//@RequestMapping(/notice/*) : 2번째 URL 매핑 방법
public class BoardController implements Controller {
Logger logger = Logger.getLogger(BoardController.class);
BoardLogic nLogic = new BoardLogic();//이른
//@GetMapping("noticeList.gd") : 객체 주입 받으려면 ApplicationContext로부터 빈 관리를 받을 때만 사용 가능함 - req, res 주입해주기 때문에
//public String noticeList(HttpServletRequest req, HttpServletResponse res) {}
@Override
public String execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String upmu[] = (String[])req.getAttribute("upmu");
String path = null;
/*
//전체조회일 때 : select / n건 - List<Map | VO> - list.jsp
//상세보기와 응답페이지 이름이 달라서 메소드를 분리한다
// 1) 배포위치가 WEB-INF 일 때 : /WEB-INF/jsp/(workname)/메소드이름 or upmu[1].jsp
// 2) 배포위치가 webapp 일 때
if(true) {
return path = "1";
}
//상세조회일 때 : select / 1건 - Map or VO - read.jsp
else if("boardDetail".equals("boardDetail")) {
path = "redirect:2";//redirect
}
//공통분모 : 반환값이 int이다. commit과 rollback 대상이다
//입력 | 수정 | 삭제인 경우 모두 1이라면 어느 페이지로 이동할까? - 목록(select: /board/boardList.gd2 - 이 뒤에서 forward 처리해야 함)을 보여주세요
//등록일 때 : post 방식 - insert : 1(수정성공) or 0(수정 안 됨)
else if("boardInsert".equals("boardInsert")) {
path = "redirect:3";
}
//수정일 때 : get, put 방식(모든 방식을 doService로 묶었기 때문에 큰 의미는 없다) - Restful 상징성을 표현함 - update : 1(수정성공) or 0(수정 안 됨)
else if(true) {
path = "redirect:4";
}
//삭제일 때 : delete 방식 - 스프링 수업 땐 분리해서 할 것임 - delete : 1(수정성공) or 0(수정 안 됨)
else if(true) {
path = "redirect:/board/boardDetail";
}
*/
return path;
//return "redirect:/notice/noticeList.jsp"; //webapp
//return "forward:/notice/noticeList.jsp"; //webapp - 요청이 유지되는 것으로 판단해서 서블릿이 쥐고 있는 값을 jsp에서도 사용할 수 있다.
//return "/notice/noticeList"; //WEB-INF/jsp/notice 아래
}
}
package com.example.demo.pojo2;
import org.apache.log4j.Logger;
//나는 Controller라는 인터페이스를 implements 하지 않아서 어떤 제약조건(추상 메소드(execute(req, res) - Tomcat이 제공해 줌.
// 단, 서블릿이어야 한다 - 그런데 서블릿 아니어도 스프링은 지원해줌)도 해당 안 됨
//나는 순수한 자바 클래스이다(순수성 - 원자성 : 다른 이종간의 결합에서 사용 가능한 상태라는 것이 장점).
public class BoardLogic {
Logger logger = Logger.getLogger(BoardLogic.class);
}
forward : select인 경우에 해당 - 어떤 서버가 쥐고 있는 정보를 유지해야한다
redirect
upmu[] : 내려갈 때
pageMove : 올라올 때
전체 문자열 -> "redirect:/workname-컨트롤클래스이름결정/메소드이름(if문 조건문)
pageMove[]
pageMove[0] = "redirect" or "forward"
pageMove[1] = "/notice/noticeList.jsp" or "/board/boardList.jsp"
루트 태그 떼어내고 확장자를 떼어내면 notice/noticeList가 남음 -> split, splice를 통해 둘도 구분할 것