제목: 상세 페이지 구성 (Client)

최초작성일: 2014-12-30

최종수정일: 2014-12-30

연말연시는 언제나 아쉬움이 많이 남는 것 같습니다. 그래도 이번 연말은 좋네요. 회사란 곳에 입사를 하고서부터는 정말 바쁘게 시간이 흘러가서 따로 한 해를 정리할 시간이 없었는데 좋은건지 안좋은건지 올해는 지나온 시간들 중 가장 한가한 연말이 아닌가 합니다. 지나온 바쁜 시간들에 물론 감사하는 마음도 있지만 올해처럼 이런 여유를 가질 수 있는 연말도 퍽이나 좋네요. 모두에게 다사다난했을 2014년도 잘 정리하시기 바랄게요. 지난 시간에는 목록 페이지를 개발해 보았습니다. 비록 아직 서버가 없는 관계로 인터페이스는 없지만 단말에서 데이터를 생성하여 UI에 Binding 하고, Binding 하는 부분의 모듈을 Templates을 사용하여 개선하는 부분도 진행해 보았습니다. 이번에는 상세 페이지를 만들어 보겠습니다. 상세 페이지는 목록에서 볼 수 없는 해당 게시물에 대한 전체 정보를 조회하는 기능을 제공하는 페이지입니다. 우리가 만들 게시판은 정보가 많지 않기 때문에 목록 페이지에 보이는 데이터가 거의 다지만, 실제 프로젝트에서는 정말 많은 데이터를 모바일 화면에서도 조회하게 됩니다. 자 그럼 상세 페이지 개발을 시작해보겠습니다.
1. 기본 HTML 구조 작성
상세 페이지 역시 [헤더] - [본문] - [푸터]로 영역을 분할하여 구성하겠습니다. 아래 소스 코드를 참고하셔서 ./bbs_detail.html 파일로 저장합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html lang="ko">
    <head>
        <meta http-equiv="content-type" content="text/html;charset=UTF-8">
        <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
        <title>Mobile BBS</title>
        <link rel="stylesheet" type="text/css" href="./css/common.css" />
    </head>
    <body>
        <header>
            <h1>Mobile BBS</h1>
        </header>
         
        <div class="wrap">
            <div class="content_line">
                <span id="subject"></span>
            </div>
            <div class="content_line">
                <span id="additionalInfo"></span>
            </div>
            <div class="content_line">
                <span id="content"></span>
            </div>
        </div>
         
        <footer>
            <button id="btnWrite">글쓰기</button>
        </footer>
    </body>
    <script type="text/javascript" charset="utf-8" src="./scripts/jquery-1.10.2.min.js"></script>
    <script type="text/javascript" charset="utf-8" src="./scripts/common.js"></script>
</html>
HTML 페이지는 구조만 선언하므로 큰 내용이 없습니다. 상세 페이지에서 표시할 정보에 대한 구조를 선언했습니다. 먼저 subject로 표시한 부분은 제목을 표시할 영역이고, additionalInfo 영역에는 작성자 + 작성일자 + 조회 수를 한 행으로 정리하여 표시하도록 하겠습니다. content 영역에는 글의 본문 내용을 표시해보겠습니다.
그럼 이제 상세 페이지의 모양을 위해 간단하게 StyleSheet를 추가해보겠습니다.
2. StyleSheet 작성
StyleSheet는 모든 페이지에서 common.css를 사용하도록 하겠습니다. common.css에 상세 페이지와 관련된 부분을 추가해보겠습니다.
1
.wrap .content_line{padding:7px 5px 7px 5px;border-bottom:1px solid #ccc;}
3. JavaScript 작성
이제 목록 페이지에서 받아온 데이터를 상세 페이지에 Bind하는 부분을 구현해야 합니다. 여기서 우리가 잊고 온 부분이 있네요. 목록 페이지에서 값을 보내는 부분을 구현해보지 않았습니다. 목록 페이지에서 상세를 조회할 게시물의 행을 터치하면 선택한 게시물의 정보를 어딘가에 담아서 상세 페이지로 전달해야 합니다.

여기서 정보를 전달하는 방법에 크게 두 가지 방법이 있습니다. 먼저 일반적인 방법으로 게시물의 식별자(Key) 정보만 전달하고 상세 페이지에서 식별자로 해당 게시물 정보를 조회해오는 방법이 있습니다. 하지만 우리가 개발하는 게시판처럼 목록에서 보여지는 내용과 상세에서 보여지는 컬럼 수가 많이 차이나지 않는다면 목록을 조회할 때 모든 정보를 조회한 후 상세 페이지에서는 별도의 서버 인터페이스를 하지 않는 방법이 있습니다. 우리는 후자의 방법을 선택하여 목록 페이지에서 모든 데이터를 받아서 상세 페이지로 전달하기로 하겠습니다.
3-1. 데이터 전달 방법
데이터를 전달하는 방법에 대한 고민도 필요합니다. 만약 어플리케이션이 하나의 HTML로 이루어진 구조라면 전역 변수를 사용할 수도 있습니다. 하지만 업무 별로 페이지를 분할하여 개발하는 경우에는 전역 변수를 공유할 수 없으므로, 어떤 방식으로던 데이터의 전달이 필요합니다. 전달하는 방법은 크게 두 가지로 나눌 수 있을 것 같습니다.
먼저 쿼리스트링으로 전달하는 방법입니다. 이동할 페이지 정보 뒤에 쿼리스트링으로 값을 연결하여 보내는 경우 받는 페이지에서 location.href 정보에서 쿼리스트링 정보를 잘라서 사용할 수 있습니다. 쿼리스트링으로 데이터를 전달할 경우 아래와 같은 코드로 손쉽게 잘라올 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var REQUEST = {};
 
REQUEST.getParameter = function(parm) {
    var parmVal = null;
    var pageUrl = location.href;
 
    var parms = (pageUrl.slice(pageUrl.indexOf("?") + 1, pageUrl.length)).split("&");
 
    for (var inx=0; inx<parms.length; inx++) {
        if (parms[inx].split("=")[0].toUpperCase() == parm.toUpperCase()) {
            parmVal = decodeURIComponent(parms[inx].split("=")[1]);
            break;
        }
    }
    return parmVal;
};
두 번째 방법은 Local Storage를 사용하는 방법입니다. Local Storage는 key-value pair로 브라우저의 메모리에 저장할 수 있으며, 저장 형태는 string만 가능합니다. 따라서 사용하기 위해서는 JSON Object를 Serialize하여 저장해야 합니다. 우리는 이 두 번째 방법인 Local Storage를 사용하여 개발해보도록 하겠습니다.
두 번째 방법을 선택한 이유는 Android의 4.0.x 대 일부 단말의 OS에서 쿼리스트링까지 페이지 파일명으로 인식하는 버그가 있고, 글 본문 내용 같은 큰 데이터 전달을 위함입니다. 쿼리스트링은 1024 바이트까지만 전달 가능합니다.
3-2. 페이지 이동 function 개발
그럼 이전 시간에 개발했던 ./scripts/common.js 파일을 열어 Local Storage 저장 및 페이지 이동에 대한 기능을 개발하겠습니다. common.js 파일에 아래 내용을 추가합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
COMM.setItem = function(key, value) {
    var saveValue = (typeof value == 'string') ? new String(value) : JSON.stringify(value);
    localStorage.setItem(key, saveValue);
};
 
COMM.getItem = function(key) {
    var value = localStorage.getItem(key);
    try {
        value = JSON.parse(value);
    } catch (e) {
     
    }
    return value;
};
 
COMM.removeItem = function(key) {
    localStorage.removeItem(key);
};
 
COMM.movePage = function(page, pageData) {
    if (pageData) COMM.setItem('__pageData', pageData);
    location.href = page;
};
 
COMM.getPageData = function() {
    var data = COMM.getItem('__pageData');
    COMM.removeItem('__pageData');
    return data;
};
먼저 위의 3개의 function은 브라우저의 LocalStorage를 사용하기 위한 공통 함수입니다. Parameter가 Object로 들어올 경우 자동으로 Serialize하여 저장합니다. 그 다음 COMM.movePage function은 페이지를 이동하기 전에 전달할 데이터를 저장하고 이동하는 기능을 합니다. COMM.getPageData function은 이동한 페이지에서 전달받은 데이터를 꺼내어 오는 기능을 합니다. 그럼 이제 작성한 공통 함수를 가지고 실제 데이터를 전달하여 페이지 이동을 구현해보도록 하겠습니다.
3-3. 목록 페이지 수정

이제 목록 페이지를 수정하여 게시물을 터치하면 상세 페이지로 이동하도록 수정하겠습니다. 목록 데이터를 Bind하는 function인 BBS.display function을 아래와 같이 수정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BBS.display = function(rows) {
    var listArea = $('#bbsListArea');
    var html = null;
 
    COMM.loadTemplate('bbs_list_line', function() {
        for (var inx=0; inx<rows.length; inx++) {
            html = COMM.getHTMLObj('bbs_list_line', rows[inx]);
            html.find('.bbs_list_txt').attr('data', rows[inx]);
            html.find('.bbs_list_txt').on('click', function(event) {
                COMM.movePage('./bbs_detail.html', $(this).attr('data'));
            });
            listArea.append(html);
        }
    });
}
여기까지 개발을 마쳤다면, 잠시 한 번 쉬어가도록 하겠습니다. 청산유수로 개발을 끝내면 좋겠지만 세상에는 그렇게 만만한 일들은 잘 일어나지 않더군요. 그래서 여기까지 작성한 코드가 정상적으로 동작하는지 확인해보고 지나가도록 하겠습니다. 확인할 사항은 아래와 같이 크게 없습니다.
1. ./bbs_detail.html 페이지로 이동하는지(물론 헤더와 푸터만 있는 빈 페이지입니다.)
2. Chrome 브라우저의 localStorage에 __pageData 값이 잘 저장되어 있는지
위 두 가지 사항을 확인해 보겠습니다. Google Chrome에서 마우스 우클릭 후 나타나는 바로가기 메뉴에서 [요소 검사]를 선택하거나 [F12] Key를 누르면 구글 개발자 도구가 열립니다. 먼저 페이지 이동에 대한 부분은 상세 보기 페이지로 이동하고 Console에 JavaScript 오류가 표시되는 내용이 없다면 정상으로 구현된 것입니다. localStorage 값 확인은 Google 개발자 도구의 Resources 탭에서 확인할 수 있습니다. 아래 그림과 같이 Resources 탭을 확인해보면 데이터 저장 여부를 확인할 수 있습니다.

다른 방법으로 개발자 도구의 Console에서 직접 COMM.getItem function을 호출하여 확인해볼 수 있습니다.

위 내용이 모두 정상적으로 확인되었다면 다음 Step으로 넘어가볼까요?

3-4. 상세 페이지 개발

이제 목록 페이지에서 전달한 데이터를 받아서 상세 페이지에 Bind하는 부분을 구현해보겠습니다. 상세 페이지에서 사용할 JavaScript 파일은 ./scripts/bbs_detail.js 로 생성하여 아래 코드를 반영합니다.

1
2
3
4
5
6
7
8
9
10
11
var DETAIL = {};
 
DETAIL.display = function(data) {
    $('#subject').text(data.subject);
    $('#additionalInfo').text(data.updateDate + ' | ' + data.inputUserName + ' (' + data.readCount + ')');
    $('#content').text(data.content);
};
 
$(document).ready(function() {
    DETAIL.display(COMM.getPageData());
});
./bbs_detail.html 파일에도 작성한 ./scripts/bbs_detail.js 파일을 추가합니다.
1
<script type="text/javascript" charset="utf-8" src="./scripts/bbs_detail.js"></script>
잘 따라하셨나요? 위의 내용까지 따라하시면 목록을 터치하면 상세 페이지로 이동하고 아래 그림과 같이 상세 내용이 표시되는 것을 보실 수 있습니다.

여기까지 모두 잘 되실거라 믿겠습니다. 그런데 지금은 값을 표시할 부분이 3군데 밖에 없어서 코딩이 짧게 끝났지만 값을 20개, 30개 세팅해야 한다고 하면 Selector로 Element를 찾아서 값을 세팅하는 코딩이 20 라인이 들어가게 됩니다. 그럼 이제 위에서 만든 Template을 이용해서 코딩을 좀 짧게 할 수 있는 방법으로 다시 개발해 보겠습니다.

먼저 기본 HTML 페이지를 아래와 같이 수정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="ko">
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
<title>Mobile BBS</title>
<link rel="stylesheet" type="text/css" href="./css/common.css" />
</head>
<body>
<header>
    <h1>Mobile BBS</h1>
</header>
 
<!-- 수정 시작 -->
<div class="wrap" style="display:none;">
    <div class="content_line">
        <span>{subject}</span>
    </div>
    <div class="content_line">
        <span>{additionalInfo}</span>
    </div>
    <div class="content_line">
        <span>{content}</span>
    </div>
</div>
<!-- 수정 종료 -->
 
<footer>
    <button id="btnWrite">글쓰기</button>
</footer>
</body>
<script type="text/javascript" charset="utf-8" src="./scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript" charset="utf-8" src="./scripts/common.js"></script>
<script type="text/javascript" charset="utf-8" src="./scripts/bbs_detail.js"></script>
</html>
위와 같이 실제 Contents를 표시하는 부분인 .wrap 부분을 hidden 처리해줍니다. 치환하기 전에 화면이 먼저 Rendering 될 것이므로 치환을 완료하기 전까지는 화면에 표시하지 않기 위함입니다. 그리고 실제로 값을 표시할 부분을 {}로 감싸서 식별할 수 있도록 해둡니다. 여기까지 마쳤으면 상세 화면을 출력하는 함수인 DETAIL.display 함수를 수정해 보겠습니다.
1
2
3
4
5
6
7
DETAIL.display = function(data) {
    var area = $('.wrap');
    var html = COMM.dynamicExpression(area.html(), data);
    area.empty();
    area.html(html);
    area.show();
}
코드를 살펴보면 의도는 간단합니다. $('.wrap') 안에 있는 내용을 모두 String 형태의 HTML로 받아서 {} Parameter 처리한 부분을 데이터에서 찾아서 자동으로 세팅하겠다는 의미입니다. 하지만 위 내용까지 따라해보시면 {additionalInfo}가 Bind되지 않는 것을 보실 수 있습니다. 이유는 간단합니다. 목록에서 전달하는 데이터에 additionalInfo라는 컬럼이 없기 때문입니다. 우리가 만든 template은 기본적으로 컬럼끼리의 Mapping에 대한 선언은 하지 않으므로 동일한 데이터는 동일한 이름으로 선언해주어야 합니다.

그럼 목록에서 데이터를 전달할 때 additionalInfo 컬럼을 세팅해서 보내보도록 하겠습니다. bbs_list.js 파일을 열어 BBS.display 함수에 컬럼을 추가해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BBS.display = function(rows) {
    var listArea = $('#bbsListArea');
    var html = null;
     
    COMM.loadTemplate('bbs_list_line', function() {
        for (var inx=0; inx<rows.length; inx++) {
            rows[inx].additionalInfo = rows[inx].updateDate + ' | ' +   rows[inx].inputUserName + ' (' + rows[inx].readCount + ')'
            html = COMM.getHTMLObj('bbs_list_line', rows[inx]);
            html.find('.bbs_list_txt').attr('data', JSON.stringify(rows[inx]));
            html.find('.bbs_list_txt').on('click', function(event) {
                COMM.movePage('./bbs_detail.html', $(this).attr('data'));
            });
            listArea.append(html);
        }
    });
}
여기까지 수정한 후 다시 목록을 조회해서 상세 페이지로 이동하면 데이터가 모두 표시되는 것을 확인할 수 있습니다. 어떠신가요? 다들 잘 따라하셨나요? 이젠 생각보다 날이 많이 풀려서 사무실에 앉아있지만 솔솔 잠도 오네요. 연말연시라 싱숭생숭하고 변화가 많은 시기지만 모두 2014년 한 해 잘 정리해서 보내주고 다가오는 신년을 잘 맞이했으면 합니다. 다음 시간은 2015년에 만나도록 하겠습니다.


300x250

+ Recent posts