Naver Clova Face Recognition을 사용하면서 혼동했던 res.render()와 res.json() 차이를 명확히 이해하고 넘어가기
서론
Node.js를 배운지는 이제 한 달이 갓 넘어가는 시기인데, Django로 프로그래밍 언어 학습을 처음 시작하면서 그래도 MVT 패턴에 대해 이해를 하고 있었고, 덕분에 Node.js를 학습할 때 MVC 패턴을 사용하는 방식이 상당히 유사해 배우는데 러닝커브가 빨랐던 것 같다(사용하는 언어가 다르더라도 패턴이 유사하기 때문에)
다만, 학습할 때 “Node.js 교과서”라는 책을 많이 참고했고, 이 책에서 나오는 코드를 내가 바꿔가면서 작업을 많이 했더니 Javascript와 Node를 좀 더 깊이 이해하는데 도움이 많이 되었던 것 같다. (아직도 모르는 것이 많지만)
본론
지금 작성하는 이 글은 res.render와 res.json의 차이를 설명하기 위한 글이다. 왜 이 두 가지가 헷갈려서 글을 작성하게 되었냐면, 아래의 코드를 보면서 설명하는 것이 좀 더 빠를 것이다.
이 코드는 내가 네이버 얼굴인식 API와 node.js 활용 프로젝트 에서 main.pug라는 템플릿 코드에 작성한 코드다. 다른 부분은 볼 것 없고, 템플릿에서 40, 41번째 코드에 잠깐 집중해보자.
let url = JSON.parse(results.request.response).url;
let faceAge = JSON.parse(results.request.response).faces
이 두 코드가 의미하는 바가 조금 중요한데, axios로 Server Side에 post 메소드를 보냈을 때 응답 결과를 가져오는 코드이다. 나는 이 2줄의 코드에서 multer를 사용해 서버에 업로드 된 이미지를 페이지 전환 없이 동일 화면에 Thumbnail 형태로 미리보기를 할 수 있게 만들고 싶었고, 마찬가지로 동일한 페이지에 클로바 얼굴 인식 기능이 제대로 동작해 사진 속 얼굴의 연령대를 함께 보여주는 것을 유도했다. JSON.parse를 사용해서 결과값을 객체로 만들 때, url/faces는 내가 서버 사이드에서 아래와 같이 만들어 요청한 결과다.
마찬가지로 집중해야할 코드는 4줄인데, 68 ~ 71번째 줄을 잘 보자.
return res.json({
faces: response.data.faces,
url: `/image/${req.file.filename}`,
});
처음 이 코드를 작성하기 전에 내가 의도했던 것은 클로바 얼굴인식 API에서 인지한 이미지의 얼굴의 연령대를 템플릿에 보내 클라이언트에게 보여주는 결과를 유도했다. 그 과정에서 사용했던 것이 res.render() 값이고, res.render(‘main’, {faces: response.data.faces}) 를 클라이언트가 받기를 원했다. 그런데 이렇게 진행할 경우, multer에서 업로드한 이미지를 클라이언트에 보내줄 수 없었고, (이미지의 경로를 찾지 못하기 때문에) 클로바 api가 정상적으로 얼굴을 인지한 결과값이 콘솔에 출력되어도 클라이언트에선 받지 못했다.
즉, 이미지 경로와 얼굴 인지 결과 값 모두를 json으로 만들어서 보내줘야하는 경우인 것이다.
Node.js를 좀 사용해 본 사람들이라면 res.render, json을 한 개의 route에서 모두 사용할 수 없다는 것은 알고 있을 것이고, 나도 이 2개를 한 화면에 보여줄 수 있는 방안을 찾아다녔다.
이유를 몰라서 고민을 많이 했는데, 여기서 내가 이야기하고 싶었던 res.render()와 res.json()차이를 네이버 개발자 포럼에서 답변을 얻었다.
답변은 아래와 같았다.
res.render의 경우, 특정 view 템플릿 (여기서는 ‘main’)을 이용하여 html을 빌드하여 response로 내려주는 메서드로 기능을 수행하기 때문에 적절하지 못했고,
현재 사용하는 시나리오대로라면, 파일이 업로드 되면 이것이 백그라운드에서 업로드 되고, 페이지 이동 없이 썸네일로 보여져야 하기 때문에, view를 이용한 렌더링 보다는 json을 이용한 http api호출을 하는 것이 적합한 방법
이라는 답변을 얻었다.
즉, 애초에 res.render()를 사용하는 이슈가 아니라, res.json을 사용해야만 했던 이슈였던 것이다.
좀 더 구체적으로 express.js의 공식문서를 인용하면
view 화면을 렌더링하고, 렌더링된 html을 클라이언트에 보내주는 역할을 한다.
아마 문맥으로 추측하건데, 렌더링하는 시점에 local에 입력된 값이 없다면 빈 객체로 클라이언트로 보내는 것이다.
반대로 res.json은 “렌더링 시점”을 기준으로 삼는 것이 아니라 클라이언트의 응답(response)에 대해 JSON으로 결과 값을 보내주는 것인데
request가 응답 결과에 상응할 수 있게 정상적으로 클라이언트에서 보냈고, json 응답 결과를 보여줄 routing만 잘 되어 있다는 전제가 되어 있으면 “렌더링 시점”과는 전혀 무관하게 클라이언트에 결과 값을 보내줄 수 있는 듯하다.
이것도 어떻게 보면 res.json은 좀 더 비동기적으로 결과 값을 통신하는 것이고, res.render는 동기적으로 통신하는 것 같다는 느낌을 내게 줬다. (특히 내가 res.render를 사용하는 경우들을 잘 생각해보면 mongoose, sequelize를 사용해서 이미 받아올 결과 값들이 명확하게 존재하는 장소가 있을 때 프로젝트에서 활용했다. 반대로 multer로 업로드한 이미지를 가져올 땐 항상 res.json을 사용했다.)
결론
둘 차이를 모르고 프로젝트를 하다보니 삽질을 한 경우지만, 배우는 게 너무 많았다.
특히 node.js 교과서에도 설명이 안되고 넘어간 부분들이 종종 있어서 이해를 정확히 못하고 넘어간 것들이 프로젝트를 하면서 제대로 잡고 넘어간 경우가 정말 많았던 것 같다.
간단하더라도 사이드 프로젝트를 많이 해보라는 이유를 알겠다. 두루뭉실하게 배운 것들을 스스로 명확하게 잡아갈 수 있는 특혜를 누릴 수 있다.
한 동안 글 쓰는 것에 재미를 못 느꼈는데 결국 “1인 팀”이라는 관점에서 과거에 내가 작업한 코드를 다시 봐야하는 일들은 자주 발생할 것이고, 문서화가 잘 안되어 있으면 반드시 “미래의 나”는 시간적으로 비용을 낭비할 것이다. 문서화는 “과거의 나”가 “미래의 나”에게 주는 선물인 것이다.
애매하게 알고 있던 개념을 명확하게 이해하면, 반드시 문서화 작업을 진행하자.
Ryan
참고 자료