자바스크립트 수업 중에 실습을 진행했는데, 흥미로운 주제가 있어서 정리해보고자 글을 쓰게 되었다 🙃
더 좋은 코드를 작성하기 위해서 예제 코드를 하나 작성하고, 문제점을 찾아 이를 발전시켜나가는 방식으로 글을 정리하려고 한다
const infos = [
{ id: 1, name: 'Lee', age: 24 },
{ id: 2, name: 'Kim', age: 21 },
{ id: 3, name: 'Park', age: 28 },
];
const render = infos =>
infos.map(({ id, name, age }) => `<li id="${id}">${name}, ${age}</li>`).join('\n');
console.log(render(infos));
// <li id="1">Lee, 24</li>
// <li id="2">Kim, 21</li>
// <li id="3">Park, 28</li>
render 함수는 infos 객체 배열을 화면에 렌더링 하기 위해 html 문자열을 생성해 반환하는 함수이다.
데이터 infos와 데이터를 사용하는 함수 render 사이의 응집도가 낮다
여기서 응집도란 내부의 기능적인 응집 정도를 말한다
const Infos = {
state: [
{ id: 1, name: 'Lee', age: 24 },
{ id: 2, name: 'Kim', age: 21 },
{ id: 3, name: 'Park', age: 28 },
],
render() {
return this.state.map(({ id, name, age }) => `<li id="${id}">${name}, ${age}</li>`).join('\n');
},
};
console.log(Infos.state);
console.log(Infos.render());
// [
// { id: 1, name: 'Lee', age: 24 },
// { id: 2, name: 'Kim', age: 21 },
// { id: 3, name: 'Park', age: 28 }
// ]
// <li id="1">Lee, 24</li>
// <li id="2">Kim, 21</li>
// <li id="3">Park, 28</li>
상태와 상태를 사용하는 함수의 응집도를 높이기 위해 Infos 객체의 프로퍼티와 메서드로 state와 render를 바인딩하였다. Infos는 생성자 함수나 클래스가 아니지만 네임스페이스의 역할을 하기 때문에 파스칼 표기법으로 변수명을 정했다.
그리고 상태 데이터의 프로퍼티 키를 infos로 작명한다면 객체 상태에 접근할 때 Infos.infos로 접근해야 하기 때문에 상태 데이터의 프로퍼티 키를 state로 작명하였다
위 코드는 첫 코드에 비해 응집도는 올라갔지만, 여전히 상태는 render 함수 이외의 로직에도 변경될 수 있어서 안전하지 못하다. 즉 정보 은닉이 되지 못한 상태이다
이 문제점을 해결하기 위해서는 상태가 함수에 의해서만 참조 가능하도록 해야 하고, Infos 객체가 전역에서 한 번만 생성되는 싱글톤 객체가 되야 한다
const Infos = (() => {
const infos = [
{ id: 1, name: 'Lee', age: 24 },
{ id: 2, name: 'Kim', age: 21 },
{ id: 3, name: 'Park', age: 28 },
];
return {
render() {
return infos.map(({ id, name, age }) => `<li id="${id}">${name}, ${age}</li>`).join('\n');
},
};
})();
console.log(Infos.render());
// <li id="1">Lee, 24</li>
// <li id="2">Kim, 21</li>
// <li id="3">Park, 28</li>
클로저를 활용하여 정보 은닉을 구현했다. 즉시 실행함수로 infos를 참조하는 render 함수가 바인딩된 객체를 반환했는데, 이렇게 코드를 작성하면 응집도도 높이고, render 함수에 의해서만 infos를 참조할 수 있게 된다. 또한 즉시 실행 함수를 사용해서 코드를 한 번만 실행하고, 함수 로직 내부에 의도치 않은 오류를 발생시킬 수 있는 this 키워드를 사용하지 않아도 된다.
이 코드는 처음 작성한 코드에 비해서는 훨씬 좋지만, 너무 장황하다는 문제가 있다.
<!DOCTYPE html>
<html>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
// Infos.js
const infos = [
{ id: 1, name: 'Lee', age: 24 },
{ id: 2, name: 'Kim', age: 21 },
{ id: 3, name: 'Park', age: 28 },
];
const render = () => {
return infos.map(({ id, name, age }) => `<li id="${id}">${name}, ${age}</li>`).join('\n');
};
const Infos = { render };
export default Infos;
// app.js
import Infos from './Infos.js';
console.log(Infos.render());
위와 같이 코드를 작성하고 live server를 켜서 콘솔창을 열어보면 render 함수가 정상적으로 동작하는 것을 확인할 수 있다.
Infos 객체를 모듈로 구현한 코드인데, 모듈에 상태와 함수를 모았기 때문에 응집도를 높이고 함수만 export 하여 사용하기 때문에 정보 은닉도 보장한다. 이 또한 클로저를 활용한 방법인데, 이전 코드에 비해 장황하지 않고 모듈별로 분리되어 있어 관리에 용이하다. 실무에서는 웹팩을 설정하여 모듈을 사용한다고 하는데, 이는 추후에 사용해 볼 계획이다.
예제 코드를 수정하고 다시 한번 실습을 진행하면서 응집도, 정보 은닉, 모듈 관점에서 코드를 발전시키고 정리해보았다. 같은 결과를 내는 코드더라도 더 좋은 코드로 발전시키기 위해 고려해야 할 부분이 많다는 것을 느꼈다.
대부분 개발자들이 새 코드를 작성하는 일보다 기존 코드를 유지 보수 하는 일에 더 많은 시간을 쓴다고 한다. 그렇기 때문에라도 단순히 결과를 내는 데 의의를 두지 않고, 코드 퀄리티를 고려하는 개발자가 되기 위한 노력을 부단히 해야겠다❗️