티스토리 뷰

Programming/JSP

JSP실습 - 게시판 서비스

글그리 2019. 5. 17. 14:17

간단한 게시판 서비스를 만들면서 jsp를 사용한 데이터베이스 연동을 연습해보자. 구현을 위해 준비해야 할 스펙은 아래와 같다.

 

스펙

  • jsp
    jsp이기 때문에 java를 기본적으로 사용하며 html과 javascript에 대한 기본적인 문법지식을 요구한다.
  • oracle database 18c
    맥에서 작업하고 있기 때문에 오라클 데이터베이스를 로컬에서 사용할 수 없어서 윈도우 데스크탑에 설치하고 원격으로 접속하는 형태로 구현한다.
  • eclipse
  • tomcat 9.0.19

 

이번 실습에서 기본적인 흐름은 입력-검증-DB입출력 순서로 흘러간다.
예를들어 로그인 기능의 경우 Login.jsp에서 입력 폼을 제공하고 동시에 javascript로 입력값을 검증한 후 LoginOK.jsp로 입력된 값을 넘겨준다. LoginOK.jsp에서는 실제로 데이터베이스에 접속하고 전달받은 값으로 데이터베이스에 접속하여 값을 수정한다. 즉 OK가 접미사로 붙는 페이지는 실제 사용자의 화면에 보이지는 않고 데이터베이스에 접근하는 동작만 수행하고 다음 페이지로 리다이렉션한다.

 

 

목표

아래 기능들을 구현하면서 jsp와 오라클 데이터베이스를 사용하여 웹 어플리케이션을 구현하는 연습을 한다.

  1. 일반 사용자로 회원가입할 수 있다.
  2. 일반 사용자계정을 수정할 수 있는 관리자계정이 존재한다.
  3. 일반 사용자와 관리자는 메인 게시판화면에서 볼 수 있는 메뉴가 다르다.
  4. 게시글은 날짜 순서로 정렬한다.
  5. 관리자의 글은 굵게표시되고 가장 최근에 작성된 3개만 볼 수 있다.
  6. 관리자는 게시글을 삭제할 수 있다.

 

 

데이터베이스 구조

create table USER_INFO(id varchar2(20) primary key, pw varchar2(10), name varchar2(20), age number);

create table MEMO(num number, post varchar2(100), writer varchar2(20) foreign key(writer) USER_INFO(id), publish_date date default sysdate);

 

 

데이터베이스 접근

페이지에서 데이터베이스에 접근하기 위해서는 설치된 오라클 데이터베이스의 이름과 계정정보를 알아야한다. OS X 에는 오라클 데이터베이스를 설치할 수 없기 때문에 윈도우 데스크탑에 설치하고 네트워크를 통해 접속하는 형태로 구현했다.

 

  • url : jdbc:oracle:thin:@[database ip address]:1521:XE
  • ID : [database id]
  • PW : [database pew]

 

접속

<!-- 1. import java.sql.* -->
<%@ page import="java.sql.*" /%>
  1. Connection, Statement, ResultSet 등 데이터베이스 객체를 사용하기 위해서 페이지에 java.sql.* 을 포함시킨다.
String url = "jdbc:oracle:thin:@192.168.0.1:1521:XE";
Connection con = null;

// 1. register driver
Class.forName("oracle.jdbc.driver.OracleDriver");

// 2. get connection
con = DriverManager.getConnection(url, "c##eastroot", "1590");
  1. 페이지에 드라이버를 등록해서 사용할 수 있도록한다.
  2. 등록한 드라이버를 통해 원격 데이터베이스에 접속을 얻어온다.

 

쿼리문 사용

// 1. send query
Statement stat = con.createStatement();
ResultSet rs = stat.executeQuery("select * from USER_INFO");

// 2. get value from resultset
while (rs.next()) {
    String id = rs.getString("id");
    String pw = rs.getString("pw");
}
  1. Statement 객체를 만들고 쿼리를 사용한다.
  2. 결과가 여러개일 경우 rs.next()를 호출하여 다음 줄로 넘어갈 수 있다. rs.next()는 호출할 때마다 다음 줄로 넘어가고 다음 줄이 없으면 false를 반환하기 때문에 모든 결과를 순환하면서 검사할 수 있다.

쿼리에 입력해야할 변수가 많다면 PreparedStatement를 사용할 수 있다.

PreparedStatement stat = con.prepareStatement("select * from USER_INFO where id=?, age=?";
stat.setString(1, "eastroot");
stat.setInt(2, 25);
ResultSet rs = stat.executeQuery();

 

종료

데이터베이스의 사용이 끝났다면 안전하게 연결을 해제해주어야한다.

// 1. reverse
if (rs!=null) rs.close();
if (stat!=null) stat.close();
if (con!=null) con.close();

종료는 생성한 반대 순서로 한다.

 

 

각 서비스 페이지를 구체적으로 어떻게 구현하는지 알아보자.

 

 

Login

아이디와 비밀번호를 입력하고 데이터베이스에서 값을 검증하여 게시판으로 이동한다.

// 1. get id, pw from post parameter
String id = request.getParameter("ID");
String pw = request.getParameter("PW");

// 2. select * from USER_INFO where id="ID" and pw="PW"
rs = stat.executeQuery();

// 3. success
if (rs.next()) {
    response.sendRedirect("MemoBoard.jsp");
    session.setAttribute("ID", id);
}
// 4. fail
else {
    response.sendRedirect("Login.jsp");
}
  1. form에서 입력한 값으로부터 아이디와 비밀번호를 얻어온다.
  2. PreparedStatement를 만드는 과정은 생략했지만 1번에서 얻은 값으로 검색하는 쿼리를 만든다.
    select * from USER_INFO where id="admin" and pw="1111";
  3. 로그인에 성공하면 세션에 아이디를 저장하고 게시판 페이지로 이동한다.
  4. 실패하면 다시 로그인 페이지로 이동한다.

 

 

Join

아이디는 버튼으로 팝업되는 창에서 중복검사를 해야하기 때문에 기본 창에서는 입력할 수 없도록 막는다.

<input type="text" value="ID" readonly>

 

아이디 유효성 검사

아이디 입력칸 옆에있는 버튼으로 팝업되는 윈도우에서 아이디의 유효성검사를 한다.

window.open("IDValidCheck.jsp", "_IDValidCheck", "width=420, height=200").focus();

 

IDValidCheck.jsp는 아래와 같은 흐름을 가지도록 구현한다.

boolean valid = false;
String id = request.getParameter("ID").toString();
// 1. id valid check
if (id!="") {
}
// 2. id confirm
if (valid) {
}
else {
    // 3. input id
    if (id == "") {
    }
    // 4. id collapse
    else {
    }
}
  1. 넘어온 아이디가 있다면 해당 아이디로 중복검사를 수행한다. 데이터베이스에 접속하고 쿼리를 날려서 검색한다.
    select * from USER_INFO where id="eastroot";
  2. 1번에서 유효성 검사를 통과했다면 아이디를 사용하겠냐고 묻는 페이지를 보여준다.
  3. 아이디를 입력하지 않고 페이지에 들어왔다면 아이디를 입력하라는 문구가 보인다.
  4. 아이디가 중복되었다는 뜻이므로 중복 되었다는 문구가 보인다.

모든 값의 유효성이 겁증된다면 joinOK.jsp로 값을 넘겨주고, 데이터베이스에 회원정보를 삽입한다.

insert into USER_INFO(id, pw, name, age) values("admin", "1111", "admin", 25);

 

 

MemoBoard

회원들이 작성한 메모를 볼 수 있다. 이 페이지부터는 관리자와 일반유저가 볼 수 있는 페이지가 다르다. 이를 위해서 관리자와 일반유저의 접근을 구분하는 분기가 필요하다.

 

관리자와 일반유저 구분

// 1. not login
if (session.getAttribute("ID") == null) {
    response.sendRedirect("Login.jsp");
}
else {
    String sessionID = session.getAttribute("ID").toString();
    boolean isAdmin = false;
    // 2. admin
    if (sessionID.equals("admin")) {
        isAdmin = true;
    }
    // 3. common user
}
  1. 아예 로그인하지 않았다면 로그인 페이지로 이동한다.
  2. 관리자라면 플래그를 true로 설정한다.
  3. 일반 유저라면 false인 상태로 진행한다.
    else 내부에서 정해진 플래그를 사용해서 화면 구성을 다르게 한다.

 

게시글 조회

반복문을 통해 동적인 테이블을 생성한다.

out.print("<table>");
out.print("<tr><td>index</td><td>post</td><td>writer</td><td>date</td></tr>");
while (rs.next() {
    out.print("<tr><td><%= rs.getString('num') %></td>");
    out.print("<tr><td><%= rs.getString('post') %></td>");
    out.print("<tr><td><%= rs.getString('writer') %></td>");
    out.print("<tr><td><%= rs.getString('publish_date') %></td></tr>");
}
out.print("</table>");

jsp인 만큼 out.print가 아니라 <% %>, <%= %> 같은 액션 태그를 활용해서 html과 적절히 섞어서 작성하면 더 읽기쉬운 코드가 된다.

테이블의 가장 상단 3행은 관리자가 작성한 글이 보여야 하기 때문에 관리자의 글만 선택하는 쿼리를 작성한다.

select * from MEMO where writer="admin" order by publish_date desc;

그 아래에는 일반 사용자들이 작성한 글이 날짜 순서대로 보여야하기 때문에 일반유저의 글을 선택하는 쿼리를 작성한다.

select * from MEMO where writer<>"admin" order by publish_date desc;

 

게시글 작성

메모를 작성하기 위한 페이지로 로그인한 누구나 접근할 수 있다. 작성자 이름(readonly), 비밀번호, 메모를 작성할 수 있는 form이 제공되며 비밀번호는 글을 삭제할 때 사용한다.
아래와 같은 쿼리를 만들어서 게시글을 추가한다.

insert into MEMO(num, writer, post, pw)
values(memo_seq.NEXTVAL, "admin", "test memo", "1111");

 

게시글 삭제

메모를 삭제할 때는 삭제를 위한 페이지로 이동해서 삭제하고싶은 메모의 비밀번호를 입력할 form이 제공된다. 입력받은 비밀번호, 글번호와 일치하는 메모가 있을 때만 삭제한다.
입력받은 정보를 바탕으로 아래와 같은 형태의 쿼리를 만들어서 유효성을 검사하고 삭제한다.

select from MEMO where num=64 and pw='1111';
delete from MEMO where num=64 and pw='1111';

 

 

Users (관리자 전용)

회원목록을 볼 수 있는 페이지이며, 이곳에서 모든 회원의 회원정보를 수정할 수 있다.

select * from USER_INFO where id<>'admin'

 

 

Edit

실제 회원정보를 수정할 수 있는 페이지이며 유저 권한에 따라 수정할 수 있는 정보가 다르다. MemoBoard.jsp와 같은방식으로 관리자와 일반유저를 구분하고 추가로 일반유저는 다른 유저의 정보를 수정할 수 없기 때문에 다음 조건을 추가해야한다.

if (!isAdmin) {
    if (!sessionID.equals(editID)) {
        // 1. can't edit other user
    }
    else {
        // 2. edit my information
    }
}
  1. 일반유저는 다른 유저의 정보를 수정할 수 없기 때문에 Login.jsp로 리다이렉션하거나 적절한 조치를 취해준다.
  2. 자신의 정보를 수정하려고 접근했다면 수정을 허용한다.

 

 

최신글 이미지 (Bonus)

현재 데이터베이스 날짜를 기준으로 2일 이내에 작성된 글은 글 옆에 이미지를 띄우도록 해보자. 우선 이미지를 추가하는 html은 아래와 같다.

<img src="new.png">

이 이미지를 2일 이내에 작성된 글에만 추가하기 위해서 조건문을 추가한다.

// 1. get database system time
rs = stat.executeQuery("select sysdate from dual");
// parse from rs (system date)
int sys_year, sys_month, sys_day;

// 2. in while(rs.next()) loop
    // parse from post publish date
    int year, month, day

    // same year
    if (year == sys_year) {
        if (month == sys_month && sys_day - day <= 2) {
            // 3. same month
            isNew = true;
        }
        else if (sys_month - month == 1) {
            if (day - sys_day >= days[month-1]) {
                // 4. last month
                isNew = true;
            }
        }
    }
    // last year
    else if (sys_year - year == 1) {
        if (month == 12 && sys_month == 1) {
            if (day - sys_day >= 29) {
                // 5. last year
                isNew = true;
            }
        }
    }
  1. 접속한 사용자의 로컬시간이 아니라 데이터베이스의 시간을 사용해서 정확성을 높인다.
  2. 루프를 돌면서 현재 게시글의 작성시간을 얻는다.
  3. 같은 연도, 같은 달에 작성했다면 날짜의 차이만 비교한다.
  4. 같은 연도 지난 달에 작성한 글이라면 그에 맞는 차이를 계산한다. days는 해당 달의 일수를 저장해두었다.
  5. 작년에 작성한 글이라면 그에 맞는 차이를 계산한다. 예를들어 현재 1월 1일이라면 작년 12월 29일 이후에 작성된 글만 검출한다.

 

 

소스코드

프로젝트 전체 코드는 여기에서 볼 수 있다.

'Programming > JSP' 카테고리의 다른 글

JSP 개발환경 구축하기  (0) 2019.05.20
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함