이 포스트는 form 태그의 요소와 속성, 특징, 동적 form 전송으로 ajax, axios, fetch, 그리고 body-parser 만으로 불가능했던 파일 업로드에 대해서 정리한다.
form 태그는 입력된 데이터를 한번에 서버로 전송하기 위해 사용한다. 사용자와 웹사이트 / 어플리케이션이 상호 작용하는 기술 중 하나이다.
input
: 입력 컨트롤 정의select
: 드롭다운 목록 정의label
: 입력 요소 레이블 정의, 그 자체로는 효과가 없으나 input 태그를 제어하여 상태값을 변경하도록 돕는 역할을 한다.fieldset, legend
: 폼 엘리먼트들을 그룹화할 때 사용, legend
로 그룹화한 폼 엘리먼트들의 이름을 지정<!-- for 속성을 통해 태그를 분리할 수도 있다 -->
<input id="fruitItem" type="checkbox" />
<label for="fruitItem">바나나</label>
action
: 폼을 전송할 서버 쪽 스크립트 파일 지정name
: 폼을 식별하기 위한 이름 지정accept-charset
: 폼 전송에 사용할 문자 인코딩 지정target
: action 에서 지정한 스크립트 파일을 현재 창이 아닌 다른 위치에 열도록 지정method
: 폼을 서버에 전송할 http 메소드 지정 (GET / POST)autocomplete
: 자동완성. on 으로 명시하면 브라우저는 사용자가 이전에 입력했던 값들을 기반으로 사용자가 입력한 값과 비슷한 값들을 드롭다운 옵션으로 보여준다.enctype
: 폼 데이터가 서버로 제출될 때 해당 데이터가 인코딩되는 방법 명시application/x-www-form-urlencoded
기본값, 모든 문자들은 서버로 보내기 전에 인코딩됨을 명시multipart/form-data
모든 문자를 인코딩하지 않음을 명시. 이 방식은 form 요소가 파일이나 이미지를 서버로 전송할 때 주로 사용text/plain
공백문자는 "+" 기호로 변환하나 나머지 문자는 모두 인코딩되지 않음을 명시novalidate
: 폼 데이터를 서버로 제출시 해당 데이터의 유효성을 검사하지 않음을 명시<form name="profile" action="/" autocomplete="on">
사용자가 정보를 입력하는 부분을 만들어야 할때 사용한다.
type
: 태그 모양을 변경 | text / radio / checkbox / password / button / hidden / fileupload / submit / resethidden
: 서버로 보내는 값들을 보내는 필드(사용자에게 보이지 않는다)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 요소 내부의 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 value="apple">사과</option>
<option value="banana">바나나</option>
<option value="grape">포도</option>
</datalist>
버튼 요소, 페이지에 버튼을 넣고 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 태그정리
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 를 통해 받은 데이터를 가공, 수정하여 화면에 보낼 수 있다.
required
: 양시 제출전 채워야 하는지 여부 지정minlength, maxlength
: 텍스트 데이터의 최소 및 최대 길이 지정min, max
: 숫자 입력 유형의 최소값과 최대값 지정type
: 데이터가 숫자, 이메일 주소, 기타 유형이어야 하는지 여부 지정pattern
: 정규표현식 지정hamait - 정규표현식 티스토리에 정리가 잘 되어있으니 해당 블로그를 참고하자.
표현식 | 의미 |
---|---|
^x | 문자열의 시작을 표현하며 x 문자로 시작됨을 의미한다. |
x$ | 문자열의 종료를 표현하며 x 문자로 종료됨을 의미한다. |
.x | 임의의 한 문자의 자리수를 표현하며 문자열이 x 로 끝난다는 것을 의미한다. |
x+ | 반복을 표현하며 x 문자가 한번 이상 반복됨을 의미한다. |
x? | 존재여부를 표현하며 x 문자가 존재할 수도, 존재하지 않을 수도 있음을 의미한다. |
x* | 반복여부를 표현하며 x 문자가 0번 또는 그 이상 반복됨을 의미한다. |
x|y | or 를 표현하며 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 를 표현하며 ^ 를 문자로 사용함을 의미한다. |
\b | word boundary를 표현하며 문자와 공백사이의 문자를 의미한다. |
\B | non word boundary를 표현하며 문자와 공백사이가 아닌 문자를 의미한다. |
\d | digit 를 표현하며 숫자를 의미한다. |
\D | non digit 를 표현하며 숫자가 아닌 것을 의미한다. |
\s | space 를 표현하며 공백 문자를 의미한다. |
\S | non space를 표현하며 공백 문자가 아닌 것을 의미한다. |
\t | tab 을 표현하며 탭 문자를 의미한다. |
\v | vertical tab을 표현하며 수직 탭(?) 문자를 의미한다. |
\w | word 를 표현하며 알파벳 + 숫자 + _ 중의 한 문자임을 의미한다. |
\W | non word를 표현하며 알파벳 + 숫자 + _ 가 아닌 문자를 의미한다. |
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) {
},
});
}
npm i axios
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,
});
}
{
// 요청에 사용될 서버 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 // 기본값
}
위의 config 를 보면 HTTP 중 GET 은 params, 나머지는 data 를 통해 데이터를 전송한다고 할때 GET 은 파라미터에 string 만 전송할수는 없고 URLSearchParams 객체나 일반객체여야 함을 알 수 있다.
const requestParamData = new URLSearchParams({
port: selectedPort,
});
만약 withCredential
설정이 true 라면 브라우저에서 CORS 요청을 할 때 쿠키와 같은 인증정보를 포함하여 요청을 보낼 수 있다.
{
// `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
타임아웃이란 특정 요청이 너무 오래 걸려서 응답을 받지 못할 경우 일정 시간 이후에 요청을 취소하고 에러를 처리하는 기능을 말한다.
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 객체는 HTML 폼 데이터를 나타낸다. fetch 의 경우 body 로 formData 를 받고 axios 의 경우 data 로 formData 를 받는다. 이때 Content-Type 속성은 multipart/form-data 이다.
서버 입장에선 FormData 를 사용한 방식과 일반 폼 전송 방식에 차이가 없다.
FormData 객체는 HTML 폼을 직접 넘겨 new FormData(form)
으로 만들 수도 있고 HTML 폼 없이 append, set 등의 메서드로 필드를 추가해 만들수도 있다.
set
메서드는 name 이 같은 필드 모두를 지우고 append
메서드는 그렇지 않다.
ajax | axios | fetch |
---|---|---|
요청객체에 url 존재 | 요청 객체에 url 존재 | 요청 객체에 url 미존재 |
data property | data property | body 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는 파일 업로드를 위해 사용되는 multipart/form-data 를 다루기 위한 node.js 의 미들웨어이다. Multer는 multipart (multipart/form-data)가 아닌 폼에서는 동작하지 않는다.
npm install --save multer
Multer는 body 객체와 한 개의 file 혹은 여러개의 files 객체를 request 객체에 추가한다. body 객체는 폼 텍스트 필드의 값을 포함하고, 한 개 혹은 여러개의 파일 객체는 폼을 통해 업로드된 파일들을 포함하고 있다.
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
에 저장된다.
파일이 갖는 정보
:
Key | Description | Note |
---|---|---|
fieldname | 폼에 정의된 필드 명 | |
originalname | 사용자가 업로드한 파일 명 | |
encoding | 파일의 엔코딩 타입 | |
mimetype | 파일의 Mime 타입 | |
size | 파일의 바이트(byte) 사이즈 | |
destination | 파일이 저장된 폴더 | DiskStorage |
filename | destination 에 저장된 파일 명 | DiskStorage |
path | 업로드된 파일의 전체 경로 | DiskStorage |
buffer | 전체 파일의 Buffer | MemoryStorage |
multer 옵션
Key | Description |
---|---|
dest or storage | 파일이 저장될 위치 |
fileFilter | 어떤 파일을 허용할지 제어하는 함수 |
limits | 업로드 된 데이터의 한도 |
preservePath | 파일의 base name 대신 보존할 파일의 전체 경로 |
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
: 폴더안에 저장되는 파일명을 결정하는데 사용된다. 주어지지 않을 경우 파일 확장자를 제외한 랜덤 이름으로 지어진다.const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
buffer
라고 불리는 필드를 포함한다. 속성 | 설명 | 기본값 |
---|---|---|
fieldNameSize | 필드명 사이즈 최대값 | 100 bytes |
fieldSize | 필드값 사이즈 최대값 | 1MB |
fields | 파일형식이 아닌 필드의 최대 개수 | 무제한 |
fileSize | multipart 형식 폼에서 최대 파일 사이즈(bytes) | 무제한 |
files | multipart 형식 폼에서 파일 필드의 최대 개수 | 무제한 |
parts | For multipart forms, the max number of parts (fields + files) | 무제한 |
headerPairs | multipart 형식 폼에서 파싱할 헤더의 key=>value 쌍의 최대 개수 | 2000 |
docs
multer github
axios
blog
nextree - form 태그 이해
infa - form 태그
hamait - 정규표현식
javascript.info - FormData
devscb - ajax, fetch, axios 비교
파일 업로드와 관련된 form, input, button 태그, FormData 객체와 multer 미들웨어에 대해 알아보았다. 속성의 종류도 굉장히 많았다. 특히 이번에 알아봤던 axios 는 리엑트에서도 사용하고 있으니 좀더 관심을 갖고 자료조사를 했던 것 같다. 그중에서도 axios config 중 url 과 baseURL 이 어떻게 다른지, HTTP Method 별 data 와 param 차이, URLSearchParams 등에 대해 알아봤던게 기억에 남는다.