세상의 모든 웹사이트, api가 utf-8을 사용한다면 인코딩에 대해 전혀 신경쓸 필요가 없을 것이다. 물론 그런 일은 일어나지 않는다.
그러므로 어느 시점에는 인코딩에 대해 신경써야될 일이 생긴다는 것이다.
현재 진행 중인 프로젝트에서 실명 인증 기능이 필요해서 구글링을 하다보니 나이스 평가정보에서 안심체크/실명확인이라는 서비스를 제공하고 있었다.
신청을 하고 며칠 뒤에 메일을 열어보니 암호화 모듈과 세 개의 매뉴얼을 보내주셨다. (JSP, ASP.NET, PHP)
개발 환경이 Node.js(Express)를 사용하기 때문에 잠깐 멘붕이 왔지만 다행히 PHP 매뉴얼을 참고하면 서비스 적용이 가능한 구조였다.
간략히 서비스의 구조를 설명하면 아래와 같다.
충분히 예상가능하듯 /niceinfo
라는 써드파티 서비스를 감싸는 라우트를 만든뒤,
exec
을 활용해 암호화 모듈에 필요한 정보를 넘기고 와 같이 굉장히 단순한 형태로 만들 수 있습니다.
이렇게 모든 일이 일사천리로 진행되고 아무런 어려움도 없었다면 제목이 삽질기록이 되지는 않았겠죠? 😢
문제는 /niceinfo
라우트에 보내는 요청이 계속 같은 리턴코드만을 돌려준다는 것이었다.
리턴코드 관련 매뉴얼을 읽어보니 이는 사용자 ID/PW, 실명, 주민등록번호의 네 가지 인자 중 하나라도 누락되면 발생하는 에러코드였다.
물론 사용자 정보는 정확히 입력되어 있었고, 클라이언트로부터 실명과 주민등록번호도 잘 내려오고 있었다.
그렇게 서너시간 정도를 방황하다가 서비스 담당자분께 메일을 보냈다.
다음날 메일을 열어보니
라는 메일이 와 있었다.
이제까지 인코딩에 대해 크게 신경써본 적이 없어서 EUC-KR이라는 인코딩이 있다는 것 정도밖에 몰랐기 때문에 더욱 당황스러웠다.
인코딩 변환 모듈을 찾다보니 iconv-lite
라는 게 있었다.
곧바로 아래와 같은 코드를 작성했다.
function verifyRealName(name, juminNumber) {
const encoded = iconv.encode(name, 'euc-kr');
const decoded = iconv.decode(encoded, 'euc-kr');
return exec(`${exePath} ... ${decoded}`);
}
위 함수의 동작은 우선 js 스트링(name)을 EUC-KR 버퍼로 인코딩한 뒤,
이를 다시 EUC-KR 인코딩으로 읽어들여 js 스트링으로 저장한다.
그리고 exec
함수에 인자로 넘겨준다.
예상으로는 decoded
가 EUC-KR 인코딩을 사용한다고 생각할 수 있다.
그러나 실제로는 아무 일도 일어나지 않는다.
이는 decoded
가
req.body
로부터 utf-8로 인코딩된 내용을 EUC-KR로 읽어 버퍼에 저장한다.와 같은 과정을 거치기 때문이다.
인코딩된 버퍼의 인코딩을 그대로 유지하면서 암호화 모듈에 인자로 보내려면 아래와 같이 해야한다.
cat
명령어를 통해 버퍼를 그대로 읽어서 넘긴다.그러므로 위의 코드는
function verifyRealName(name, juminNumber) {
const encoded = iconv.encode(name, 'euc-kr');
// 인코딩된 버퍼를 임시저장할 경로
const tempPath = path.resolve('app/temp/name');
fs.writeFileSync(tempPath, encoded);
// command substitution -> `` or $()
// 명령어 실행 결과를 다른 명령어의 인자로 넘겨줌
// 이 경우에는 cat이 파일에 담긴 바이트를 읽어서 그대로 전달
return exec(`${exePath} ... \`cat ${tempPath}\``);
}
이렇게 해주고나서야 겨우 제대로된 리턴코드를 받아볼 수 있었다.
미래의 저를 위해 전체 코드 샘플도 같이 올립니다. 😄
import { exec } from 'child_process';
import path from 'path';
import util from 'util';
import iconv from 'iconv-lite';
import fs from 'fs';
import config from '../config';
async function saveEncodedName(name, filePath) {
const encoded = iconv.encode(name, 'euc-kr');
const promiseWriteFile = util.promisify(fs.writeFile);
return promiseWriteFile(filePath, encoded);
}
async function verifyRealName({
name,
juminNumber,
}) {
const {
cbEncodePath, namePath, siteId, sitePW,
} = config.niceinfo;
const promiseExec = util.promisify(exec);
const exectuablePath = path.resolve(cbEncodePath);
const tempFilePath = path.resolve(namePath);
/**
* Niceinfo module accepts euc-kr encoded name only.
* The only workaround to get things done is to get 'name' from file with euc-kr encoding
* So read raw text from 'cat' command and pass it as argument for module
* Ref:
* How do I use the lines of a file as arguments of a command? (https://stackoverflow.com/questions/4227994/how-do-i-use-the-lines-of-a-file-as-arguments-of-a-command/4229151)
*/
await saveEncodedName(name, tempFilePath);
return promiseExec(
`${exectuablePath} ${siteId} ${sitePW} ${juminNumber} \`cat ${tempNamePath}\``,
)
.then(({ stdout }) => stdout)
.catch(err => String(err.code));
}
export default {
verifyRealName,
};
가끔 어떤 버튼을 누르면 특정 위치로 이동하는 기능을 구현해야될 때가 있습니다. 이 경우 가장 쉽게 사용할 수 있는 방법은 html의 hash link (#)를 사용하는 것입니다.
Nuxt (Vue.js의 서버사이드 렌더링 프레임워크)의 라우팅은 vue-router를 통해 이뤄지는데, hash link (#id) 처리가 전통적인 html 페이지들과는 다릅니다.
전통적인 html 페이지에서 hash link는 몇 번을 누르더라도 언제나 해당 요소의 스크롤 위치로 이동합니다.
그러나 vue-router를 사용할 경우 scroll behavior에 hash link 처리를 해주더라도 처음 페이지를 로드할 때만 스크롤이 동작할 뿐, 두 번째로 링크를 누를 경우에는 아무런 반응이 없습니다.
이 문제를 해결할 유일한 방법은
이 경우 아래와 같은 방식으로 코드를 작성할 수 있습니다.
goToSection(idx) {
if (idx === 3) {
setTimeout(() => {
// sectionId를 가진 요소를 찾음
const [target] = document.querySelector('#sectionId').getClientRects();
const y = window.scrollY + target.top;
// smooth scroll 옵션이 지원되는 브라우저에는 smooth scroll 옵션을 집어넣음.
const scrollOptions = scrollHelpers.buildScrollOptions(0, y);
window.scroll(scrollOptions);
// vuetify 탭은 애니메이션을 사용하므로 약간의 딜레이를 줘서 동작을 자연스럽게 한다.
}, 200);
}
}
참고
안녕하세요!!! iconv 글보다 혹시 질문 남겨보아요. 노드에서 엑셀 euc-kr 형식을 export 해야되는 상황인데 utf8 인 문자열을 iconv-lite을 사용해서 euc-kr 버퍼로 바꾸기까지 했는데 이것을 euc-kr 형태의 string으로 변경하려면 어떻게하는지 아시나요 ?