[포스코x코딩온] KDT-Web-8 5주차 회고2 - form, 동적 form, 파일 업로드

Yunes·2023년 8월 3일
0

[포스코x코딩온]

목록 보기
12/47
post-thumbnail

📚 서론

이 포스트는 form 태그의 요소와 속성, 특징, 동적 form 전송으로 ajax, axios, fetch, 그리고 body-parser 만으로 불가능했던 파일 업로드에 대해서 정리한다.

📘 form

form 태그는 입력된 데이터를 한번에 서버로 전송하기 위해 사용한다. 사용자와 웹사이트 / 어플리케이션이 상호 작용하는 기술 중 하나이다.

📗 form 요소

  • input : 입력 컨트롤 정의
  • select : 드롭다운 목록 정의
  • label : 입력 요소 레이블 정의, 그 자체로는 효과가 없으나 input 태그를 제어하여 상태값을 변경하도록 돕는 역할을 한다.
    • 레이블 태그 클릭시 버튼만이 아니라 텍스트 영역도 포함하도록 하여 사용성이 높아진다.
    • label 태그의 for 속성을 통해 input 태그의 id와 매핑시킨다.
  • fieldset, legend : 폼 엘리먼트들을 그룹화할 때 사용, legend 로 그룹화한 폼 엘리먼트들의 이름을 지정
<!-- for 속성을 통해 태그를 분리할 수도 있다 -->
<input id="fruitItem" type="checkbox" /> 
<label for="fruitItem">바나나</label>

📗 form 속성

  • action : 폼을 전송할 서버 쪽 스크립트 파일 지정
    • 전송되는 서버 url 혹은 html 링크
  • name : 폼을 식별하기 위한 이름 지정
  • accept-charset : 폼 전송에 사용할 문자 인코딩 지정
  • target : action 에서 지정한 스크립트 파일을 현재 창이 아닌 다른 위치에 열도록 지정
  • method : 폼을 서버에 전송할 http 메소드 지정 (GET / POST)
    • get : 256 ~ 4096 byte 까지만 전송 가능
    • post : 입력 내용의 길이 제한 없다
  • autocomplete : 자동완성. on 으로 명시하면 브라우저는 사용자가 이전에 입력했던 값들을 기반으로 사용자가 입력한 값과 비슷한 값들을 드롭다운 옵션으로 보여준다.
  • enctype : 폼 데이터가 서버로 제출될 때 해당 데이터가 인코딩되는 방법 명시
    • application/x-www-form-urlencoded 기본값, 모든 문자들은 서버로 보내기 전에 인코딩됨을 명시
    • multipart/form-data 모든 문자를 인코딩하지 않음을 명시. 이 방식은 form 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용
    • text/plain 공백문자는 "+" 기호로 변환하나 나머지 문자는 모두 인코딩되지 않음을 명시
  • novalidate : 폼 데이터를 서버로 제출시 해당 데이터의 유효성을 검사하지 않음을 명시
<form name="profile" action="/" autocomplete="on">

📕 input 태그

사용자가 정보를 입력하는 부분을 만들어야 할때 사용한다.

  • type : 태그 모양을 변경 | text / radio / checkbox / password / button / hidden / fileupload / submit / reset
    • hidden : 서버로 보내는 값들을 보내는 필드(사용자에게 보이지 않는다)
    • text : 한 줄짜리 텍스트를 입력할 수 있는 텍스트 상자
    • tel : 전화번호 입력 필드
    • url : url 주소 입력 필드
    • email : 메일 주소 입력 필드 (@ 가 들어갔는지 검사)
    • password : 비밀번호 입력 필드
    • number : 숫자 조절
    • range : 숫자 범위 조절하는 슬라이드 막대
    • color : 색상표
    • checkbox : 체크박스, 2개 이상 선택 가능
    • radio : 라디오 버튼, 1개만 선택
    • datetime : 국제 표준시 (UTC) 로 설정된 날짜와 시간 (연, 월, 일, 시, 분, 초, 분할 초)
    • datetime-local : 사용자가 있는 지역을 기준으로 한 날짜와 시간 (연, 월, 일, 시, 분, 초, 분할 초)
    • date : 사용자 지역을 기준으로 한 날짜 (연, 월, 일)
    • month : 사용자 지역을 기준으로 한 날짜 (연, 월)
    • week : 사용자 지역을 기준으로 한 날짜 (연, 주)
    • time : 사용자 지역을 기준으로 한 시간 (시, 분, 초, 분할 초)
    • button : 버튼을 만들어 클릭했을 때 이벤트를 발생시킬 수 있고 submit과 차이가 있다.
    • file : 파일을 첨부할 수 있는 버튼 (accept 로 제출가능한 파일 양식 지정 가능)
    • submit : 서버전송 버튼
    • reset : 리셋 버튼, 사용자가 입력한 모든 정보를 지울 수 있다.
  • name : 이름 지정
  • readonly : 읽기전용
  • maxlength : 태그 최대 글자수 지정
  • required : 필수태그로 지정
  • placeholder : 힌트 제공
  • pattern : 정규표현식 사용하여 유효한 값 입력받을 때 사용
  • autofocus : 페이지를 불러오자 마자 특정 부분에 마우스 커서가 표시되도록 한다
  • autocomplete : 자동완성 설정
  • max / min : input 최대값과 최소값 지정
  • step : 숫자의 간격 설정, input 이 date, datetime, datetime-local, month, week, time, number, range 일때 사용가능

📕 select 태그

드롭다운에서 선택할 수 있는 양식 생성
select 요소 내부의 option 요소는 드롭다운 리스트의 각 옵션을 정의한다.

  • size : 한번에 표시할 항목 수
  • multiple : 다중선택 허용할지 여부 지정
  • optgroup : 목록들을 그룹화
  • label : 그룹 이름 지정
  • option : 목록을 나타낸다.
  • autofocus : 페이지가 로드될 때 자동으로 포커스가 드롭다운 리스트로 이동됨을 명시
  • disabled : 해당 드롭다운 리스트가 비활성화됨을 명시
  • form : 해당 드롭다운 리스트가 포함될 하나 이상의 form 요소 명시
  • name : 드롭다운 리스트의 이름 명시
  • required : 폼 데이터가 서버로 제출되기 전 사용자가 반드시 드롭다운 리스트의 값을 선택해야 한다.
<select name="pets" id="pet-select">
      <option value="">--Please choose an option--</option>
      <option value="dog">Dog</option>
      <option value="cat">Cat</option>
      <option value="hamster">Hamster</option>
</select>

📕 datalist 태그

  • 입력값 후보 지정
  • 내부에 option 태그 사용
  • select 태그는 선택만 할 수 있으나 datalist 는 리스트에서 한가지를 선택하거나 키워드에 입력할 수 있다.
<datalist>
  <option value="apple">사과</option>
  <option value="banana">바나나</option>
  <option value="grape">포도</option>
</datalist>

📕 button 태그

버튼 요소, 페이지에 버튼을 넣고 form 전송하거나 reset 할때 사용

  • submit : form 제출, 기본값 (IE 는 button 이 기본값)
  • reset : form 리셋
  • button : 버튼의 형태만 만든다. 눌리거나 했을때 다른 동작을 하려면 따로 함수를 연결해야 한다.

input type=button 과 button 의 차이점

  • <input> 태그는 종료태그 없이 type 속성 이용해 버튼에 글자나 이미지 넣지만, <button> 태그는 시작태그와 종료태그 사이에 글자나 이미지 넣는다.

  • 항상 <button> 태그의 type 속성을 명시해줘야 한다. 왜냐하면, IE(익스플로러) 경우 기본 타입이 button 이지만, 타 브라우저 경우 기본 타입이 submit 이기 때문이다.

  • <form> 태그 안에서 버튼 만들 땐, 반드시 <input> 태그 이용해 버튼 만들어야 한다. 왜냐하면, 타 브라우저가 <button> 태그의 속성값을 전송하는 반면, IE는 시작태그와 종료태그 사이의 텍스트나 이미지 전송하기 때문이다.
    출처 : inpa.tistory - form 태그정리

📗 form 정보 받기

app.get("/getForm", (req, res) => {
  console.log(req.query);
  res.render("result", {
    title: "GET 요청 폼 결과 확인하기",
    userInfo: req.query,
  });
});

app.post("/postForm", (req, res) => {
  console.log(req.body);
  res.render("result", {
    title: "POST 요청 폼 결과 확인하기",
    userInfo: req.body,
  });
});

GET 메서드는 쿼리스트링을 사용하므로 req.query 를 통해 클라이언트에서 URL 쿼리를 통해 보낸 정보를 얻을 수 있고

POST 메서드는 body 를 통해 데이터를 보내므로 req.body 를 통해 form 정보를 받을 수 있다.

res.render 를 통해 받은 데이터를 가공, 수정하여 화면에 보낼 수 있다.

📗 form 유효성 검사

  • required : 양시 제출전 채워야 하는지 여부 지정
  • minlength, maxlength : 텍스트 데이터의 최소 및 최대 길이 지정
  • min, max : 숫자 입력 유형의 최소값과 최대값 지정
  • type : 데이터가 숫자, 이메일 주소, 기타 유형이어야 하는지 여부 지정
  • pattern : 정규표현식 지정

📕 정규표현식

hamait - 정규표현식 티스토리에 정리가 잘 되어있으니 해당 블로그를 참고하자.

표현식의미
^x문자열의 시작을 표현하며 x 문자로 시작됨을 의미한다.
x$문자열의 종료를 표현하며 x 문자로 종료됨을 의미한다.
.x임의의 한 문자의 자리수를 표현하며 문자열이 x 로 끝난다는 것을 의미한다.
x+반복을 표현하며 x 문자가 한번 이상 반복됨을 의미한다.
x?존재여부를 표현하며 x 문자가 존재할 수도, 존재하지 않을 수도 있음을 의미한다.
x*반복여부를 표현하며 x 문자가 0번 또는 그 이상 반복됨을 의미한다.
x|yor 를 표현하며 x 또는 y 문자가 존재함을 의미한다.
(x)그룹을 표현하며 x 를 그룹으로 처리함을 의미한다.
(x)(y)그룹들의 집합을 표현하며 앞에서 부터 순서대로 번호를 부여하여 관리하고 x, y 는 각 그룹의 데이터로 관리된다.
(x)(?:y)그룹들의 집합에 대한 예외를 표현하며 그룹 집합으로 관리되지 않음을 의미한다.
x{n}반복을 표현하며 x 문자가 n번 반복됨을 의미한다.
x{n,}반복을 표현하며 x 문자가 n번 이상 반복됨을 의미한다.
x{n,m}반복을 표현하며 x 문자가 최소 n번 이상 최대 m 번 이하로 반복됨을 의미한다.
[]범위, [a-z] 는 a~z / [가-힣] 는 한글의 모든 음절
{}개수, {2, 4} 는 2개부터 4개까지
[^xy]not 을 표현하며  x 및 y 를 제외한 문자를 의미한다.
[x-z]range를 표현하며 x ~ z 사이의 문자를 의미한다.
\^escape 를 표현하며 ^ 를 문자로 사용함을 의미한다.
\bword boundary를 표현하며 문자와 공백사이의 문자를 의미한다.
\Bnon word boundary를 표현하며 문자와 공백사이가 아닌 문자를 의미한다.
\ddigit 를 표현하며 숫자를 의미한다.
\Dnon digit 를 표현하며 숫자가 아닌 것을 의미한다.
\sspace 를 표현하며 공백 문자를 의미한다.
\Snon space를 표현하며 공백 문자가 아닌 것을 의미한다.
\ttab 을 표현하며 탭 문자를 의미한다.
\vvertical tab을 표현하며 수직 탭(?) 문자를 의미한다.
\wword 를 표현하며 알파벳 + 숫자 + _ 중의 한 문자임을 의미한다.
\Wnon word를 표현하며 알파벳 + 숫자 + _ 가 아닌 문자를 의미한다.

📘 동적 form 전송

📗 ajax

  • 자바스크립트를 이용해 클라이언트와 서버 간에 데이터를 주고 받는 비동기 HTTP 통신

📕 장점

  • jQuery 를 통해 쉽게 구현
  • Error, Success, Complete 의 상태를 통해 실행 흐름 조절

📕 단점

  • jQuery 를 사용해야 간편하고 호환성 보장
  • Promise 기반 아님
app.get("/ajax", (req, res) => {
  console.log("backend", req.query);
  res.send(req.query);
});

app.post("/ajax", (req, res) => {
  console.log("backend", req.body);
  res.send(req.body);
});
function ajaxGet() {
  $.ajax({
    type: "GET",
    url: "/ajax",
    data,
    success: function (res) {
    },
  });
}
function ajaxPost() {
  $.ajax({
    type: "POST",
    url: "/ajax",
    data,
    success: function (res) {
    },
  });
}

📗 axios

  • Node.js 와 브라우저를 위한 Promise 기반 HTTP 클라이언트
  • 비동기 HTTP 통신 가능, return 이 Promise 객체로 온다.
  • 서버에서는 node.js 의 http 모듈을 사용하고 클라이언트에서는 XMLHttpRequests 를 사용한다.
npm i axios

📕 장점

  • Promise 기반으로 만들어졌고 브라우저 호환성이 뛰어나다.

📕 단점

  • 모듈 설치 or 호출을 해줘야 사용이 가능하다.
app.get("/axios", (req, res) => {
  console.log("backend", req.query);
  res.send(req.query);
});

app.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
function axiosGet() {
  axios({
    method: "GET",
    url: "/axios",
    params: data,
  }).then(function (res) {
  });
}

async function axiosPost() {
  const result = await axios({
    method: "POST",
    url: "/axios",
    data,
  });
}

📕 axios config

{
  // 요청에 사용될 서버 URL, 오직 url 만 필수
  url: '/user',

  // 지정하지 않으면 GET 
  method: 'get', // 기본값

  // `url`이 절대값이 아닌 경우 `baseURL`은 URL 앞에 붙습니다.
  // 상대적인 URL을 인스턴스 메서드에 전달하려면 `baseURL`을 설정하는 것은 편리합니다.
  baseURL: 'https://some-domain.com/api',


  // `transformRequest`는 요청 데이터를 서버로 전송하기 전에 변경할 수 있게 해줍니다.
  // 이것은 'PUT', 'POST', 'PATCH', 'DELETE' 메소드에서만 적용됩니다.
  // 마지막 함수는 Buffer, ArrayBuffer, FormData 또는 Stream의 인스턴스 또는 문자열을 반환해야 합니다.
  // 헤더 객체를 수정할 수 있습니다.
  transformRequest: [function (data, headers) {
    // 데이터를 변환하려는 작업 수행

    return data;
  }],

  // `transformResponse`는 응답 데이터가 then/catch로 전달되기 전에 변경할 수 있게 해줍니다.
  transformResponse: [function (data) {
    // 데이터를 변환하려는 작업 수행

    return data;
  }],

  // `headers`는 사용자 지정 헤더입니다.
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params`은 요청과 함께 전송되는 URL 파라미터입니다.
  // 반드시 일반 객체나 URLSearchParams 객체여야 합니다.
  // 참고: null이나 undefined는 URL에 렌더링되지 않습니다.
  params: {
    ID: 12345
  },

  // `paramsSerializer`는 `params`의 시리얼라이즈를 담당하는 옵션 함수입니다.
  // (예: https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data`는 요청 바디로 전송될 데이터입니다.  
  // 'PUT', 'POST', 'PATCH', 'DELETE' 메소드에서만 적용 가능합니다.
  // `transformRequest`가 설정되지 않은 경우 다음 타입 중 하나여야 합니다.
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 브라우저 전용: FormData, File, Blob
  // - Node 전용: Stream, Buffer
  data: {
    firstName: 'Fred'
  },

  // 바디로 전송하는 데이터의 대안 문법
  // POST 메소드
  // 키가 아닌 값만 전송됩니다.
  data: 'Country=Brasil&City=Belo Horizonte',

  // `timeout`은 요청이 시간 초과되기 전의 시간(밀리초)을 지정합니다.
  // 요청이 `timeout`보다 오래 걸리면 요청이 중단됩니다.
  timeout: 1000, // 기본값은 `0` (타임아웃 없음)

  // `withCredentials`은 자격 증명을 사용하여 사이트 간 액세스 제어 요청을 해야 하는지 여부를 나타냅니다.
  withCredentials: false, // 기본값

  // `adapter`'은 커스텀 핸들링 요청을 처리할 수 있어 테스트가 쉬워집니다.
  // 유효한 Promise 응답을 반환해야 합니다. (lib/adapters/README.md 참고)
  adapter: function (config) {
    /* ... */
  },

  // `auth`는 HTTP Basic 인증이 사용되며, 자격 증명을 제공합니다.
  // `auth`를 사용하면, `Authorization` 헤더가 설정되어 `headers`를 사용하여 설정한 기존의 `Authorization` 사용자 지정 헤더를 덮어씁니다.
  // 이 파라미터를 통해 HTTP Basic 인증만 구성할 수 있음을 참고하세요.
  // Bearer 토큰 등의 경우 `Authorization` 사용자 지정 헤더를 대신 사용합니다.
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // `responseType`은 서버에서 받는 데이터의 타입입니다.
  // 옵션: 'arraybuffer', 'document', 'json', 'text', 'stream'
  // 브라우저 전용: 'blob'
  responseType: 'json', // 기본값

  // `responseEncoding`은 응답 디코딩에 사용할 인코딩입니다.
  // Node.js 전용
  // 참고: 클라이언트 사이드 요청 또는 `responseType`이 'stream'이면 무시합니다.
  responseEncoding: 'utf8', // 기본값

  // `xsrfCookieName`은 xsrf 토큰 값으로 사용할 쿠키의 이름입니다.
  xsrfCookieName: 'XSRF-TOKEN', // 기본값

  // `xsrfHeaderName`은 xsrf 토큰 값을 운반하는 HTTP 헤더의 이름입니다.
  xsrfHeaderName: 'X-XSRF-TOKEN', // 기본값

  // `onUploadProgress`는 업로드 진행 이벤트를 처리합니다.
  // 브라우저 전용
  onUploadProgress: function (progressEvent) {
    // 업로드 진행 이벤트 작업 수행
  },

  // `onDownloadProgress`는 다운로드로드 진행 이벤트를 처리합니다.
  // 브라우저 전용
  onDownloadProgress: function (progressEvent) {
    // 다운로드 진행 이벤트 작업 수행
  },

  // `maxContentLength`는 node.js에서 허용되는 http 응답 콘텐츠의 최대 크기를 바이트 단위로 정의합니다.
  maxContentLength: 2000,

  // `maxBodyLength`는 허용되는 http 요청 콘텐츠의 최대 크기를 바이트 단위로 정의합니다.
  // Node.js 전용
  maxBodyLength: 2000,

  // `validateStatus`는 지정된 HTTP 응답 상태 코드에 대한 Promise를 이행할지 또는 거부할지 여부를 정의합니다. 
  // 만약 `validateStatus`가 true를 반환하면(또는 'null' 또는 'undefined'으로 설정) Promise는 이행됩니다.
  // 그렇지 않으면, 그 Promise는 거부될 것이다.
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 기본값
  },

  // `maxRedirects`는 node.js에서 리디렉션 최대값을 정의합니다.
  // 0으로 설정하면 리디렉션되지 않습니다.
  maxRedirects: 5, // 기본값

  // `socketPath`는 node.js에서 사용될 UNIX 소켓을 정의합니다.
  // 예: '/var/run/docker.sock' 도커 데몬에 요청을 보냅니다.
  // 오직 `socketPath` 또는 `proxy`만 지정할 수 있습니다.
  // 둘 다 지정되면 `socketPath`가 사용됩니다.
  socketPath: null, // 기본값

  // `httpAgent`와 `httpsAgent`는 각각 node.js에서 http 및 https 요청을 수행할 때 사용할 사용자 지정 에이전트를 정의합니다.
  // 이렇게 하면 기본적으로 활성화되지 않은 `keepAlive`와 같은 옵션을 추가할 수 있습니다.
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // `proxy`는 프록시 서버의 호스트이름, 포트, 프로토콜을 정의합니다.
  // 기존의 `http_proxy`와 `https_proxy` 환경 변수를 사용하여
  // 프록시를 정의할 수도 있습니다.
  // 프록시 구성에 환경 변수를 사용하는 경우, 'no_proxy' 환경 변수를 
  // 쉼표로 구분된 프록시가 되지 않는 도메인 목록으로 정의할 수도 있습니다.
  // `false`를 사용하면 프록시를 사용하지 않고, 환경 변수를 무시합니다.
  // `auth`는 프록시에 연결하는데 HTTP Basic auth를 사용해야 함을 나타내며, 
  // 자격 증명을 제공합니다. 그러면 `Proxy-Authorization` 헤더가 설정되고,
  // `headers`를 사용하여 기존의 `Proxy-Authorization` 사용자 지정 해더를 덮어씁니다.
  // 만약 프록시 서버가 HTTPS를 사용한다면, 프로토콜을 반드시 `https`로 설정해야 합니다.
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken`은 요청을 취소하는 데 사용할 수 있는 취소 토큰을 지정합니다.
  // (자세한 내용은 요청 취소 섹션 참조)
  cancelToken: new CancelToken(function (cancel) {
  }),

  // `decompress`는 응답 바디의 자동 압축 해제 여부를 나타냅니다.
  //  `true`로 설정하면, 압축 해제된 모든 응답에서 'content-encoding' 헤더도 제거됩니다.
  // Node.js 전용 (XHR은 압축 해제할 수 없습니다)
  decompress: true // 기본값

}

출처: axios docs - config

위의 config 를 보면 HTTP 중 GET 은 params, 나머지는 data 를 통해 데이터를 전송한다고 할때 GET 은 파라미터에 string 만 전송할수는 없고 URLSearchParams 객체나 일반객체여야 함을 알 수 있다.

const requestParamData = new URLSearchParams({
	port: selectedPort,
});

만약 withCredential 설정이 true 라면 브라우저에서 CORS 요청을 할 때 쿠키와 같은 인증정보를 포함하여 요청을 보낼 수 있다.

📕 axios response

{
  // `data`는 서버가 제공하는 응답입니다.
  data: {},

  // `status`는 HTTP 상태 코드입니다.
  status: 200,

  // `statusText`는 HTTP 상태 메시지입니다.
  statusText: 'OK',

  // `headers`는 HTTP 헤더입니다.
  // 모든 헤더 이름은 소문자이며, 괄호 표기법을 사용하여 접근할 수 있습니다.
  // 예시: `response.headers['content-type']`
  headers: {},

  // `config`는 요청을 위해 `Axios`가 제공하는 구성입니다.
  config: {},

  // `request`는 이번 응답으로 생성된 요청입니다.
  // 이것은 node.js에서 마지막 ClientRequest 인스턴스 입니다.
  // 브라우저에서는 XMLHttpRequest입니다.
  request: {}
}

출처: axios docs - response schema

📗 fetch

  • ES6 부터 들어온 JS 내장 라이브러리, Promise 기반

📕 장점

  • Javascript 내장 라이브러리, 별도 import 필요 없다.
  • Promise 기반

📕 단점

  • 최신 문법
  • Timeout 기능 없다.
  • 상대적으로 Axios 에 비해 기능이 부족하다.

타임아웃이란 특정 요청이 너무 오래 걸려서 응답을 받지 못할 경우 일정 시간 이후에 요청을 취소하고 에러를 처리하는 기능을 말한다.

app.get("/fetch", (req, res) => {
  console.log("backend", req.query);
  res.send(req.query);
});

app.post("/fetch", (req, res) => {
  console.log("backend", req.body);
  res.send(req.body);
});
function fetchGet() {
  const url = `?name=${form.name.value}&gender=${form.gender.value}`;

  fetch(`/fetch${url}`, {
    method: "GET",
  })
    .then((res) => res.json())
    .then((data) => {
  });
}
function fetchPost() {
  fetch(`/fetch`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
    // JSON.stringify() : 자바스크립트 객체를 JSON 텍스트로 변환
    // JSON.parse() : JSON 텍스트를 자바스크립트 객체로 변환
  })
    .then((res) => res.json())
    .then((data) => {
  });
}

fetch - response

  • response.text() : 응답을 읽고 텍스트 반환
  • response.json() : 응답을 JSON 형태로 파싱(실제 확인시 javascript 객체 형태로 반환)
app.post('/fetch', (req, res) => {
	res.send("안녕"); // response.text()
  	
 ...res.send("data"); // response.json()
})

📘 파일 업로드

📗 FormData

FormData 는 폼을 쉽게 보내도록 도와주는 객체이다. FormData 객체는 HTML 폼 데이터를 나타낸다. fetch 의 경우 body 로 formData 를 받고 axios 의 경우 data 로 formData 를 받는다. 이때 Content-Type 속성은 multipart/form-data 이다.

서버 입장에선 FormData 를 사용한 방식과 일반 폼 전송 방식에 차이가 없다.

FormData 객체는 HTML 폼을 직접 넘겨 new FormData(form) 으로 만들 수도 있고 HTML 폼 없이 append, set 등의 메서드로 필드를 추가해 만들수도 있다.

set 메서드는 name 이 같은 필드 모두를 지우고 append 메서드는 그렇지 않다.

ajaxaxiosfetch
요청객체에 url 존재요청 객체에 url 존재요청 객체에 url 미존재
data propertydata propertybody property
반환값을 객체로 사용하기 위해서 json() 메서드 사용반환값이 자동으로 JSON data 로 반환된다.반환값을 객체로 사용하기 위해서 json() 메서드 사용
timeout 설정방법 존재timeout 설정방법 존재timeout 지정불가
let formData = new FormData([form]);
const formData = new FormData();
const file = document.getElementById("dynamic-file");
formData.append("dynamic-userfile", file.files[0]);

axios({
  method: "POST",
  url: "/dynamicFile",
  data: formData,
  headers: {
    "Content-Type": "multipart/form-data",
  },
}).then(function (response) {
});
  • formData.append(name, value) : - name, value 를 가진 폼 필드 추가
  • formData.delete(name) : name 에 해당하는 필드를 FormData 객체에서 제거
  • formData.get(name) : name 에 해당하는 첫 번째 필드 값 가져온다.
  • formData.entries() : FormData 객체의 모든 필드를 [name, value] 형태의 배열로 가져온다.
  • formData.set(name, value) : name 과 value으로 기존의 필드를 새 값으로 대체. 혹은 없었다면 새 필드 추가
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');

// key/value 쌍이 담긴 리스트
for(let [name, value] of formData) {
  alert(`${name} = ${value}`); // key1 = value1, then key2 = value2
}

📗 multer middleware

Multer는 파일 업로드를 위해 사용되는 multipart/form-data 를 다루기 위한 node.js 의 미들웨어이다. Multer는 multipart (multipart/form-data)가 아닌 폼에서는 동작하지 않는다.

npm install --save multer

Multer는 body 객체와 한 개의 file 혹은 여러개의 files 객체를 request 객체에 추가한다. body 객체는 폼 텍스트 필드의 값을 포함하고, 한 개 혹은 여러개의 파일 객체는 폼을 통해 업로드된 파일들을 포함하고 있다.

multer github 기본 사용 예제

const express = require('express')
const multer  = require('multer')
const upload = multer({ dest: 'uploads/' })

const app = express()

app.post('/profile', upload.single('avatar'), function (req, res, next) {
  // req.file 은 `avatar` 라는 필드의 파일 정보입니다.
  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.
})

app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
  // req.files 는 `photos` 라는 파일정보를 배열로 가지고 있습니다.
  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.
})

const cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {
  // req.files는 (String -> Array) 형태의 객체 입니다.
  // 필드명은 객체의 key에, 파일 정보는 배열로 value에 저장됩니다.
  //
  // e.g.
  //  req.files['avatar'][0] -> File
  //  req.files['gallery'] -> Array
  //
  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.
})

텍스트 전용 multipart 폼을 처리해야 하는 경우, 어떠한 multer 메소드 (.single(), .array(), fields()) 도 사용할 수 있다.

  • .single(fieldname) : fieldname 인자에 명시된 이름의 단수 파일을 전달 받는다. 이 파일은 req.file 에 저장된다.

  • .array(fieldname[, maxCount] : fieldname 인자에 명시된 이름의 파일 전부를 배열 형태로 전달 받는다. 선택적으로 maxCount 에 명시된 값 이상의 파일이 업로드 될 경우 에러를 출력할 수 있다. 전달 된 배열 형태의 파일은 req.files 에 저장된다.

  • .fields(fields) : fields 인자에 명시된 여러 파일을 전달 받는다. 파일 객체는 배열 형태로 req.files 에 저장된다. fields 는 name, maxCount(opt)을 포함하는 객체의 배열이다.

  • .none() : 오직 텍스트 필드만 허용하니 파일이 업로드시 LIMIT_UNEXPECTED_FILE 과 같은 에러 코드가 발생한다.

  • .any() : 전달된 모든 파일을 허용하며 파일 배열은 req.files 에 저장된다.

파일이 갖는 정보 :

KeyDescriptionNote
fieldname폼에 정의된 필드 명
originalname사용자가 업로드한 파일 명
encoding파일의 엔코딩 타입
mimetype파일의 Mime 타입
size파일의 바이트(byte) 사이즈
destination파일이 저장된 폴더DiskStorage
filenamedestination 에 저장된 파일 명DiskStorage
path업로드된 파일의 전체 경로DiskStorage
buffer전체 파일의 BufferMemoryStorage

multer 옵션

KeyDescription
dest or storage파일이 저장될 위치
fileFilter어떤 파일을 허용할지 제어하는 함수
limits업로드 된 데이터의 한도
preservePath파일의 base name 대신 보존할 파일의 전체 경로

📕 storage

📖 DiskStorage

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, '/tmp/my-uploads')
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + '-' + Date.now())
  }
})

const upload = multer({ storage: storage })
  • 파일을 디스크에 저장하기 위한 모든 제어 기능 제공. 아래의 두 옵션은 파일을 어디에 저장할 지를 정한다.
  • destination : 어느 폴더안에 업로드한 파일을 저장할지를 결정한다. string 형태로 주어진다. destination 을 함수로 사용시 디렉토리를 생성해야 한다.
  • filename : 폴더안에 저장되는 파일명을 결정하는데 사용된다. 주어지지 않을 경우 파일 확장자를 제외한 랜덤 이름으로 지어진다.

📖 MemoryStorage

const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
  • 메모리 스토리지 엔진은 파일을 메모리에 Buffer 객체로 저장된다.
  • 메모리 스토리지 사용시 파일 정보는 파일 전체를 포함하는 buffer 라고 불리는 필드를 포함한다.
  • 메모리 스토리지 사용시 매우 큰 사이즈 파일이나 많은 양의 작은 파일들을 빠르게 업로드할시 응용 프로그램의 메모리 부족이 발생할 수 있다.

📕 limit

속성설명기본값
fieldNameSize필드명 사이즈 최대값100 bytes
fieldSize필드값 사이즈 최대값1MB
fields파일형식이 아닌 필드의 최대 개수무제한
fileSizemultipart 형식 폼에서 최대 파일 사이즈(bytes)무제한
filesmultipart 형식 폼에서 파일 필드의 최대 개수무제한
partsFor multipart forms, the max number of parts (fields + files)무제한
headerPairsmultipart 형식 폼에서 파싱할 헤더의 key=>value 쌍의 최대 개수2000

📔 레퍼런스

docs
multer github
axios
blog
nextree - form 태그 이해
infa - form 태그
hamait - 정규표현식
javascript.info - FormData
devscb - ajax, fetch, axios 비교

📓 결론

  • form, input, datalist, button 태그에 대해 자세히 알아봤다.
  • 동적 form 전송으로 ajax, axios, fetch 방법을 알게 되었다.
  • 파일 업로드로 FormData, multer 미들웨어에 대해 조사했다.
    FormData 는 폼을 쉽게 보내도록 도와주는 객체이고 multer 는 텍스트가 아닌 파일 업로드를 위해 multipart/form-data 를 다루기 위한 미들웨어이다.
  • multer 미들웨어는 multipart/form-data 가 아닌 폼에서는 동작하지 않고 single, array, fields 등을 통해 파일을 업로드한다.

파일 업로드와 관련된 form, input, button 태그, FormData 객체와 multer 미들웨어에 대해 알아보았다. 속성의 종류도 굉장히 많았다. 특히 이번에 알아봤던 axios 는 리엑트에서도 사용하고 있으니 좀더 관심을 갖고 자료조사를 했던 것 같다. 그중에서도 axios config 중 url 과 baseURL 이 어떻게 다른지, HTTP Method 별 data 와 param 차이, URLSearchParams 등에 대해 알아봤던게 기억에 남는다.

profile
미래의 나를 만들어나가는 한 개발자의 블로그입니다.

0개의 댓글