무엇을 배울것인가
개발자가 고려해야 하는 것
- 어떻게 코드를 안전하게 바꿀 수 있는지
- 어떤 코드 형태가 내용을 잘 파악할 수 있는 형태인지
소프트웨어 개발, 프로그래밍 패턴과 같은 것들이 대부분 변경 용이성에 초점이 맞춰져 있음
- 어떻게 잘, 안전하게, 빠르게 바꿀수 있는지
- 바꿔도 올바르게 동작할 수 있을지
- 올바르게 동작하지 못하더라고 올바르지 못한 동작이 최소한의 영향 범위를 갖게 할 것인지
리펙토링 예시
function createEl(type, props){
switch(type){
//type에 들어오는 문자열에 따라 createElement를 이용하여 DOM API로 DOM node를 하나 만들고
//Attribute에 data-id 속성을 추가하여 내용을 넣음
//props 객체를 setAttruibute를 통해 주입하고 만들어진 element를 return
case 'h1':
return [document.createElement('h1')]
.map(el => {
Object
.entries({...props, 'data-id':'title'})
.forEach(([name, value]) => el.setAttribute(name, value))
return el;
})[0];
case 'div':
return [document.createElement('div')]
.map(el => {
Object
.entries({...props, 'data-id':'layout'})
.forEach(([name, value]) => el.setAttribute(name, value))
return el;
})[0];
}
}
switch case문이 여러가지 늘어 날 수 있음(확장 가능성이 있는 함수)
위에 코드가 변경될 수 있는 변화요소
- createEl의 case 추가
- case 구문 안의 로직 변경
h1태그 생성 로직을 수정하면, div태그 생성 로직은 연관이 없어도 변경됨
→ h1 생성 로직 범위만 변경 해 안전하게 아무 이상없이 동작된다는 것을 테스트 해야하는 범위가 위 코드로는 createEl전범위가 되는 것. 현재는 바뀐 부분만 테스트를 할 수 없음
변경 이후 안정성에 측면에서 본다면 위의 예시는 변경에 용이하지 않는 구조임
//리펙토링
function createH1(props) {
return [document.createElement("h1")].map((el) => {
Object.entries({ ...props, "data-id": "title" }).forEach(([name, value]) =>
el.setAttribute(name, value)
);
return el;
})[0];
}
function createDiv(props) {
return [document.createElement("h1")].map((el) => {
Object.entries({ ...props, "data-id": "title" }).forEach(([name, value]) =>
el.setAttribute(name, value)
);
return el;
})[0];
}
function createEl(type, props) {
switch (type) {
case "h1":
return createH1(props);
case "div":
return createDiv(props);
}
}
바깥쪽으로 createH1함수와 createDiv함수를 분리
→ h1 태그를 생성하는 로직에 변경사항이 생기면 createH1만 온전히 테스트하면 됨. 하지만 아직 추가에 취약함
새로운 태그를 계속해서 추가한다면 case문이 추가됨
createEl함수의 다른 case와 아무런 상관이 없음에도 createEl 함수를 전체 테스트 해봐야한다는 문제가 생김
//리팩토링2
function createH1(props) {
return [document.createElement('h1')]
.map(element => {
Object
.entries({ ...props, 'data-id': 'subject' })
.forEach(([name, value]) => element.setAttribute(name, value))
return element;
})[0];
}
function createDiv(props) {
return [document.createElement('div')]
.map(element => {
Object
.entries({ ...props, 'data-id': 'layout' })
.forEach(([name, value]) => element.setAttribute(name, value))
return element;
})[0];
}
const creatorMap = {
h1: createH1,
div: createDiv,
};
function createEl(type, props) {
return creatorMap[type][props]
}
리팩토링1에 switch문을 보면 문자열과 함수가 연결 되어 있는 구조이고, type은 어떤 함수를 호출 할 것인가에 대한 트리거 역할만 하는 값임
이를 이용하여 맵핑 정보를 만들어 맵핑 정보만 정달하고, 그 정보에 따라 호출만 해주는 식으로 변경
태그 생성 함수가 추가되면 creatorMap에 추가를 하면 되기 때문에 추가시에도 createEl함수는 변경이 없음
하지만 createEl함수가 creatorMap이라는 외부 변수에 의존하고 있음
외부 변수인 creatorMap이 바뀌게 되었을 경우 createEl함수가 동작이 잘 안될 가능성 존재
//리팩토링3
function createH1(props) {
return [document.createElement('h1')]
.map(element => {
Object
.entries({ ...props, 'data-id': 'subject' })
.forEach(([name, value]) => element.setAttribute(name, value))
return element;
})[0];
}
function createDiv(props) {
return [document.createElement('div')]
.map(element => {
Object
.entries({ ...props, 'data-id': 'layout' })
.forEach(([name, value]) => element.setAttribute(name, value))
return element;
})[0];
}
const creatorMap = {
h1: createH1,
div: createDiv,
};
const coupler = map => (type, props) => map[type](props);
const createEl = coupler(creatorMap);
createEl은 외부 변수인 createMap 객체의 종속성을 끊어냈고 coupler함수를 이용해서 안전하게 동작할 수 있는 구조
리팩토링으로 코드는 약간씩 변경이 되었지만 최종적으로 유저가 사용하는 방법은 원본, 리팩토링1,2,3이 모두 같음
1타 2피 3피 4피
Single-Page Application(SPA)
서버로부터 새로운 페이지를 불러오지 않고 현재의 페이지를 동적으로 다시 작성함으로써 사용자와 소통하는 웹 애플리케이션이나 웹사이트를 말함. 단일 페이지로 구성되며 기존의 서버 사이드 렌더링과 비교할 때, 배포가 간다하여 네이티브 앱과 유산한 사용자 경험을 제공하고 웹 애플리케이션에 필요한 모든 정적 리소스를 최초에 한번만 다운로드 함
DOM API
HTML문서를 브라우저가 랜더링 하기 위해서 그리고 그 안에 많은 것들을 작동 시키기 위해서 가공시켜 놓은 객체. DOM이라고 부르기도 함. DOM이 굉장히 중요한 이유는 JS를 이용해서 웹에 UI를 핸들링할 수 있는 유일한 방법이 DOM API를 사용하는 방법이기 때문 하지만 다루기가 매우 까다로움(애초에 목적이 웹 앱을 만들게 설계된 것이 아니기 때문)
DOM API의 문제점
const h1 = document.getElementsByTagName('h1');
h1.length // 0
document.body.appendChild(document.createElement('h1'));
// <h1></h1>
h1.length // 1
document.body.appendChild(document.createElement('h1'));
// <h1></h1>
h1.length // 2
DOM API가 반환하는 값이 라이브 오브젝트라는 컨셉을 갖고 있기 때문에 참조를 그대로 유지하고 있음
이것을 코드상에서 핸들링한다면 까다로움
document.body.appendChild(document.createElement('h2'));
// <h2></h2>
h2.length // 2
// h1을 query
const H1 = document.querySelectorAll('h1');
H1.length // 2
h1.length // 2
document.body.appendChild(document.createElement('h1'));
// <h1></h1>
h1.length // 3
H1.length // 2
똑같이 DOM API를 이용해서 query를 했지만 어떤 것은 참조를 그대로 가지고 있고 어떤 것은 참조를 가지고 있지 않음
일관성이 없음...
Console을 이용해 확인해 보면 h1은 HTMLCollection이고 H1은 NodeList임을 확인 할 수 있음
문제는 이렇게 일관성이 없는 동작이 DOM API에 굉장히 많이 있음
그렇기에 이런것들 하나하나 신경을 써야 하고
많은 브라우저가 환경이 다르기 때문에 이런점 많은 사항을 모두 점검하는 것은 엄청나게 고통스러운 일이 됨
이런 Cross browsing 문제 뿐 아니라 SPA는 규모가 커질수록 복잡도가 굉장히 증가함
웹페이지 화면이 바뀌면 다 reload되어 새로 갱신되고
해당 화면에서만 다루는 상태를 다루는 코드만 따로 격리되어 있지 않기 때문
모든 화면에 들어가 있는 다양한 데이터(상태)들을 모두 애플리케이션 단위에서 유지 관리해야 하므로
참조 무결성의 원칙이 위배되거나 혼란스럽고 미묘하게 작동되는 것들은 다양한 버그를 일으킬 가능성을 가지고 있음
규모가 커져도 복잡도를 낮출 수 있는 프로그래밍 패턴
MVC
모델-뷰-컨트롤러(model-view-controller, MVC)는 소프트웨어 공학에서 사용되는 소프트웨어 디자인 패턴
성격이 다른 것들이 뒤섞여 있으면 복잡도가 높아지기 때문에 분리하여 복잡도를 낮춘 것
MVVM(model-view-viewmodel, MVVM)
클라이언트 쪽에서 상태를 조금 독립적으로 가지고 있고 독립적 상태를 잘 유지관리 할수 있는 여러가지 컨셉들이 들어가 있는 패턴
뷰는 DOM을 이용한 UI 요소를 다루는 것에 많은 부분 치중되어 있고 DOM API 자체 문제점을 개선해 주지 않음
DOM의 여러가지 문제점을 개선하기 위한 시도는 JQuery가 있을 수 있는데 DOM을 쉽게 포장했을 뿐 근본적인 문제점들을 해결하지 못했음
그런데 React는 이러한 해결방법으로 DOM자체를 쓰지 않는다는 컨셉을 냄
DOM이 문제니 DOM을 쓰지 말자는 것. 이 컨셉으로 개발의 패러다임을 뒤집어 놓음
작게 시작하기
소프트웨어는 지속적으로 변경되고 있음
변경되는 경우
- 기존의 코드가 바뀜
- 새로운 것이 추가 됨
- 기존의 것이 사라짐&삭제됨
가장 빈번히 변경되는 경우는 보통은 새로운 기능이 추가되는 것임
기능 추가로 인해서 기존코드가 변경되는 경우도 생기고 삭제되는 일이 있을 수 있음
코드를 변경시킬 때 가장 안전하게 변경 시킬 수 있는 방법은 어떤 부분은 안 바뀌게 만들어 놓고 어떤 부분은 바뀌는 코드로 소프트웨어를 2가지로 나눠 놓는 것 즉 안바뀌는 부분과 바뀌는 부분을 분리시키는 것
바뀌는 부분은 계속 바뀔 수 있게하고 안바뀌는 부분은 안 바뀔수 있게 그리고 상당히 중요한 코드는 안바뀌는 쪽으로 이동
이렇게 하면 소프트웨어 안정선은 상대적으로 높아짐
바뀌는 구조와 안 바뀌는 구조를 분리해서 개발하는 구조가 여러가지가 있는데 그 중 대표적인 2가지로 플러그인과 미들웨어가 있음
플러그인(plugin)
플러그인 또는 추가기능(에드인;add-in, 에드온;add-on)은 호스트 응용 프로그램과 서로 응답하는 컴퓨터 프로그램이며, 특정한 "주문식" 기능을 제공
대표적인 플러그인 아키텍처로 크롬이 있음
크롬 브라우저가 플러그인이라는 구조를 제공하는 소프트웨어
크롬 웹스토어에 들어가보면 확장 프로그램이 있는데 이때 이 확장 프로그램이 플러그인이라고 생각하면 됨
확장 프로그램이 하는 일은 크롬 브라우저가 가지고 있는 원래의 기능은 건드리지 않고 더 많은 일을 할 수 있게 해주는 프로그램임
사용자입장에서는 크롬 익스텐션(확장 프로그램)이 제공하는 기능은 크롬을 통해서 제공되기 때문에 크롬이 기능처럼 느껴짐 하지만 크롬이 변경된 것은 아님
크롬 개발자가 크롬을 만든 코드에는 크롬 익스텐션 소프트웨어가 갖고 있는 기능이 전혀 포함되어 있지 않음 하지만 사용자는 최종적으로 크롬 익스텐션을 크롬을 통해서 사용할 수 있게 됨
크롬이라는 호스트 어플리케이션에서 확장프로그램이 작동할 수 있는 구조를 제공해주어야 함
구조만 제공해주면 크롬 브라우저는 새로운 기능들이 계속 추가됨
즉 크롬은 변경되지 않지만 추가 기능들은 계속 추가됨으로써 사용자는 많은 기능들을 제공 받을 수 있음
변경되는 부분과 변경되지 않는 부분을 나눠서 다루는 것. 플러그인이라는 아키텍처
미들웨어(middleware)
컴퓨터 제작 회사가 사용자의 특정한 요구대로 만들어 제공하는 프로그램으로, 운영 체제와 응용소프트웨어의 중간에서 조정과 중개의 역할을 수행하는 소프트웨어
미들웨어는 플러그인과 유사한 구조
많이 사용된느 소프트웨어가 있음(Express라고 하는 노드의 Web application framwork)
Express라고 하는 framework도 자신이 제공하는 몇몇 기능들이 있기는 하지만, 추가적인 많은 기능들을 모두 다 제공할 수는 없기 때문에 미들웨어라는 소프트웨어를 추가할 수 있는 구조를 제공
실제적인 기능은 미들웨어(어떤 소프트웨어가 될지는 Express 자체는 모름) 소프트웨어를 함께 구성할 수 있는 구조를 제공함으로써 전체적으로 Express가 제공하는 많은 기능들이 확대될 수 있는 형식으로 만들어져 있음
Express 웹문서(미들웨어를 작성하는 방법): Writing middleware for use in Express apps
Writing middleware for use in Express apps
Writing middleware for use in Express apps Overview Middleware functions are functions that have access to the request object (req), the response object (res), and the next function in the application’s request-response cycle. The next function is a func
expressjs.com
기존코드는 바뀌지 않고 안전하게 작동하면서 새로운 기능을 제공할 수 있는 컨셉 이것이 바로 미들웨어
플러그인과 미들웨어가 공통적인 부분도 있지만 차이점도 있음
둘다 코드의 변경은 전혀 없이 새로운 기능을 추가할 수 있는 환경을 만들어 준다는 공통점이 있지만 사용하는 형태와 구조에 있어서는 약간 차이가 있음
이런 컨셉들을 명확하게 숙지하고 있다면 애플리케이션을 만들 때 규모가 커지면 플러그인 아키텍쳐를 만들어서 확장성에 유연하게 만들어볼 수 있음
미들웨어와 플러그인같은 아키텍쳐와 비슷하게 UI를 만들면서 굉장히 익숙한 아키텍쳐가 하나 더 있는데 바로 컴포넌트라고 하는 아키텍처임
컴포넌트
굉장히 오래됬지만 현재도 잘 쓰고 있음. 대표적인 컴포넌트 아키텍처로 React가 있음
플러그인과 미들웨어 보다는 훨씬 더 큰 범주의 방법론이지만, 컴포넌트라고 하는 것도 변경되지 않는 단단한 소프트웨어 구조물을 두고 새로운 것을 계속 추가할 수 있는 방법 중에 하나로, 유용한 컨셉
수많은 컨셉, 방법론들이 소프트웨어에 변경과 어떤식으로든 연관되어 있다는 것을 알 수 있음
앞으로 예시 웹프론트앤드 개발환경
웹프런트앤드 개발 환경이라고 하면 흔히 말하는 번들링 세팅과 관련된 환경을 이야기 할 수 있음
여기서 번들러는 웹팩을 사용 할 것임redux와 react를 만듦에 있어 번들러가 웹팩일 필요는 없지만웹팩이 갖고있는 여러가지 아키텍처측면에서 번들러 웹팩으로 선택https://webpack.js.org/
webpack
webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
webpack.js.org
실제로 웹팩에 코어(변하지 않는 부분) 실제 기능을 제공하기 보다는 개별적인 기능들을 제공하는데 필요한 코어의 구조만을 가지고 있고 실제 사용자들이 필요로 하는 기능은 대부분 플러그인이나 혹은 미들웨어 혹은 로더라고 불리는 컨셉의 소프트웨어들로 제공을 하고 있음. 유저에게 훨씬 더 유연하고 다양한 기능 제공
Babel은 프론트엔드 개발에 있어서 필수적인 툴중에 하나가 됬음
Babel · The compiler for next generation JavaScript
The compiler for next generation JavaScript
babeljs.io
Babel도 플러그인 아키텍처로 만들어져 있음핵심코어는 거의 변경이 없고 결국 대부분의 기능들은 플러그인이라고 하는 소프트웨어를 이용해서 결합되고 조합됨
'기초 > 프론트엔드 종합반 HTML&CSS, JS, React' 카테고리의 다른 글
7주차_ HTML/CSS/JS로 만드는 스타벅스 웹사이트3 (0) | 2023.01.22 |
---|---|
7주차_ HTML/CSS/JS로 만드는 스타벅스 웹사이트2 (0) | 2023.01.20 |
6주차_ HTML/CSS/JS로 만드는 스타벅스 웹사이트3 (0) | 2023.01.18 |
6주차_ HTML/CSS/JS로 만드는 스타벅스 웹사이트2 (0) | 2023.01.17 |
6주차_ HTML/CSS/JS로 만드는 스타벅스 웹사이트 (0) | 2023.01.17 |
댓글