본문으로 바로가기

폼 필드 디자인중 마지막을 장식할 파일 필드에 대한 내용입니다. 이 또한 브라우저 호환에 어려움을 겪곤 하는데, 관련해서 대안책들을 알아봅니다.

파일 필드 디자인 현황

파일 필드의 디자인은 브라우저마다 제 각각이며, 선택한 파일의 이름을 표시하는 부분과 업로드 버튼이 한데 묶여 있어 이를 각자 컨트롤할 수 있는 방법이 없다. 또한 스타일링을 위해 CSS를 적용해보면 어떤 것은 내부 박스에 영향을 주고 어떤것은 외부를 감싼 박스에만 영향을 미친다.

브라우저별 기본 파일 필드 모양

이미 기능과 디자인을 향상시킨 파일 업로드 관련 플러그인들이 많이 존재하니 가능하다면 그것들을 사용하길 바란다. 아래에서는 기본적인 파일 필드 디자인과 기능을 흉내내는 것에 대하여 다루어 본다.

파일 필드 버튼형 디자인

기본 구조는 아래와 같다.

<div class="filebox">
  <label for="ex_file">업로드</label>
  <input type="file" id="ex_file"> 
</div>
  • 상호작용을 위해 lable 요소의 for 속성과 input 요소의 id 값을 일치시킨다.
  • 단순히 버튼형으로 표기하기에 선택된 파일의 이름을 표시하려면 추가적인 코드가 필요하다.

CSS는 아래와 같다.

.filebox label {
  display: inline-block;
  padding: .5em .75em;
  color: #999;
  font-size: inherit;
  line-height: normal;
  vertical-align: middle;
  background-color: #fdfdfd;
  cursor: pointer;
  border: 1px solid #ebebeb;
  border-bottom-color: #e2e2e2;
  border-radius: .25em;
}

.filebox input[type="file"] {  /* 파일 필드 숨기기 */
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip:rect(0,0,0,0);
  border: 0;
}
  • lable 디자인을 원하는데로 꾸미면 된다.

See the Pen file button design by Douglas Ham (@uzugoer) on CodePen.

파일 필드 기능 흉내

위의 예제에 추가적인 코드를 삽입하여 파일 필드와 비슷한 기능을 흉내낸다.

기본 구조는 아래와 같다.

<div class="filebox">
  <input class="upload-name" value="파일선택" disabled="disabled">

  <label for="ex_filename">업로드</label> 
  <input type="file" id="ex_filename" class="upload-hidden"> 
</div>
  • input type="text" 필드를 disabled 형태로 삽입한다. 클래스명을 설정(upload-name)하고 기본적으로 보여질 문구를 value 값으로 입력한다.
  • label 요소의 for 속성과 file 필드의 id 값을 일치시킨다.
  • file 필드의 클래스명(upload-hidden)을 설정한다.

disabled 속성에 대한 스크린리더의 접근성 문제가 발생할 수 있다. 스크린리더중에 disabled 속성을 읽지 않는 것이 있고 이를 읽어 주는 것이 있어 중복 문구의 출력이 문제가 될 수 있겠다.

CSS는 아래와 같다.

.filebox input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip:rect(0,0,0,0);
  border: 0;
}

.filebox label {
  display: inline-block;
  padding: .5em .75em;
  color: #999;
  font-size: inherit;
  line-height: normal;
  vertical-align: middle;
  background-color: #fdfdfd;
  cursor: pointer;
  border: 1px solid #ebebeb;
  border-bottom-color: #e2e2e2;
  border-radius: .25em;
}

/* named upload */
.filebox .upload-name {
  display: inline-block;
  padding: .5em .75em;  /* label의 패딩값과 일치 */
  font-size: inherit;
  font-family: inherit;
  line-height: normal;
  vertical-align: middle;
  background-color: #f5f5f5;
  border: 1px solid #ebebeb;
  border-bottom-color: #e2e2e2;
  border-radius: .25em;
  -webkit-appearance: none; /* 네이티브 외형 감추기 */
  -moz-appearance: none;
  appearance: none;
}
  • 새로 추가된 input 필드의 디자인을 원하는데로 설정한다.

기능 구현을 위해 jQuery를 이용했다.

$(document).ready(function(){
  var fileTarget = $('.filebox .upload-hidden');

  fileTarget.on('change', function(){  // 값이 변경되면
    if(window.FileReader){  // modern browser
      var filename = $(this)[0].files[0].name;
    } 
    else {  // old IE
      var filename = $(this).val().split('/').pop().split('\\').pop();  // 파일명만 추출
    }
    
    // 추출한 파일명 삽입
    $(this).siblings('.upload-name').val(filename);
  });
}); 
  • multiple 속성에 대한 대체방안은 마련되지 않았다.

See the Pen file design by Douglas Ham (@uzugoer) on CodePen.

파일 필드 이미지 기능 추가

파일 업로드시 이미지 파일일 경우에 썸네일을 표시해주는 기능이다. 이는 [JavaScript] input:file 로컬 이미지 파일 미리보기 (preview local image file) 를 수정한 것이다. 문제가 생길경우, 링크의 원본을 사용하도록 하자.

기본 구조는 아래와 같다.

<div class="filebox preview-image">
  <input class="upload-name" value="파일선택" disabled="disabled" >

  <label for="input-file">업로드</label> 
  <input type="file" id="input-file" class="upload-hidden"> 
</div>
  • 미리보기 기능의 구분을 위해 .preview-image를 추가했다.
  • 이전 예제와 중복되는 부분의 설명은 제외했다.

CSS는 아래와 같다.

/* imaged preview */
.filebox .upload-display {  /* 이미지가 표시될 지역 */
  margin-bottom: 5px;
}

@media(min-width: 768px) { 
  .filebox .upload-display {
    display: inline-block;
    margin-right: 5px;
    margin-bottom: 0;
  }
}

.filebox .upload-thumb-wrap {  /* 추가될 이미지를 감싸는 요소 */
  display: inline-block;
  width: 54px;
  padding: 2px;
  vertical-align: middle;
  border: 1px solid #ddd;
  border-radius: 5px;
  background-color: #fff;
}

.filebox .upload-display img {  /* 추가될 이미지 */
  display: block;
  max-width: 100%;
  width: 100% \9;
  height: auto;
}
  • 이미지 업로드시 추가될 구조에 대한 스타일을 지정한다.
  • 이미지는 반응형으로 작동하고, 크기를 변경하려면 .upload-thumb-wrap의 너비를 수정하면 된다.
  • 이전 예제의 CSS에서 위의 내용을 추가로 삽입하면 된다.

기능 추가를 위해 jQuery 코드를 추가한다.

//preview image 
    var imgTarget = $('.preview-image .upload-hidden');

    imgTarget.on('change', function(){
        var parent = $(this).parent();
        parent.children('.upload-display').remove();

        if(window.FileReader){
            //image 파일만
            if (!$(this)[0].files[0].type.match(/image\//)) return;
            
            var reader = new FileReader();
            reader.onload = function(e){
                var src = e.target.result;
                parent.prepend('<div class="upload-display"><div class="upload-thumb-wrap"><img src="'+src+'" class="upload-thumb"></div></div>');
            }
            reader.readAsDataURL($(this)[0].files[0]);
        }

        else {
            $(this)[0].select();
            $(this)[0].blur();
            var imgSrc = document.selection.createRange().text;
            parent.prepend('<div class="upload-display"><div class="upload-thumb-wrap"><img class="upload-thumb"></div></div>');

            var img = $(this).siblings('.upload-display').find('img');
            img[0].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enable='true',sizingMethod='scale',src=\""+imgSrc+"\")";        
        }
    });
  • 이전 예제에 위의 코드를 더한다.
  • 임시로 만든 예제라 소스 원본을 사용하길 권한다.

See the Pen file image preview by Douglas Ham (@uzugoer) on CodePen.

좀 더 많은 기능을 구현하려면 Using files from web applicationsReading files in JavaScript using the File APIs 을 참고하라.