Skip to the content.

화면을 개발하기 전에는 반드시 화면의 전체 레이아웃이나 디자인이 반영된 상태에서 개발하는 것을 추천한다.

일부 개발자들은 화면을 나중에 처리한다고 생각하고 진행하는 경우가 있는데 결과적으로는 두 배의 시간을 들이는 결과가 될 가능성이 높기 때문에 권장하지는 않는다.

목록 페이지 작업과 includes

스프링 MVC의 JSP를 처리하는 설정은 Java기준으로

ServletConfig.class

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    InternalResourceViewResolver bean = new InternalResourceViewResolver();
    bean.setViewClass(JstlView.class);
    bean.setPrefix("/WEB-INF/views/");
    bean.setSuffix(".jsp");
    registry.viewResolver(bean);
}

 스프링 MVC의 설정에서 화면 설정은 ViewResolver라는 객체를 통해서 이루어지는데,

위의 설정을 보면 /WEB-INF/views 폴더를 이용하는 것을 볼 수 있다.

/WEB-INF 경로는 브라우저에서 직접 접근할 수 없는 경로이므로 반드시 Controller를 이용하는 방식을 기본적으로 사용한다.

게시물 리스트의 URL은 /board/list 이므로 최종적인 /WEB-INF/views/board/list.jsp가 된다.해당 경로에 list.jsp 파일을 추가한다.

list.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>List Page</h1>
</body>
</html>

list.jsp는 우선 정상적으로 URL 처리가 되는지를 확인해야 하므로 Tomcat으로 실행해서 확인한다.

Intellij같은 경우에는 내장 Tomcat을 사용하려면 eclipse와 다른 방식으로 등록해준다.

사진처럼 내장 Tomcat을 등록해서 사용하면 된다.

Intellij의 경우에는 경로가 다르기때문에

http://localhost:8080/jex02_war_exploded/board/list

‘jex02_war_exploded’ 부분 경우엔 Intellij가 자동으로 부여해주기 때문에 따로 신경쓰지 않아도 된다.

주의!

경로에 대한 설정은 css,js,이미지 파일들의 경로에 치명적인 영향을 주기 때문에 처음부터 절대 경로 혹은 상태 경로에 대해서 명확히 결정한 후에 프로젝트를 진행하도록 한다.일반적인 경우라면 절대 경로를 이용하는 것이 좋다.

SB Admin2 페이지 적용하기

정상적으로 /board/list 페이지가 동작한다면 SB Admin2의 pages 폴더에 있는 tables.html 의 내용을 list.jsp의 내용으로 그대로 복사해서 수정하고 실행한다.

[startbootstrap-sb-admin-2-gh-pages.zip

1.48MB](./file/startbootstrap-sb-admin-2-gh-pages.zip)

수정할 때는 list.jsp의 상단에 JSP의 Page 지시자는 지우지 않아야 한다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

브라우저에서는 css 등이 완전히 깨진 상태이므로 텍스트만 출력되는 것을 볼 수 있다.

css와 js 파일들의 경로를 수정하는 작업은 브라우저의 개발자 도구를 통해서 확인하며 진행한다.

개발자 도구를 통해서 현재 브라우저의 network 부분을 확인하고 페이지를 새로고침하면 잘못된 url의 정보를 확인 할 수 있다.

SB Admin2의 css의 경로는

<!-- Bootstrap Core CSS -->
    <link href="../vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

인데 현재 프로젝트에서는 제대로 서비스될 수 없다.

ServletConfig.class

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**").
    addResourceLocations("/resources/");
}

해당 클래스의 부분을 보면 정적인(static) 리소스는 resources 라는 경로를 지정하고 있다.

SB Admin2의 압출을 풀어둔 모든 폴더를 프로젝트 내 webapp 밑의 resources 폴더로 복사해 넣는다.

src/main/webapp/resources

해당 경로로 복사해준다.

파일들을 resources 경로로 넣어도 아직은 페이지에서 경로를 수정하지 않았기 때문에 문제가 생기문 것은 동일하다.

list.jsp 파일에서 css나 js 파일의 경로를 /resources로 시작하도록 수정한다.

(Intellij 같은 경우에는 경로가 조금 다를 수 있으니 해당 경로로 확인하면 된다.)

<!-- Bootstrap Core CSS -->
    <link href="../vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    .
    .
    .
    \/
<!-- Bootstrap Core CSS -->
<link href="../resources/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">

css,js 파일들의 링크는 모든 페이지에서 사용될 것이므로 화면에서 디자인이 깨지지 않는 것을 확인한 후 다음 내용을 진행한다.

includes 적용

jsp를 작성할 때마다 많은 양의 html 코드를 이용하는 것을 피하기 위해 jsp의 includes 지시자를 활용해서 페이지 제작시에 필요한 내용만을 작성할 수 있게 사전에 작업을 해야 한다.

현재 프로젝트 views 폴더에 includes 폴더를 작성하고 header.jsp와 footer.jsp를 선언한다.

header.jsp 적용

header.jsp는 페이지에서 핵심적인 부분이 아닌 영역 중에서 위쪽의 html 내용을 처리하기 위해서 작성한다.

브라우저에서 검사 기능을 활용하면 특정한 <div>가 어떤 부분을 의미하는지 확인 할 수 있다.

SB Admin2는 <div>들 중에서 id 속성값이 page-wrapper 부터가 핵심적인 페이지의 내용이므로

list.jsp 파일의 처음 부분에서 <div id='page=wrapper'> 라인까지 잘라서 header.jsp의 내용으로 처리한다.

footer.jsp 적용

가 끝나는 태그부터 마지막까지는 footer.jsp의 내용으로 작성한다. 최종적으로 수정하였다면 브라우저를 통해 정상적으로 동작하는지 테스트해보는것이 좋다. ### jQuery 라이브러리 변경 jsp 페이지를 작성하다 보면 js로 브라우저 내에서의 조작이 필요한 경우가 많다. 예제는 jQuery를 이용할 것인데 문제는 위의 방식대로 처리했을 때 jQuery 라이브러리가 footer.jsp 내에 포함되어 있다는 점이다.성능을 조금 손해 보더라도 jQuery를 header.jsp에 선언해 두면 작성하는 jsp에서 자유롭게 사용할 수 있으므로 수정해야 한다. footer.jsp의 상단에 있는 jquery.min.js 파일의 ... ``` *반응형 웹 처리* SB Admin2는 반응형으로 설계되어 있어서 브라우저의 크기에 맞게 모바일용으로 자동으로 변경되지만 jQuery의 최신 버전을 사용한 상태에서는 모바일 크기에서 새로고침시 메뉴가 펼쳐지는 문제가 발생한다. 이 문제를 해결하기 위해서 includes 폴더 내 footer.jsp에 아래와 같은 코드를 기존 코드 대신에 추가한다. ```javascript ``` ### 목록 화면 처리 list.jsp 페이지의 일부를 include 하는 방식으로 처리했음에도 많은 html의 내용들이 존재하므로 아래와 같이 최소한의 태그들만 적용시킨다. list.jsp에는 JSTL의 출력과 포멧을 적용할 수 있는 태그 라이브러리를 추가한다. ```javascript <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <!DOCTYPE html> <%@include file="../includes/header.jsp"%>

Tables

Board List Page
#번호 제목 작성자 작성일 수정일
<%@include file="../includes/footer.jsp"%> ``` 수정된 list.jsp를 저장하고 브라우저를 통해서 원하는 형태로 출력되는지 확인한다. #### Model에 담긴 데이터 출력 /board/list를 실행했을 때 이미 BoardController는 Model을 이용해서 게시물의 목록을 list라는 이름으로 담아서 전달했으므로 list.jsp에서는 이를 출력한다.출력은 JSTL을 이용해서 처리한다. list.jsp내에 태그와 각을 아래와 같이 입력한다. ```java ... ... ``` 브라우저를 통해서 결과를 확인하면 데이터베이스에 있는 전체 목록이 출력된다. ![](/images/89/img_3.png) ### 등록 입력 페이지와 등록 처리 게시물의 등록 작업은 POST 방식으로 처리하지만,화면에서 입력을 받아야 하므로 GET 방식으로 입력 페이지를 볼 수 있도록 BoardController에 메서드를 추가한다. BoardController.class ```java @GetMapping("/register") public void register() { } ``` register()는 입력 페이지를 보여주는 역할만을 하기 때문에 별도의 처리가 필요하지 않는다. views 폴더에는 includes를 적용한 입력 페이지를 작성한다. register.jsp ```java <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <!DOCTYPE html> <%@include file="../includes/header.jsp" %>

Board Register

Board Register
<%@include file="../includes/footer.jsp" %> ``` register.jsp 페이지에서는 태그를 이용해서 필요한 데이터를 전송한다. 이나
<input class="form-control" name="writer" value='' readonly="readonly"/>
<%@include file="../includes/footer.jsp" %> ``` 브라우저에서 /board/get?bno=숫자 와 같이 게시물의 번호를 반드시 파라미터로 전달해야 한다. 파라미터로 전달하는 bno 값이 존재한다면 아래와같이 출력될 것이다. ![](/images/89/img_7.png) 화면 하단의 버튼은 /board/list와 /board/modify?bno=xx 와 같이 이동하는 링크를 추가한다. ```java <button data-oper="modify" class="btn btn-default" onclick="location.href='../board/modify?bno='"> Modify </button> ``` #### 목록 페이지와 뒤로 가기 문제 목록 페이지에서 각 게시물 제목에 태그를 적용해서 조회 페이지로 이동하게 처리한다. 최근에 웹페이지들은 사용자들의 트래픽을 고려해 목록 페이지에서 새창을 띄워서 조회 페이지로 이동하는 방식을 선호하지만 전통적인 방식에서는 현재창 내에서 이동하는 방식을 사용한다. 의외로 이러한 처리가 제대로 되지 않는 경우들을 많이 보게 된다. *목록에서 조회 페이지로의 이동* list.jsp 페이지는 다음과 같이 수정한다. ```java </tr> </c:forEach> ``` 브라우저를 통해서 화면을 확인해 보면 각 게시물의 제목에 링크가 걸리는 것을 확인할 수 있고, 제목을 클릭하면 정상적으로 조회 페이지로 이동하는 것을 볼 수 있다. 조회 페이지로 이동은 JavaScript를 이용해서 처리할 수 있고 위와 같이 태그를 이용해서도 처리가 가능하다. 만일 조회 페이지를 이동하는 방식이 아니라 새창을 통해서 보고 싶다면 태그의 속성으로 target='\_blank'를 지정하면 된다.태그와 태그에는 target 속성을 지정할 수 있는데 '\_blank'는 새로운 창에서 처리된다. ```java
#번호 제목 작성자 작성일 수정일
</td> </td> ``` 아래의 사진은 목록에서 target='\_blank'를 지정했을 때 새로운 창이 옆에 뜨는 모습이다. ![](/images/89/img_8.png) *뒤로가기의 문제* 동일한 페이지 내에서 목록 페이지와 조회 페이지의 이동은 정상적으로 처리된 것 같아 보이지만 한 가지 문제가 남아 있다.등록->목록->조회 까지 순조롭지만 브라우저의 뒤로가기를 선택하는 순간 다시 게시물의 등록 결과를 확인하는 방식으로 동작한다는 것이다. 이러한 문제가 생기는 원인은 브라우저에서 뒤로가기나 앞으로 가기를 하면 서버를 다시 호출하는 것이 아니라 과거에 자신이 가진 데이터를 활용하기 때문이다. 브라우저에서 조회 페이지와 목록 페이지를 여러 번 앞으로 혹은 뒤로 이동해도 서버에서는 처음에 호출을 제외하고 별다른 변화가 없는 것을 확인할 수 있다. 이 문제를 해결하려면 window의 history 객체를 이용해서 현재 페이지는 모달창을 뜨울 필요가 없다고 표시를 해 두는 방식을 이용해야 한다. window의 history 객체는 스택 구조로 동작한다. ![](/images/89/img_9.png) 그림1은 사용자가 브라우저를 열고 /board/list를 최초로 호출한 것이다. history에 쌓으면서 현재 페이지는 모달창을 보여줄 필요가 없다는 표시(그림의 작은상자)를 해둔다. 그림2는 사용자가 /board/register를 호출한 경우이다.스택의 상단에 /board/register가 쌓이게 된다. 만일 이 상태에서 뒤로가기를 실행하면 아래쪽의 /board/list가 보여지는데 이때 심어둔 표시를 이용해서 모달창을 띄울 필요가 없다는 것을 확인할 수 있다. 그림3은 사용자가 등록을 완료하고 /board/list가 호출되는 상황이다. 브라우저에서 앞으로가기나 뒤로가기로 이동한 것이 아니므로 스택의 상단에 추가된다.등록 직후에 /board/list로 이동하는 경우에는 모달창이 동작한다. 그림3에서 모달창을 보여준 후에는 스택의 상단에 모달창이 필요하지 않다는 표시를 해주어야 한다. 이후에 /board/register를 호출하면 그림 4와 같이 된다. window.history 객체를 조작하는 것은 이론으로는 복잡해 보이지만 사실 코드는 약간의 변화가 있을 뿐이다. list.jsp ```java ``` 기존과 달라진 점은 맨 마지막에 추가된 history.replaceState() 부분과 checkModal() 에서 history.state를 체크하는 부분이다. javaScript의 처리는 우선 checkModal()을 실행하는데 만일 등록된 후에 이동한 것이라면 위의 그림3처럼 되기때문에 모달창이 보이게 된다. 모달창이 보이는 여부와 관계없이 javaScript의 모든 처리가 끝나게 되면 history에 쌓이는 상태는 모달창을 보여줄 필요가 없는 상태가 된다.최종적인 결과는 아래과 같이 처리된다. ![](/images/89/img_10.png) ### 게시물의 수정/삭제 처리 게시물의 수정 작업은 일반적으로 1.조회 페이지에서 직접 처리하는 방식 2.별도의 수정/삭제 페이지를 만들어서 해당 페이지에서 수정과 삭제를 처리하는 방식을 많이 사용한다. 최근에는 게시물의 조회 페이지에서 댓글 등에 대한 처리가 많아지면서 수정과 삭제는 별개의 페이지에서 하는것이 일반적이다. 조회 페이지에서는 GET 방식으로 처리되는 URL을 통해서 수정/삭제 버튼이 존재하는 화면을 볼 수 있게 제작해야 한다. 수정 혹은 삭제 작업은 POST 방식으로 처리되고 결과는 다시 목록 화면에서 확인할 수 있어야 한다. #### 수정/삭제 페이지로 이동 BoardController에서 수정/삭제가 가능한 화면으로 이동하는 것은 조회 페이지와 같다.따라서 get()메서드를 조금 수정해서 화면을 구성한다. BoardController.class ```java @GetMapping({"/get", "/modify"}) public void get(@RequestParam("bno") Long bno, Model model) { log.info("/get or modify"); model.addAttribute("board", service.get(bno)); } ``` @GetMapping이나 @PostMapping 등에는 URL을 배열로 처리할 수 있으므로 위와 같이 하나의 메서드로 여러 URL을 처리할 수 있다. 브라우저에서는 /board/modify?bno=숫자 와 같은 방식으로 처리하므로 views폴더 내 modify.jsp를 작성한다. modify.jsp는 get.jsp와 같지만 수정이 가능한 제목이나 내용 등이 readonly 속성이 없도록 작성한다. POST 방식으로 처리하는 부분을 위해서는 태그로 내용들을 감싸게 한다. modify.jsp ```java <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <!DOCTYPE html> <%@include file="../includes/header.jsp" %>

Board Modify

Board Modify Page
<%@include file="../includes/footer.jsp" %> ```
태그는 action 속성을 /board/modify로 지정했지만 삭제를 하면 /board/remove 와 같이 action 속성의 내용을 수정해서 사용하게 된다.게시물의 제목,내용은 수정이 가능한 형태로 사용해서 사용자가 편집할 수 있게 된다. 등록일과 수정일은 나중에 BoardVO로 수집되어야 하므로 날짜 포멧을 yyyy/MM/dd의 포멧으로 해야 한다. 브라우저에서는 http://localhost:8080/board/mpdify?bno='존재하는 번호'와 같이 게시물 번호를 이용해서 수정 페이지가 정상적으로 출력되는지 확인한다. ![](/images/89/img_11.png) javascript에서는 버튼에 따라서 다른 동작을 할 수 있도록 한다. modify.jsp ```java ``` javascript에서는