목적

  • CRUD 을 익히는데 도움이 되는 게시판을 PHP 를 이용하여 만듦으로써 PHP 에 대한 지식을 습득

개발 환경

기능 목록

  • 글 목록 보기
  • 글 작성
  • 글 삭제
  • 글 수정

기본 화면

HTML과 bootstrap의 컴포넌트들과 적당한 css 를 이용하여 제일 먼저 게시판의 모양을 만들겠습니다.

board list

그림1. 게시글 목록

board content

그림2. 게시판 내용 출력

데이터베이스 설계

다음으로 데이터 저장을 위해 데이터베이스에 테이블 생성을 하겠습니다.

CREATE TABLE board (
    no INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    user_id VARCHAR(255) NOT NULL,
    created_date INT UNSIGNED NOT NULL,
    modified_date INT UNSIGNED,
    deleted_date INT UNSIGNED,
    readed_count MEDIUMINT NOT NULL,
    enabled BOOLEAN NOT NULL,
    PRIMARY KEY(no)
);
  • 게시판의 기본적인 기능만을 테스트 하기 위해 게시글에 대한 내용만 저장하는 테이블을 만들었습니다.
  • date 컬럼 들을 int 타입으로 하였는데 이는 다른 데이터 베이스로 전환시 데이터의 호환성을 위해 작성하였습니다.

CRUD 를 위한 클래스 설계

데이터의 CRUD를 위하여 데이터를 처리하는 서비스 클래스를 설계 하겠습니다.

게시판 v1 을 설계 하면서 생각한 것이 모델과 컨트롤러 뷰의 분리에 중점을 두고 설계하였고 데이터베이스와의 통신은 전적으로 서비스 클래스만을 이용하여 처리 하였습니다.

쿼리에 대해서는 별도의 파일로 작성하여 쿼리의 변경에 따른 부담을 최소화 하였습니다. 데이터베이스 접속 정보도 별도의 파일로 관리를 하게 됩니다.

그럼 하나씩 코드를 살펴 보겠습니다. 참고로 너무 내용이 길어지는것을 방지 하기 위해 모든 소스의 전체 내용을 보여드리지는 않습니다. 전체 소스를 보고 싶으신분은 github 저장소에서 확인 하실 수 있습니다. 물론 내려받아서 실행까지 해볼 수도 있습니다.

  • 게시판 Article 모델 클래스 입니다.
<?php
class BoardArticle {
    public $no;
    public $title;
    public $content;
    public $user_id;
    public $created_date;
    public $modified_date;
    public $deleted_date;
    public $readed_count;
    public $enabled;
}
?>
  • 데이터베이스와 통신을하는 DAO 서비스 클래스 (PDO 를 사용하여 Object Mapping 을 합니다)
<?php
class BoardService {
    // return_type 으로 article 클래스의 이름을 적어주면 article 오브젝트가 리턴됩니다.
    public static function query($query, $params=NULL, $return_type=NULL){}
    // update, insert, delete 를 위한 공통 함수 입니다.
    public static function update($query, $params=NULL) {}
    private static function setParameters($stmt, $params) {}
    private static function getPDO() {}
?>
  • 뷰에 데이터를 전달해주는 컨트롤러 클래스 입니다.
<?php
class BoardController {
    function readArticle($b_no) {}
    function readAllArticle($page_no=1) {}
    function createArticle($article) {}
    function updateArticle($article) {}
    function deleteArticle($article) {}
    function getTotalArticleCount() {}
}
?>
  • 쿼리를 관리하는 PHP 파일입니다.
<?php
$query['selectAllArticle'] = 'SELECT * FROM board INNER JOIN
(SELECT * FROM board ORDER BY no DESC LIMIT :start, :count) AS b USING(no)';

$query['selectArticleByNo'] = 'SELECT * FROM board WHERE no = :no';

$query['createArticle'] = 'INSERT INTO board VALUES (
NULL, :title, :content, :user_id, :c_date, 0, 0, 1, 1
)';

$query['updateArticle'] = 'UPDATE board SET 
title = :title, content = :content, modified_date = :m_date
WHERE no = :no AND user_id = :user_id';

$query['deleteArticle'] = 'DELETE FROM board WHERE no = :no AND user_id = :user_id';

$query['selectArticleCount'] = 'SELECT COUNT(*) AS CNT FROM board';
?>
  • 데이터베이스 접속 관리 PHP 파일입니다.
<?php
$db['username'] = 'root';
$db['password'] = 'root';
$db['hostname'] = 'localhost';
$db['database'] = 'portfolio';
?>

후기

위와 같은 설계 구조를 가지고 가장 간단한 게시판을 만들어 봤습니다.

게시판은 아마도 가장 많이 사용되면서도 가장 생각을 많이 하게 만드는게 아닌가 생각됩니다.

개인적으로 bootstrap 을 이용하면서 디자인에 대한 고민을 많이 하지 않아도 되었던것과 PHP 와 같은 스크립트 언어의 장점인 수정 후 확인까지 딜레이가 없이 바로바로 된다는게 참 좋았던것 같습니다. PHP 에는 웹 어플리케이션을 만들때 편리한 함수들이 미리 정의되어 있던것도 좋았습니다. 다만 손에 익숙하지 않아서 그런지 <?php ?> 이렇게 쓰는것과 변수선언때 $를 쓰는게 좀 힘들었습니다.

오랫동안 자바로 웹 개발을 하다 PHP 를 이용하여 게시판을 만들어 보았는데 서로 장단이 있는것 같습니다.

그리고 PHP 에 대하여 안좋은 이야기들을 인터넷상에서 본적이 있는데 그들이 하는 말에도 일리가 있긴 하지만 PHP 가 그들이 말하는것처럼 못쓸 언어라면 진즉에 없어지거나 거의 사용되지 않았겠지요. 하지만 여전히 제가 생각했던것보다 많은곳에서 PHP 를 주력으로 사용하는곳이 많다는건 그만큼 PHP 가 생각보다는 쓸만하고 PHP 에 대한 대안이 없다는 반증이 아닐까 조심스럽게 생각해봅니다.

이제 간단한 게시판 v1 을 완성하였으니 다음에는 외부 편집기를 도입하고, 로그인을 구현한 조금 더 보강된 게시판 v2 를 만들어 보도록 하겠습니다.




소스 저장소


Posted by #HanaLee
공부/JSF2014. 10. 7. 19:59

JSF 입력, 출력 예제

이번에는 이전 예제 보다 조금 더 복잡한 예제를 만들어 보겠습니다.

이번 예제는 이름을 입력하고 입력 받은 이름을 출력하는 예제이며 총 두개의 페이지가 필요합니다.

앞으로 만들게 될 모든 JSF 어플리케이션은 기본적으로 primefaces 구현체를 가지고 작업하게 되며 컴포넌트 태그의 앞에는 primefaces 를 의미하는 네임스페이스인 p: 를 붙여서 사용합니다.

이름 입력 페이지

페이지의 이름은 input.xhtml 로 만들겠습니다.

이 페이지에는 하나의 h:form 과 두개의 p:commandButton, 하나의 p:inputText JSF 컴포넌트를 사용하였습니다.

<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:p="http://primefaces.org/ui">
<h:head>
	<title>Facelets Hello Greeting</title>
</h:head>
<h:body>
	<p:messages id="messages" autoUpdate="true" />
	<h:form>
		<h2>Hello, my name is Hana. What's yours?</h2>
		<p:inputText id="username" title="My name is: "
			value="#{greeting.name}" required="true"
			requiredMessage="오류 : 이름을 입력해주세요." maxlength="25" />
		<p></p>
		<p:commandButton id="submit" value="Submit" action="response">
		</p:commandButton>
		<p:commandButton id="reset" value="Reset" type="reset">
		</p:commandButton>
	</h:form>
</h:body>
</html>

이 페이지에서 가장 복잡한 컴포넌트는 p:inputText 이며 여기에 사용된 속성에 대해 간단히 알아보겠습니다.

maxlength 속성은 입력필드에 최대로 입력 할 수 있는 값을 나타냅니다. required 속성은 입력필드에 빈값을 허용하지 않는다는것을 의미 합니다. requiredMessage 속성은 required 속성의 값이 true 일때 입력필드에 값이 채워지지 않은채 form 제출을 하였을 경우 보여줄 오류 메세지를 입력합니다. title 속성은 시각장애인을 위한 스크린리더를 위해 입력합니다. 마지막으로 value 속성은 표현식을 포함하고 있고 greeting 이라는 이름을 가진 bean 과 상호 작용을 하게 되며 greeting bean 의 구성요소(필드) 의 값을 바인딩 하게됩니다.

이 웹페이지는 Greeting managed bean 의 name 에 해당하는 구성요소(필드) 를 의미하는 #{greeting.nama} 이라는 표현언어(EL)를 통해 Greeting bean 의 name 구성요소(필드)와 연결이 됩니다. 참고로 표현식에서 사용한 greeting 은 Greeting 이라는 managed bean 의 이름 입니다. 이것은 @ManagedBean 어노테이션의 name 속성으로 임의로 다른 이름을 사용할 수도 있습니다. managed bean 의 @ManagedBean 에 name 속성이 지정되지 않을 경우 이 managed bean 은 항상 bean 의 첫번째 단어가 소문자인 이름으로 접근 할 수 있습니다.

Submit commandButton 컴포넌트의 action 속성에 지정된 값에 의해 버튼이 클릭 될때 response.xhtml 페이지를 표시 합니다.

이름 출력 페이지

페이지의 이름은 response.xhtml 로 만들겠습니다.

이 페이지의 결과는 첫 greeting.xhtml 페이지보다 훨씬 더 간단하며 표현식에 의해 Greeting managed bean 으로 부터 읽은 name 을 표시하고 이전 페이지로 돌아갈 수 있는 버튼이 있습니다.

<!DOCTYPE html>
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://xmlns.jcp.org/jsf/html"
	xmlns:p="http://primefaces.org/ui">
<h:head>
	<title>Facelets Hello Response</title>
</h:head>
<h:body>
	<h:form>
		<h2>Hello, #{greeting.name}!</h2>
		<p></p>
		<p:commandButton id="back" value="Back" action="input" />
	</h:form>
</h:body>
</html>

managed bean

입력 받은 이름을 저장하고 response.xhtml 페이지에서 읽어 오기 위해 사용할 bean 을 하나 만들겠습니다.

이름은 Greeting 이며 이 클래스는 managed bean 클래스라고 부르며 표현식 #{greeting.name} 이 name 구성요소(필드)로 부터 값을 읽고 쓰는것을 지원하기 위해 getter 와 setter 메소드가 포함되어 있습니다. 위에서도 언급한것같이 기본적으로 표현식은 클래스의 첫문자를 소문자로된 이름을 참조 합니다.

package hello;

import javax.faces.bean.ManagedBean;

@ManagedBean
public class Greeting {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

실행

웹 서버(Tomcat) 을 실행하고 http://localhost:8080/hello/input.xhtml 에 접속하면 이름을 입력 받는 화면이 나타나며 이름을 입력하고 submit 버튼을 누르면 response.xhtml 페이지가 나타나면서 입력한 이름을 표시 하는것을 볼 수 있습니다.

만약 이름을 입력하지 않고 submit 버튼을 누르게 되면 아래 그림에서 처럼 requiredMessage 에 입력한 내용이 화면에 표시되는 것을 볼 수 있습니다.




출처


Posted by #HanaLee
공부/JSF2014. 10. 6. 09:04

Managed Bean 스코프

일반적으로 Managed bean 은 아래와 같은 스코프를 가지고 있습니다. 각각의 스코프는 상황에 따라 맞춰서 사용하면 됩니다. 기본값은 request 스코프 입니다. 추후 다루게 될 스프링 프레임워크와 통합을 하게 되면 스프링의 스코프가 적용되며 그때에는 아래의 스코프는 사용되지 않습니다.

  • Application (javax.enterprise.context.ApplicationScoped)
  • Session (javax.enterprise.context.SessionScoped)
  • Flow (javax.faces.flows.FlowScoped)
  • Request (javax.enterprise.context.RequestScoped)
  • Dependent (javax.enterprise.context.Dependent)

어플리케이션 스코프 (ApplicationScoped)

어플리케이션 스코프는 웹 어플리케이션과 모든 사용자의 상호 작용을 통해 지속 됩니다.

세션 스코프 (SessionScoped)

세션 스코프는 웹 어플리케이션에서 여러 HTTP 요청에 의해 지속됩니다. 일반적인 웹의 세션이 지속되는 동안 유지 됩니다.

플로우 스코프 (FlowScoped)

플로우 스코프는 JSF 2.2 에서 새로 추가된 기능입니다. 플로우 스코프는 웹 어플리케이션의 특정 흐름(flow)과 함께 유저와 상호 작용(interaction)동안 지속됩니다. 플로우 스코프에 대해 더 알아보기

요청(리퀘스트) 스코프 (RequestScoped)

요청(리퀘스트) 스코프는 웹 어플리케이션에서 단일 HTTP 요청 동안 지속 됩니다.

의존 스코프 (Dependent)

의존 스코프는 빈(bean) 이 다른 빈에 의존한다는 것을 의미 합니다.

managed bean 이 다른 managed bean 을 참조 할때 @Dependent 를 사용 할 수 있습니다. 두번째 bean 은 그것이 생성되어 참조의 대상이 될 것으로 예상 될 때에는 스코프(@Dependent)에 있을 수 없습니다. 만약 @Dependent 로 빈이 정의 되면 그 빈은 참조 될때 마다 새로운 인스턴스가 생성되고 어떤 스코프에도 저장이 되지 않습니다.

managed bean 이 컴포넌트 태그의 결합(binding) 속성에 의해 참조 되면 그 빈은 요청 스코프(@RequestScoped) 로 정의 해야 합니다. 만약 빈을 세션 스코프나 어플리케이션 스코프에 배치할 경우 javax.faces.component.UIComponent 인스턴트들은 단일 스레드에 의존적으로 실행 되기 때문 스레드 세이프에 대해 주의 해야 합니다.

만약 빈을 구성 할때 뷰와 관계가 있는 속성(attribute)들이 있으면 뷰 스코프를 사용 할 수 있습니다. 뷰 스코프는 유저가 다음 뷰를 볼때까지 지속됩니다.




출처


'공부 > JSF' 카테고리의 다른 글

JSF 간단 예제 두번째  (0) 2014.10.07
JSF 라이프 사이클 상세  (0) 2014.10.06
JSF 라이프 사이클 개요  (0) 2014.10.05
JSF 어플리케이션 Hello World 예제  (0) 2014.10.04
JavaServer Faces 어플리케이션 개발 준비  (0) 2014.10.04
Posted by #HanaLee