본문 바로가기

개발과제

[노말틱 모의 해킹 취업반 8주차 개발과제 ] 게시판 만들기(4) - 파일 업로드

안녕하세요! 이번엔 파일 업로드 하는 기능과 다운로드까지 가능한 기능을 넣겠습니다!

그리고 시간이 되면 조회수 기능까지 넣고요 만약 안되면 내일에 하면 되겠죠!

.

.

.

시작합니다.

 

일단 저는 파일을 따로 저장 해둘 디렉터리를 만들겠습니다!

 

sudo mkdir -p /path/to/upload/directory 만들겠습니다!

 

이렇게 따로 디렉토리를 만든 이유는

행여나 웹에서 악영향이 되는 코드가 업로드될 때 웹에서 분리시키기 위함입니다!

 

 

그리고 업로드 기능을 먼저 만들겠습니다.

 

일단 write_board.php에 파일 올릴 수 있는 칸을 만들겠습니다!

 

        <form action="write_process_board.php" method="POST"  enctype="multipart/form-data"> 
            <p><input type="text" name="title" maxlegth="44" placeholder="제목 입력, 최대 44자까지 가능합니다" required></p>
            <p><textarea name="detail" rows="20" cols="20" maxlength="254" placeholder="내용 작성,최대 254자 가능합니다" required></textarea></p>
            <p><input type="file" name="file" id="fileToUpload"></p>
            <p><input type="submit" value="올리기"></p>
        </form>

 

 

이렇게 저는 수정했습니다.

 

form에 enctype="multipart/form-data"는 이 폼에 파일 업로드가 가능하다고 알려주는 겁니다.

 

일단 확인해보겠습니다

 

좋아요!

이제 write_process_board.php로 가서 코드 수정하겠습니다.

 

    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file'])) {
        // 업로드된 파일 정보 가져오기
        $file_name = $_FILES['file']['name'];
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif'];
    
        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {
            // 파일 MIME 타입 확인
            $file_mime_type = mime_content_type($file_tmp_name);
    
            if (in_array($file_mime_type, $allowed_mime_types)) {
                // 파일 저장 경로
                $upload_path = '/path/to/upload/directory/' . $file_name;
    
                // 파일 이동 및 저장
                if (move_uploaded_file($file_tmp_name, $upload_path)) {
                    $_SESSION['write_error'] =  '파일 업로드 성공';
                } else {
                    $_SESSION['write_error'] =  '파일 업로드 실패';
                }
            } else {
                $_SESSION['write_error'] =  '잘못된 파일 형식입니다';
            }
        } else {
            $_SESSION['write_error'] =  '파일 업로드 실패';
        }
    }

 

뭔가 어려워 보이죠.. 저도 이거 찾고 이해하느라 오래걸렸..

 

차근차근 설명 들어가겠습니다.

그리고 조금씩 수정하겠습니다. 최종 코드는 맨 밑에도 있어요!

 

 

먼저

if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file']))

이 코드에 $_SERVER['REQUEST_METHOD'] 는 REQUEST_METHOD에는 POST, GET인지 확인하는 변수입니다!

그러니깐 어떤 방식으로 요청을 하는지 알기 위해 하는 겁니다. 저희는 POST방식으로 보냈죠?

 

그리고 !empty($_FILES['file']) 는 파일에 비어 있지 않으면 이 조건절을 실행합니다!

 

 

그다음은

        $file_name = $_FILES['file']['name'];
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif','text/plain','application/zip','application/x-hwp','application/msword','application/vnd.ms-excel','application/pdf']; //MIME 허락 된 것
        //MIME 형식에 허락된 것

첫 줄은 file의 이름을 가져오는 것입니다.

tmp_name은 서버에 임시 파일을 저장한 경로를 뜻합니다! 잠깐 여기 두었다가 이제 업로드할 때 옮기는 거죠

size는 파일 크기입니다

error는 파일 업로드 중 오류 발생하면 저장하게 됩니다.

allowed_mim_types는 저희가 여러 파일을 받게 되는데 그중에 어느 파일 형식만 받는지 저장하는 겁니다!

이상한 파일을 받는 걸 방지하는 거죠

 

 

만약 파일 형식을 추가로 받고 싶거나 다른 걸 받고 싶다면 저 배열에서 추가만 하면 되겠죠!

MIME 형태의 파일형식 이름은 구글에 MIME [파일형식] 치면 나옵니다!

저는 이미지, zip, hwp, word, 엑셀, pdf 받을 수 있게 했습니다

 

 

그다음으로

        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {

만약 error에서 UPLOAD_ERR_OK가 나오면 정상적으로 들어와 있을 때 실행 합니다

 

            // 파일 MIME 타입 확인
            $file_mime_type = mime_content_type($file_tmp_name);

올린 파일의 형식을 가져옵니다!

 

if (in_array($file_mime_type, $allowed_mime_types)) {

여기서 in_array() 함수는 배열에 특정 값이 존재하는지 검사해 주는 함수입니다.

 

즉 file_mime_type변수가 allowed_mime_types에 들어가 있는지 검사하는 겁니다.

 

저희가 허락된 파일 확장자면 이 조건문을 실행합니다!

 

                // 파일 이동 및 저장
                if (move_uploaded_file($file_tmp_name, $upload_path)) {
                    $_SESSION['file_error'] =  '파일 업로드 성공';
                } else {
                    $_SESSION['file_error'] =  '파일 업로드 실패';
                }

 

다음은 파일의 저장 경로를 저장하고

move_uploaded_file 함수로 파일을 저장하게 합니다!

.

.

후.. 이렇게 업로드는 이렇게 마치겠습니다.

 

그런데! 여기서 문제점이 있네요 파일 이름이 겹치면 나중에 가져올 때 문제점이 생기겠죠?

 

그러니깐 저는 파일에 날짜까지 저장하겠습니다.

 

    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file'])) {
        // 업로드된 파일 정보 가져오기
        $file_name = $_FILES['file']['name'];
        $timestamp = time(); // 현재 시간을 초로 반환
        $new_file_name = $timestamp . '_' . $file_name; // 현재 시간과 원래 파일 이름을 합쳐 새로운 파일 이름 생성
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif','text/plain','application/zip','application/x-hwp','application/msword','application/vnd.ms-excel','application/pdf']; //MIME 허락 된 것
    	
        //sql공격 방지용
        $new_file_name = mysqli_real_escape_string($conn,$new_file_name);
        
        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {
            // 파일 MIME 타입 확인
            $file_mime_type = mime_content_type($file_tmp_name);
    
            if (in_array($file_mime_type, $allowed_mime_types)) {
                // 파일 저장 경로
                $upload_path = '/path/to/upload/directory/' . $new_file_name;
    
                // 파일 이동 및 저장
                if (move_uploaded_file($file_tmp_name, $upload_path)) {
                    $_SESSION['file_error'] =  '파일 업로드 성공';
                } else {
                    $_SESSION['file_error'] =  '파일 업로드 실패';
                }
            } else {
                $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
            }
        } else {
            $_SESSION['file_error'] =  '파일 업로드 실패';
        }
    }

이러면 초단위로 파일이름이 다 다르겠죠!

그리고 sql 공격 방지용으로 escape 함수 썼습니다.

 

 

 

그리고 view_board.php에 file_error 세션을 받겠습니다

 

        <p>
        <?php session_start();
                if (isset($_SESSION['file_error'])) {
                    echo $_SESSION['file_error'];
                    unset($_SESSION['file_error']);
                }
        ?>
        </p>

맨 밑에다가 넣었습니다

 

.

.

.

이렇게 업로드를 마치겠습니다.. 한번 해보죠!

 

happy하진 않아요 솔직히!

 

happy.txt 간단하게 만들고 이렇게 업로드해보겠습니다!

 

 

음..

 

아마 저희가 설정한 디렉터리에 다른 그룹 사용자가 파일 업로드하는 권한이 없어서 일 겁니다

 

저희가 지정한 디렉터리가 root 꺼라 저희 서버가 이용이 불가능합니다!

 

그러니 저희는 설정을 해야만 합니다

 

#sudo chown -R www-data /path/to/upload/directory

 

아파치는 www-data가 이름입니다! 그러니 이렇게 소유자를 변경하고 난 뒤에 다시 업로드하면?

 

참고로 ls -l 명령어로 누가 소유잔지 알 수 있습니다

 

test.txt 는 무시하세요! 제가 그냥 만든겁니다

 

잘 됬네요 HAPPY!

 

좋아요!

이렇게 업로드를 마치겠습니다.

 

 

다음은 다운로드이죠!

 

일단은 view_board.php에 이 게시물에 어느 파일이 저장되어 있는지 알아봐야 되겠죠??

그러니깐 그 파일의 경로를 알아야만 되겠죠.

 

그러니 일단 저는 데이터베이스에 새로운 칼럼 추가하겠습니다. 파일명 저장하기 위해서요!

 

 

이렇게요. 다운로드 할땐 파일경로를 따로 불러와서 파일명만 갖다 붙이기만 하면 되죠

 

 

그리고 write_process_board.php에 파일명도 같이 저장되게 하겠습니다!

 

    //session으로 유저 이름 받기
    $user_id = $_SESSION['login_id'];
    echo $board_id;
    if(isset($board_id)) {
        //board_id가 있다는 것은 수정을 의미
        $sql = "UPDATE $db_board SET title = '$title', detail='$detail', file_name='$new_file_name' WHERE board_id='$board_id' ";
    }
    else {
        //board_id가 없으니 새로 만드는 sql문
        $sql = "INSERT INTO $db_board (id, title, detail,file_name) VALUES ('$user_id', '$title', '$detail','$new_file_name')";
    }

이러면 되겠죠

 

그리고 view_board.php에 파일명도 보이게 하겠습니다.

 

            //게시물 출력
            while($row = mysqli_fetch_assoc($result)) {
                echo '<p>ID: '.$row['id'].'</p>';
                echo '<p>Title: '.$row['title'].'</p>';
                echo '<p>Detail: '.$row['detail'].'</p>';
                echo '<p>Date: '.$row['date'].'</p>';
                if(isset($row['file_name'])) {
                    echo '<p>FILE: '.$row['file_name'].'</p>';
                }

                //로그인한 사용자의 정보 가져온다
                $user_id = $_SESSION['login_id'];

                //게시물 작성자와 로그인한 ID와 일치한 경우
                if($row['id'] == $user_id) {
                    //게시물 수정 버튼을 보여준다
                    echo "<form method='POST' action='fix_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>게시물 수정</button></p>
                          </form>";

                    //게시물 삭제 버튼을 보여준다
                    echo "<form method='POST' action='delete_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>삭제</button></p>
                          </form>";
                }
            }

그리고 한 번 봐보겠습니다!

 

 

다시 새로 저장!

 

나오네요!!

 

그런데 시간 초까지 보여주니깐 조금 불편하네요 _ 앞에 있는 건 지우고 나오게 하겠습니다.

 

                if(isset($row['file_name'])) {
                    $file_name = explode('_', $row['file_name'])[1]; // '_' 문자를 기준으로 분리 후 두번째 요소(파일명)만 가져옵니다.
                    echo '<p>FILE: '.$file_name.'</p>';
                }

저장하고 다시 실행하면?

 

 

너무 맘에 들어요!

 

이제 다운로드 만들겠습니다.

 

저는 파일 명에 다운로드 링크를 걸겠습니다!

 

 

                if(isset($row['file_name'])) {
                    $file_path = 'http://path/to/upload/directory/'.$row['file_name']; // 저장된 파일 경로
                    $file_name = explode('_', $row['file_name'])[1]; // '_' 문자를 기준으로 분리 후 첫번째 요소(파일명)만 가져옵니다.
                    echo '<p>FILE: <a href="'.$file_path.'" download>'.$file_name.'</a></p>'; // 다운로드 링크 생성
                }

 

일단 이렇게 하이퍼링크에 download을 걸었습니다.

 

저장 후 다시 실행해보겠습니다!

 

 

 

뭔가 있네요! 클릭

 

네.. 안되네요.. 왜 여러개냐구요?? 진짜 이것저것 다 해봤는데 안되네요 결국 코딩 바꾸겠습니다

 

 

그래서 아예 download_process.php을 따로 만들었습니다!!

 

view board.php엔

 

                    $file_name = explode('_', $row['file_name'])[1]; // '_' 문자를 기준으로 분리 후 첫번째 요소(파일명)만 가져옵니다.
                    $download_url = 'download_process.php?filename='.$row['file_name'];
                    echo '<p>FILE: <a href="'.$download_url.'">'.$file_name.'</a></p>';

이렇게 download_process.php와 이어지게 걸었고요

 

 

 

<?php
    if(isset($_GET['filename'])) {
        // 파일 이름 및 경로 가져오기
        $filename = $_GET['filename'];
        $filepath = '/path/to/upload/directory/'.$filename;
    
        // 파일이 존재하는지 확인
        if(file_exists($filepath)) {
            // 다운로드 헤더 설정
            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            readfile($filepath);
            exit;
        } else {
            echo '파일이 존재하지 않습니다.';
        }
    } else {
        echo '파일 이름이 전달되지 않았습니다.';
    }
?>

이렇게 했습니다.

 

각각 설명하자면

 

Content-Descrtiption은 파일 전송에 대한 설명을 추가합니다!

 

Content-Type는 다운로드되는 파일의 MIME 설정합니다! 저 octet-stream은 이진 데이터를 의미합니다!

 

Content-Disposition은 다운로드 시 다운로드 대화 상자에 표기된 파일 이름을 설정합니다! $변수에 따라 이름이 바뀝니다

 

Expires는 캐시의 만료일을 설정합니다! 0을 설정하면 브라우저가 항상 새로운 파일을 가져오게 합니다.

 

Cache-Control은 캐시 제어합니다! must-revalidate는 캐시를 사용하기 전에 서버로부터 새로운 데이터를 가져오라고 명형 합니다

 

Pragma는 http1.0과 호환되게 합니다.

 

Content_length는 다운로드되는 파일의 크기를 설정합니다

 

readfile은 파일을 읽고 출력합니다!

 

exit은 다운로드가 완료되면 스크립트 실행을 중지합니다

 

 

사실.. 다 되고 맞춤법 검사하고 있는 데 갑자기 컴터 꺼져서 중간 부터 다시 입력 중이랍니다 ㅎ.. HAPPY..

 

잘 되네요!!

 

아직 부족합니다..

 

너무 늦었으니 조회수 기능은 내일 만들고 보안도 신경 써서 다시 코딩하겠습니다!!

 

파일 검증, 파일 이름 암호화, 세션 방어 이러한 보안 문제는 차차 고치겠습니다!

.

.

.

.

.

.

-view_board.php

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>게시물 보는중</title>
    </head>
    <body>
        <p>
        <?php 
            include 'DB_INFO.php'; //데이터 베이스 정보

            //데이터베이스 연결
            $conn = mysqli_connect($host,$username,$password,$db_board);

            //데이터베이스 오류시 종료
            if(mysqli_connect_errno()) {
                die("데이터 베이스 오류: ". mysqli_connect_error());
            }

            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }

            //GET방식으로 전달된 index 받는다
            $board_id = $_GET['index'];

            //작성된 게시물들 조회 문
            $sql = "SELECT * FROM $db_board WHERE board_id = '$board_id' ";
            //쿼리문 실행
            $result = mysqli_query($conn, $sql);
            
            //게시물 출력
            while($row = mysqli_fetch_assoc($result)) {
                echo '<p>ID: '.$row['id'].'</p>';
                echo '<p>Title: '.$row['title'].'</p>';
                echo '<p>Detail: '.$row['detail'].'</p>';
                echo '<p>Date: '.$row['date'].'</p>';
                if (isset($row['file_name'])) {
                    $file_name = explode('_', $row['file_name'])[1]; // '_' 문자를 기준으로 분리 후 첫번째 요소(파일명)만 가져옵니다.
                    $download_url = 'download_process.php?filename='.$row['file_name'];
                    echo '<p>FILE: <a href="'.$download_url.'">'.$file_name.'</a></p>';
                }
                
                //로그인한 사용자의 정보 가져온다
                $user_id = $_SESSION['login_id'];

                //게시물 작성자와 로그인한 ID와 일치한 경우
                if($row['id'] == $user_id) {
                    //게시물 수정 버튼을 보여준다
                    echo "<form method='POST' action='fix_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>게시물 수정</button></p>
                          </form>";

                    //게시물 삭제 버튼을 보여준다
                    echo "<form method='POST' action='delete_board.php'>
                            <input type='hidden' name='board_id' value='".$row['board_id']."'>
                            <p><button type='submit'>삭제</button></p>
                          </form>";
                }
            }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['write_error'])) {
                    echo $_SESSION['write_error'];
                    unset($_SESSION['write_error']);
                }
        ?>
        </p>
        <p>
        <?php session_start();
                if (isset($_SESSION['file_error'])) {
                    echo $_SESSION['file_error'];
                    unset($_SESSION['file_error']);
                }
        ?>
        </p>
        <p></p>
        <form action="write_board.php" method="POST"> 
            <p><input type="submit" name="write" value="게시판 작성"></p>
        </form>
        <form action="board.php">
            <p><input type="submit" value="메인페이지로"></p>
        </form>
    </body>
</html>

 

 

-download_process.php

 

<?php
    if(isset($_GET['filename'])) {
        // 파일 이름 및 경로 가져오기
        $filename = $_GET['filename'];
        $filepath = '/path/to/upload/directory/'.$filename;
    
        // 파일이 존재하는지 확인
        if(file_exists($filepath)) {
            // 다운로드 헤더 설정
            header('Content-Description: File Transfer');
            header('Content-Type: application/octet-stream');
            header('Content-Disposition: attachment; filename="'.$filename.'"');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            readfile($filepath);
            exit;
        } else {
            echo '파일이 존재하지 않습니다.';
        }
    } else {
        echo '파일 이름이 전달되지 않았습니다.';
    }
?>

 

 

 

 

-write_process_board.php

<?php
    include 'DB_INFO.php'; //데이터 베이스 정보

    session_start(); //세션 시작

    if(!isset($_SESSION['login_id'])) {
        //로그인하지 않은 사용자
        header("Location: login.php"); //login 화면으로 바꾼다
        exit(); //이 페이지를 바로 닫는다
    }

    //게시판 데이터베이스 연결
    $conn = mysqli_connect($host,$username,$password, $db_board);

    //데이터베이스 오류시 종료
    if(mysqli_connect_errno()) {
        die("데이터 베이스 오류: ". mysqli_connect_error());
    }

    //POST로 전달된 정보 받기
    if(isset($_POST['board_id'])){
        $board_id = mysqli_real_escape_string($conn,$_POST['board_id']);
    } else {
        $board_id = null;
    }
    $title = mysqli_real_escape_string($conn,$_POST['title']);
    $detail = mysqli_real_escape_string($conn,$_POST['detail']);

    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_FILES['file'])) {
        // 업로드된 파일 정보 가져오기
        $file_name = $_FILES['file']['name'];
        $timestamp = time(); // 현재 시간을 초로 반환
        $new_file_name = $timestamp . '_' . $file_name; // 현재 시간과 원래 파일 이름을 합쳐 새로운 파일 이름 생성
        $file_tmp_name = $_FILES['file']['tmp_name'];
        $file_size = $_FILES['file']['size'];
        $file_error = $_FILES['file']['error'];
        $allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif','text/plain','application/zip','application/x-hwp','application/msword','application/vnd.ms-excel','application/pdf']; //MIME 허락 된 것
        
        //sql공격 방지용
        $new_file_name = mysqli_real_escape_string($conn,$new_file_name);

        // 파일 업로드가 정상적으로 처리되었는지 확인
        if ($file_error === UPLOAD_ERR_OK) {
            // 파일 MIME 타입 확인
            $file_mime_type = mime_content_type($file_tmp_name);
    
            if (in_array($file_mime_type, $allowed_mime_types)) {
                // 파일 저장 경로
                $upload_path = '/path/to/upload/directory/' . $new_file_name;
    
                // 파일 이동 및 저장
                if (move_uploaded_file($file_tmp_name, $upload_path)) {
                    $_SESSION['file_error'] =  '파일 업로드 성공';
                } else {
                    $_SESSION['file_error'] =  '파일 업로드 실패';
                }
            } else {
                $_SESSION['file_error'] =  '잘못된 파일 형식입니다';
            }
        } else {
            $_SESSION['file_error'] =  '파일 업로드 실패';
        }
    }

    //session으로 유저 이름 받기
    $user_id = $_SESSION['login_id'];
    echo $board_id;
    if(isset($board_id)) {
        //board_id가 있다는 것은 수정을 의미
        $sql = "UPDATE $db_board SET title = '$title', detail='$detail', file_name='$new_file_name' WHERE board_id='$board_id' ";
    }
    else {
        //board_id가 없으니 새로 만드는 sql문
        $sql = "INSERT INTO $db_board (id, title, detail,file_name) VALUES ('$user_id', '$title', '$detail','$new_file_name')";
    }

    //sql문 실행
    if(mysqli_query($conn,$sql)) {
        //수정인 경우 바로 게시판 보이게 함
        if(isset($board_id)) {
            $_SESSION['write_error'] = '수정되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
        else {
            $board_id = mysqli_insert_id($conn); // 새로 생성한 게시물의 id를 가져옴
            $_SESSION['write_error'] = '작성되었습니다!';
            header("Location: view_board.php?index=$board_id");
        }
    } else {
        $_SESSION['write_error'] = '작성 중 오류가 발생하였습니다.';
        header("Location: view_board.php");
    }

    exit();
?>

 

 

-write_board.php

 

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>게시판 작성 중</title>
    </head>
    <body>
        <h1>게시판 작성!</h1>
        <?php 
            session_start(); //세션 시작

            if(!isset($_SESSION['login_id'])) {
                //로그인하지 않은 사용자
                header("Location: login.php"); //login 화면으로 바꾼다
                exit(); //이 페이지를 바로 닫는다
            }
        ?>
        <form action="write_process_board.php" method="POST"  enctype="multipart/form-data"> 
            <p><input type="text" name="title" maxlegth="44" placeholder="제목 입력, 최대 44자까지 가능합니다" required></p>
            <p><textarea name="detail" rows="20" cols="20" maxlength="254" placeholder="내용 작성,최대 254자 가능합니다" required></textarea></p>
            <p><input type="file" name="file" id="fileToUpload"></p>
            <p><input type="submit" value="올리기"></p>
        </form>
        
        <form action="board.php"> 
            <p><input type="submit" value="돌아가기"></p>
        </form>
        <p>
        <?php session_start();
                if (isset($_SESSION['write_error'])) {
                    echo $_SESSION['write_error'];
                    unset($_SESSION['write_error']);
                }
        ?>
        </p>
    </body>
</html>

 

 

당연히 질문받고 있고요 제 코드에 이상한 게 있으면 지적해주세요!