기초단계/JSP&Servlet

2023.04.08 JSP

춘핑이 2023. 4. 10. 12:09

nuwlec JSP

52. JDBC를 이용해 글 목록 구현하기

String dbID = "chun";
String dbPwd = "1234";
String dbURL = "jdbc:mysql://localhost:3306/dbpractice"; // 접속url
String sql = "SELECT * FROM NOTICE";

Class.forName("com.mysql.cj.jdbc.Driver");
Connection con= DriverManager.getConnection(dbURL, dbID, dbPwd);
Statement st = con.createStatement(); 
ResultSet rs = st.executeQuery(sql);

db에 필요한 소스들을 넣어주자.
웹프로젝트는 톰캣으로 배포하는 것이다.
라이브러리를 같이 배포해야하니 WEB-INF lib에 같이 담아서 포함시켜줘야한다.

<% 
String dbID = "chun";
String dbPwd = "1234";
String dbURL = "jdbc:mysql://localhost:3306/dbpractice"; // 접속url
String sql = "SELECT * FROM NOTICE";

Class.forName("com.mysql.cj.jdbc.Driver");
Connection con= DriverManager.getConnection(dbURL, dbID, dbPwd);
Statement st = con.createStatement(); 
ResultSet rs = st.executeQuery(sql);
%>

<%while(rs.next()){ %>
<tr>
    <td><%=rs.getInt("ID") %></td>
    <td class="title indent text-align-left"><a href="detail.html"><%=rs.getString("TITLE") %></a></td>
    <td><%=rs.getString("WRITER_ID") %></td>
    <td>
        <%=rs.getDate("REGDATE") %>    
    </td>
    <td><%=rs.getInt("HIT") %></td>
</tr>
<%
}
%>

53. 자세한 페이지 구현하기

db에있는 데이터를 가져다가 초기버전의 목록페이지를 구현했다.
이번에 구현할 내용은 자세한 페이지이다.
자세한 페이지라는 것은 특정글에 들어가면 내용이 나오도록하는 것이다.
detail.jsp의 이름으로 만들것이다.

자세한페이지를 만들때 주의할점이 있다.
목록을 클릭해서 넘어갓을때 detail.jsp?쿼리값 id에따라서 달라진다.
id값을 전달하지 않으면 보여줄게 없어서 오류가 날것이다.
이값을 제목 하이퍼링크에 추가하여 심을 수 있다.
<a href="detail.jsp?id=<%=rs.getInt("ID")%>">
=값뒤에는 ID값이 오도록 해주기

list.jsp를 만들엇던 것처럼 db를 연결해주면된다.
해당 id의 글만 가져와야하니 where만 추가해주면된다.

<% 
String dbID = "chun";
String dbPwd = "1234";
String dbURL = "jdbc:mysql://localhost:3306/dbpractice"; // 접속url
String sql = "SELECT * FROM NOTICE WHERE ID = ?";

Class.forName("com.mysql.cj.jdbc.Driver");
Connection con= DriverManager.getConnection(dbURL, dbID, dbPwd);
PreparedStatement pstmt = con.prepareStatement(sql);
int id = Integer.parseInt(request.getParameter("id"));
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();

rs.next();
%>

<tbody>
    <tr>
        <th>제목</th>
            <td class="text-align-left text-indent text-strong text-orange" colspan="3"><%= rs.getString("TITLE") %></td>
        </tr>
    <tr>
        <th>작성일</th>
        <td class="text-align-left text-indent" colspan="3"><%= rs.getDate("REGDATE") %></td>
    </tr>
    <tr>
        <th>작성자</th>
        <td><%= rs.getString("WRITER_ID") %></td>
        <th>조회수</th>
        <td><%= rs.getInt("HIT") %></td>
    </tr>
    <tr>
        <th>첨부파일</th>
        <td colspan="3"><%= rs.getString("FILES") %></td>
    </tr>
    <tr class="content">
        <td colspan="4"><%= rs.getString("CONTENT") %></td>
    </tr>
</tbody>

54. 자세한 페이지 MVC model1으로 변경하기

지금 작성한것은 일명 스파게티 코드이다.
한번꼬이면 전부다 하나씩 찾아봐야하기때문에 유지보수가 어렵다.
코드에서 꽃아놓을 수있는 최소한의 변수만 남겨놓으면된다.
화면 밑단에서 사용할 변수인 model을 만들고 넣어주면된다.

<%
int id = Integer.parseInt(request.getParameter("id"));

String url = "jdbc:oracle:thin:@localhost:1521/orcl";
String sql = "SELECT * FROM NOTICE WHERE ID=?";
Class.forName("oracle.jdbc.OracleDriver");
Connection con = DriverManager.getConnection( url, "java", "oracle");
PreparedStatement st = con.prepareStatement(sql);
st.setInt(1, id);
ResultSet rs = st.executeQuery();

rs.next();
String title = rs.getString("TITLE");
Date regdate = rs.getDate("REGDATE") ;
String writerId= rs.getString("WRITER_ID");
int hit = rs.getInt("HIT") ;
String files = rs.getString("FILES");
String content = rs.getString("CONTENT") ;

rs.close();
st.close();
con.close();
%>

<table class="table">
    <tbody>
        <tr>
            <th>제목</th>
            <td class="text-align-left text-indent text-strong text-orange" colspan="3"><%= title %></td>
        </tr>
        <tr>
            <th>작성일</th>
            <td class="text-align-left text-indent" colspan="3"><%= regdate %></td>
        </tr>
        <tr>
            <th>작성자</th>
            <td><%= writerId %></td>
            <th>조회수</th>
            <td><%= hit %></td>
        </tr>
        <tr>
            <th>첨부파일</th>
            <td colspan="3"><%= files %></td>
        </tr>
        <tr class="content">
            <td colspan="4"><%= content %></td>
        </tr>
    </tbody>
</table>

55. 자세한페이지 MVC model2로 변경하기

모델 1을 모델2로 변경할 것이다. 모델1에서는 지역적으로 나누기만 했엇다.
아예물리적으로 나누는 것이다.

입력처리(Control)(서블릿) - model - 문서츌력(View)(JSP)
서블릿과 서블릿을 만드는 것이라고 볼 수 있다. 이 모델을 이어줄 수 잇는 방법이 필요하다.

나눠서 만들면 개별적으로 유지관리가 가능, 협업도 가능 재사용시 각각 재사용가능하다.
실행면에서도 jsp는 컴파일 하는과정을 해야하는데 굉장히 커다란 덩치인데 줄일 수 있다. 실행성능에 영향을 준다.

이 model이 가능한것은 pageContext / request / session /application 중에서 페이지 영역인 pageContext를 제외한 뒤 3가지이다.
제일적합한것은 범위가 가장좁은 request이다.
request는 입력도구인데 입력도구에다가 저장소로도 사용할 수 있다.

두가지가 필요하다. 1.흐름전이 2.데이터 모델로 보내기
특화된 함수를 사용해야하니 get요청이니 doget에 담자.
예외처리를 해주자.

이것들을 view단에 넘겨야한다.

서블릿에서 서블릿으로 전이
1.redirect
현재작업과 관련없는 새로운요청
2.forward
현재작업한 내용을 이어갈수있도록 공유하는 것이다.

현재작업햇던 내용들을 담고있다면 jsp 서블릿에 이어지면 jsp에서 사용하는 request와 response가 이 서블릿의 것이다.
jsp에서 꺼내서 사용할수도있고 이용하면 포워드 새로요청하면 리디렉트하면된다.
둘사이에 포워드하는 것은 request를 사용한다.

jsp파일이아니라 서블릿으로 열어야한다.

해당페이지로 포워드해주자.

@WebServlet("/notice/detail")
public class NoticeDetailController extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String dbID = "chun";
        String dbPwd = "1234";
        String dbURL = "jdbc:mysql://localhost:3306/dbpractice"; // 접속url
        String sql = "SELECT * FROM NOTICE WHERE ID = ?";

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection con= DriverManager.getConnection(dbURL, dbID, dbPwd);
            PreparedStatement pstmt = con.prepareStatement(sql);
            int id = Integer.parseInt(request.getParameter("id"));
            pstmt.setInt(1, id);
            ResultSet rs = pstmt.executeQuery();

            rs.next();
            String title = rs.getString("TITLE");
            Date regdate = rs.getDate("REGDATE") ;
            String writerId= rs.getString("WRITER_ID");
            int hit = rs.getInt("HIT") ;
            String files = rs.getString("FILES");
            String content = rs.getString("CONTENT") ;

            request.setAttribute("title", title);
            request.setAttribute("regdate", regdate);
            request.setAttribute("writerId", writerId);
            request.setAttribute("hit", hit);
            request.setAttribute("files", files);
            request.setAttribute("content", content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        request.getRequestDispatcher("/notice/detail.jsp").forward(request, response);
    }
}

56. Model 데이터를 구조화하기

컨트롤러 - 뷰로 나누엇다. 사실 프로그램만들때는 나눠도되고아니어도된다.
프로그램의 크기, 상황, 숙련도에따라 나눠도 안나눠도 된다. 방법의 차이이다.
사용자는 관심도 없도 없고 알수도 없다.
만드는 방법은 만드는사람은 어떻게 만드는게 유지보수가 편한지를 정하려는 것이다.
일반적으로 기업형은 나누는게 일반적이다.

데이터를 구조화하지않아서 view단을 정리하지 못햇다
request.setAttribute("title", title);
request.setAttribute("writerId", writerId);
request.setAttribute("regdate", regdate);
request.setAttribute("hit", hit);
request.setAttribute("files", files);
request.setAttribute("content", content);
출력을 위해 이런 데이터를 사용했다. 이런 데이터를 한단어로 말하면 개념적으로 '공지사항'이라 표현할수잇다.
개념적으로 말할수잇는데이터집합을 NTT, 객체라고한다.
이걸 묶어서 또 구조적인 데이터라고 한다. 데이터를 속성으로 대체하고 묶어서 표현는게 좋다.
간단하고 개념적으로 묶어서 볼 수 있게 된다.

public class Notice {
    private int id;
    private String title;
    private Date regdate;
    private String writerId;
    private int hit;
    private String files;
    private String content;
    //getter들 setter들
}

Notice를 객체화해서 notice.id이런식으로 꺼내쓰는 것이 편하다.
getId 게터 메소드를 ${notice.id} Property el로 작성하면 꺼내쓸수잇다.

담기 쉽게 생성자로 담아주었다.
Notice notice = new Notice(id, title, regdate, writerId, hit, files, content);
request.setAttribute("n", notice);

57. 목록 페이지도 MVC model2로 수정하기

여러개의 목록을 보여줘야하니 List컬렉션에 객체를 담아야한다.
리스트에 객체를 담고 포워드하면된다.

List<Notice> list = new ArrayList<>();
list.add(notice);
request.setAttribute("list", list);
RequestDispatcher dispatcher = request.getRequestDispatcher("/notice/list.jsp");
dispatcher.forward(request, response);

그래서 담은 리스트를 담고 포워드해서 보내면된다.

58. View 페이지 은닉하기

컨트롤단이 뷰단에서 만들어졌는데 습관적으로 뷰단에서 실행하려고 한다.
사용자가 요청할수 있는데에 있어서 요청이 가능해진다.
그러나 컨트롤러를 기준으로 선택하기 위해 jsp가 직접 나오게 하면안된다.
아예 뷰단을 사용차가 요청할수없는데에 넣어야한다.
WEB-INF에 넣으면된다.

컨트롤러를 사용하기 위해 동적으로 만든페이지이기 때문이다.
request.getRequestDispatcher("/WEB-INF/view/notice/detail.jsp");
request.getRequestDispatcher("/WEB-INF/view/notice/list.jsp");
이런식으로 컨트롤러 링크 바꿔줘야한다.

59. View(list.jsp)에서 반복문 제거하기

뷰단에서 흐름제어를 위한 자바코드블록을 제거해아한다.
개념에 맞게 만들었다면 코드블록이 없어져야한다.
그래서 '태그'를 사용한다.

보통 태그란 감싸는 것이 무엇인지 what에 대한 내용인데
<forEach> 이녀석은 행위에 해당되는 내용이다. 액션을 가진 태그로 사용한다.

이것을 사용하려면 라이브러리를 다운받아야한다.
태그사용하는 라이브러리로 jstl-1.2 라이브러리 설정해야한다.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>일 넣어서 사용해야한다.
{c:컨트롤스페이스}하면 사용할수 있는 목록들이 나온다.
이중 <c:forEach var="n" items="${list}">
items: 저장소에 담긴거를 뽑아서 담을수잇다. 그리고 반복될때마다 꺼낸것을 담을것 var="n"하면 pageContext에 담아준다.
저장소에서 값을꺼내서 담는것이다 EL로 담은것을 forEach에서 꺼내주는 것이다.
태그를 사용해서 담는것이 자바코드로 담는것보다는 이해하기 쉽다.

이것이 뷰단을 최종적으로 마무리하는 방식이다.

<c:forEach items="${notice}" var="notice">
    <tr>
        <td>${notice.id}</td>
        <td class="title indent text-align-left"><a href="detail?id=${notice.id}">${notice.title }</a></td>
        <td>${notice.writerId }</td>
        <td>
            ${notice.regdate}    
        </td>
        <td>${notice.hit }</td>
    </tr>
</c:forEach>

60. Tag 라이브러리와 JSTL

JSTL이란 JSP Standard Tag Library이다.
제어를 담당하는것이다.
5가지범주가 있다.
Core 태그라이브러리가 필요로하는 제어의 행위 (c)
Formation 숫자 날짜 등 포맷하는거 (fmt)
Functions 데이터를 EL을 통해서 꺼내쓰는데 저장소의 데이터를 쓸때 문자열을쪼개쓰거나 대소문자로 변경할때 이것을 조작하는것이다.
SQL, XML은 있지만 사용하지 않는게 좋다. 코드의 구성이 깨지게 되서 사용안한다.

1.Core
c:접두사를 사용하지 않으면 재스퍼가 Html인지 아닌지 이해하지 못한다.
그래서<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
로 접두사를 제공하고 jstl을 사용한다는 uri를 제공한다.

태그를 만들고 사용하기 위해서는 .tld라는 확장자를 가진 태그 Descripotor 태그설명서를 만들어줘야한다.
uri는 만든 태그를 식별하기 위해서 만드는 것이다.
도메인명이들어가서 다른사람이 만든거와 충돌하지 않도록 하기 전세계에서 유일한 이름을 만드는 것이다.
이 태그 식별코드를 매번 넣기에는 너무 길기때문에 c라는 접두사(prefix)에 넣는다는 것을 의미한다.
재스퍼가 taglib임을 알 수 있게 해야한다.
각각의 이벤트에서 발생하는 행위를 하고싶을때 하는것이다.

과거에는 따로 만들어서 사용했지만 이제 이미 만들어져있어서 만들필요가 없다.
만약 커스텀으로 태그를 만들어도 가능하긴하다.

62. forEach의 속성 사용하기

begin="" end="" 반복되는 것에서 범위를 한정할 수 있다.
인덱스로따졋을때 0부터시작해서 보여준다.
반복해서 사용하는 인덱스와 비교해서 사용한다.

인덱스를 보고싶다면 varStatus를 사용하면된다.
varStatus="st"
${st.current} 현재 반복되는 아이템
${st.index} 현재 반복되는 반복 인덱스(0부터 시작)
${st.count} 현재 반복 횟수
${st.first} 현재 아이템이 첫번째 아이템인지를 알려준다(true 또는 false)
${st.last} 현재 아이템이 마지막 아이템인지를 알려준다(true 또는 false)
${st.begin} begin 속성에 설정한 값
${st.end} end 속성에 설정한 값
${st.step} 반복되는 인덱스의 증가치

${st.index + 1} / ${n.id}을하면 1/1 1/2 이런식으로 출력이되게된다.

상태값을 활용할 필요가 있다면 적절하게 사용하면된다.

63. JSTL : forEach문으로 Pager 번호 만들기

page로 만들기
<ul class="-list- center">
<li><a class="-text- orange bold" href="?p=1&t=&q=" >1</a></li> 이부분이 페이지다.
레코드가 많아지면 1,2,3,4,5이런식으로 보여지게하고 페이지에 맞게 들어가지게 하고싶다.

하나씩 복사가아닌 반복문으로 하는게맞다.
클릭했을때 겟요청도 해줘야한다.

<ul class="-list- center">
    <c:forEach var="i" begin="0" end="4">
        <li><a class="-text- orange bold" href="?p=${1+i}&t=&q=" >${i + 1}</a></li>
    </c:forEach>
</ul>

그런데 만약 15까지 있는데 7을 선택햇다면? 6~10이런식으로 나와야한다.
번호를 요청하는 페이지에 따라서 만들어 줘야한다.

번호를 만드는 매개값
?p=3 ->15 ?p=17 ->1620 일련번호에 맞게 페이지번호가 나올수있도록 해야한다.

이거 구현하는 방법은 일정패턴을 가지고있다.

같은 페이지에서 어떤 번호가 전달되냐에 따라 달라진다.
시작번호가 몇번인지 startNum=???을 사용해서 할수잇고 이 시작번호는 페이지파라미터로 만들수잇다.

사람마다 페이징 찍는 방식이 다르다.
현재 페이지 번호에서 startNum 거리만큼 빼주면된다.

현재페이지 % 보여줄 번호수를 나누면 첫페이지와의 간격이 나오는데 원하는 값보다 1이크다.
현재페이지 - (현재페이지 - 1) % 5


page=3 -> 3 -(3-1)%5 -> 1 이런식으로하면 자신의 스타트 번호를 찾을수있다.
따라서 startNum = page - (page -1)%5; 그래서 이 page부터 만들어야한다.

현재 페이지는 파라미터로 부터 받아오면된다. 처음 페이지를 들어오면 null이니 기본값으로 1을 넣어주자.
JSTL의 set을 사용하면 임시 변수를 페이지 영역에 만들 수있다.

<c:set var="page" value="${ (param.p == null) ? 1 : param.p}"/>
<c:set var="startNum" value="${page - (page -1) % 5}" />
<ul class="-list- center">
    <c:forEach var="i" begin="0" end="4">
        <li><a class="-text- orange bold" href="?p=${startNum+i}&t=&q=" >${startNum+i}</a></li>
    </c:forEach>
</ul>

64. if 문으로 Pager 이전/다음 번호 만들기

다음페이지를 누르면 페이저가 5추가 된 페이지가 나오도록해주자. 없다면 경고문을 나오게 하면된다.
jstl의 if태그를 사용해서 조건을 처리할 수 있다.
else if가 없기때문에 배타적으로 만들어서 if문에 넣어야한다.

db에연결해서 조건을 설정해주어야하지만 일단 가상으로 lastNum 23이라고 가정해보자.

시작번호 + 보여줄 페이지수가 마지막 번호보다 작으면 다음으로가고
아니라면 다음 페이지로 못가게 해줘야한다.

<div>
    <c:if test="${startNum+5 < lastNum}">
        <a class="btn btn-next">다음</a>
    </c:if>
    <c:if test="${startNum+5 >= lastNum}">
        <span class="btn btn-next" onclick="alert('다음 페이지가 없습니다.');">다음</span>
    </c:if>
</div>

이전페이지는 1페이지를 기준으로 하면된다.

<div>
    <c:if test="${startNum> 1}">
        <a class="btn btn-prev" href="?p=${startNum-1}&t=&q=">이전</a>
    </c:if>
    <c:if test="${startNum<= 1}">
        <span class="btn btn-prev" onclick="alert('이전 페이지가 없습니다.');">이전</span>
    </c:if>
</div>

65. forTokens로 첨부파일 목록 출력하기

JSTL forTokens을 사용하는 방법을 알아보자.

첨부파일을 서버에 올릴건데 이 파일목록을 컬럼하나에 ','로 구분해서 올릴 것이다.
test.zip, aa.gif, bb.png 이런식으로 넣고 하나하나 하이퍼링크로 감싸야한다.

${notice.files} 단일화된게 아니라 ,로 구성된 목록이다.
<c:forTokens var="fileName" items="${notice.files}" delims=","/>
delims구분자를 단위로 끊어주고 목록을 리턴하게 된다.
자바 String의 split메소드라고 볼 수 있다.
,단위로 items를 잘라주면 토큰이 만들어지고 이것을 반복하는 것이다.
변수이름을 만들어서 담아준다.

각각 하이퍼링크가 되서 나오는데 문자사이에 구분자를 넣고싶다.
넣고싶은 것을 그냥 forTokens사이에 넣어주면된다.
마지막문자에는 안나오게 하고싶으니 last가 아닐경우만 나오게 if문을 작성해주면된다.

<tr>
    <th>첨부파일</th>
    <td colspan="3" style="text-align: left">
        <c:forTokens var="fileName" items="${notice.files}" delims="," varStatus="st">
            <a href="${fileName}">${fileName}</a>
            <c:if test="${!st.last}">
            /
            </c:if>
        </c:forTokens>
    </td>
</tr>

forTokens은 forEach와 비슷하게 반복을 하는 것인데 토큰으로 잘라서 반복한다고 보면될듯 하다.
파일 업로드를 하게되면 실제로 링크를 누르면 파일이 다운로드 되도록 변경할 수 있게 되는 것이다.

66. JSTL : format 태그로 날짜 형식 변경하기

regdate를 날짜 형태로 나타내고 싶다. db에서는 연월일 시간 초 인데 연월일만 나온다.
날짜는 db에 DATETIME 큰 정수로 저장된다.
jsp에게 내가 원하는 방식으로 나타내고 싶을때 포매핑을 해야한다.

구글에 jstl format검색해보고 정리된 종류를 보자
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=imf4&logNo=220654812087

fmt 라이브러리를 사용해야하기 때문에 페이지 지시자에 추가해줘야한다.
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>

formatDate 날짜 정보를 담고 있는 객체를 포맷팅하여 출력할 때 사용한다.
<fmt:formatDate pattern="yyyy-MM-dd hh:mm:ss" value="${n.regdate}"/>
pattern에 원하는 모양을 넣으면된다. 월MM 분 mm이니 구분잘하자.

67. 숫자 출력 형식 지정하기 JSTL:formatNumber

출력하는 숫자를 변경해보도록하자.
JSTL:formatNumber
조회수로 해보자 단위를 나누지 않았으니 큰 숫자로 해보자 일반적으로 세자리마다 ,를 넣는다.

<fmt:formatNumber value= "${notice.hit}"/>
지정하지않아도 알아서 0,000형태로 보여준다.
원하는데로 변경하고자하면 정리된 글들를 참조하자.
https://treasurebear.tistory.com/43 참조

type = "{number | currency | percent}"
pattern = "패턴" ###.### 111.111
currencySymbol="화폐 단위"
groupingUsed="{true | false}"
var="변수 이름"
scope="{page | request | session | application}"

68. EL에서 함수 이용하기 JSTL:functions

문서내에서 포맷을 바꾸는 것도 좋지만 문서내 문자열을 조작하고 싶을때 사용한다.

자주사용하지는 않지만 가끔 사용한다.
만약 첨부파일 이름을 전부 대문자로 바꾸고싶다면?
1.db서 이름다바꾸기 2.출력만바꾸기 두가지 방식이 있다.

태그라이브러리를 사용하기 위해 추가해주어야한다.
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>

특이하게 EL표기안쪽에서 함수호출하는 패키지 형태로 만든다.
${fn:toUpperCase(fileName)}

조건처리도 할 수 있다.

<c:forTokens var="fileName" items="${notice.files}" delims="," varStatus="st">
<c:if test="${fn:endsWith(fileName, '.zip') }">
    <c:set var="style" value="font-weight:nomal; color:#979797;"/>
</c:if>
<a href= "${fileName}" style="${style}">${fn:toUpperCase(fileName)}</a>
<c:if test="${!st.last}">

좀더 복잡한 함수가 필요하다면 어쩔수없이 함수를 만들어서 클래스파일을 만들고 추가할 수 있다.
jstl function custom exapmle등 검색해서 해보자.
그런데 복잡한 함수를 넣을때 데이터변경을 뷰에서 하는게 맞는지를 고민할 필요가 있다.

69.기업형으로 레이어를 나누는 이유와 설명

지금까지 배운 것만으로도 웹개발을 할 수는 있다.
기업형일 때는 수많은 사람들이 협업을해서 만든다.
코드를 세분화하게 분담하는데 이것을 나누는 방법을 알아보자.

컨트롤러가 사용차 입출력을하면서 업무처리도 했다.
혼자하면 이렇게 해도되는데 사람이 많아지면 나눠서 해야한다.

경험이 적은사람 입출력만들기 경험적은사람 업무서비스(트랜잭션) 만들기등을 하게 된다.

업무서비스는 바꿀일이 별로 없기때문에 분리되어서 화면을 변경할때 부담이 낮아진다.
예를들어 .계좌이체()
때로는 데이터서비스 db를 활용할때 식별문을 이용할때 다른쪽에서 바뀐다면?
업무서비스를 처리할때 데이터가 바뀌는 것을 신경쓰기 싫다.
데이터만 소스를 숨기는 데이터만 처리하는 것을 담당하는 것을 만들 수도있다.
그래서 1.입출력하는 컨트롤러 2.업무서비스 3.실제 데이터를 조작하는 부분
데이터를 하는사람은 sql문을 작성할수있는사람이 객체로 담아서 넘겨준다.
업무는 model을 만들어서 컨트롤러에 담고 뷰에서 보여지게 한다.

먼저 컨트롤러-업무서비스만만들고 dao를 분리하며 어떤문제가있는지 이걸 어떻게 처리하는지 스프링을에서 배우고자 한다.

70. 서비스 함수 찾아내기

서비스로 어떤 함수를 구현할지를 찾아내보자. 별도의 클래스로 만들어 낼것이다.
데이터베이스 이용하는 코드를 어떻게 시스템 단위로써 나누어서 만들것이다.

관리자는 회원이 하는일을 할수 있어야하기 때문에 상속관계로 둔다.
먼저 구체적인 서비스들은 공지목록조회 공지상세조회 만들고 관리자 페이지에서는 다른 것들을 추가해야한다.
사용자할수있는것은 다음과 같다.
1.페이지 요청 list 2.목록 페이지 요청 3.검색요청
-> 서비스해줘야할 내용은 기본적으로 NOTICE이다.

1.getNoticeList() 최근글로부터 10개주기
2.getNoticeList(int page)
3.getNoticeList(String filed, String query, int page)
검색내애서 페이지를 또 볼수있으니 페이지도준다. 현재페이지의 계수를 빼먹지 말자.
4.getNoticeCount() 전체 목록 아무런인자x 12에 넣을 총 글 갯수
5.getNoticeCount(String filed, String query) 검색시 3에 넣을 내용
5개의 함수가 서비스함수로 필요하다.

detail에서 페이지 요청은 사전조건 id를 넘겨받는다.
getNoitce(id)
밑부분에 다음글 이전글 getNextNoitce(id) getPrevNoitce(id)
페이지를 만들때 데이터가 필요하면 각 작업을 하는 역할자가 따로있는것이다.
서비스 레이어가 따로있게 되는 것이다.
데이터가 필요하거나 수정하고자하면 그 쪽으로 가서 하게되는것이다.

71. 서비스 클래스 구현하기

서비스레이어 만들것이다. 서비스레이어에서 필요한 클래스가 notic클래스이고 필요한 함수들을 뽑아보았다.

NoticeService클래스를 만들고 함수를 만들어주자.
이름이 같다는 것은 기능이 같다는 것이다.
개발자가 구현했다가는 코드 중복이 많아진다.
수정할때 어려움이 발생할 수 있다. 이름이 같으면? 세개중에 하나만 하고 나머지는 재호출기능으로 하자.
인자 제일 많은거 구현하고 메소드를 호출하면된다.

public class NoticeService {

    public List<Notice> getNoticeList() {
        return getNoticeList("title", "", 1);
    }

    public List<Notice> getNoticeList(int page) {
        return getNoticeList("title", "", page);
    }

    public List<Notice> getNoticeList(String field, String query, int page) {
        return null;
    }

    public int getNoticeCount() {
        return 0;
    }

    public int getNoticeCount(String field, String query) {
        return 0;
    }

    public Notice getNotice(int id) {
        return null;
    }

    public Notice getNextNotice(int id) {
        return null;
    }

    public Notice getPrevNotice(int id) {
        return null;
    }
}

72. getNoticeList 메소드의 SQL 쿼리 작성하기

getNoticeList 등록일을 기준으로 순서정렬 + 페이징이니 게시물 몇개씩만 보이게 해줘야한다.

SELECT * FROM NOTICE ORDER BY REGDATE DESC
우리가 등록하지 안했지만 자동으로 부여되는 로우넘버를 출력해서 사용하자 https://youtu.be/ekEBoJn2gF8하자
SELECT ROWNUM, NOTICE.* FROM NOTICE ORDER BY REGDATE DESC
이러면 로우넘버를 출력하고 정렬을하기때문에 섞인다.
정렬된 결과물을 만든 후 이것을 기준으로 로우넘버 출력
SELECT ROWNUM, N.*
FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) N;
결과물은 더이상 노티스가 아니니 별칭을 만들어주고 별칭의 로우넘버를 출력해야한다.

SELECT * FROM (
SELECT ROWNUM NUM, N.*
FROM (SELECT * FROM NOTICE ORDER BY REGDATE DESC) N
)
WHERE NUM BETWEEN 1 AND 5;

원래 SQL문에 WHERE절을 그냥 붙여버리면 모든게 실행된 후 WHERE절을 실행하는 거라 6~10을 볼수없다.
그래서 위를 또 하나의 서브쿼리로 만들어서 NUM이라는 별칭을 준 새로운 로우넘버를 만들어내고 그 NUM으로 WHERE절을 실행하자.
또다른 로우넘버뽑는 함수

SELECT * FROM(
SELECT ROW_NUMBER() OVER (ORDER BY REGDATE DESC) NUM,
NOTICE.* FROM NOTICE
)

둘중 아무거나 사용해도 된다.

mysql의 경우 limit를 사용하면된다.
SELECT * FROM NOTICE ORDER BY REGDATE DESC LIMIT 시작, 보여줄개수;