2012년 10월 2일 화요일

쓰고, 저장하고, 보는 프로그램 만들기

자, helloWorld 프로그램을 만들어 보고,
view와 controller를 나누어서, notFound인 경우를 잘 처리 했다면,
아마 여러분은 조금 더 복잡한 프로그램들을 만들고 싶어 할 것입니다.

물론, 이 blog는 php5를 중점적으로 다루기 때문에, html이나 css, javascript에 대한 내용은 다른 곳에서 많이 보셨다는 가정을 하고, view쪽은(html) 정상적인 html5 코딩으로 가 보겠습니다.

웹 프로그래밍에서 가장 자주 접하는 문제는 사람의 정보를 저장하는 것입니다. helloWorld가 이름을 쓰면 "hello world! XXX"와 같이 붙여주는 단순한 프로그램이었다면, 사람의 정보를 전체적으로 넣고, 그 정보를 찾아보는 프로그램을 만드는 것이 필요할 겁니다.
일반적으로 사용자(User)라는 표현을 많이 쓰므로, 이번 프로그램은 User라고 해 봅시다.

먼저, controllers/user.php라는 파일을 만들어 봅시다.


<?
header("Content-Type: text/html; charset=UTF-8");
include 'views/user.html';

header(...)은 charset을 설정하기 위해서 사용했는데, 이번 프로그램에서는 저장의 편의성을 위해서 utf-8을 사용합니다. 편집기에 따라 다르지만, 저장시에 utf-8으로 저장하셔야 합니다.
그리고, view파일을 만들어야겠죠. 정식적인 html 파일로 만들어 봅시다.

<!DOCTYPE html>
<html>
<head>
<title>사용자</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body>
<form method='post'>
<ul>
<li>
<label for='name'>이름</label>
<input type='text' id='name' name='name' required='required' />
</li>
<li>
<label for='birthday'>생일</label>
<input type='datetime' id='birthday' name='birthday' />
</li>
<li>
<fieldset id='gender'>
<legend>성별</legend>
<label for='남성'>남성</label>
<input type='radio' value='남성' id='남성' name='gender' checked='checked' />
<label for='여성'>여성</label>
<input type='radio' value='여성' id='여성' name='gender' />
</fieldset>
</li>
<li>
<label for='tel'>전화번호</label>
<input type='tel' id='tel' name='tel' pattern='[0-9]+'/>
</li>
<li>
<label for='address'>주소</label>
<input type='text' id='address' name='address' />
</li>
</ul>
<button type='submit'>저장</button>
</form>
</body>
</html>

예...
조금 복잡하지만, 보통 기본적인 html파일이 이런 식으로 생겨 먹었습니다.
html5에서는 <form>태그 안의 input들이 많은 타입이 생겨서, tel이나 datetime, number 같은 형식을 지정할 수 있습니다. 브라우저 마다 서로 틀리게 작용하지만, 스마트 폰 등에서 자동으로 그에 편리한 자판을 띄어주거나 하는 방식으로 작동하게 됩니다.
이제, http://localhost/user.php로 들어가 보면, 적당한 화면을 보실 수 있게 되겠죠.


내용을 채운 후에 "저장"이라는 버튼을 눌러도 아무런 변화가 없으니, 고생하지 마시고, 다시 user.php를 열어서 어떤 내용이 어떤 식으로 오는지를 먼저 확인합시다.

<?
if ( ! empty( $_POST ) ) {
print_r( $_POST );
} else {
include 'views/user.html';
}

이제 내용을 적당히 채운 후 확인을 눌러서 확인하면... 한 줄로 되어있어서 불편하므로, 소스보기를 하면, POST 방식으로 전송된 내용을 편하게 보실 수 있습니다.


POST가 비어있지 않으면, print_r() 이라는 명령으로 자원(resource)을 보여달라고 한 것이죠. 이제 자원을 확인했으면, 이를 저장하면 됩니다. data라는 폴더를 하나 만들어서 여기에 저장을 하는 것이 좋겠네요.
루트에 data라는 폴더를 만들고, user.php를 고칩니다.

<?
header("Content-Type: text/html; charset=UTF-8");
if ( ! empty( $_POST ) ) {
$fileName = "data/user.json";
file_put_contents( $fileName, json_encode( $_POST ) );
include 'views/userView.html';
} else {
include 'views/user.html';
}

$fileName에 파일명을 넣고, file_put_contents()는 파일을 열고 내용을 씁니다. $_POST는 여러개의 데이타를 가지고 있는데, json_encode()는 이런 데이타를 JSON이라는 텍스트 형식으로 바꾸어 주어서 저장을 할 수 있도록 합니다.
그리고 나서 html파일 하나를 불러왔는데, 이는 아직 만들어 지지 않은 내용이죠.
그러므로, views/userView.html 파일을 하나 만들어 줍니다.

<!DOCTYPE html>
<html>
<head>
<title>사용자</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body>
<section>
<ul>
<li>
<span class='title'>이름</span> : <span><?=$_POST['name']?></span>
</li>
<li>
<span class='title'>생일</span> : <span><?=$_POST['birthday']?></span>
</li>
<li>
<span class='title'>성별</span> : <span><?=$_POST['gender']?></span>
</li>
<li>
<span class='title'>전화번호</span> : <span><?=$_POST['tel']?></span>
</li>
<li>
<span class='title'>주소</span> : <span><?=$_POST['address']?></span>
</li>
</ul>
</section>
</body>
</html>

user.html 파일보다는 단순한 모양새이죠? $_POST는 POST형식으로 받은 내용이고, '<?='는 아시다시피 내용을 보여주는 짧은 형식입니다. 이전에 print_r()로 소스보기를 했을 때, $_POST의 생김새가 [name] => Madstorm 과 같은 형식이었는데, 이를 하나하나 접근하는 방식이 $_POST['name']과 같은 형식입니다.


결국 저장을 하고 나면, 위와 같은 모습으로 자신이 넣은 자료를 보실 수 있으실 겁니다. 또한 data라는 폴더에 가면 user.json이라는 파일을 보실 수 있으실 텐데요. 열어보면 한글이 써 있어야 할 부분에 조금 난해한 글씨들이 보이시겠지만, 대략 어떤 방식으로 저장이 되는지 살펴볼 수 있으실 겁니다.

"근데, 주소가 똑같은데 어디에서는 폼을, 어디에선 내용을 보여주잖아."
"뭐야? 보통때는 어떻게 저장된 내용을 볼 수 있지?"

...
맞습니다. 사실 이 프로그램은 저장할 폼을 보여주고, 저장을 하고, 내용을 보여주는 세 개의 내용들이 합쳐져 있습니다. 이 내용들을 분리하면, 훨씬 사용자들이 수월하겠죠.
자, 파일들을 나누어 봅시다.

단계 1 : controllers/userWrite.php

<?
header("Content-Type: text/html; charset=UTF-8");
include 'views/userWrite.html';

쓰기는 view파일만 불러오면 되므로, 이렇게 구성하되, view 파일을 userWrite로 고치는 편이 좋겠군요.

단계 2 : views/userWrite.html

...
<form method='post' action='/userSave.php'>
...
<button type='submit'>저장</button>
<a href='/userView.php'>저장내용 보기</a>
...
...

userWrite.html은 user.html을 그대로 파일명만 바꾸고, form을 보낼 곳을 /userSave.php로 변경시켜 줍니다. 또한 저장 버튼 옆으로 "저장내용 보기" 링크를 하나 두면, 저장 내용을 볼 수 있을 겁니다.

단계 3 : controllers/userView.php

<?
header("Content-Type: text/html; charset=UTF-8");
$fileName = "data/user.json";
$user = json_decode( file_get_contents( $fileName ) );
include 'views/userView.html';

전체가 새로운 프로그램이지만, 내용은 단순하게 file_get_contents()로 파일로 부터 내용을 가져오고, 저장할때와 반대로, json_decode() 로 내용을 풀어 줍니다. 그러면 이를 받은 $user는 $user->name과 같은 형식으로 내용에 접근 할 수 있습니다.
물론, 이미 만들어 놓았던 'views/userView.html'도 고칠 곳이 있죠. 이제 $_POST['name']이 아니라, 위와 같이 써야 합니다.

단계 4 : views/userView.html

<!DOCTYPE html>
<html>
<head>
<title>사용자</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body>
<section>
<ul>
<li>
<span class='title'>이름</span> : <span><?=$user->name?></span>
</li>
<li>
<span class='title'>생일</span> : <span><?=$user->birthday?></span>
</li>
<li>
<span class='title'>성별</span> : <span><?=$user->gender?></span>
</li>
<li>
<span class='title'>전화번호</span> : <span><?=$user->tel?></span>
</li>
<li>
<span class='title'>주소</span> : <span><?=$user->address?></span>
</li>
</ul>
<a href='/userWrite.php'>고치기</a>
</section>
</body>
</html>

일단, 데이타에 접근하는 방식이 바뀌었고, 아래쪽에 userWrite.php로 가는 링크도 달렸습니다.
이제 마지막으로 저장을 담당 하는 /userSave.php를 만들어 봅니다.

단계 5 : controllers/userSave.php

<?
header("Content-Type: text/html; charset=UTF-8");
if ( ! empty( $_POST ) ) {
$fileName = 'data/user.json';
file_put_contents( $fileName, json_encode( $_POST ) );
header('Location: /userView.php');
} else {
header('Location: /userWrite.php');
}

저장할 POST내용이 있으면, 저장을 한 후, 저장 내용을 보여주는 /userView.php로 이동하고, 내용이 없을 경우에는 /userWrite.php로 가서 저장을 하도록 합니다.

자, 이제 읽고, 쓰고, 저장하는 세 개의 행위들이 잘 정돈 되었습니다. 또한 링크들도 적절히 이동이 되는군요.

그런데, controller 파일들(php)을 보니, 뭔가 계속 반복되는 것 같지 않습니까?
바로, header("...UTF-8") 부분인데, 앞으로 계속 utf-8으로 프로그래밍을 한다면, 여기에 있을 이유가 없습니다. 우리에게는 전면의 관리자가 존재하니까요. 각 php파일들에서 이 부분을 지우고, 루트의 index.php파일을 열어서 여기에 붙여 줍니다.

<?
header("Content-Type: text/html; charset=UTF-8");
$target = "controllers" . $_SERVER['REQUEST_URI'];
if ( is_file( $target ) ) {
include $target;
} else {
include 'controllers/notFound.php';
}

이제, 기본적인 설정에 해당하는 header()부분을 전면 관리자에게 넘기고 더 깔끔한 관리자들을 얻으셨습니다.

2012년 9월 29일 토요일

Not Found

pure하게 php5를 이용해서 프로그래밍을 하는 blog. 그 두번 째는,
먼저 자신의 주소창에 http://localhost/abc.php 라고 치는 것으로 시작합니다.


자, 생각하던 결과입니까? 물론, abc.php를 만든 적이 없고, 파일이 없으니, "이 서버에는 /abc.php 따위는 못찾겠다."라고 친절하게 가르쳐 주는 것이 쓸만해 보이기는 합니다. 물론,
"요즘 월드 와이드한 세상에 저 정도 못 읽는 사람이 있겠어?"
라는 주장도 설득력이 있기도 합니다.
그렇다면 http://localhost/index.html 혹은, http://localhost 로 들어가서 이름을 쓰고 프로그램을 확인 해 봅시다. 왠지 모양은 같은데, 프로그램이 동작하지 않는가요?

만약 이런 식으로 오작동 하거나, Not Found가 기분 나쁘거나 하시다면, 편집기를 열고,


RewriteEngine On
RewriteRule !\.(js|jpg|png|gif|css|swf)$ index.php

라고 쓴 후에 .htaccess라는 이름으로 저장합니다. 탐색기 상에서는 파일명을 변경할 수 없으므로, 반드시 편집기에서 save as 등으로 저장해야 합니다.
자, 위에 있는 주소들을 들어가 보면, 이제 Not Found나 잘못된 곳으로 향하지 않고, 항상 index.php파일 내용을 볼 수 있습니다. 끝~.


"이런, 이미 다른 파일들을 만들어서 홈페이지를 구축했는데, 이런 삐리리한 경우가."
"다른 파일은 만들지 말고, 한페이지만 만드는 것이었군요."

...
아, 성급했군요.
저 .htaccess를 만들고 나면, 모든 파일들이 index.php로 오기 때문에, 이미 만든 프로그램을 적절한 곳에 놓는 편이 좋습니다.
우선 controllers, views 라는 두개의 디렉터리(폴더)를 만드시고, index.php와 index.html을 각각의 디렉터리에 이름을 helloWorld로 바꾸어 넣기로 하죠.
controllers/helloWorld.phpviews/helloWorld.html로 말입니다.
파일이 많아지면, 이와같이 프로그램의 이름이 명확하면 찾기 편해 집니다.
그리고 index.php를 새로 만드는 거죠.

<?
$target = "controllers" . $_SERVER['REQUEST_URI'];
if ( is_file( $target ) ) {
include $target;
} else {
print "<h1>잘못된 접근이신 듯... 아래 링크로 가세요.</h1>";
print "<a href='helloWorld.php'>Hello World Program.</a>";
}


그러면 http://localhost/abc.php로 들어가 보면,



이와 같이 나옵니다. 이처럼 모든 요청을 처리하는 파일을 front controller 즉, '전면 관리자'라고 부릅니다.
$_SERVER['REQUEST_URI']는 웹서버가 php를 통해 제공하는 변수인데, 위와 같은 경우 '/abc.php' 값이죠. 말 그대로, 서버의['요청받은_주소']입니다.
$target은 'controllers/abc.php'가 되는 것이죠.
is_file은 파일이 존재하는지 확인하는 함수입니다.
결국 if문으로 둘러쌓인 부분은,
"만약, controllers/abc.php가 있으면 불러오고, 아니면 print를 해라."가 되겠죠.

이제, 링크를 클릭하면,



이런 에러가 뜨는군요. 이런, 프로그램이 망가진 듯 하죠? 찬찬히 읽어보면, index.html을 찾아 보았는데, include_path가 이러이러한데 찾아봐도 없더라. 하고 말하고 있습니다.
당연하죠. index.html은 views/helloWorld.html디렉터리로 옮겨 놓았는 걸. 그렇다면 controllers/helloWorld.php파일을 열고,

include 'index.html';

include 'views/helloWorld.html';
로 고쳐 줍니다.

이제, 잘 동작하나요? 앞으로는 controllers라는 디렉터리에 php파일을 만들면 내용이 읽힐 것입니다.
말이 나온 김에, 루트 디렉토리(index.php와 controllers, views폴더가 있는 곳을 말합니다)의 index.php파일을 보기 좋게 정리를 해 볼까요?


단계 1 : index.php


<?
$target = "controllers" . $_SERVER['REQUEST_URI'];
if ( is_file( $target ) ) {
include $target;
} else {
include 'controllers/notFound.php';
}

이렇게 html부분을 잘라내고, controllers에 파일이 없을 때 처리할 파일을 지정합니다.

단계 2 : controllers/notFound.php

<?
include 'views/notFound.html';

이렇게 해당 파일을 만듭니다.

단계 3 : views/notFound.html

<h1>잘못된 접근이신 듯... 아래 링크로 가세요.</h1>
<a href='helloWorld.php'>Hello World Program.</a>

오오~. print가 사라지고, 깔끔한 html파일이 되었군요.


자, 전체적인 그림을 설명하면,
index.php파일은 front controller(전면 관리자)가 되고,
이 전면 관리자는 controllers에 요청한 이름과 같은 파일이 있는지 확인합니다.
있으면, 그것을 가져오고, 없으면, controllers/notFound.php를 가져옵니다.

자, 이제는 controllers에 새로운 php파일들을 만들고, views에 html을 넣어서, 자신이 원하는 프로그램들을 계속 만들어 가면 되겠습니다.

=========== FAQ =============
Q : 그런데, 왜 디렉터리 명이 phps와 htmls이 아니라, 더 길고 복잡한 controllers와 views 인가요?
A : 프로그램 쪽에서 무언가 관리를 하는 쪽을 controller(관리자)라고 부르고, 보이는 것을 담당하는 쪽을 view라고 불러 왔습니다. 관습인 거죠. 다양한 프로그래밍에서 이와 같이 사용을 하면, 다른 언어의 다른 프로그램에서도 어떤 역할을 하는지 알기 쉽습니다. 물론, 관습이므로, 반드시 따를 필요는 없습니다. 참고로 index.php파일과 같이 controller들을 관리하는 쪽은 front controller라고 불리죠.

hello world!

이 글을 읽는 분이 프로그래밍을 처음 접해본다면,
아마도 서버가 무엇인지, 데이타베이스가 무엇인지, 어떤 언어를 배워야 하는지에 대해서 궁금할 것입니다.
하지만, 웹 프로그래밍을 하고 싶다고 생각하셨다면, 사실 너무 많은 지식들을 머리에 구겨넣고 싶은 것이 아니라, 자신의 브라우저에서 당장 볼 수 있는 프로그래밍을 원할 것이라고 생각합니다.

그래서, 여러분이 처음 해야 하는 일은 php 프로그래밍 환경을 만드는 것입니다.
먼저, http://www.ampps.com/downloads 로 이동하셔서 자신의 컴퓨터에 맞는 프로그램을 다운로드 하고, 설치합니다.
그러면, 자신이 설치한 곳의 www라는 디렉토리(폴더)에 자신이 원하는 프로그래밍을 할 수 있는 준비가 된 것입니다.

이 곳에서 자신이 좋아하는 편집기로 다음과 같은 내용을 쓰고, 'index.php'라는 파일명으로 저장합니다.



<?php
print "<h1>hello world!</h1>";

이제, 브라우저에서 주소에 'localhost'라고 치면, 자신이 처음 만든, php프로그래밍이 hello world!라고 큰 글씨로 환영을 해 줄 것입니다.



그리고, 여러분은 생각하겠죠.
"그래서 뭐? 그냥 html파일로 만드는 것과 뭐가 틀려?"
"뭐 이딴걸 가르친답시고 인터넷 공간을 낭비하는 거지?"

...
물론, 이딴 것으로도 php에 대해서 많은 설명을 할 수 있지만, 별로 원하지 않을 겁니다.
그러므로, 다시 'index.php'를 열고, 이번에는 정말로 웹에서 하는 프로그래밍을 해 봅시다.

<?php
$name = $_GET['name'];
print "<h1>hello world! $name</h1>";


이렇게 저장을 하고, 주소에 'localhost?name=Madstorm' 하고 입력을 합니다.


감이 잡히시나요?
주소에 '?'를 넣고 난 다음의 xxx=yyy라는 형식으로 입력을 하면 브라우저는 GET이라는 방식으로 서버에 전달을 하고, php는 $_GET['xxx']라는 형식으로 그 이름을 받을 수 있습니다.
사용자와 상호작용을 할 수 있는 것이죠. 주소창에 여러가지 이름을 넣어 보시며 실험해 보시면 됩니다.

...
그럼, 사용자가 저 '?name=xxxx'를 기억해 두었다가 사용을 해야 하느냐고요?
아무래도 그건 껄끄럽겠죠. 게다가 주소가 지저분해 보이기도 하죠. 이름이 주소창에 보이는 것도 왠지 기분 나쁩니다. 개인정보는 매우 중요하기도 하고요.
만일 이런 생각이 드신 분이 있다면, 다시 index.php파일을 열고, 이렇게 해 봅시다.

<?php
$name = $_POST['name'];
print "<form method='post'>";
print "<label>이름을 써 주세요.</label>";
print "<input name='name' />";
print "</form>";
print "<h1>hello world! $name</h1>";

다시 localhost페이지를 열면 위쪽에 설명과 상자가 있고, 그 상자에 이름을 쓰고 엔터를 치면, 다음과 같은 모습이 나타날 것입니다.


<form>이라는 태그는 사용자로 부터 입력을 받고, method라는 속성에 따라 GET이나 POST, PUT, DELETE 등의 방식으로 서버에 전달합니다. php는 역시 $_POST['name'] 이라는 방식으로 그 내용을 받을 수 있고요.

보통 검색을 하는 경우는 GET방식을 사용해서 위치 정보를 노출시키고, 입력을 받아서 저장을 하는 경우는 POST방식으로 정보를 노출 시키지 않는 것이 보편적입니다.

"그럭저럭... 뭔가 프로그래밍 같기는 한데, 저렇게 계속 print 라고 써야 하는 건가?"
"계속 print와 ;를 반복하는 게 프로그래밍 인 건가? 뭔가 짜증나는데?"

....
만일 이렇게 생각하셨다면, 여러분은 프로그래머로서의 자질을 갖추고 계신 겁니다. 프로그래머는 반복적인 것을 보면 눈쌀을 찌푸리는 사람들 입니다. 같은 typing을 계속 하면 짜증이 나죠. 보통은. 게다가 보기에도 피곤합니다.
자, 마지막으로 코드를 정리해 봅시다. 

1단계 :

<?php
$name = $_POST['name'];
?>
<form method='post'>
<label>이름을 써 주세요.</label>
<input name='name' />
</form>
<h1>hello world! <?php print $name?></h1>

'<'와 '>'로 둘러쌓인 부분은 html태그입니다.
'<?php'와 ?>로 둘러쌓인 부분은 php프로그램입니다.
php는 이렇게 언제라도 중간 중간에 끼어들게 되어있죠.
이렇게 변경을 하고 브라우저에서 보면, 아까와 동일한 내용의 동일한 프로그램이 돌아갑니다.
이게 뭔 짓거리 일까요? 이 짓거리의 공식 명칭은 '리펙터링(refactoring)'이라고 하는데, 내용은 똑같지만, 코드를 더 보기 좋고 명확하게 하는 것을 뜻합니다. 한 페이지에 점점 많은 내용이 들어가게 되면, 여러분은 점점 더 이 무의미해 보이는 짓거리를 좋아하게 될 것입니다.

2단계 : index.php파일

<?
$name = $_POST['name'];
include 'index.html';

어라? simple해 졌군요. 그런데, 아래의 html 코드들은 어디로 갔을까요?
include 명령은 다른 파일을 가져오는 역할을 합니다. 사실 html 코드와 php코드가 섞여 있다면, 디자이너와 프로그래머가 각각 작업을 할 수 없기 때문에, 기본적으로 html파일을 떼어 내어 다른 파일에 저장하고, 디자이너에게 던져 주곤 하는거죠. 뭐, 현재는 여러분이 디자이너자 프로그래머 이기는 합니다만.
그래서 새로운 파일 'index.html'을 하나 만드셔야 합니다. 그리고는,

2단계 : index.html파일

<form method='post'>
<label>이름을 써 주세요.</label>
<input name='name' />
</form>
<h1>hello world! <?=$name?></h1>

이렇게 분리를 해 줍니다. 그리고 이번에는 주소창에 'localhost/index.php'로 들어갑니다.
웹서버는 파일명을 써 주지 않았을 때, 기본적으로 index.php나 index.html같은 파일을 자동으로 읽어 들이는 기능이 있는데, 기본적으로 index.html 파일을 먼저 읽게 되어 있군요. 그래서 index.php를 써 주는 것 입니다.
한가지 더. php는 option으로 'short tag'라는 것을 지원하는데, '<?php'는 <?로 줄여 쓸 수 있고, '<?php print'는 <?=로 줄여 쓸 수 있습니다.

이제 여러분은 간단한 웹 프로그래밍을 할 수 있는 "php 프로그래머라"고 할 수 있습니다.
코드를 정리하고 난 후라면, "좋은 php 프로그래머"입니다.

그러나, 아직 위의 index.html파일은 정확한 형식의 html이 아니군요. 이 blog는 php5에 관한 것이므로, html에 대한 부분은 w3schools.com 같은 곳이나, 다른 곳을 이용해서 배우시는 것이 좋을 듯 합니다.