몽고디비 연결에러
127.0.0.1로 바꿔야하는건 알고있었는데 몽고디비 연결부를 못찾고있다가 왜 안보이나 연결방법이 다른가 해서 튜터님과 팀원분께 질문드렸다. 스키마에 연결부가 있던걸 까먹었다. 해결하고 나니 connect 함수 선언부를 찾으면 되는게 보이는데 아깐 왜 못찾았을까
tcp: 데이터 누락이 없고 신뢰성이 높다. 전송속도가 udp보다 느리다.
udp: 신뢰성 없음. 데이터 유실되면 버리고 감. 속도가 빠름.
소켓: 네트워크 데이터를 송수신하기위해 반드시 거쳐야하는 연결부
패킷: 소켓을 통해 송수신하는 데이터 덩어리 하나
웹소켓: 실시간 웹 서비스를 제공하기 위해 만들어진 소켓
클라이언트의 요청이없어도 서버가 알아서 요청을 내려주는거
socket.io: 자바스크립트를 사용해 웹소켓을 사용하기위한 라이브러리
!!! 웹소켓!=socket.io
연결준비 공식문서
https://socket.io/docs/v3
서버에서 데이터보내는 코드 : socket.send("메세지값")
프론트엔드 이벤트 핸들링 코드:
socket.on("customEventName", (data) => {
console.log(data);
});
서버의 커스텀 이벤트로 데이터 발행: socket.emit("이벤트이름", "데이터") : broadcast의 의미. 연결된 모든 클라이언트에게 보내라
https://socket.io/get-started/chat#the-web-framework
express로 앱(app)을 생성하고 http 모듈로 래핑(Wrapping)하여 서버를 생성
const { createServer } = require("http");
const http = createServer(app);
위에서 생성한 http 서버 객체를 원래 있던 코드에서 "3000"을 지우고 대신 넣어주면 express와 socket.io를 동시에 사용가능
toString() vs toISOString
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
https://stackoverflow.com/questions/29747550/difference-between-moment-tostring-and-moment-toisostring
access token : 사용자의 권한이 확인 되었을 경우 해당 사용자를 인증하는 용도로 발급
무상태
그 차제로도 사용자를 인증하는 모든 정보를 가지고있음
refresh token : access토큰처럼 해당하는 사용자의 모든 인증 정보를 관리하는 것이 아니라, 특정한 사용자가 access 토큰을 발급받기 위한 용도로만 사용
사용자가 아닌 서버에서 해당 사용자의 정보를 저장소 또는 별도의 db에 저장하여 관리
특정 token만료가 필요할 경우 저장된 토큰을 제거하여 사용자의 인증 여부를 언제든지 제어가 가능하다는 장점이 있음
토큰이 탈취당할 경우 피해를 최소화하기 위해 사용
사용자가 서버와 최초 인증시에 발급
validateAccessToken과 validateRefreshToken:
Access Token 또는 Refresh Token이 우리가 발급한 토큰이 맞는지 검증
Access Token 또는 Refresh Token의 만료 여부를 검증
GET /set-token/:id
Refresh Token과 Access Token을 발급
createAccessToken는 Access Token을 생성하는 함수
createRefreshToken는 Refresh Token을 생성하는 함수
tokenObject라는 변수안에 할당
GET /set-token/:id API를 호출했을때 Access Token과 Refresh Token을 2개 발급하고, Refresh Token을 Key 값으로 입력된 id를 찾을 수 있음
accessToken, refreshToken이라는 Key로 Cookie를 2개 발급
GET /get-token API
validateAccessToken과 validateRefreshToken은 사용자가 전달한 Token이 정상적인 토큰인지 확인하는 함수
사용자가 Cookie를 전달할 때, Access Token이 없다면 에러가 발생
사용자가 Cookie를 전달할 때, Refresh Token이 없다면 에러가 발생
사용자가 전달한 Refresh Token이 인증되지 않았다면 에러가 발생
Refresh Token이 인증되었지만, 서버에 존재하지 않을 때 에러가 발생
Access Token VS Refresh Token
프로젝트를 빠르게 구현해야하거나 사용자의 요청에 대한 인증을 최소화 하기 위해서는 Access Token만을 사용할 것이고, 보안성을 중요시 여기고, 서버를 좀더 탄탄하게 구성해야 할 경우에는 Refesh Token을 사용
트랜잭션 Transaction :
여러개의 작업(쿼리)을 묶어 하나의 작업 단위로 그룹화하여 처리
데이터의 일관성을 유지해야하는 다양한 상황에서 사용
사용자가 항상 프로그램 실행을 완료하도록 구성하고, 만약 실행을 중단할 만한 치명적인 오류가 발생하더라도, DB에 피해가 가지않아 더욱 안전하게 구성
ACID : 트랜잭션을 이용하여 데이터베이스를 더욱 안전하게 구성할 수 있게 도와주는 트랜잭션 특징 4가지
원자성(Atomicity)
트랜잭션 내에서 실행되는 명령들을 하나의 묶음으로 처리하여, 내부에서 실행된 명령들이 전부 성공하거나, 아니면 모두 실패해야한다는 특징
하나의 함수처럼 트랜잭션을 사용 가능
일관성(Consistency)
작업이 성공할 경우 아무런 문제가 발생하지 않고, 실패하더라도 작업을 진행하던 도중 실패한 상태로 데이터를 방치하지 않는 특징
격리성(Isolation)
트랜잭션을 수행하는 중간 상태를 보거나 변경할 수 없도록 구성하는 특징
MySQL에서는 사용중인 DB 오브젝트에 락(Lock)을 걸어 격리성을 구현하게 됩니다. 여기서 락(Lock)을 건 상태는 DB에 접속한 또다른 클라이언트가 해당하는 DB 오브젝트를 읽거나, 사용할 수 없도록 만든다는 개념
→ 격리성이란 특징에서 동시성(Concurrency)과 격리 수준(Isolation Level)라는 개념이 나타남
동시성: 여러명의 클라이언트가 하나의 데이터를 동시에 사용및 공유 하는 것
지속성(Durability)
트랜잭션을 성공적으로 수행하면 수정된 데이터를 시스템에 영구적으로 적용하는 특징
트랜잭션의 중간 결과가 아니라 완성된 결과만 저장하여 데이터베이스에 이상이 생기더라도 자동 복구할 수 있는 특성
COMMIT: 성공시 작업 내역을 DB에 반영
ROLLBACK: 실패시 START TRANSACTION이 실행되기 전 상태로 작업 내역을 취소
락(Lock):
동시성을 제어하기 위해 사용하는 기능
당하는 데이터를 점유하여 다른 트랜잭션의 접근을 막아 동시성과 일관성의 균형을 맞추기 위해 사용
종류
락킹 수준(Locking Level)
글로벌 락(Global Locks) | 데이터베이스 락(Database Locks):
데이터베이스의 모든 테이블에 락을 걸어, 현재 트랜잭션을 제외한 나머지 트랜잭션들이 모든 테이블을 사용할 수 없도록 만듬
가장 높은 수준의 락, 가장 큰 범위
테이블 락(Table Locks):
다른 사용자가 작업중인 테이블을 동시에 수정 불가
네임드 락(Named Locks): 특정한 문자열을 점유
메타데이터 락: 데이터베이스 객체(대표적으로 테이블이나 뷰 등)의 이름이나 구조를 변경하는 경우에 획득하는 잠금
https://steady-coding.tistory.com/545#%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0_%EB%9D%BD
잘못된 락 설정을 하게 될 경우 여러분들은 모든 API가 동작하지 않는 교착 상태(Dead Lock){: 여러 테이블에 락(Lock)을 적용하여, 다른 작업이 처리되지 못하게 점유하고 있는 작업이 있을 때, 다른 작업을 끝나는 것을 무한정 기다리는것 } 가 발생하게 되어, 프로그램이 멈춰버리는 문제가 발생
트랜잭션의 격리 수준 (Isolation Level)
READ UNCOMMITTED
커밋 되지 않은 읽기(Uncommitted Read){: 다른 트랜잭션에 의해 작업중인 데이터를 읽게 되는 것 }를 허용하는 격리 수준
가장 낮은 수준의 격리수준이며, 락을 걸지 않아 동시성이 높지만 일관성이 쉽게 깨질 수 있음
READ COMMITTED
커밋 된 읽기(Committed Read)만을 허용하고, SELECT 문을 실행할 때 공유락을 검
다른 트랜잭션이 데이터를 수정하고 있는 중에는 데이터를 읽을 수 없어 커밋되지 않은 읽기현상이 발생하지 않음
REPEATABLE READ
읽기를 마치더라도 공유락을 풀지 않으며, 트랜잭션이 완전히 종료될 때 까지 락을 유지
공유락이 걸린 상태에서 데이터를 수정하는 것은 불가능하지만, 데이터를 삽입하는 것이 가능. 그로인해 팬텀 읽기(: 트랜잭션을 수행하던 중 다른 트랜잭션에 의해 삭제된 데이터 - 팬텀행(Phantom Rows)에 해당하는 데이터를 읽는 것 )가 발생할 수 있는 문제점
SERIALIZABLE
데이터를 읽는 동안 다른 트랜잭션이 해당 데이터를 읽거나 삽입할 수 없고, 새로운 데이터를 추가하는 것 또한 불가능
가장 높은 수준의 격리 수준이므로, 동시성이 떨어지는 문제점
Sequelize Transaction
// Sequelize의 트랜잭션을 변수에 할당하여 트랜잭션을 시작
const t = await sequelize.transaction();
try {
const user = await User.create({
firstName: '용우',
lastName: '이',
}, { transaction: t }); // 해당 쿼리에 트랜잭션을 적용
// 트랜잭션을 사용한 모든 로직을 Commit, DB에 반영
await t.commit();
} catch(transactionError) {
// 에러가 발생하였다면, 트랜잭션을 사용한 모든 쿼리를 Rollback, DB에 미반영
await t.rollback();
}
// 콜백으로 함수를 할당하여 비즈니스로직을 처리
const result = await sequelize.transaction( async(t) => {
const user = await User.create({
firstName: '용우',
lastName: '이',
}, { transaction: t }); // 해당 쿼리에 트랜잭션을 적용
return user;
});
Sequelize에서 격리 수준 설정
const t = await sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED,
});
UUID(범용 고유 식별자):
https://ko.wikipedia.org/wiki/%EB%B2%94%EC%9A%A9_%EA%B3%A0%EC%9C%A0_%EC%8B%9D%EB%B3%84%EC%9E%90
UUID(Universally Unique Identifier, 범용 고유 식별자)
총 4개의 정보를 하이픈(-) 으로 구분하여 순차적으로 저장한 데이터 타입
시간 정보를 포함하고 있어 생성된 순서대로 정렬이 되는 특징

디코드 사이트 : https://www.uuidtools.com/decode
사용자 히스토리 테이블의 경우 다양한 정보를 저장해야하므로 createdAt, updatedAt과 같은 컬럼을 사용하는것 보단, UUID를 사용하여 컬럼을 최소화 하는것이 로그 테이블에서는 더욱 효율적인 설계
사용자 이름 변경 API
트랜잭션을통해 발급받고 트랜잭션에서 격리수준 설정
userinfos 테이블에 데이터 업데이트
변경된 데이터를 userhistory 테이블에 삽입