
기본 파일 첨부 방식은 브라우저 기본 디자인이라서 수정이 어려워서 modal 창을 활용해 보았습니다.
팝업을 통해 드래그 앤 드롭과 클립보드 복사 또는 찾아보기 모두 지원해서 범용으로 사용하기 좋습니다.
웹 환경에서 파일을 다루는 방식은 단순히 데이터를 서버로 전송하는 것을 넘어, 사용자가 느끼는 편의성과 직결되는 듯합니다. 기본 파일 입력 창의 투박함을 벗어나, Bootstrap 5의 디자인 시스템을 활용해 조금 더 정돈된 인터페이스를 구성해보았습니다. 특히 사용자가 파일을 찾기 위해 경로를 탐색하는 번거로움을 줄이고, 직관적인 드래그 앤 드롭이나 클립보드 붙여넣기(Ctrl+V) 같은 현대적인 경험을 제공하는 데 초점을 맞추었습니다.

사용자 경험을 위한 인터페이스 구성
화면에서 가장 먼저 마주하는 것은 간결한 입력 필드와 아이콘입니다. 파일 첨부 버튼을 눌렀을 때 비로소 모달(Modal) 창이 열리며 작업 공간이 확보됩니다. 이러한 방식은 좁은 화면에서도 공간을 효율적으로 사용할 수 있게 하며, 사용자의 시선을 필요한 순간에만 집중시키는 효과가 있는 것으로 보입니다.
모달 내부에는 드래그 앤 드롭이 가능한 영역을 배치하여, 마우스 조작만으로도 손쉽게 파일을 등록할 수 있도록 구성했습니다. 이미지, 문서, 혹은 전체 파일 등 목적에 따라 허용되는 파일 형식을 다르게 설정함으로써, 시스템이 의도한 데이터만 안전하게 받을 수 있도록 유도합니다.

화면과 모달의 연결
사용자가 버튼이나 입력창을 클릭했을 때 모달을 호출하고, 선택된 파일의 유형(이미지 또는 문서)을 스크립트로 전달하는 구조입니다.
<!-- 메인 입력 화면 (test-file.php 일부) -->
<label class="form-label fw-bold small text-secondary">사업자등록증 (Image Only)</label>
<div class="input-group mb-3">
<button class="btn btn-primary" type="button"
onclick="setTarget('capcorp', 'IMAGE')"
data-bs-toggle="modal" data-bs-target="#upModal" title="이미지 업로드">
<i class="bi bi-images"></i>
</button>
<input type="text" class="form-control" id="view_capcorp" readonly
placeholder="이미지를 선택하세요"
onclick="setTarget('capcorp', 'IMAGE')"
data-bs-toggle="modal" data-bs-target="#upModal">
<input type="hidden" name="capcorp" id="val_capcorp" value="">
</div>
비동기 처리를 통한 데이터의 이동
파일이 선택되는 순간, 화면은 새로고침 없이 조용히 데이터를 처리해야 합니다. 이를 위해 AJAX 방식을 채택하여, 사용자가 인식하지 못하는 사이에 파일을 임시 경로(/file/tmp)로 전송합니다. 이 과정에서 서버는 파일의 확장자를 검증하고, 보안상 위험할 수 있는 실행 파일들을 걸러내는 역할을 수행합니다.
임시 폴더에 저장된 파일은 최종적으로 폼(Form)이 전송될 때 실제 경로로 이동하게 됩니다. 이는 사용자가 글을 작성하다가 이탈했을 때 불필요한 파일이 실제 서비스 폴더에 남는 것을 방지하기 위한 흐름으로 해석됩니다.

서버 측의 검증과 저장
서버는 클라이언트가 요청한 파일 타입(IMAGE, DOC, ALL)에 맞춰 확장자를 엄격히 검사한 후, 임시 파일명을 생성하여 반환합니다.
<?php
// 서버 처리 (test-file-upload.php 일부)
header('Content-Type: application/json');
$dir = $_SERVER['DOCUMENT_ROOT'] . '/file/tmp/';
try {
$file = $_FILES['file'];
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$type = $_POST['type'] ?? 'ALL';
// 허용 확장자 정의
$imgs = ['jpg','jpeg','png','gif','webp'];
$docs = ['txt','doc','docx','pdf','xls','xlsx'];
// 타입별 검증 로직
if ($type === 'IMAGE' && !in_array($ext, $imgs)) throw new Exception('이미지만 가능');
if ($type === 'DOC' && !in_array($ext, $docs)) throw new Exception('문서만 가능');
$name = date('YmdHis') . '_' . uniqid() . '.' . $ext;
move_uploaded_file($file['tmp_name'], $dir . $name);
echo json_encode(['status' => 'OK', 'name' => $name]);
} catch (Exception $e) {
echo json_encode(['status' => 'ERR', 'msg' => $e->getMessage()]);
}
?>
선택을 되돌리는 과정
사용자는 언제든 실수를 할 수 있으며, 방금 올린 파일을 취소하고 싶어 할 수 있습니다. 파일이 업로드된 직후 나타나는 '취소(X)' 버튼은 이러한 심리를 배려한 장치입니다. 이 버튼을 누르면 화면에 표시된 파일명은 사라지고, 기존에 저장되어 있던 값이 있다면 그 값으로 조용히 복구됩니다.
단순히 입력창을 비우는 것을 넘어, '원래 상태'를 기억하고 있다가 되돌려준다는 점이 이 로직의 핵심인 듯합니다.
// 자바스크립트 취소 로직 (func.upload.php 내 스크립트)
function cancelUpload(id, originVal) {
// Hidden 값과 보여지는 Input 값을 원래 데이터(originVal)로 복구
document.getElementById("val_" + id).value = originVal;
document.getElementById("view_" + id).value = originVal;
// 취소 버튼은 다시 숨김 처리
document.getElementById("btn_cancel_" + id).classList.add("d-none");
}
이러한 일련의 과정들은 단순히 기능을 구현하는 것을 넘어, 웹이라는 공간에서 사용자가 느끼는 답답함을 조금이나마 해소하려는 노력의 기록으로 남을 것 같습니다.