크롱

상태 관리와 옵저버 패턴

데이터를 눈으로 볼 수 없음. 뷰를 통해서 보는데 뷰는 다양한 형태 store(model) view 모델이 여러개일 경우 이런저런 관계가 될 수 있음 애플리케이션이 간단할 때는 적합하지 않은 오버엔지니어링 복잡한 경우에는 모델과 뷰를 나눔 왜 나누느냐? 변경때문 서로 바뀔 때마다 바뀌어야 하기 때문에 모델과 뷰를 분리(변경이 자주 되는 부분을 분리) 여러개의 복잡한 관계 통신이 복잡해짐 중개자(Controller)를 두고 모델과 뷰를 나눠 의존도를 낮춤 store와 view를 어떻게 분리할 것인가? 에 대한 이야기

view간에 바로 통신 todo라는 store를 분리해서 변경이 일어날 경우, store에 알려주고(통신이 일어남) store가 다른 뷰에 알려줌

view store view 관계의 흐름 변경을 일으키는 존재는 뷰나 컨트롤러를 통해 조작하고 통보해주고, 자동으로 렌더링 render함수를 등록

class Observable {
    constructor() {
        this._observers = new Set();
    }
    subscribe(observer) {
        this._observers.add(observer);
    }
    unsubscribe(observer) {
        this._observers.delete(observer);
    }
    notify(data) {
        this._observers.forEach(observer => observer(data));
    }
}
  • 생성자
    • 구독 리스트를 보관
  • 구독
    • 뷰는 모델을 구독
  • 해지
  • 통보
    • 구독 리스트를 돌면서 실행

M-V 관계에서 옵저버 패턴 적용

  • Model이 Observable이 되고 View가 Observer가 됨.
  • 상태관리
    • 뷰에서 상태를 분리
    • 상태관리만 라이브러리에서 관리
    • 프레임워크들이 상태에 대한 처리를 도와주기는 하는데 대부분이 옵저버 패턴과 유사함
    • store-view관계에서 뷰가 많을수록 옵저버 패턴이 유릭함
class TodoModel extends Observable {
    constructor() {
        super();
        this.todos = [];
    }
 
    addTodo(todo) {
        this.todos = [...this.todos, todo];
        this.notify(this.todos);  // Observer들에게 업데이트된 todos 전달
    }
}

프로토타입 체이닝을 통해 사용 데이터를 Immutable하게 변경 store가 옵저버를 구독하고 있다가 로그뷰에 쌓임 다른 뷰에서도 통신하여 업데이트

에러 핸들링

에러처리의 목표 정하기 exception 에러처리 어떠한 상황에서 어떻게 하기 위함인지? 에 대한 목표를 가져야 함 에러 메커니즘에 대한 이해도 좋지만 목표 또한 중요 에러는 호출되는 지점에서 받기 콜백 안에서 실행한 비동기 코드는 잡지 못함 404는 코드지만 에러가 아님

function renderError({ errorMsg }) {
  alert(errorMsg);
}
 
async function getData() {
  try {
    const res = await fetch('https://httpstat.us/404');
 
    if (!res.ok) {
      const status = res.status;
      if (status === 404) {
        renderError({ errorMsg: `요청주소에 문제가 있어요 ㅠ.ㅠ : ${status}` });
      } else if (status === 401) {
        renderError({ errorMsg: `넌 누구니 ?? 🧐 : ${status}` });
      } else {
        renderError({ errorMsg: `서버에 요청 중 문제가 생겼어요 >.< : ${status}` });
      }
      return;
    }
 
    const data = await res.json();
    renderPage(data);
  } catch (error) {
    // 네트워크 오류
    renderError({ errorMsg: `네트워크 에러 발생: ${error.message}` });
  }
}
 
getData();
 
 

여기서 400대 에러는 대부분 에러가 아님 status로 분기하여 에러를 처리해야함 메커니즘

  • 던지는게 뭔지, 누가 받는지
  • try/catch문의 사용
  • 비동기 코드는 안잡힘
  • await new Promise하는 방법으로 에러를 잡을 수 있음

기타

모노레포 뷰 함수(역할을 줄이기 위함) 클래스 여러가지 기능의 복합체

호눅스

인증 복습

세션

서버 안에 세션이 존재 세션이란? 서버에서 유지하는 사용자 연결 정보 사용자가 N명이 있을 경우 N개의 세션 세션의 경우 선택사항.

게임의 관점에서 채널 서버 / 방 세션 매칭이되면 stateful한 연결

세션 아이디를 해시맵 | 오브젝트의 키값을 이용해 세션을 찾음 set cookie를 통해 브라우저의 쿠키 저장소에 저장 클라이언트를 요청을 보낼 때 헤더에 담아 보냄 same site에만 cookie가 유효함 프로토콜 도메인 포트 등 모두 같아야 함

인증 권한부여

토큰의 경우

response를 json 형태의 바디로 보내줌 저장할 수 있는 곳 로컬스토리지나 세션스토리지 토큰은 요청에 따라서 미들웨어를 통해 세션 정보를 알아옴 리프레시와 엑세스

JWT

JSON Web Token 세션을 쓸 수도 있고 안쓸 수도 있음 JWT는 토큰에 정보를 담을 수 있음 서버에 세션을 저장할 필요도 없이 그대로 해석할 수 있음 토큰에는 외부에서 봐도 괜찮은 정보(위변조를 알 수 있음)를 담음

세션 없이 닉네임을 저장 확인해서 id,닉네밍 등을 토큰으로 만들어서 보내줌(JWT의 엑세스 토큰으로 만들어서 씀) 서버는 받아서 토큰을 받은 다음에 verify해야 함 signiture를 가지고 검증

인프라

인프라 서버 개발 서버의 운용 IDC(Internet Datat Center)에 서버를 입주시킴 서버에 아파치 설치 WAS 설치

프록시(proxy)

  • 포워드 프록시
    • 외부에서 접근할 떄 서버의 주소를 모르게 함
    • 사용자가 직접 서버로 요청을 했는데, 서버로 보내는 요청을 가로채서 처리
  • 리버스 프록시
    • 사용자가 프록시에 요청을 하면, 프록시가 전달해서 회신

서버가 하나 더 추가될 경우 db를 분리해서 스위치(네트워크 2계층)을 사용해서 네트워크 통신 네트워크의 오버헤드

  • 긴 쿼리 하나 사용
    • 성능이 좋을 때가 많음
    • 원하는 것만 가져오기
    • 쿼리 자체가 복잡하고 잘 짜기 어려움
    • 가독성과 유지보수가 낮음
  • 짧은 쿼리 여러개 사용
    • db랑 네트워크 통신 과정에서 Db의 부하가 적음
    • 네트워크 부하의 가능성이 있긴 함
    • 중간에 데이터가 더 커질 가능성
    • 객체지향적 커서 기반 페이징(더보기)

서버 3대(기업의 최소한도) DB 이중화(필수) 복제된 DB는 장애가 났을 때 사용

서버 4대

  • DB 이중화 + 웹 서버 추가(Nginx + WAS server + DB 2대) 리버스 프록시 사용자 요청이 Nginx로 들어감

  • Nginx+Was , Nginx + Was, DB1, DB2 사용자의 요청을 위해서는 로드 밸런서(L4,L7)가 필요. 라운드로빈으로 처리 L4 4계층(tcp) L7 7계층(http) sticky session 우리나라에서 많이 사용 Read Replica를 통해 읽기 요청을 분산 세션을 복제하여 broadcast 하거나 별도의 세션 Db를 운용

프론트는 S3에 빌드된 결과물을 버킷에 넣고 CDN을 통해 들어옴 API Gateway

msa 자기만의 API와 db가 따로 있음 서버관리가 복잡해짐 쿠버네티스나 도커를 사용

배포하기

nvm으로 nodejs 설치 git clone으로 기존 레포 받아오기 git branch로 타겟브랜치 확인 npm install npm start 보안그룹에 인바운드 3000번 추가 npm start which bash >> deploy.sh vi deploy.sh

git fetch 
git pull
 
LOCAL=`git rev-parse HEAD`
REMOTE=`git rev-parse origin/dev`
 
if[[$LOCAL==$REMOTE]]; then
	echo "No need to deploy"
	exit 0
fi
 
PID = `lsof -t -i :3000`
kill -9 $PID
 
npm install
nohup npm start &
 
 
...
crontab -e
vi
*/5 * * * mon-fri /path >> /home/ubuntu/log.txt 2>&1
 
#2번은 에러의 경우 같이 표준출력으로 갈 수 있도록
 

cd express-sample-deploy 터미널을 닫으면 sighup

bash shell이 명령어를 실행하는 원리 pstree 명령어 systemd 모든 프로세스의 조상 bash아래에 Pstree에 달려있기 때문에 bash가 fork 2개 부모는 자식에게 명령어 실행하도록 fork됐던 pstree로 변신 복제 fork 실행 exec deploy 명령어를 실행할 때도 npm start가 bash의 자식 프로세스 부모는 sshd 종료하면서 sshd의 자식프로세스를 모두 종료 터미널이 닫히면 sighup 발생 kill 시그널을 보내는 명령어인데 defaultl가 sigterm sig kill 강제종료 sighup 나 꺼지니까 너도 같이 꺼져 시그널의 디폴트 행동은 그냥 종료가 기본 동작이기 떄문에 터미널이 꺼지면 sighup이 자식 프로세스에게 퍼지고 자식 프로세스도 함께 죽어버림 nohup을 통해 자식 프로세스가 죽지 않고 백그라운드로

nohup npm start & 죽일땐 ps -ef | grep npm pid 찾아서 죽이기 or lsof -t -i :3000 PID=lsof -t -i :3000 kill -9 $PID or ps -ef | grep express | cut -d ” ” -f1

cat nohup npm start &

git rev-parse HEAD git rev-parse origin/dev