<현재 환경 세팅>
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. 작성날짜
컬럼 자료형에 대한 자료
https://devdhjo.github.io/mysql/2020/01/30/database-mysql-003.html
< 신경 써야할 요소 >
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 테이블
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
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
enctype="multipart/form-data" :
모든 문자를 인코딩하지 않음. 파일 업로드할 때 주로 사용
enctype="application/x-www-form-urlencoded" :
모든 문자 encodeURI()로 인코딩해서 보냄. 기본 값
enctype="text/plain" :
공백 문자만 "+"로 인코딩됨. 디버깅할 때 쓰이고 input type="submit"이나 button 태그에서도 가능
type = "file" :
인풋 박스의 타입을 파일 업로드 형식으로 설정함.
(password면 박스 안 글자가 기호로 바뀌고 submit이면 제출버튼으로 바뀌는 것처럼)
accept="image/*" :
파일 형식 지정. type="file"을 더 선호해서 잘 안 쓰이나봄.
저 두번째로 링크 건 사이트가 좋다.
- 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
이 변수를 출력해보면 "인풋박스 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코드 작성 노노 (오류문/경고문)
나 이제 갈 건데 거기서 뭐라뭐라 떠들어 봤자 나는 못 듣는 것이다.
대충 이정도로 정리할 수 있을 것 같다.
다른 페이지도... 화이팅..
'자습' 카테고리의 다른 글
웹 개발: 마이페이지 (2) | 2024.11.25 |
---|---|
CTF: 어드민은 내 것이다. (0) | 2024.11.23 |
내가 만든 사이트에 세션, 로그아웃, DB 연동 파일 분리 적용하기 (1) | 2024.11.12 |