서론
오늘은 파일 업로드 및 다운로드 기능을 구현해보자
최종화면


1. 파일 업로드
1.1. board_write.php (글 작성 기능 페이지)
<form action="/board/board_write_proc.php" method="POST" enctype="multipart/form-data">
<input type="text" name="title" placeholder="제목" required>
<textarea name="content" placeholder="내용을 입력하세요."></textarea>
<input type="file" name="upload_file" id="file_btn">
<div class="write_btn2">
<button type="submit">작성하기</button>
</div>
</form>
- <form>태그에 enctype="multipart/form-data"를 넣어줬다.
→ multipart/form-data는 파일,이미지 등 여러 데이터를 각 파트로 나눠 경계(boundary)로 구분해 전송하는 폼 데이터 포맷으로, 문자 자체를 변환하는 인코딩은 적용하지 않는다. - <input> 태그에 type이 file로 하나 추가해주고, 이후 php로 처리하기 위해 upload_file로 값을 지정해준다.
1.2. board_write_proc.php (글 작성 처리기능)
$title = $_POST['title'];
$content = $_POST['content'];
$author = $_SESSION['user_id'];
if (isset($_FILES['upload_file']) && $_FILES['upload_file']['error'] !== UPLOAD_ERR_NO_FILE) {
// 파일 업로드가 있는 경우만 확장자 검사
$filename = $_FILES['upload_file']['name'];//사용자가 올린 파일 이름 (ex: hello.jpg)
$tmp_path = $_FILES['upload_file']['tmp_name'];//서버가 저장하는 임시경로
$error = $_FILES['upload_file']['error'];//파일업로드 시 에러코드
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));//확장자 소문자로 변환
$allow_ext = ['jpg', 'jpeg', 'png', 'gif', 'mp4', 'txt', 'pdf', 'doc', 'hwp', 'ppt'];//허용된 확장자
// 허용되지 않은 확장자 알림
if (!in_array($ext, $allow_ext)) {
echo "<script>alert('허용되지 않는 파일 형식입니다.'); history.back();</script>";
exit;
}
- input으로 받은 값들을 각각 변수에 할당해준다.
- 파일이 업로드 됐을때 그리고 파일에러가 4(파일없음)일 때 확장자 검사를 진행한다.
- 파일 이름을 $filename으로 변수를 할당해준다.
- 웹 서버는 파일을 업로드 할 때 임시 디렉토리에 저장한다. 이 때 생성 되는 경로를 $tmp_path로 할당해줬다.
웹 서버가 임시로 저장하는 이유?
→여러 사용자가 동시에 파일을 전송해도 서버가 안정적으로 파일을 수신·검증한 뒤
최종 저장 단계로 안전하게 처리하기 위해서다. - 게시글 작성이 안될 시 이유를 알려주기 위해서 $error에 에러코드를 할당했다.
- 확장자를 소문자로 변환 후 $ext에 할당해줬다.
- 허용이 가능한 확장자만 사용하게 화이트 리스트를 만들어줬다 (내가 주로 자주 봤던 확장자로만 설정했다..)
$unique_idx = uniqid('', true); // 랜덤값
$unique_filename = $unique_idx . '.' . $ext;//랜덤값+확장자
$file_path = $_SERVER['DOCUMENT_ROOT']."/board/uploads/".$unique_filename; //저장할 파일 경로
- 파일들이 중복되지 않게 uniqid함수를 사용해서 랜덤값을 $unique_idx에 할당했다.
- 화이트 리스트를 통과한 확장자를 랜덤값 뒤에 붙여줬다.
- 파일을 저장할 경로를 $file_path에 할당해줬다.
if ($error === UPLOAD_ERR_OK) {
if (move_uploaded_file($tmp_path, $file_path)) {
// 파일과 함께 저장
$sql = "INSERT INTO board (title,author,content,post_date,file,views,likes) VALUES ('$title','$author','$content',now(),'$unique_filename',0,0)";
}else {
// 파일 이동 실패 → 파일 없이 저장
$sql = "INSERT INTO board (title,author,content,post_date,views,likes) VALUES ('$title','$author','$content',now(),0,0)";
}
}else if ($error === UPLOAD_ERR_INI_SIZE) {
echo "<script>alert('파일 사이즈가 너무 큽니다.'); history.back();</script>";
exit;
}
}else {
// 파일이 첨부되지 않은 경우
$sql = "INSERT INTO board (title,author,content,post_date,views,likes) VALUES ('$title','$author','$content',now(),0,0)";
}
$result = $mysqli->query($sql);
if ($result) {
echo "<script>
alert('게시글이 작성되었습니다.');
location.href='/board/board.php';
</script>";
} else {
echo "관리자에게 문의하세요";
}
?>
- 업로드 에러가 없는 경우 즉, 파일 업로드가 된 경우에 move_uploaded_file함수를 이용해
임시 경로에서 저장할 경로로 옮겨줬다. - 파일이 있을 때는 파일과 함께 DB에 저장
- 파일이 이동 시 실패했을 때 파일 없이 DB에 저장
- 에러가 사이즈에러와 같다면 "파일 사이즈가 너무 큽니다" 알림창 출력
- 파일이 없을 때도 파일 없이 DB에 저장
- 이렇게 SQL구문을 만들어놓고 실행
- DB구문이 성공했다면 게시글 작성 완료!
- UPLOAD_ERR_OK 등 에러메세지는 밑에 링크 참고
https://www.php.net/manual/en/features.file-upload.errors.php
PHP: Error Messages Explained - 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
1.3. 테스트


➡️테스트 파일을 만들어 준 뒤 업로드

➡️목록에 잘 출력된다.

➡️하지만 파일이 생성되지 않은 것을 확인했고 권한을 바꿔줬다.
➡️그런데 여전히 생성이 되지 않았고 찾아보니 웹 서버는 소유자가 아니었다



➡️웹 서버 계정을 알아냈고, uploads 디렉토리의 그룹 권한에 추가해줬다.
➡️uploads 디렉토리의 권한도 775로 그룹권한에 쓰기 기능을 추가해줬다.
→디렉토리는 쓰기권한이 있어야 생성/수정/삭제 등을 할 수 있다.
→파일 업로드는 파일을 해당 디렉토리에 생성을 해야해서 권한을 부여해야한다.


➡️다시 작성해보니 uploads 디렉토리에 파일이 생긴 것을 확인할 수 있다.
2. 파일 다운로드
2.1. board_read.php( 게시글 읽기 기능)
<div class="read_wrapper">
<div class="read_title">
<p><?=$row['title'] ?>
<div class="post_meta">
작성자: <?=$row['author']?> | 작성일: <?=$row['post_date']?>
</div>
</div>
<div class="read_content">
<p><?=nl2br($row['content'])?></p>
</div>
<div class="download_file">
<a href="/board/uploads/<?= $row['file']?>"download><?= $row['file']?></a>
</div>
<div class="btn3">
<form action="/board/board_modify.php">
<button type="submit">수정하기</button>
</form>
<form action="/board/board_delete.php">
<button type="submit">삭제하기</button>
</form>
</div>
</div>
- php echo를 이용해 화면에 출력했다.
- <a>태그를 사용해서 해당경로를 링크로 출력한다.
- <a>태그 download 속성을 이용해 다운로드 할 수 있도록 설정했다.
- 게시글 조회 후 링크를 클릭하면 다운로드 창이 열리고 사용자가 원하는 루트에 다운로드 할 수 있게 된다.
2.2. 테스트

➡️아까 작성한 글 조회하면 파일이 하단에 출력된다.

➡️클릭하면 다운로드 루트를 정할 수 있는데 다운로드 테스트 파일로 바탕화면에 저장해보자


➡️다운로드 성공!! 잘 다운로드 된 것을 확인할 수 있다.
'Web > Web 개발' 카테고리의 다른 글
게시판 수정 기능 만들기 (0) | 2025.06.01 |
---|---|
조회수/좋아요(취소) 기능 (0) | 2025.05.27 |
게시판 읽기 기능 만들기 (0) | 2025.05.19 |
게시판 페이징 기능 만들기 (0) | 2025.05.19 |
게시글 리스트 출력 만들기 (0) | 2025.05.13 |