1.1디자인패턴
프로그램을 설계할 때 발생했던 문제점들을 해결할 수 있도록 하나의 규약 형태로 만들어 놓은 것을 의미한다.
1.1.1싱글톤 패턴
하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.
보통 데이터베이스 연결 모듈에 많이 사용한다.
하나의 인스턴스를 만들어놓고 해당 인스턴스를 다른 모듈들이 공유하며 사용한다.
- 장점: 인스턴스를 생성할 때 드는 비용이 줄어든다
- 단점: 의존성이 높아진다.
자바스크립트의 싱글톤 패턴
리터럴{} 또는 new Object로 객체를 생성하게 되면 다른 어떤 객체와도 같지 않기 때문에 이 자체만으로도 싱글톤 패턴을 구현할 수 있다.
const obj = {
a: 27
}
const obj2 = {
a: 27
}
console.log(obj === obj2)
// false
obj 와 obj2는 다른 인스턴스를 가진다.
실제 싱글톤 패턴은 아래와 같은 코드로 구성된다.
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this
}
return Singleton.instance
}
getInstance() {
return this
}
}
const a = new Singleton()
const b = new Singleton()
console.log(a === b) // true
데이터베이스 연결 모듈
싱글톤 패턴은 데이터베이스 연결 모듈에 많이 쓰인다.
// DB 연결을 하는 것이기 때문에 비용이 더 높은 작업
const URL = 'mongodb://localhost:27017/kundolapp'
const createConnection = url => ({"url" : url})
class DB {
constructor(url) {
if (!DB.instance) {
DB.instance = createConnection(url)
}
return DB.instance
}
connect() {
return this.instance
}
}
const a = new DB(URL)
const b = new DB(URL)
console.log(a === b) // true
이를 통해 데이터베이스 연결에 관한 인스턴스 생성 비용을 아낄 수 있다.
mongoose
싱글톤 패턴은 node.js에서 mongoDB 데이터베이스를 연결할 때 쓰는 mongoose 모듈에서 볼 수 있다.
mongoose의 데이터베이스를 연결할 때 쓰는 connect()라는 함수는 싱글톤 인스턴스를 반환한다.
Mongoose.prototype.connect = function (uri, options, callback) {
const _mongoose = this instanceof Mongoose ? this : mongoose;
const conn = _mongoose.connection;
return _mongoose._promiseOrCallback(callback, cb => {
conn.openUri(uri, options, err => {
if (err != null) {
return cb(err);
}
return cb(null, _mongoose);
});
});
};
MySQL
node.js에서 MySQL 데이터베이스를 연결할 떄도 싱글톤 패턴이 사용된다.
// 메인 모듈
const mysql = require('mysql');
const pool = mysql.createPool({
connectionLimit: 10,
host: 'example.org',
user: 'kundol',
password: 'secret',
database: '승철이디비'
});
pool.connect();
// 모듈 A
pool.query(query, function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
// 모듈 B
pool.query(query, function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
메인 모듈에서 데이터베이스 연결에 관한 인스턴스를 정의하고 A 또는 B에서 해당 인스턴스를 기반으로 쿼리를 보내는 형식으로 쓰인다.
싱글톤 패턴 단점
- TDD(Test Driven Development)를 사용할 때 걸림돌이 된다.
TDD는 단위 테스트를 주로 하는데 단위 테스트는 테스트가 서로 독립적이어야 하며 어떤 순서로든 실행 할 수 있어야한다.
싱글톤 패턴은 미리 생성된 인스턴스를 기반으로 구현하는 패턴이므로 테스트마다 독립적인 인스턴스를 만들기 어렵다.
모듈 간의 결합을 강하게 만들 수 있다.
이때 의존성 주입(DI, dependency Injection)을 통해 모듈 간의 결합을 느슨하게 만들어 해결할 수 있음
의존성 주입
- 의존성: 종속성이라고도하며 A가 B에 의존성이 있다는 것은 B의 변경 사항에 대해 A 또한 변해야 한다는 것을 의미
메인모듈이 직접 하위 모듈에 대한 의존성을 주기보단 의존성 주입자가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식이다.
메인 모듈(상위 모듈)은 하위 모뮬에 대한 의존성이 떨어지게 됨.(티커플링이 된다고한다)
의존성 주입 장점
- 테스팅하기 쉽고 마이그레이션하기 수월하다
- 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어주기 때문에 애플리케이션 의존성 방향이 일관 된다.
- 쉽게 추론할 수 있다.
- 모듈 간의 관계들이 조금 더 명확해진다.
의존성 주입 단점
- 클래스 수가 늘어나 복잡성이 증가한다.
- 런타임 패널티가 생기기도 한다.
의존성 주입 원칙
- 상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야한다.
- 둘 다 추상화에 의존해야 하며 이때 추상화는 세부 사항에 의존하지 말아야한다.
1.1.2팩토리 패턴
객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자
상속 관계에 있는 클래스에서 상위 클래스가 중요한 뼈대를 결정, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴이다.
- 상위,하위 클래스가 분리되기 때문에 느슨한 결합을 가짐
- 상위 클래스는 인스턴스 생성 방식에 알 필요가 없기 때문에 더 많은 유연성을 갖게됨
- 유지 보수성 증가
예) 라떼, 아메리카노, 우유 레시피라는 구체적인 내용이 들어 있는 하위 클래스가 컨베이어 벨트를 통해 전달 -> 상위 클래스인 바리스타 공장에서 이 레시피를 토대로 우유 등을 생산하는 공정
자바스크립트 팩토리 패턴
간단히 new Object()로 구현할 수 있음
const num = new Object(42)
const str = new Object('abc')
num.constructor.name; // Number
str.constructor.name; // String
전달받은 값에 따라 다른 객체를 생성하며 인스턴스의 타입등을 정한다.
class CoffeeFactory {
static createCoffee(type) {
const factory = factoryList[type]
return factory.createCoffee()
}
}
class Latte {
constructor() {
this.name = "latte"
}
}
class Espresso {
constructor() {
this.name = "Espresso"
}
}
class LatteFactory extends CoffeeFactory{
static createCoffee() {
return new Latte()
}
}
class EspressoFactory extends CoffeeFactory{
static createCoffee() {
return new Espresso()
}
}
const factoryList = { LatteFactory, EspressoFactory }
const main = () => {
// 라떼 커피를 주문한다.
const coffee = CoffeeFactory.createCoffee("LatteFactory")
// 커피 이름을 부른다.
console.log(coffee.name) // latte
}
main()
- CoffeeFactory(상위 클래스)가 중요한 뼈대를 결정
- 하위 클래스인 LatteFactory가 구체적인 내용을 결정
이것을 의존성 주입이라고도 볼 수 있다.
커피팩토리에서 라떼팩토리의 인스턴스를 생성하는게 아니라 라떼팩토리에서 생성한 인스턴스를 커피 팩토리에 주입하고 있기 때문
createCoffe() 메서드를 정적 메서드로 선언한 것은 정적 메서드로 정의하면 클래스를 기반으로 객체를 만들지 않고 호출이 가능하며
해당 메서드에 대한 메모리 할당을 한 번만 할 수 있는 장점이 있음
Enum 이것 머예요
상수의 집합을 정의할 때 사용되는 타입임
예) 월, 일, 색상 등 상수 값을 담음
1.1.3전략 패턴
정책 패턴이라고도 하며 객체의 행위를 바꾸고 싶은 경우 직접 수정하지 않고
전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴
예시 코드 30p ~32p 참고
그래서 이게 뭔말이냐묜
행동(알고리즘)을 갈아끼울 수 있게 만든다는 뜻임
예) 어떤 객체(캐릭터,프로그램 요소 등)가 어떻게 행동할지를 내부에 직접 코드로 박아넣지 않고
외부에서 전략을 갈아끼우는 방식으로 바꾸는 패턴
예시)
게임 캐릭터가 있다고 가정
캐릭터는 공격 방법을 가지고 있음
캐릭터1은 칼로 공격
캐릭터2는 총으로 공격
캐릭터3은 마법으로 공격
이걸 캐릭터 클래스 안에 직접 attack() 함수를 다 다르게 넣으면
나중에 공격 방법을 바꾸려면 캐릭터 코드를 고쳐야함
그걸 전략 패턴으로하게 되면
공격방법을 클래스로 분리해서, 캐릭터에 전략처럼 끼워넣을 수 있게 만든다.
// 전략 (공격 방법 인터페이스)
class AttackStrategy {
attack() {
throw new Error("이건 구현해야 함");
}
}
// 각각의 전략 (행동)
class SwordAttack extends AttackStrategy {
attack() {
console.log("칼로 공격!");
}
}
class GunAttack extends AttackStrategy {
attack() {
console.log("총으로 공격!");
}
}
class MagicAttack extends AttackStrategy {
attack() {
console.log("마법으로 공격!");
}
}
// 컨텍스트 (전략을 사용하는 객체)
class Character {
constructor(attackStrategy) {
this.attackStrategy = attackStrategy;
}
setAttackStrategy(strategy) {
this.attackStrategy = strategy;
}
attack() {
this.attackStrategy.attack();
}
}
const warrior = new Character(new SwordAttack());
warrior.attack(); // 칼로 공격!
warrior.setAttackStrategy(new MagicAttack());
warrior.attack(); // 마법으로 공격!
- 전략 패턴은 행동을 캡슐화해서 나중에 갈아끼우기 쉽게 만드는 패턴이다.
- 캐릭터는 어떻게 공격하는지 몰라도 됨 -> 전략이 알아서 함
- 유지보수도 편하고, 확장도 쉬움
passport의 전략 패턴
전략 패턴을 활용한 라이브러리로는 passport가 있다.
- passport: node.js에서 인증 모듈을 구현할 때 쓰는 미들웨어 라이브러리
전략을 기반으로 인증할 수 있게 해줌 localStartegy전략, OAuth 전략 등 지원함
코드34p 참고
1.1.4옵저버 패턴
주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴
- Subject: 주제/데이터를 가진 것. 상태가 바뀌면 옵저버들한테 알려줌
- Observer: 구독자. 주제한테 등록해놓고 상태가 바뀌면 update() 호출돼서 반응
옵저버 패턴을 활용한 서비스: 트위터
앤나
옵저버 패턴은 이벤트 기반 시스템을 사용하며 MVC(Model-View-Controller) 패턴에도 사용한다.
모델에서 변경사항이 생김 -> update()메서드로 옵저버인 view에게 알려주고 -> 이를 기반으로 컨트롤러가 작동
코드 37p ~ 39p 참고
자바:상속과 구현
- 상속(extends): 자식 클래스가 부모 클래스의 메서드 등을 상속 받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있는 것
재사용성, 중복성의 최소화가 이루어짐 - 구현(implements): 부모인터페이스를 자식 클래스에서 재정의하여 구현. 반드시 부모 클래스의 메서드를 재정의하여 구현
차이점
- 상속: 일반 클래스 abstract 클래스 기반으로 구현
- 구현: 인터페이스 기반으로 구현
자바스크립트 옵저버 패턴
js에서 옵저버 패턴은 프록시 객체를 통해 구현할 수도 있음
- 프록시 객체: 어떠한 대상의 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 작업을 가로챌 수 있는 객체. 두 개의 매개변수를 가짐
- target: 프록시할 대상
- handler: 타켓 동작을 가로채고 어떠한 동작을 할 것인지가 설정되어 있는 함수
function createReactiveObject(target, callback) {
const proxy = new Proxy(target, {
set(obj, prop, value){
if(value !== obj[prop]){
const prev = obj[prop]
obj[prop] = value
callback(`${prop}가 [${prev}] >> [${value}] 로 변경되었습니다`)
}
return true
}
})
return proxy
}
const a = {
"형규" : "솔로"
}
const b = createReactiveObject(a, console.log)
b.형규 = "솔로"
b.형규 = "커플"
// 형규가 [솔로] >> [커플] 로 변경되었습니다
- createReactiveObject: 원래 객체를 감시할 수 있는 프록시 객체로 바꿔주는 함수
- Proxy: js에서 객체의 동작을 가로채서 조작할 수 있게 해주는 기능
- callback: 상태가 바뀔 때마다 실행되는 함수(지금 코드에선 console.log)
function createReactiveObject(target, callback) {
- target: 감시할 원본 객체: (예: { "형구": "솔로" } )
- callback: 값이 바뀔 때 호출할 함수
const proxy = new Proxy(target, {
set(obj, prop, value){
- proxy를 만들고 있음. 이때 set()은 객체의 속성을 바꿀 때 실행됨
- obj: 원본 객체 (target)
- prop: 바꾸려는 속성 이름 (예: "형구")
- value: 새로 넣으려는 값 (예: "커플")
if(value !== obj[prop]){
- 값이 바뀔 때만 반응하도록함.
커플을 넣으려는데 이미 커플이면 -> 아무일도 안함
const prev = obj[prop]
obj[prop] = value
- 기존 값을 prev로 저장
- 새 값을 실제 객체에 반영
callback(`${prop}가 [${prev}] >> [${value}] 로 변경되었습니다`)
- 값이 바뀌었으니까 콜백함수를 실행
- 여기서는 conosl.log(...)실행됨
}
return true
- set()에서는 true를 리턴해야 설정이 정상적으로 완료됨 (프록시 규칙)
return proxy
}
- 감시 기능이 들어간 프록시 객체 리턴
const a = {
"형규" : "솔로"
}
const b = createReactiveObject(a, console.log)
- a: 원본 객체
- b: 감시 가능한 버전 (프록시 객체)
1.1.5 프록시 패턴과 프록시 서버
프록시 객체는 디자인 패턴중 하나인 프록시 패턴이 녹아들어 있는 객체이다.
프록시 패턴
대상 객체에 접근하기 전 그 접근에 대한 흐름을 가로채 해당 접근을 필터링하거나 수정등의 역할을 하는 계층이 있는 디자인 패턴
객체의 속성, 변환 등을 보완하며 보안, 데이터 검증, 캐싱, 로깅에 사용한다.
프록시는 아래와 같은 역할을 할 수 있다.
- 접근 제어: (예: 권한 쳌)
- 로깅
- 캐싱
- 지연 로딩 (Lazy loading)
- 네트워크 통신 대리
프록시 객체로 쓰이기도 하지만 프록시 서버로도 활용된다.
프록시 서버
서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터시스템이나 응용프로그램을 말함
nginx
비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹 서버.
주로 node.js 서버 앞단의 프록시 서버로 활용된다.
많은 사람들이 node.js 서버를 구축할 때 앞단에 nginx를 둔다.
이를 통해 익명 사용자가 직접적으로 서버에 접근하는 것을 차단, 간접적으로 한 단계를 더 거치게 만들어 보안을 강화할 수 있음
nginx를 프록시 서버로 둬서 실제 포트를 숨길 수 있고 정적 자원을 gzip 압축하거나 메인 서버 앞단에서 로깅을 할 수도 있음
CloudFlare
전 세계적으로 분산된 서버가 있고 이를 통해 어떠한 시스템의 콘텐츠 전달을 빠르게 할 수 있는 *CDN 서비스이다.
웹 서버 앞단에 프록시 서버로 두어 DDOS 공격 방어나 HTTPS 구축에쓰인다.
의심스러운 트래픽이 많이 발생하면 이 때문에 클라우드 서비스 비용이 발생할 수도 있는데 이때 cloudFlare가 의심스러운 트래픽인지를 먼저 판단해 CAPTCHA등을 기반으로 일정부분 막아주는 역할도 수행한다.
- DDOS 공격 방어: 짧은 기간 동안 네트워크에 많은 요청을 보내 네트워크를 마비시켜 웹 사이트의 가용성을 방해하는 사이버 공격 유형
CloudFlare는 의심스러운 트래픽, 사용자가 접속하는 것이 아닌 시스템을 통해 오는 트래픽을 자동으로 차단해 DDOS 공격으로 부터 보호한다. - HTTPS 구축: 서버에서 HTTPS를 구축할 때 인증서를 기반으로 구축할 수도 있다. CloudFlare를 사용하면 별도의 설치 없이 좀 더 손쉽게 HTTPS를 구축할 수 있다.
CDN(Content Delivery Network)
사용자가 인터넷에 접속하는 곳과 가까운 곳에서 콘텐츠를 캐싱 또는 배포하는 서버 네트워크
CORS와 프론트엔드의 프록시 서버
CORS(Cross-Origin Resource Sharing)는 서버가 웹 브라우저에서 리소스를 로드할 때 다른 오리진을 통해 로드하지 못하게 하는 HTTP 헤더 기반 메커니즘이다.
프론트엔드에서 서버를 만들어서 백엔드 서버와 통신할때 CORS 에러를 마주치는데 이를 해결하기 위해 프론트엔드에서 프록시 서버를 만들기도 한다.
오리진
프로토콜과 호스트 이름, 포트의 조합을 말함.
예) https://example.com:12010/test 일때 오리진은 https://example.com:12010을 뜻함
프론트에서는 127.0.0.1:3000으로 테스팅 할 때 백엔드는 127.0.0.1:8080이라면 포트번호가 다르기 때문에 CORS 에러가 난다.
이때 프록시 서버를 둬서 프론트엔드 서버에서 요청되는 오리진을 127.0.0.1:8080 바꾸는 것임.
- 127.0.0.1: 루프백(loopback)IP로 본인 PC 서버의 IP를 뜻함.
프론트엔드 서버 앞단에 프록시 서버를 놓아 /api 요청은 users API, /api2 요청은 users API2에 요청할 수 있다.
CORS 에러 해결은 물론 다양한 API 서버와의 통신도 매끄럽게 할 수 있음
1.1.6 이터레이터 패턴
이터레이터를 사용하여 컬렉션의 요소들에 접근하는 디자인 패턴이다.
순회할 수 있는 여러가지 자료형의 구조와 상관없이 이터레이터라는 하나의 인터페이스로 순회가능
const mp = new Map()
mp.set('a', 1)
mp.set('b', 2)
mp.set('cccc', 3)
const st = new Set()
st.add(1)
st.add(2)
st.add(3)
const a = []
for(let i = 0; i < 10; i++)a.push(i)
for(let aa of a) console.log(aa)
for(let a of mp) console.log(a)
for(let a of st) console.log(a)
/*
a, b, c
[ 'a', 1 ]
[ 'b', 2 ]
[ 'c', 3 ]
1
2
3
*/
다른 자료구조인 set, map임에도 똑같은 for a of b 라는 이터레이터를 통해 순회하는것을 볼 수 있음.
- 이터레이터 프로토콜: 이터러블한 객체들을 순회할 때 쓰이는 규칙
- 이터러블한 객체: 반복 가능한 객체로 배열을 일반화한 객체
1.1.7 노출모듈 패턴
즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴을 말함
js는 그런거 없고 전역 범위에서 스크립트가 실행됨. 그래서 노출모듈 패턴을 통해 private와 pubilc 접근 제어자를 구현하기도 함
const pukuba = (() => {
const a = 1
const b = () => 2
const public = {
c : 2,
d : () => 3
}
return public
})()
console.log(pukuba)
console.log(pukuba.a)
// { c: 2, d: [Function: d] }
// undefined
- a,b는 다른 모듈에서 사용할 수 없는 변수,함수이며 private 범위를 가짐.
- c,d는 다른 모듈에서 사용할 수 있는 변수,함수이며 pubilc 범위를 가짐.
노출모듈 패턴을 기반으로 만든 js 모듈 방식으로는 CJS(CommonJS) 모듈 방식이 있다.
- public: 클래스에서 정의된 함수에서 접근 가능하며 자식 클래스와 외부 클래스에서 접근 가능한 범위
- protected: 클래스에서 정의된 함수에서 접근 가능, 자식 클래스에서 접근 가능하지만 외부 클래스에서는 접근이 불가능한 범위
- private: 클래스에서 정의된 함수에서 접근 가능하지만 자식 클래스와 외부 클래스에서 접근 불가능한 범위
- 즉시 실행 함수: 함수를 정의하자마자 바로 호출하는 함수. 초기화 코드, 라이브러리 내 전역 변수의 충돌 방지 등에 사용한다.
1.1.8 MVC 패턴
모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 디자인 패턴
사용자 -> 컨트롤러 -> 모델 (데이터) -> 뷰( 화면 표시)
- 사용자가 버튼 클릭 (-> 컨트롤러)
- 컨트롤러가 모델에 데이터 변경 요청
- 모델이 데이터 수정하고, 뷰가 자동으로 바뀜
애플리케이션 구성요소를 세 가지 역할로 구분해 개발 프로세스에서 각각 구성 요소에만 집중해서 개발할 수 있음
- 장점: 재사용성과 확장성이 용이함
- 단점: 애플리케이션이 복잡해질수록 모델과 뷰의 관계가 복잡해짐
모델
애플리케이션 데이터인 데이터베이스, 상수, 변수등을 뜻함
긍께
데이터와 그 데이터를 다루는 로직을 담당하는 부분이다.
- 앱 상태를 저장( 예: 유저정보, 게시글 목록, 쇼핑카트 등)
- 그 데이터를 읽고/쓰기 위한 메서드를 제공
- 데이터를 검증하거나 가공할 수도 있음
- 뷰나 컨트롤러에 UI 정보는 신경안씀
뷰
사용자에게 보여지는 화면(UI)을 담당한다.
데이터를 시작적으로 표현해주는것이다.
- 모델에 있는 데이터를 가져와서 보여준다.
- 사용자의 시선을 책임짐 (예쁘게 보여주기!!!!)
- 버튼, 목록, 입력창 같은 UI 요소 담당
- 직접 데이터 처리나 제어는 하지 않는다.
뷰는 말이지요
모델이 바뀌면 뷰도 바뀐다. ( 자동 or 수동)
사용자와 직접 상호작용하는 부분이지만 그 입력을 컨트롤러에게만 전달한다. 입니다.
긍께 얘는 데이터가 어떻게 저장되든가 말든가 신경 안씀
버튼 누르면 뭘 해야할지는 컨트롤러에게 맡긴다.
그저. 데이터를 받아서 그린다.
컨트롤러
사용자 입력(클릭, 타이핑 등)을 받고 모델을 업데이트 하거나 뷰를 갱신하도록 중간에서 다리 역할을 한다.
버튼 클릭됨 -> 컨트롤러가 처리 -> 모델에 새 데이터 추가
모델이 바뀜 -> 뷰가 갱신됨.
예와 같이 모델과 뷰의 생명 주기도 관리하며 변경 통지를 받으면 이를 해석하여 각각 구성요소에 해당 내용에 대해 알려줌
역할 | 요약 |
Model | 데이터 저장 & 처리 담당 |
View | 사용자에게 보이는 화면 |
Controller | 사용자 입력 받아 처리하고 모델 or 뷰 연결 |
책에선 스프링을 다루지만 나는 . 프론트엔드니께. 리액트로 다루겟읍니다. (54p 참고하십시오.)
리액트랑 MVC는 어떻게 연결되나예.
리액트에선 전통적인 MVC 처럼 Controller를 따로 두지 않는다.
왜.
리액트 컴포넌트가 view + controller 역할을 동시에 하기 때문임
function Counter() {
const [count, setCount] = useState(0); // Model
return (
<div>
<p>{count}번 클릭됨</p> {/* View */}
<button onClick={() => setCount(prev => prev + 1)}> {/* Controller 역할 */}
클릭
</button>
</div>
);
}
- useState: 모델 (데이터 저장)
- onClick: 컨트롤러 (사용자 입력 받아서 모델 변경)
- {count}: 뷰( 데이터 보여줌)
요소 | 역할 |
useState | 상태 데이터(model) 저장 |
return(...) | 화면 구성 (view) |
이벤트 핸들러 (onClick) | 입력처리(controller 역할) |
1.1.9 MVP 패턴
MVC 패턴으로 파생되었으며 MVC에서 C에 대항하는 컨트롤러가 프레젠터로 교체된 패턴
뷰와 프레젠터는 일대일 관계이기 때문에 MVC 패턴보다 더 강렬한 결합을 지닌 디자인 패턴이다
1.1.10 MVVM 패턴
MVC의 C에 해당하는 컨트롤러가 뷰모델로 바뀐 패턴
뷰모델은 뷰를 더 추상화한 계층
MVC와 다르게 커멘드와 바인딩을 가지는것이 특징이다.
뷰와 뷰모델 사이의 양방향 데이터 바인딩을 지원하며 UI를 별도의 코드 수정없이 재사용할 수 있고 단위 테스팅하기 쉽다는 장점이 있다.
긍께. 데이터 바인딩(data Binding)으로 viewmodel 상태가 바뀌면 자동 반영 됨.
view는 단순히 보여주는데 집중.
뷰에서 직접 로직 돌리거나 처리를 하지 않는다잉. 뷰모델에 위임혀.
- View: 화면 출력 담당
- ViewModel: 뷰를 위한 데이터 + 로직 제공자 (컨트롤러 너낌도 있음)
- Model: 실제 데이터와 로직
여기서 뷰는
사용자에게 보여지는 화면을 담당하고
모델이나 상태를 직접 건드리지않고
뷰모델이 알려주는대로만 표시하는 역할한다.입니다
뷰.
MVVM 패턴을 가진 대표적인 프레임워크로 뷰가 있음.
뷰는 반응형이 특징인 프론트엔드 프레임워크임.
function TodoView({ viewModel }) {
return (
<ul>
{viewModel.todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
- viewModel.todos: ViewModel이 준 상태
- View는 그냥 보여주기만 함. 가공 X
- “할 일 추가” 같은 건 ViewModel에게 부탁함
그닉가는. MVVM에서 View는 UI만 담당하고 상태나 로직은 ViewModel에 전부 맡긴다.
그래서 구조가 엄청 깔끔하게 분리됨. -> 유지보수 good.
- 커맨드: 여러가지 요소에 대한 처리를 하나의 액션으로 처리할 수 있게 하는 기법
- 데이터 바인딩: 화면에 보이는 데이터와 웹 브라우저의 메모리 데이터를 일치시키는 기법으로 , 뷰모델을 변경하면 뷰가 변경된다.
이어서. 프로그래밍 패러다임으로 오겠습니다.
'cs' 카테고리의 다른 글
02. 네트워크 기초 (0) | 2025.04.30 |
---|---|
01-2. 프로그래밍 패러다임 (1) | 2025.04.22 |
03. SQL (0) | 2025.04.04 |
02. RDBMS의 기본 (0) | 2025.04.04 |
01. 데이터베이스 (0) | 2025.04.04 |