Problem Drilling

node.js express/multer 사용시 UTF-8 filename 미지원 이슈 해결 과정

남희정 2024. 10. 13. 15:55

요즘 실무에서 node.js express를 사용하여 백엔드 역할까지 하고 있는데 그 중 이슈를 해결한 부분에 대해 깊이 파보려 한다. 개인적으로 실무에서는 이해 없이 구현을 해버리는 경우가 많아서 아쉬움이 큰데, 이렇게 짚고 넘어감으로써 넓게 이해해보고 싶었다.

 

이슈 내용

파일 이름에 UTF-8 문자가 포함되어 있으면 파싱을 실패하는 이슈

 

Before

원인

Multer has nothing to do with it, Busboy has changed something.

 

꽤 모듈을 파고 파고 들어가야하는 문제였다. 

express ➡︎ multer ➡︎ busboy

express의 multer 미들웨어가 의존하고 있는 busboy라는 모듈 때문인데, 해당 모듈의 속성 중 `defParamCharset`의 Default가 `latin1`이기에 디코딩에 문제가 생기는 것이다.

 

Express는 웹 및 모바일 애플리케이션을 위한 일련의 강력한 기능을 제공하는 간결하고 유연한 Node.js 웹 애플리케이션 프레임워크이다. Node.js를 사용하여 쉽게 서버를 구성할 수 있게 만든 클래스와 라이브러리의 집합체라고 보면 된다.
Multer는 파일 업로드를 위해 사용되는 `multipart/form-data`를 다루기 위한 node.js의 미들웨어이다. 효율성을 최대화 하기 위해 busboy를 기반으로 하고 있다.
주 : Multer는 multipart (multipart/form-data)가 아닌 폼에서는 동작하지 않는다.
Busboy는 HTML formdata를 파싱하는 Node.js 모듈이다. 파일 업로드를 스트리밍으로 처리하여 효율적이다. 파일을 전부 읽은 후에 처리하는 것이 아니라 데이터가 들어오는 즉시 처리하여 메모리 사용량이 적고 파일이 커도 효율적으로 처리할 수 있다.

 

busboy의 docs에 defParamCharset에 대한 설명이 잘 적혀있다.

defParamCharset - string - For multipart forms, the default character set to use for values of part header parameters (e.g. filename) that are not extended parameters (that contain an explicit charset). 
Default: 'latin1'.

 

이 이슈는 2022년부터 multer에 이슈로 올라왔었다. 각 나라의 개발자들이 UTF-8이 반영되도록 PR도 올리고, npm 패키지도 만들었으나 여전히 반영되지 않았다. 그 이유는 무엇이고 어떻게 해결할 수 있을까? busboy 이슈에서 개발자의 답변에서 확인할 수 있었다.

 

해결 방법

file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8')

busboy 개발자의 해결방안 답변

 

즉, 직접 latin1로 인코딩된 file의 original name을 Buffet를 사용해 utf-8로 변환해준다. 그런데 여기서 다른 개발자가 의문을 제기한다.

 

CleyFaye 🧑‍💻 : *RFC 5987에서 확장된 파라미터에 대해 언급하고 있긴 하지만, RFC 7578에서는 그 사용을 권장하지 않습니다. 실제로 일부 브라우저(Chrome, Edge, Firefox 포함)는 filename*을 추가로 보내지 않고, 대신 UTF-8 이름을 filename에 넣고 있습니다. 현재 상황에서는 위에서 언급한 변환을 busboy(또는 제 경우엔 multer) 외부에서 수동으로 처리할 수 있지만, filename*의 사용이 실제 환경에서는 그다지 일반적이지 않다는 점을 감안할 때, 정말 이 라이브러리의 범위를 벗어나는 문제일까요?

mscdex/busboy#20

 


RFC(Request for Comments)란 미국의 국제 인터넷 표준화기구인 IETF(Internet Engineering Task Force)에서 제공, 관리하는 문서로 인터넷 개발에 있어서 필요한 기술, 연구 결과, 절차 등을 기술해놓은 메모를 나타낸다. 거의 모든 인터넷 표준은 항상 RFC로 문서화가 되어 있으며, 인터넷 개발에 관련된 기술을 하거나 알고있는 사람은 누구나 RFC 문서를 작성할 수 있다. RFC 문서가 필요한 단계를 통과하게 되면 IETF에서는 문서에 번호를 붙여주게 되는데, RFC **** 형식으로 번호가 순서대로 부여된다. 

 

CleyFaye의 말처럼 정말 권장하고 있지 않은지 찾아보았다.

RFC7578 Section 4.2

// multer type 정의에서 originalname 부분. 주석에서 RFC7578을 확인하라고 되어있다.
originalname: string;
		/**
		 * Value of the `Content-Transfer-Encoding` header for this file.
		 * @deprecated since July 2015
		 * @see RFC 7578, Section 4.7
		 */

RFC7578 Section 4.7

 

실제로 RFC7578에서 바로 찾을 수 있었다. 그는 라이브러리에서 이런 문제를 자동으로 처리해줘야 하는게 아닌지, 저렇게 개발자들이 직접 수동으로 처리하는 것이 맞는지 의문을 제기했다. 그에 대한 busboy 개발자의 답변은 이러하다.

 

CleyFaye의 의문에 대한 busboy 개발자의 답변

 

모든 환경에서 호환되는 라이브러리를 만드는 것은 현실적으로 어렵기 때문에, 호환성을 위해 기존 방식(역사적 방식)을 따르는 것이 안전하다는 입장이다. 아무래도 이 부분은 계속 수동으로 처리하는 게 맞을 듯하다. 

 

After

 

 

 

단순히 한 줄을 추가하여 해결하기보다, 왜 이렇게 되어야 하고 쉽게 바꿀 순 없을지를 고민해볼 수 있었고, 개발자들이 이슈로 여러 의견을 나누는 것을 보면서 매우 흥미로웠다. 이 한 줄에 많은 고민이 담겨있구나 했다!

내가 사용 중인 라이브러리에서 고쳤으면 하는 문제가 생기면 이의를 제기하고 그에 따른 해결 방안을 같이 고민할 수 있었음 한다.


https://datatracker.ietf.org/doc/rfc7578/

https://github.com/expressjs/multer

https://github.com/expressjs/multer/blob/master/doc/README-ko.md

https://developer.mozilla.org/ko/docs/Glossary/UTF-8

https://github.com/expressjs/multer/issues/1219

https://github.com/expressjs/multer/issues/1104

https://github.com/mscdex/busboy/issues/20

https://net-study.club/entry/RFC-Request-for-Comments%EB%9E%80-RFC%EC%9D%98-%EC%97%AD%EC%82%AC-RFC-%EC%A2%85%EB%A5%98-RFC-%ED%91%9C%EC%A4%80%ED%99%94-%EC%A0%88%EC%B0%A8 

https://gngsn.tistory.com/37

https://github.com/expressjs/multer/blob/master/lib/make-middleware.js

https://www.codecademy.com/article/what-is-express-js

ChatGPT 🤖