자습

웹 개발: 게시판 - 글 작성

whydontyoushovel 2024. 11. 13. 15:31

 <현재 환경 세팅>

1. Virtual Box 7.1.4 + 우분투 리눅스 20.04

2. 우분투 APM (Apache2 + PHP + MySQL)

3. 기본 웹 루트 경로 /var/www/html

4. VScode SSH

5. 사진 등은 termius

 

 

 

< DB에 필요한 컬럼 >

(수정될 수 있음)

1. idx

2. 제목

3. 내 서버에 저장한 이미지 url

4. 글 내용

5. 작성자명

6. 작성날짜

테이블 구조를 보는 sql은 desc(describe)이다. 내림차순을 왜 쓰나 했네; 아마도 이 테이블은 앞으로 웹개발을 하면서 여러번 바뀔 것 같다.

 

 

컬럼 자료형에 대한 자료

https://devdhjo.github.io/mysql/2020/01/30/database-mysql-003.html

 

[MySQL] 003# MySQL 데이터 타입 (자료형) 정리

MySQL 8.0 기준으로 정리 된 데이터 자료형입니다. – 출처 : MySQL 8.0 Reference Manual > Data Types

devdhjo.github.io

 

 

 

< 신경 써야할 요소 >

1. idx

  • 자동으로 일련번호가 생성되게 설정

 

2. 제목

  • 입력받은 값 변수 저장

 

3. 내 서버에 저장한 이미지 url

  • 이미지 넣을 input 태그가 있는 form 태그에 enctype="multipart/form-data" 필요.
  • 이미지 넣을 input 태그에 type="file" 필요. accept = "image/*"는 넣어도 되고 안 넣어도 되는 듯.
  • $_FILES 변수, basename 함수 이용해서 내 서버에 저장할 url 만듦.
  • move_uploaded_file 함수 이용해서 임시 디렉토리에 저장된 사진을 내가 지정한 url(파일경로)로 옮김.
  • 옮기고 나서 DB에 INSERT

 

4. 글 내용 (그림에 대한 설명 등)

  • 입력받은 값 변수에 저장

 

5. 작성자명 (사용자 닉네임)

  • 사용자의 $_SESSION['id'] 값으로 DB에서 닉네임 찾음 -> 변수 저장

 

6. 작성날짜

  • DB 데이터 타입 : DATETIME
  • 기본값 : CURRENT_TIMESTAMP 
  • INSERT 시 컬럼명에도 안 쓰고  VALUES 값도 안 써야 함.
  • 게시물 수정 시 수정한 날짜로 변경하려면 UPDATE 할 때 "컬럼명 = CURRENT_TIMESTAMP" 추가. (안 쓰면 변경X)

 

 

 

♨ 4주차 결과물

1) 업로드 폼

업로드 페이지 화면. 페이지 위에는 네비게이션 바가 있고 아래로 업로드 폼이 있다.

2) upload_table 테이블

업로드된 DB 테이블. 여러번 실행해서 행이 2개인데 idx가 10, 11이다. 저거 그냥 두는 게 나으려나?

 

3) 서버의 이미지 저장소

서버에 사용자가 업로드한 이미지가 저장된 모습이다. 물론 다 내가 그렸다.

 

 

 

4) upload.php

 

(여기에 html 폼이랑 php코드 다 씀)

<?php

    session_start();

    require 'session_init.php';
    require 'db_connection.php';


    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);

    if(!isset($_SESSION['id'])) {

      echo "<script> 
        alert('로그인이 필요한 서비스입니다.');

        window.location.href = 'login.php';
      
        </script>";

        exit;

    } else { ?>


<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>올리기 _페인티</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <script src="logout_func.js"></script>
  </head>
  <body>
  <nav class="navbar navbar-expand-lg bg-body-tertiary ">
        <div class="container-fluid">
          <a class="navbar-brand" href="index.php">페인티</a>
          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
          </button>
            <div class="collapse navbar-collapse" id="navbarNavDropdown">
                <ul class="navbar-nav col-sm-5">
                    <li class="nav-item">
                        <a class="nav-link active" aria-current="page" href="index.php">게시판</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="upload.php">올리기</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="my_uploads.php">내 게시물</a>
                    </li>
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                        마이페이지
                        </a>
                        <ul class="dropdown-menu">
                        <li><a class="dropdown-item" href="#">좋아요</a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li><a class="dropdown-item" href="mypage.php">회원정보 수정</a></li>
                        </ul>
                    </li>
                    
                </ul>
                <div class="offset-md-5">
                <button type="button" class="btn btn-warning" id="logout" onclick="logout()">로그아웃</button>
                </div>
            </div>
            
        </div>
    </nav>              <!--네비게이션-->

    <form class="container mt-5" method="POST" action="" enctype="multipart/form-data">
        <div class="row mb-3">
            <label for="inputEmail3" class="col-sm-2 col-form-label">제목</label>
            <div class="col-sm-10">
            <input type="text" class="form-control" id="inputTitle" name="title" required>
            </div>
        </div>
        <div class="row mb-3">
            <label for="inputPassword3" class="col-sm-2 col-form-label">그림 파일</label>
            <div class="col-sm-10">
            <input type="file" class="form-control" id="inputPaint" name="image" accept="image/*" required/>
            </div>
        </div>
        <div class="row mb-3">
            <label for="inputPassword3" class="col-sm-2 col-form-label" style="white-space:pre-wrap">내용</label>
            <div class="col-sm-10">
            <input type="textarea" class="form-control" name="content" required/>
            </div>
        </div>
        <button type="submit" class="btn btn-warning">올리기</button>

    </form>            <!--업로드 폼-->

            
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>

      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
      <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
  </body>
</html>


<?php } 

   if($_SERVER['REQUEST_METHOD'] === 'POST') {
   
   $title = isset($_POST['title'])?$_POST['title']:"";   //게시물 제목
   $description = isset($_POST['content'])?$_POST['content']:"";   //게시물 글
   $id = $_SESSION['id'];   // 식별할 아이디


   $select_sql = "SELECT * FROM registration_list WHERE id = '$id'";
   $result = mysqli_query($db_conn,$select_sql);

   $row= mysqli_fetch_array($result);

   if(!$row) {
    echo "sql error";
   }

   $nick = isset($row['nick'])?$row['nick']:"";       //닉네임 뽑아내기

   
   if (isset($_FILES['image']) && $_FILES['image']['error'] === 0) {
        $img_name = basename($_FILES['image']['name']);
        $file_path = "/var/www/html/img/";
        $targetPath = $file_path.$img_name;


        if(move_uploaded_file($_FILES['image']['tmp_name'],$targetPath)) {

            
            $uploadFile = $targetPath;

            $insert_sql = "INSERT INTO upload_table(idx,title,image_url,description,nick) VALUES(null,'$title','$uploadFile','$description','$nick')";

            $insert_result = mysqli_query($db_conn,$insert_sql);


            header("location: post.php");

            exit;
            }

        } else {
            echo "<script>
            alert('파일이 서버에 저장되지 않았습니다');
            </script>";
        }
    } else {
        echo "<script>
        alert('파일이 선택되지 않았거나 업로드 오류가 발생했습니다.');
        </script>";
    }
}
   
?>

 

 

 

2) 설명

 

  • 맨 위에 php코드

세션 시작, 쿠키와 세션 데이터 만료시간 설정 파일 연결, DB 연동 파일 연결

php 코드 에러 출력 함수

로그인할 때 정의된 세션 변수값이 없으면 경고문 출력

있으면 html 출력

 

  • html 코드

head 태그 :

파일의 메타 정보 + 브라우저에 표시될 페이지 이름 + 부트스트랩 CDN 링크 + 로그아웃 기능을 위한 함수가 있는 자바스크립트 파일 연결

 

body 태그 :

크게 네비게이션 바와 업로드 폼으로 이루어짐.

 

네비게이션 바 ( 게시판 홈 + 업로드 페이지 + 내 게시물 페이지 + 마이페이지 + 로그아웃 버튼 )

업로드 폼 ( 제목 + 그림 파일 + 그림 설명 텍스트창 + 제출 버튼 )

 

폼의 enctype="multipart/form-data"와 type="file", accept = "image/*"에 대한 짧은 설명

더보기

https://www.tcpschool.com/html-tag-attrs/form-enctype

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file

 

<input type="file"> - HTML: HyperText Markup Language | MDN

<input> elements with type="file" let the user choose one or more files from their device storage. Once chosen, the files can be uploaded to a server using form submission, or manipulated using JavaScript code and the File API.

developer.mozilla.org

 

 

enctype="multipart/form-data" :

모든 문자를 인코딩하지 않음. 파일 업로드할 때 주로 사용

 

enctype="application/x-www-form-urlencoded" :

모든 문자 encodeURI()로 인코딩해서 보냄. 기본 값

 

enctype="text/plain" :

공백 문자만 "+"로 인코딩됨. 디버깅할 때 쓰이고 input type="submit"이나 button 태그에서도 가능

 

type = "file"  : 

인풋 박스의 타입을 파일 업로드 형식으로 설정함.

(password면 박스 안 글자가 기호로 바뀌고 submit이면 제출버튼으로 바뀌는 것처럼)

input type="password"
input type="file" 개신기함!!!!!

 accept="image/*" :

파일 형식 지정. type="file"을 더 선호해서 잘 안 쓰이나봄.

accept="video/*" 비디오 형식의 모든 확장자를 받음
accept="image/png, image/jpeg" 이미지 형식의 .png, .jpeg 종류 확장자를 받음

 저 두번째로 링크 건 사이트가 좋다.

 

 

  • php 코드

서버가 POST 요청을 받으면 아래를 실행  

*POST로 받은 제목 변수 저장

*POST로 받은 텍스트 변수 저장

사용자 닉네임을 찾을 $_SESSION['id'] 변수 저장

 

사용자 닉네임 찾는 쿼리

*찾아낸 사용자 닉네임 변수 저장

 

업로드한 이미지를 서버가 받았고 그 과정에 에러가 발생하지 않았다면 (아니면 오류문 출력)

받은 파일의 ㄹㅇ 찐 이름(사용자 파일 경로)에서 맨 뒤에 ㄹㅇ 찐찐 이름만 받아서(ex. yesterday.png) 변수 저장

그 파일을 저장할 내 서버의 경로 변수 저장

내 서버 경로랑 ㄹㅇ찐찐 이름이랑 합쳐서 변수 저장

 

내 서버의 임시 저장 경로에 있던 이미지 파일을 내가 정한 경로로 옮기는 데 성공하면 (아니면 오류문 출력)

내가 정한 경로를 다른 변수로 옮기고

INSERT문 실행

올린 게시물로 리다이렉션 (추가할 예정)

코드 종료

 

* 삼항식 사용.

사용자가 업로드해야 변수에 값이 들어오기 때문에 그 전까지는 오류문 출력됨

방법이 두 가지 생김

         1) 맨 위 php 코드에 필요한 변수 초기화 해두고 서버가 post 요청 받을 때 값 할당

         2) 서버가 post 요청 받을 때 삼항식으로 값 할당.

 

근데 이제 보니 if($_SERVER['REQUEST_METHOD'] === 'POST')  이거 쓰면 변수 초기화 필요 없네.. 

 

 

$_FILES 슈퍼 글로벌 변수에 대한 자료

더보기

종류 (두번째 인자?는 고정임)

 

1) $_FILES['인풋박스 name']['name']

 -> 사용자가 저장한 이름 (원래 이름)

 

2) $_FILES['인풋박스 name']['tmp_name']

-> 임시 파일 경로에 저장된 버전의 파일 이름

 

3) $_FILES['인풋박스 name'][size']

-> 받은 파일의 크기 (용량 검사해서 조건 줄 때 사용하는 듯)

 

4) $_FILES['인풋박스 name']['type']

-> MIME 타입. jpeg 등.

 

5) $_FILES['인풋박스 name']['error']

-> 업로드 중 발생한 오류 코드.

     0이면 오류 없음.

     1이면 최대 파일 용량 초과 (php.ini에서 수정; upload_max_filesize

      2이면 html 폼이 가질 수 있는 용량 초과 (php.ini에서 수정; MAX_FILE_SIZE)

      3이면 파일이 부분만 업로드 됨.

      4면 업로드된 파일 없음.

      6이면 임시 저장될 디렉토리가 php.ini에 설정되지 않음.

      7이면 writing the file to disk failed. 디스크에 파일을 쓰는 게 실패함..?

      8이면 php 확장자가 파일 업로드를 중단.

 

https://www.php.net/manual/en/reserved.variables.files.php

 

PHP: $_FILES - Manual

PHP is a popular general-purpose scripting language that powers everything from your blog to the most popular websites in the world.

www.php.net

 

  

 

이 변수를 출력해보면 "인풋박스 name/두번째인자 값" (image/jpeg) 같은 형식이라고 함.

 

 

 

♨ 겪은 문제들

정말 많은데 간추려보겠다..

 

1) 변수 초기화 문제

 

폼에서 post된 값을 php 변수에 저장하려는데

그걸 바로 그렇게 저장하면 업로드 페이지 딱 들어갈 때 변수에 값이 없어서 초기화를 하든 삼항식을 쓰든 해야하는데

변수에 post 값 할당하기 전에 if ($_SERVER['REQUEST_METHOD') === "POST") 넣어서 하니까 잘 됨.

당연함. 요청 넣기 전에 실행돼서 문제라면 요청 넣은 후에 실행되도록 하면 됨.

 

 

2)  SQL문에는 세션 변수 못 씀.

 

SQL문 안에서 쓸 땐 세션 변수의 값을 다른 변수에 저장해서 써야 함.

$id = $_SESSION['id']   -> $id를 SELECT문에 사용

 

 

3) 업로드한 파일을 저장할 내 서버의 파일 경로

내가 만약 /var/www/html/img에 파일을 저장할 생각이다~ 했을 때

 

1) 저 디렉토리의 사용자 권한을 drwxrw-rw--  www-data:www-data 로 해야 서버가 이미지를 내 서버에 저장 가능.

     웹 서버가 내 운영체제 파일 시스템에서 이 디렉토리를 사용할 수 있도록 권한을 주는 거.

sudo chmod 755 /var/www/html/img
sudo chown  www-data:www-data /var/www/html/img

 

2) 저장할 파일 경로를 변수에 저장할 때 

     $file = $_FILES['image']['name'] 

     $upload_dir = "/var/www/html/img"   =>> 이렇게 하면 안됨!!!!!

             -> "/var/www/html/img/"   =>> 이게 맞다. 그래야 이 뒤에 파일 이름이 붙어서 저 디렉토리에 저장됨.

 

3) DB에 INSERT는 됐는데 내 서버에 이미지가 안 들어왔다 하면 

    DB에 어떻게 저장됐는지 확인해보면 좋다.

 

 

4) 파일 이름 한국어 노노

MySQL은 아마 한국어로도 저장되는 것 같다.

근데 내 디렉토리에는 한국어로 저장이 안됨.

"안녕하세요.png" 라고 저장했다면 ".png"로 바뀜. 기껏 인사했는데!!!

 

인코딩하는 법이 있는 것 같았지만 일단 영어 제목으로 바꿔서 저장해뒀다.

 

 

5) header 함수 쓰기 바로 전에는 echo나 html코드 작성 노노 (오류문/경고문)

나 이제 갈 건데 거기서 뭐라뭐라 떠들어 봤자 나는 못 듣는 것이다.

 

 

 

 

대충 이정도로 정리할 수 있을 것 같다.

다른 페이지도... 화이팅..