PHP

php mail() 첨부파일 메일 보내기 1탄_미리보기

raim-2 2023. 5. 30. 00:17
반응형

php mail() 사용해 첨부파일 메일 보내기

 

첫번째 포트폴리오 제작 시 수업에서 들었던대로 만든 php 파일로 문의하기 사이트를 만들었다.

당시에는 기간 안에 검사를 받아야 된다는 생각에 구현하고 싶었던 첨부파일 부분은 그냥 넘어갔다.

오류 나는 부분 고치면서 이 부분도 같이 구현해보려 했다가 개고생....🤦‍♀️

 

*내가 보기 위해 정리한 것임. 틀리거나 코드가 더러울 수 있음 주의*

 

👉 이번에 구현하려 했던 부분 2가지

  1. 선택한 파일의 미리보기 이미지 보여주기
  2. php mail() 사용해 첨부파일 메일 보내기 + 메일 본문에서 이미지 미리보기
    👉 얘는 됐다 안됐다 반복중.....🤦‍♀️(해결)

미리보기 이미지 구현한 모습

 

✔️ 파일 미리보기 

우선 해결한 1번부터 정리!

1) form의 action값은 mail.php로, method는 post, enctype는 multipart/form-data으로 지정해준다.

2) 업로드할 파일 타입을 정해줄 때는 input 내에 accept 속성을 사용한다. (모든 이미지 가능 accept="image/*")

 

👉method 특성이 post인 경우, enctype은 양식 제출 시 데이터의 MIME 유형을 나타낸다. multipart/form-data 값은 <input type="file">이 존재하는 경우 사용한다.

https://developer.mozilla.org/ko/docs/Web/HTML/Element/form

 

* 굳이 dl태그에 안넣어도 될 것 같은데 다른 것 수정하느라 바꾸진 않았음

<form action="mail.php" method="post" enctype="multipart/form-data">
<ul>

 ⁝

  <li class="uploadBox">
    <dl>
       <dt><label for="file">첨부파일</label></dt>
       <dd>
         <input type="file" id="file" name="file" accept="image/*">
         <!-- multiple - 여러개 선택 가능,  accept="image/png, image/jpeg" -->
         <div class="imgPreview off"></div> 
       </dd>
     </dl>
  </li>
 </ul>
</form>

 

3) input file 타입은 별도의 파일이 첨부되지 않았을 경우 input 내 span태그로 ''선택된 파일 없음" 이 표현된다.

이 span 태그는 수정이 불가하므로, 파일이 선택되지 않았을 때 얘를 대체할 부분을 자바스크립트로 처리했다.

 

input file 타입 속성에는 files라는 속성이 있음 👉 얘를 이용하면 FileList 객체를 반환 가능하다.

파일을 선택하지 않았을 때 files.length는 0이므로 얘가 0일때 위 기본 span 태그를 대체할 애를 만들어주면 된다.

 

files 속성 이미지

 

그리고선, css로 기존 span 태그 위에 위치하게끔 만들어주면 끝!

기본 span 태그 대체

let uploadBox = document.querySelector('.uploadBox dl')
let uploadInput = document.querySelector('.uploadBox input')
let uploadData = document.querySelector('.uploadBox dd')
let imgPreview = document.querySelector('.imgPreview');

if(uploadInput.files.length === 0) {
    let uploadResult = document.createElement('span'); //span 태그 생성
    uploadResult.innerText = '*선택된 파일이 없습니다. 이미지 형식만 가능합니다.';
    uploadResult.className = 'nofile'; // class 추가
    uploadData.appendChild(uploadResult); // dd 내부 맨 뒤에 uploadResult를 추가한다.
}

 

4) 다음으로는 파일을 첨부했을 때의 이벤트를 설정하는 단계이다.

input의 요소값이 변경될 때 이벤트를 적용할 것이므로 'change' 이벤트 사용한다.

 

👉 input에 첨부파일을 선택할 때는 두 가지 상황이 있다. 처음 선택할 때는 위에서 만들었던 span 태그를 없애줘야 하고, 다시 선택하는 경우 기존에 선택했던 이미지 요소가 남기 때문에 해당 이미지를 없애줘야 한다. 이 때는 input 내에 첨부파일이 선택이 된 상태이므로 files.length가 0이 아니다라는 조건을 이용한다.

  • 첨부파일을 처음 선택할 때 
  • 첨부파일을 선택한 후 다시 다른 파일을 선택할 때

👉 removeChild()를 사용해 해당 span 태그와 기존 미리보기 이미지를 없애주고, 그렇지 않을 땐 fileReader 함수를 실행한다.

 

removeChild()는 DOM트리에서 해제해 해당 노드값을 반환한다.
노드 자체를 삭제하는 것이 아니라 메모리에 남기 때문에 참조하여 다시 사용할 수도 있다.
다만, 해제한 노드를 참조해 사용하지 않으면 자바스크립트 엔진에 의해 잠시 뒤 메모리에서 삭제된다.
바로 노드 자체를 삭제하길 원하면 remove()를 사용한다.

https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild

 

uploadInput.addEventListener('change', updateFile);

function updateFile(e) {
    let uploadFile = e.target.files; // FileList 객체
     //console.log(uploadFile); // { 0: File, 1: File, length: 2 }
     //console.log(uploadFile[0]); //업로드된 파일의 정보를 보여줌
    
    if(uploadFile.length !== 0) {
        let lastChildText = uploadData.lastChild.innerText;
        if(lastChildText == '*선택된 파일이 없습니다. 이미지 형식만 가능합니다.' || lastChildText =='유효한 파일 형식이 아닙니다.') {
            uploadData.removeChild(uploadData.lastChild);
        } 
        fileReader(e);
    }
}

 

5) 여기서 포인트! 미리보기를 위해 사용한 fileReader api

👉 간단하게 정리하자면, 아래의 과정대로 진행했다.

 

  1. fileReader 생성 후
  2. readAsDataURL() 메소드를 이용 👉 선택한 파일을 읽고, base64 형식으로 인코딩한다.
    이 때 인코딩한 데이터는 fileReader의 result에 저장된다.
  3. 파일 읽기가 끝나면, load 이벤트가 실행된다. 
  4. 미리보기가 될 이미지 태그의 src에 fileReader로 읽어와 result에 저장된 데이터를 할당한다.

그 외에 css 처리해야 되는 부분은 css로 스타일을 준 후 classList.add/remove를 사용해 클래스 적용여부를 처리해줬다.

 

function fileReader(e) {
    let uploadFile = e.target.files; // FileList 객체

    //validFileType() 매개변수로 file객체 받아 type이 fileTypes 값과 동일한지 판단
    const fileTypes = [
    "image/apng",
    "image/bmp",
    "image/gif",
    "image/jpeg",
    "image/pjpeg",
    "image/png",
    "image/svg+xml",
    "image/tiff",
    "image/webp",
    "image/x-icon"
    ];

    function validFileType(file) {
    return fileTypes.includes(file[0].type);
    }

    //파일리더 api
    let fileReader = new FileReader(); //객체 생성

    //주소 반환해라_이미지 -> base64로 인코딩(클라이언트 측에서만)
    fileReader.readAsDataURL(e.target.files[0]); //파일 읽기
    fileReader.onload = function(e) { //파일 읽기 성공 시 load 이벤트 동작
        if (imgPreview.children.length == 1 ) {
            imgPreview.classList.remove('on')
            imgPreview.classList.add('off') 
            imgPreview.removeChild(imgPreview.firstChild);
        }
        
        if(validFileType(uploadFile)) {
            let imgInList = document.createElement('img');
            imgInList.src = e.target.result;
            imgInList.alt = '첨부파일 이미지 미리보기';
            imgPreview.appendChild(imgInList);

            imgPreview.classList.remove('off')
            imgPreview.classList.add('on') 
        } else {
            let uploadResult = document.createElement('span');
            uploadResult.innerText ='유효한 파일 형식이 아닙니다.';
            uploadResult.className = 'invalidType';
            uploadData.appendChild(uploadResult);
        }
    } 
}

 

 

* 참고 사이트