배움 __IL/TIL 1기

TIL : 53번째- 230217 [2-2-금]

Mo_bi!e 2023. 2. 17. 18:37

I. 지난시간 복습

어제 드디어 코드를 나누었다 컨트롤러 서비스다오를 분리했다

실행결과는 동일! 나누는 필요성!!을 느끼는 것이 중요하다

 

(1) controller 에서 Service를 분리하고, 추가로 dao 까지 분리한 연유?

1) -퀴즈

Q. 컨트롤러에서 빼버린 것은 무엇을 뺀 것?

어떤 역할? : 서비스 다시말하면 업무자가 하는 역할인 업무 빠진 것이다.

컨트롤러 만드는 사람은 더이상 업무를 몰라도 된다

 

Q. 그러면 컨트롤러는 업무를 몰라도 되는데, 이 경우 컨트롤러가 하는 역할은?

이전에 뷰랑 컨트롤러(서비스, 다오) 모두 다 한 곳이 있자나!

그런데 뷰를 처음에 자르고, 추가로 컨트롤러에서 서비스를 또 잘라냈자나

사용자가 아니라 개발자인 나를 위해서자나!! 왜 했어?

 

- 동일한 질문 : 컨트롤러에서 서비스를 분리했잖아 어떤 역할 하는거지?

업무! 여기서 업무란? 사용자에게 사용자가 요구하는 기능업무라고한다.

 

2)

그런데 컨트롤러는 직무유기??! 자기가 할 일을 넘겨버렸다

아니다 컨트롤러는 업무는 서비스를 담당하고 데이터를 주는데, 컨트롤러는 요청(입력) 및 출력한다

서비스에게 요청을 전달하고, 출력을 담당한다.

 

Q. 가장중요한 것은 업무이다. 서비스에서 업무를 잘하고있는데 또 Dao 를 뻇다 왜 뻇는가?

A. 업무자자바 외에는 몰라도 되게끔 하고싶어서!!!

 

 

데이터가 오라클,빅,마이 뭘 하던 내가 상관없이 자바만 다룰줄 알면 됨

데이터의 변화, 데이터의 소스가 바뀌더라도 업무자는 영향을 받지않는다 나는 자바 소스만 다룬다

 

Dao 분리함으로서 업무자는 업무에만 집중이 가능함

 

 

(2) 4가지 부분으로 분리 그리고 이로인해 기인하는 문제의 해결방법

1)

그런데 문제가 있음!! 우리가 분리 후, Dao 소스 고쳐야 할 일이 발생함!!!

데이터 구현을 JDBC에서 하던것을 mybatis로 했다고 치자!

수정하려면 소스코드 열어서 수정해야해? 안해도 된다.

 

왜? 결과적으로는 SOLID원칙에 따른거지만, 대체 왜?

open - close 원칙 때문에 함부로 수정하면 안되기 때문에다. 확장을 해서 바꿔치기 해야한다.

 

바꿔치기 할 경우 도미노 현상 발생함! 생성했던 코드가 사용했던 쪽에 있고 등의 쓰나미를 차단해야해!

두가지 문제를 해결하기 위해서 두가지 어떤방법을 했어?

(발생하는 문제 : 참조 자료형문제, 객체 생성 문제)

 

2)

1. 자료형은 참조 형식을 인터페이스를 이용하고,

2. 객체생성은 클래스 생성을 외부 설정(class.forname) 등으로 라이브러리 도움을 받음

 

결국 우리가 만드는 코드, web application 구조는 총 4개로 대별된다

(4가지 : view, controller, service, dao)

이 모든 결합력인터페이스로 하고, 객체 생성라이브러리로 도움 받는다

 

 

(3) 라이브러리 사용의 필요성과 용도 파악의 필요성

지난 시간에 메뉴 말고 멤버로 달라고 해도 같을까??

메뉴 관리, 공지관리 시스템 등 뭘 만들던 구조는 동일해

처음에는 어렵지만 몇년지나면 지겹다고 느껴진다! 복붙에 불과하기 때문이다 지금 어려운건 숙달이 없어서 그러하다

 

구조에 나타나는 반복작업을 라이브러리로 쓰고, 인터페이스 할 때 해결이 안되는게 있다

 

<DefaultMenuService.java>

public DefaultMenuService() {
    // TODO Auto-generated constructor stub

    menudao = new JdbcMenuDao();

}

객체 생성하는 이 부분이 문제이다

그리고 스프링을 쓴다.

 

<JdbcMenuDao.java>

try {
    // java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
    Class.forName("oracle.jdbc.driver.OracleDriver");
    String url = "jdbc:oracle:thin:@oracle.newlecture.com:1521/xepdb1";
    Connection con = DriverManager.getConnection(url, "NEWLEC", "rland");

    Statement st = con.createStatement();
    ResultSet rs = st.executeQuery(sql);

여기도 라이브러리 쓸 것이다.

 

 

4)

라이브러리 목적은 시간절약을 위한 것이야! 깊이 공부한다거나 방법을 몰라서 어렵게 쓰는일이 발생해 버린다

내가 10줄 작성을 1줄로 줄일 때 라이브러리쓰는데, 줄이는건 좋아

그런데 하다보니까 어렵게 하면 13줄 써버리는 상황이 발생해 이런경우는 안쓰는게 나아

쓰는방법만 잘 이해하면 라이브러리 쓰면서 13줄 쓰는 일을 안하게끔 할것이고 이렇게 설명할 것이다

 

예컨데 DB View 는 직접노출할수없는것이나, join 하는것을 업무적으로 할 때

읽기전용 테이블이거나 가리고 싶을 때 목적이다.

그런데 수정을 원하면 다른걸 써야해 그런데 이거를 해결하려고 행위를 해버리면 안된다

이처럼 라이브러리는 라이브러리마다 용도가 정해져있으니까 이것을 이해하자!!

 

 

5)

컨트롤러입력출력을 담당하는데 라이브러리로 바꾸기 위해서 어떻게 하는지 이해할 시간이 필요해

컨트롤러 바꾸기 위해서 프론트 컨트롤러는 알아보자

 

 

II. 웹개발

1. 알아두어야 할 에러들

- 사용자가 요청한 것이 존재하지 않는경우 404가 나온다

- 만약 사용자가 POST요청을 했는데, get만 수행가능할 때, POST요청에 응답할 수있는 메소드 처리함수가 없을 경우의 오류 상태코드는? 405 이다

- 만약 인증이 안되거나 권한이 없는것을 요청할 때 에는 403이다.

- 그렇다면 400 에러는 인자가 일치하지 않는 에러이다.

예컨데 add함수가 있을 때 값2개를 전달하게끔 함수가 정해져있는데, 다른 객체나 정수형이 전달되어있을 때 매개변수가 없는등일 때 이다.

 

2. 프론트 컨트롤러

(1) 프론트 컨트롤러

1)

컨트롤러는 기본적으로 코드구현이 위에서는 사용자의 요청을 입력받는 부분이 있다

사용자 요청이 입력받고, 입력받아서 그 입력 값에 따른 요청을 해

사용자의 요청은 문서출력이다 이에 따라서 사용자에게 response(응답)한다

이런 방식의 요청들은 연달아 있다. 그리고 각 요청들은 상술한바와 같이 동일하다 (문서출력 : 디스패처)

 

2)

그런데 불만이 생긴다.

현재 어플리케이션의 구조와 불편한 사항들의 탐구인데 익숙하다는 것이다

 

여기서 문제는 어플리케이션이 실행환경, 톰캣, 서블릿에 특화된 방식으로 입력을  처리한다

내가만든 프로그램이 입력이라는것이 특정환경에 국한되어있다.

환경이 바뀌면 문제가 생긴다.

플랫폼에 너무 특화되면 문제가 발생

 

우선

HttpServlet 을 상속받는 등으로 코드에 달고다니는건 문제가 있다

어찌보면 라이브러리인데, 사용하는게 달라지면 내 어플작동이 안될수있어

 

옛날에는 되는것에 만족했는데, 이제 더 편리해지는것을 요구하는데

입력코드 반복&불편 / 출력위한 디스패처 포워드가 매번 똑같은 코드로 반복됨!!!

 

3)

한칸 뒤로 빠져서 앞에다가 FrontController를 두는거야! 얘로 아까 c1,c2,c3에 모두 두고 얘가 모든 요청을 받고 응답을 하게하자!

출력도 입력도 경량화 하는것이다 

 

뒤에 있는 컨트롤러로 매개변수로 인자를 받게끔한다 마치 함수 받는 것 처럼

그리고 뷰가 필요하면 리턴하는 방식으로!

결국 프론트컨트톨러는 다음 컨트롤러에 전달하고, 응답을 넘겨 받아

뷰 페이지에 문서를 디스패치 해주는거야! viewresponse 해주는거야

결국 이렇게 완성 되는거야

 

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

여기서 req입력받는 도구인데 얘를통해서 받는거야

 

getParameter 를 쓴적있다 (HelloServlet.java)

옵션값 전달하는 방법으로! 

 

그러면 입력 방식 5가지를 알아보자

 

 

3. 사용자로 부터 입력을 받는 방법 (from req)

(1) 개요

5가지가 있어!

 

1) QueryString을 이용한 입력

2) Form을 이용한 입력
3) Cookie 입력
4) Header 입력
5) Hidden 필드 입력

 

이렇게 5가지가 있다.



(2)  쿼리 스트링을 이용한 입력

1) 쿼리 스트링을 이용한 입력

여기서 쿼리"질의"인데 잘 와닫지 않는다

"웹에서 질의" 란 문서를 달라 이다.

 

문서를 달라고할 때 추가적으로 제공하는 옵션값이다.

요청 할 때 사용자가 전달하는 값이라고 하면 post 값등이 차이가 없다

쿼리스트링 자체는 웹이 기본적으로 요청시(문저 달라고할때) 전달할 수 있는 옵션값이야

그래서 꼭은 안주어도 돼

 

2)

문서는 동적인 문서를 구현하고 있어 

만약 문서를 달라고하면서 색 darkmode 주세요! 라고 하며 옵션이 되고

데이터를 올해꺼로 한정해주세요! 하는것도 옵션이 된다

날짜를 달라거나 색을달라거나 문서를 달라면서 말이많다

커피숍에 가서 커피 온도,샷, 얼음 등의 옵션이 가능하다

 

여기서 중요한게 있어 가게 마다 옵션이 달라  서버마다 옵션을 제공해야 가능한거야 

만약 내 단골에서는 커피 옵션있지만 다른 카페는 없다

결국 옵션이라는 것은 서버가 제공하고 결정하는거고 서버가 제공한 옵션에 한해서 가능한거야

 

3)

@WebServlet("/input")
public class inputController extends HttpServlet{
	
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

		// 1. 쿼리 스트링을 이용한 입력

//		int page = req.getParameter("p");
		//이 경우 문자열이기 때문에 자료형을 바꾸어 주어야해
		
		//입력 값을 전달하는 사용자는 키워드로 p를 사용하도록 결정한경우
		//다음처럼 값을 전달 받을 수있다. 
		String page = req.getParameter("p");
		
		// 그럼 요청하는 방법은?
		// /input?p=1
		
		PrintWriter out = resp.getWriter();
		System.out.println(page);

그래도 출력하면 null이다

여기서 q, s 를 출력하자

String page = req.getParameter("p");
String query = req.getParameter("q");
String size = req.getParameter("s");
// 그럼 요청하는 방법은?
// /input?p=1

// 그럼 다음 요청처럼 값을 3개가 전달되는데 그것들을 출력하는 코드는?
//		http://localhost:8080/input?p=3&q=hello&s=15
PrintWriter out = resp.getWriter();
out.println(page);
out.println(query);
out.println(size);

[url : http://localhost:8080/input?p=3&q=hello&s=15]

쿼리스트링을 하면 다음과 같이 출력이 될 수있다.

 

-

 

4)

그런데 size입력받는 키워드 왜 s인가 size가 아니라? : 길이 제한 때문이다

웹의 주소는 원래 1000자 내외의 길이제한을 가지고 있어서 값을 줄일 수는 없기 때문에 키워드라도 줄여보자는 취지

과거 플랫폼과의 호환성 떄문임.

여담으로 최근에는 제한이 없다는 말도 있음

 

(3) form을 이용한 입력

1)

form은 입력이 정적으로 되어있어서 사용자는 고정된 것으로 클릭하는것임! 사용자에 좌우 되지않음

쿼리스트링에는 기존에는 사용자가 그냥 입력을 받았다

이제는 사용자가 직접 입력받을 수 있다.

 

2)

    String page = req.getParameter("p");
    String query = req.getParameter("q");
    String size = req.getParameter("s");
    // 그럼 요청하는 방법은?
    // /input?p=1

    // 그럼 다음 요청처럼 값을 3개가 전달되는데 그것들을 출력하는 코드는?
//		http://localhost:8080/input?p=3&q=hello&s=15
    PrintWriter out = resp.getWriter();
//		out.println(page);
//		out.println(query);
//		out.println(size);

    out.write(String.format("page:%s, query:%s, size:%s <br>", page,query,size));

    out.write ("<form action=\"/input\">");

    out.write (" <label>page:</label>");
    out.write (" <input type=\"text\" name=\"p\"›");
    out.write ("<div> </div>");

    out.write (" <label>검색어:</label>");
    out.write (" <input type=\"text\" name=\"q\"›");
    out.write ("<div> </div>");

    out.write (" <label>size:</label>");
    out.write (" <input type=\"text\" name=\"size\"›");

이렇게 하면 만들 수있다

 

여기다가 제출을 만들어보자

 

 

3)

    String page = req.getParameter("p");
    String query = req.getParameter("q");
    String size = req.getParameter("s");
    // 그럼 요청하는 방법은?
    // /input?p=1

    // 그럼 다음 요청처럼 값을 3개가 전달되는데 그것들을 출력하는 코드는?
//		http://localhost:8080/input?p=3&q=hello&s=15
    PrintWriter out = resp.getWriter();
//		out.println(page);
//		out.println(query);
//		out.println(size);

    out.write(String.format("page:%s, query:%s, size:%s <br>", page,query,size));

    out.write ("<form action=\"/input\">");

    out.write (" <label>page:</label>");
    out.write (" <input type=\"text\" name=\"p\"›");
    out.write ("<div> </div>");

    out.write (" <label>검색어:</label>");
    out.write (" <input type=\"text\" name=\"q\"›");
    out.write ("<div> </div>");

    out.write (" <label>size:</label>");
    out.write (" <input type=\"text\" name=\"s\"›");

    out.write ("<div> </div>");
    out.write("<input type=\"submit\" value=\"제출\">");


    out.write ("</form>");

3개의 input에다가 넣고 제출을 누르면, url에 값입력되고, 출력도 동시에 가능하다

 

4)

out.write("<a href=\" /input?p=1&q=hello&s=15\">1</a><br>");
out.write("<a href=\" /input?p=2&q=hello&s=15\">2</a><br>");
out.write("<a href=\" /input?p=3&q=hello&s=15\">3</a><br>");

out.write(String.format("page:%s, query:%s, size:%s <br>", page,query,size));

out.write ("<form action=\"/input\">");

out.write (" <label>page:</label>");
out.write (" <input type=\"text\" name=\"p\"›");
out.write ("<div> </div>");

out.write (" <label>검색어:</label>");
out.write (" <input type=\"text\" name=\"q\"›");
out.write ("<div> </div>");

out.write (" <label>size:</label>");
out.write (" <input type=\"text\" name=\"s\"›");

out.write ("<div> </div>");
out.write("<input type=\"submit\" value=\"제출\">");


out.write ("</form>");

링크도 3개 추가하였다.

 

5)

<a href=" /input?p=3&q=hello&s=15">3</a>
<br>

<div>
    page:${page} , query:${query} , size :${size} 
</div>


<form action="/input">

EL 을 쓰면 이렇게 하면은 출력이 된다

 

<정리하면>

input 에다가 name 이 p,q,s로 된 상태를 제출버튼을 누른다

제출을 하면 <inputController.java> 의 getParameter전달이 된다

그리고 setAttri 에서 포워딩 한다. 그리고 input.jsp 에서 받는다

 

6)

순서가 있다 page 부터 하나씩 찾는다!

위 4가지 순서대로 찾게된다

 

cnt 가 4개의 저장소를 쓰는데 4개의 저장소라는게 머야? 

만약 나는 session에서 찾고싶은데 page가 우선순위높다는 이유로 기회를 못찾게된다

그러면 어떡해야해? session 을 다른애들 안거치고 쓰는 방법이 있다

Scope 를 쓰면 바로 콕 짚어서 가능하다

 

    
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>
<c:set var="size" value="1000" />
<!--페이지 컨텍스트 설정 함   -->
    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

	<a href=" /input?p=1&q=hello&s=15">1</a>
	<br>
	<a href=" /input?p=2&q=hello&s=15">2</a>
	<br>
	<a href=" /input?p=3&q=hello&s=15">3</a>
	<br>
	
	<div>
		page:${page} , query:${query} , size :${size} 
	</div>

상단에 페이지 컨텍스트를 설정 (var = "size")한다

그렇다면 size는 값 추출 우선순위에 따라 pageContext1000이 있기 때문에 

1000이 출력된다.

 

그런데 이경우 

requestScope.size 를 추가 해주자 

page:${page} , query:${query} , size :${requestScope.size}



size 는 15로 출력하게된다.

7)

<div>
    page:${page} , query:${query} , size :${requestScope.size} 
    param size : ${param.s} <br>
    header userAgent : ${header["User-Agent"]} <br>
    header 유입경로 : ${header.referer}
</div>

getParameter 로 파라미터라는 저장소에 담겨있다.

param 을 쓰면 기존에 request 저장소에 있는것이 아니라 parameter저장소에서 꺼내서 올 수있다.

한편header 에서 user-Agent도 받아 오는 등 헤더 정보를 받아 올 수있다

 

헤드에서 도움이 되는 정보를 얻을 수 있다

예컨데 블로그에서 유입경로를 알고 싶을때 헤더에 유입경로가 있다.

이런것을 이용해서 알아차릴수 있다.

 

8)

사용자가 value 에다가 true 뿐만 아니라 DB에  넣을 식별자를 넣어주면 됨

 

parametervalues 로 하면 배열로 함

 

 

(3) 쿠키 입력
(4) Header 입력
(5) hidden 필드 입력

이렇게 나머지 3가지가 있다.


1. 보충

 

 

 

 

2. 회고 

1) request 를 하면서 서버에 어떻게 메시지를 전달하는지 방법을 알게되니 신기하다

'배움 __IL > TIL 1기' 카테고리의 다른 글

TIL : 55번째- 230221 [2-3-화]  (0) 2023.02.21
TIL : 54번째- 230220 [2-3-월]  (0) 2023.02.20
TIL : 52번째- 230216 [2-2-목]  (0) 2023.02.16
TIL : 51번째- 230215 [2-2-수]  (0) 2023.02.15
TIL : 50번째- 230214 [2-2-화]  (0) 2023.02.14