JS와 React에서의 디자인 패턴을 다루기 앞서, 자바스크립트 + 리액트 디자인 패턴을 읽고,
안티 패턴에 대해 정리해보려 한다.
안티 패턴(Anti-pattern)
디자인 패턴이 모범 사례를 뜻한다면, 안티 패턴은 겉으로만 해결책처럼 생긴 잘못된 패턴을 말하며 다음과 같이 정의한다.
- 문제 상황에 대한 잘못된 해결책
- 문제 상황에서 벗어나 올바른 해결책에 이르는 방법
- 반면교사로 활용하기 위해 문서화하여 기록해야 하는 나쁜 디자인 패턴
추가로,
안티 패턴이란 획일적으로 어떤 사항에 해당되면 안티 패턴이라고 판단하는 것이 아니라,
프로젝트 초기 설정 단계와 유지 보수 단계 등 처한 상황에 따라 결정되는 것이다.
자바스크립트 안티 패턴(JavaScript Anti-pattern)
자바스크립트는 변수에 저장된 값의 자료형을 명확하게 정의하지 않아도 되는 느슨한 타입의 언어로,
신속한 구현을 위한 임시방편으로 작성한 코드가 영구적으로 이어져서
이로 인한 기술부채로 안티 패턴이 되는 경향이 있다.
예시는 다음과 같다.
전역 컨텍스트에 수많은 변수를 정의하여 네임 스페이스 오염시키기
문제점
- 전역 변수는 어디서든 접근/수정이 가능해 코드의 예측 가능성이 떨어짐
- 다른 스크립트와 변수명 충돌 가능성이 높아짐
- 메모리 관리가 비효율적임
코드
// 안티 패턴 예시
var userName = "John";
var userAge = 25;
var userEmail = "john@example.com";
var isLoggedIn = true;
// 더 나은 방법
const user = {
name: "John",
age: 25,
email: "john@example.com",
isLoggedIn: true
};
// 또는 모듈 패턴 사용
const UserModule = (function() {
const userData = {
name: "John",
age: 25
};
return {
getUserInfo: () => userData
};
})();
setTimeout 이나 setInterval에 함수가 아닌 문자열을 전달해서 내부적으로 eval() 실행되게 하기
문제점
- eval()과 같은 효과로 보안 취약점 발생 가능
- 디버깅이 어려움
- 성능 저하 (문자열을 파싱하고 실행해야 함)
코드
// 안티 패턴 예시
setTimeout("alert('Hello World!')", 1000);
setInterval("updateCounter()" + count + ")", 1000);
// 더 나은 방법
setTimeout(() => alert('Hello World!'), 1000);
setInterval(() => updateCounter(count), 1000);
Object 클래스의 프로토타입을 수정하기
문제점
- 모든 객체에 영향을 미쳐 예상치 못한 동작 발생
- for...in 루프에서 의도치 않은 프로퍼티가 열거됨
- 다른 라이브러리와의 충돌 가능성
코드
// 안티 패턴 예시
Object.prototype.count = 0;
Object.prototype.increment = function() {
this.count++;
};
// 안티 패턴 예시 2
Array.prototype.first = function() {
return this[0];
};
// 더 나은 방법
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
// 또는 유틸리티 함수 사용
const arrayUtils = {
first: (arr) => arr[0]
};
자바스크립트를 사용하여 유연성을 떨어뜨리기
문제점
- 로직과 표현이 강하게 결합되어 재사용성이 떨어짐
- 테스트가 어려워짐
- 유지보수가 어려워짐
코드
// 안티 패턴 예시
function validateEmail(email) {
if(email.indexOf('@') === -1) {
alert('Invalid email!');
return false;
}
return true;
}
// 안티 패턴 예시 2
const mobileMenu = {
init: function() {
if(window.innerWidth < 768) {
this.show();
}
}
};
// 더 나은 방법
function validateEmail(email) {
const isValid = email.indexOf('@') !== -1;
return {
isValid,
message: isValid ? '' : 'Invalid email!'
};
}
const mobileMenu = {
init: function() {
this.updateVisibility();
window.addEventListener('resize', () => this.updateVisibility());
}
};
document.createElement 대신 document.write 사용하기
문제점
- 문서가 로드된 후 실행 시 전체 페이지를 덮어쓰게 됨
- HTML 파싱을 차단하여 성능 저하
- XSS(Cross-Site Scripting) 공격에 취약
- DOM 조작의 컨텍스트와 시점을 파악하기 어려움
코드
// 안티 패턴 예시
document.write("<h1>Hello World!</h1>");
document.write("<script src='script.js'></script>");
// 안티 패턴 예시 2
function addContent() {
document.write("New content");
}
// 더 나은 방법
document.getElementById('content').innerHTML = "<h1>Hello World!</h1>";
// 또는
const heading = document.createElement('h1');
heading.textContent = 'Hello World!';
document.body.appendChild(heading);
// 스크립트 추가 시
const script = document.createElement('script');
script.src = 'script.js';
document.body.appendChild(script);