PHP 5, 6일차

Fohanist·2020년 5월 2일
0

PHP

목록 보기
4/6
post-thumbnail

★구조화

이번에는 HTML 코드와 PHP 코드를 서로 분리하여 퍼블리셔 등과 함께 일을 할 때 코드가 섞이지 않도록 방지하는 방법을 배운다. 또한, 유지보수를 편리하게 하고 내가 만든 코드를 재사용할 때 좋다. 아래 include문은 include라고 실행된 위치에 다른 파일의 내용을 끼워넣는다.

file: count.php
<?php
	$output = ' ';
    for ($count=0; $count<10; $count++) {
    	$output .= $count.' ';
    }
//파일 내용이 전부 php코드로 끝난다면 ?>를 생략하는 관행이 있다.

include 'count.html.php';

include 'count.html.php'는 해당 위치에서 PHP가 count.html.php를 실행하도록 지시한다.
즉, 웹으로 요청하는 파일은 count.php 이고 이 안에 include가 아래 코드를 불러온다. 따라서 우리가 보는 뷰는 아래 HTML 이다.

file: count.html.php
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>9까지 숫자세기</title>
</head>
<body>
    <p>
        <?php echo $output; ?>
    </p>
</body>
</html>

count.heml.php 같은 코드를 PHP 템플릿이라고 한다. 거의 대부분 정적 HTML을 이루고 있으면, 중간에 작은 PHP 코드 조각이 동적인 내용을 담당한다. 복잡한 PHP코드는 별도의 스크립트 파일을 두고 변수를 생성한 다음에 PHP 템플릿에서 변숫값을 출력한다. 또한, 확장자명을 보면 중요한 점이 있다. 컴퓨터 웹 서버 입장에서는 그냥 PHP 파일이지만, 사람이 볼 때는 HTML코드가 함께 있는 PHP 템플릿이라는 파일을 인지할 수 있다.

★ 보안 고려 사항

HTML과 PHP 코드를 분리하면 문제가 한 가지 발생한다. 누구나 파일에 경로를 알고 있는 사람이 웹 브라우저에 .html.php 파일을 직접 실행 할 수 있다. 예를 들어 아래와 같은 상황이 있다.

if ($_POST['password'] == 'secret') {
	include 'protectd.html.php';
}

원래는 secret이라는 걸 제대로 입력해야 protected.html.php를 볼 수 있다. 그러나 웹 브라우저를 통해 직접 입력하면 비밀번호 검사 절차를 건너뛸 수 있다. 예방 조치 방법은 include로 가져올 파일, 예를 들어 여기서는 protected.html.php가 해당한다. public 이외의 다른 디텍토리에 두면 된다. public 디렉토리 이외의 경로는 웹 브라우저에 URL을 직접 입력하는 방식으로 접근할 수 없다. include 명령을 다른 디렉토리에서 파일을 불러올 수 있도록 변경해야 한다.

<?php include '절대경로';
?>

위와 같은 코드 처럼 작성하면 되지만, 절대경로는 가끔가다 골치아픈 상황들이 발생한다. 따라서 상대경로를 쓰는 것이 좋다.

<?php include '../count.html.php';
?>

위와 같이 쓰면 ../은 public 부모 디렉토리를 의미한다. vagrant를 사용하므로, 여기서 부모는 Project이다. 우리가 위에서 작성한 코드를 고치면 아래와 같다.

file: count.php
<?php
	$output = ' ';
    for ($count=0; $count<10; $count++) {
    	$output .= $count.' ';
    }

include '../count.html.php';

이 코드는 잘 작성되지만 문제가 또 발생한 소지가 있다. 상대 경로 기준은 호출하는 파일의 위치가 아니라 실행하는 파일의 위치다. 스크립트 파일 여기서는 count.php가 존재하는 디렉토리를 작업 디렉토리한다.

예를 들어 Project/count.html.php 파일에 include 'count2.heml.php'; 라는 구문을 추가하면 우리는 Project 디렉터리의 count2.heml.php를 불러온다고 생각한다. 하지만 상대경로는 작업 디렉터리 기준으로 위치를 판단한다. 따라서 count.html.php에 include 'count2.html.php';를 추가하고 count.php를 실행하면 결국 public 디렉터리에서 count2.heml.php를 찾는다.

작업 디렉터리는 스크립트가 시작될 때 설정되고, 이후 모든 include 구문에 영향을 미친다. 즉, 1번 파일이 작업 디렉토리에 있고, include 문으로 다른 디렉토리에 있는 2번파일을 불러오고, 또 2번 파일은 같은 디렉토리 안에 있는 3번 파일을 include문을 불러올 수 없다. 이미 경로는 처음 1번 파일에서 지정이 되었다. 그래서 2번 파일안에 include 'count2.html.php';는 경로가 아직 1번 파일에 있는 작업 디렉터리로 되어 있다.

작업 디렉터리는 코드로 임의 변경을 할 수 있다. chdir()이라는 함수이지만 이를 쓰면 더 복잡해진다. 따라서 상대경로를 보완해야한다. 그리고 PHP는 현재 파일의 절대 경로를 파악하는

__DIR__

이라는 상수를 제공한다. 이는 현재 이 상수를 선언한 파일의 절대경로를 파악한다.

include __DIR__ . '/../count.html.php ';

위와 같이 작성하면, public의 상위 디렉터리를 항상 정확하게 가리킬 수 있다.
이해를 하자면 현재 파일의 절대경로에서 현재 파일 상위 디렉토리를 가리킨다.
이렇게 작성하면 모든 서버에서 잘 작동한다. DIR은 파일이 저장된 위치에 따라 달라지고 작업 디렉터리의 영향을 받지 않기 때문이다.

사용자가 웹 브라우저로 직접 접근할 파일은 public 또, 브라우저가 요청한 그림파일, JS, CSS파일도 public에 둔다. 하지만 include 문으로 참조하는 모든 파일은 사용자가 직접 접근하지 못하도록 public 외부에 배치한다.

.html.php 템플릿파일은 Project/templates 에 넣어두자.

include __DIR__ . '/../templates/file.html.php ';

앞으로는 이렇게 경로를 작성하자.
이해를 돕자면 현재 파일 경로에서 상위 디렉토리로 가서 templates에 있는 file.html.php를 불러오는 것이다.

★ 다중 템플릿, 단일 컨트롤러

include문으로 PHP 템플릿을 불러오는 방식의 장점은 단일 PHP 스크립트에서 여러 include문을 사용해 상황에 따라 다른 템플릿으로 표시할 수 있다는 점이다. 브라우저 요청에 따라 PHP 템플릿 중 하나를 선택해 응답하는 PHP 스크립트를 일반적으로 컨트롤러라고 한다. 이 컨트롤러는 브라우저로 보낼 템플릿을 제어하는 로직을 담고 있다.

templates - file: form.html.php
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>login.html</title>
</head>
<body>
    <form action="" method="POST">
        <label for="ID">ID</label>
        <input type="text" name="ID" id="ID">
        <label for="PW">PW</label>
        <input type="text" name="PW" id="PW">
        <input type="submit" value="login">
    </form>
    
</body>
</html>

action 속성 값이 없다면 브라우저는 현재 URL를 폼으로 전송한다.
이 예제의 URL은 템플릿을 불러로는 컨트롤러의 URL이다.
컨트롤러 코드는 아래와 같다.

public - file: index.php
<?php
if (!isset($_POST['ID'])) {
    inclde __DIR__ . ' /../templates/form.html.php ';
} else {
    $id = $_POST['ID'];
    $pw = $_POST['PW'];
    if ($id == 'sumin' && $pw == 'sumin1234') {
        $output = '환영합니다, 관리자님!';
    } else {
        $output = htmlspecialchars($id, ENT_QUOTES, 'UTF-8').' '.'님, 홈페이지 방문을 환영합니다!';
    }

    include __DIR__ . '/../templates/welcome.html.php'
}

컨트롤러는 제일 먼저 현재 요청이 form.html.php의 폼에서 제출됬는지 판단한다. post 폼을 사용해 id 변수가 전달되는데 $_POST['ID']의 저장된 값을 확인하면 폼 제출 여부를 확인 할 수 있다. PHP의 내장 함수인 isset()은 특정 변수나 배열에 값이 할당 되었는지 참, 거짓으로 반환한다.

컨트롤러 처음 부분에 폼을 출력하는 코드를 작성하면 가독성이 좋아진다. 첫 if문은 부정 연산자를 사용하여 ID값을 받았는지 검사한다. 만약 ID가 입력되었다면 isset()함수는 부정 연산자를 통해 거짓이 되므로 else 항목을 실행한다. ID가 입력되지 않았다면 컨트롤러는 다시 폼을 출력하는 form.html.php 파일을 불러온다.

templates - file: welcome.html.php
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <title>welcome page</title>
</head>
<body>
    <p>
        <?php $output; ?>
    </p>
</body>
</html>

ID와 PW가 제대로 입력이 되었다면, 문자열을 컨트롤러에서 바로 출력하지 않고 welcome.html.php로 이동하여 출력한다.

index.php는 디렉터리 인덱스라고 불린다. 그리고 브라우저로 URL에 방문할 때 파일명을 지정하지 않고 디렉터리까지만 입력한다. 그럼 서버는 기본적으로 해당 디텍터리의 index.php 파일을 찾아 출력한다. 웹 서버마다 개별적으로 인덱스 파일명을 지정할 수 있다. 그러나 index.php 파일은 별로도 설정하지 않아도 거의 모든 웹 서버가 인덱스 파일로 인식한다.

사용자의 아이디를 입력받고 환영 메시지를 출력하는 동안 계속 같은 URL을 유지하면 좋은 점이 있다. 바로 즐겨찾기를 하면 항상 같은 URL로 저장되고 다시 접속 시 폼 화면을 보여준다.

이번을 끝으로 PHP의 문법과 보안을 신경쓰는 기본기를 마쳤다. 다음에는 MYSQL를 사용하여 PHP와 어떻게 연동되는 지에 대해서 배워볼 생각이다.

profile
Support Full-Stack | JavaScript Developer

0개의 댓글