취업 준비생이던 시절 채용공고에 MVC패턴 혹은 MV*패턴에 대한 이해를
자격요건 혹은 우대사항에서 많이 볼 수 있었다.
여기서 필자는 의문을 가지게 되었다.
🤔: 아니 React는 컴포넌트 패턴인데 어째서 MVC 패턴을 사용한다는거지 ?
예전 프론트엔드의 디자인 패턴의 변화에 대한 글을 쓴적이 있는데
MVC는 JSP 혹은 jQuery를 쓰던 시절에나 사용하던 디자인패턴으로 알고 있었다.
(프론트엔드의 디자인패턴에 관한 블로그 링크이다.)
https://velog.io/@userhwseo/Atomic-Design
그런데 어째서 React를 사용하는 현시대에 MVC 패턴을 사용한단 말인가 ?
그렇게 React에서의 MVC 패턴을 사용해보기로 했다.
일단 간단하게 MVC 패턴을 소개를 하겠다.
먼저 M은 Model이다.
모델은 흔히 말해 데이터를 의미한다.
서버에서 받은 데이터일 수 도 있고 클라이언트에서 사용하는 데이터일 수도 있다.
V는 View이다.
이름 그대로 보여지는 부분 즉 화면단을 말하는 것이다.
html이 될 수도 있고, JSX가 될 수도 있을 것 같다.
마지막으로 C는 Controller이다.
Model 즉 데이터를 가공하는 역활을 한다.
데이터를 바로 화면단에 뿌리는 일도 있지만 가공을 해야하는 경우도 있다.
그러한 역활은 이 Controller에서 이루어지게 된다.
// React Component
...
// 이미지 파일 이름 State
const [imgName, setImgName] = React.useState("");
// 이미지 파일 URL State
const [imgURL, setImgURL] = React.useState("");
const imgRef = useRef();
const getFilterFile = (e) => {
const path = e.target.value;
const splitPath = path.split("\\");
const fileFullName = splitPath.at(-1);
const fileExtension = fileFullName.split(".").at(-1).toLowerCase();
const fileName = fileFullName.split(".")[0];
// 이미지의 URL을 가져오는 로직
const file = imgRef.current.files[0];
if (file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgURL(reader.result);
};
}
// 파일의 확장자 확인과 이미지 이름을 가져오는 로직
if (path === "") return null;
if (
fileExtension === "jpg" ||
fileExtension === "jpeg" ||
fileExtension === "png" ||
fileExtension === "svg" ||
fileExtension === "bmp"
)
return setImageName(fileName);
return alert("이미지만 업로드가 가능합니다.");
};
return(
<div onChange={getFilterFile}>
뭐시기 뭐시기
</div>
)
export default ...
위의 코드를 보자면
간단한 이미지 파일을 받고 그 이미지 파일의 미리보기를 위한 URL을 가져오는 로직이 들어있다.
또한 그 로직 안 속에 이미지 파일만 업로드하도록 걸러주는 필터가 존재하고
이미지 파일이 아니면 alert를 하도록 되어있다.
(path === "" 조건문은 선택을 하지 않을 시 alert가 뜨지 않도록 하기 위함이다.)
이 코드에서 MVC를 나누어보자
먼저 M은 State와 Ref가 될 것이다.
클라이언트들의 데이터들이며 이 데이터들은 View(혹은 Controller)로 전달된다.
그리고 V는 해당 컴포넌트의 return 값 즉 JSX가 될 것이다.
사용자에게 직접적으로 보여지는 구간이니 말이다.
마지막으로 C는 getFilterFile 함수가 된다.
State의 값을 가공해주는 역할을 하기 때문이다.
이제 각각 MVC를 분리하여 관리하면 되는데
리액트의 특성상 State와 return 되는 JSX를 분리하여 사용하진 않는다.
그렇다면 하나 남은 Controller는 분리를 할 수 있지 않을까? 생각했다.
그렇게 폴더를 분리하여 Controller를 따로 분리하여 보았다.
📦 ImagePreview
┣ 📂 Controller
┃ ┗ 📜 index.js
┗ 📜 index.jsx
Controller 폴더 내 index.js에 로직을 넣어 사용했다.
export const getImgFile = (e, imgRef, setImgURL, setImgName) => {
const path = e.target.value;
const splitPath = path.split("\\");
const fileFullName = splitPath.at(-1);
const fileExtension = fileFullName.split(".").at(-1).toLowerCase();
const fileName = fileFullName.split(".")[0];
// 이미지 URL가져오는 로직
const file = imgRef.current.files[0];
if (file) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
setImgURL(reader.result);
};
}
// 이미지 파일 확인 및 이름 가져오는 로직
if (path === "") return null;
if (
fileExtension === "jpg" ||
fileExtension === "jpeg" ||
fileExtension === "png" ||
fileExtension === "svg" ||
fileExtension === "bmp"
)
return setImageName(fileName);
return alert("이미지만 업로드가 가능합니다.");
}
해당 함수에 event,ref, setState 함수들을 가져와 사용해주면 된다.
먼저 디자인 패턴을 유행으로서 생각하는 내 자신을 되돌아 보게 되는 날이였다.
수 많은 JD들을 보면서
"왜 리액트는 컴포넌트 패턴인데 MVC 패턴을 요구하는거지 ?"
라는 생각을 하였는데 이로서 뭔가 고민이 해결 된 느낌이다.
디자인 패턴은 선배 개발자들이 만들어낸
어떠한 상황에서 가장 효율적 패턴이 무엇인가를 연구한 유산과도 같은 것이다.
이는 시대가 흘러가더라도 변함이 없을 것이고 꾸준하게 사용될 것이다.
전문적으로 누군가에게 배운 패턴이 아니라 위의 예시가 잘못된 예시일 수 도 있다.
하지만 나름대로의 MVC 패턴을 사용했다고 할 수 있고 그것으로 인해
코드의 가독성이나 유지보수에도 도움이 된다고 자신있게 말할 수 있다.
큰 깨닳음을 얻은 날이였고 한 층 더 성장한 날이였다.
React에 더 좋은 디자인 패턴이 있다면 댓글에 피드백을 남겨주세요 :)