본문 바로가기

Coding/내일배움캠프

[내일배움캠프] Node.js 4기 TIL | Day 32 | 24.02.01.(목)

1-13. 쿠키와 세션

1) 쿠키와 세션이란?

- 쿠키(Cookie) : 브라우저가 서버로부터 응답으로 Set-Cookie 헤더를 받은 경우 해당 데이터를 저장한 뒤 모든 요청에 포함하여 보냅니다.

- 세션(Session) : 쿠키를 기반으로 구성된 기술입니다. 단, 클라이언트가 마음대로 데이터를 확인할 수 있던 쿠키와는 다르게 세션은 데이터를 서버에만 저장합니다.

2) 쿠키(Cookie) 만들어보기

- 서버가 클라이언트의 HTTP 요청(Request)을 수신할 때, 서버는 응답(Response)과 함께 Set-Cookie라는 헤더를 함께 전송할 수 있습니다.
그 후 쿠키는 해당 서버에 의해 만들어진 응답(Response)과 함께 Cookie HTTP 헤더 안에 포함되어 전달받습니다.

3) req를 이용하여 쿠키 접근하기

- 클라이언트는 서버에 요청(Request)을 보낼 때 자신이 보유하고 있는 쿠키를 자동으로 서버에 전달하게 됩니다.
여기서 클라이언트가 전달하는 쿠키 정보는 Request header에 포함되어 서버에 전달되게 됩니다.

4) cookie-parser 미들웨어 적용하기

- cookie-parser 미들웨어는 요청에 추가된 쿠키를 req.cookies 객체로 만들어줍니다.
더이상 req.headers.cookie와 같이 번거롭게 사용하지 않아도 되겠죠?

5) 세션(Session) 만들어보기

1-14. JWT가 무엇인가요?

- JWT(Json Web Token)은 웹 표준으로서, 서버와 클라이언트 사이에서 정보를 안전하게 전송하기 위해 도움을 주는 웹 토큰(Web Token)입니다.

- header.payload.signature / 머리.가슴.배

- header : 헤더는 토큰의 타입과 어떤 암호화를 사용하여 생성된 데이터인지 정의되어 있습니다.
- payload : 페이로드는 실제 전달하려는 데이터를 담고 있습니다. 대표적으로 개발자가 원하는 데이터를 저장합니다.
- signature : 서명은 헤더와 페이로드, 그리고 비밀 키(Secret Key)를 이용하여 생성됩니다. 이 서명은 토큰이 변조되지 않은 정상적인 토큰인지 확인할 수 있게 도와줍니다.

- 각 부분을 Base65로 인코딩하여, 점(.)으로 연결하면 최종적으로 JWT를 생성하게 됩니다. 이렇게 생성된 JWT는 이전에 배운 쿠키(Cookie) 또는 Path Parameter를 통해 전달될 수 있습니다.

JWT의 특성 정리하기

1. JWT는 비밀 키를 모르더라도 복호화(Decode)가 가능합니다.
2. 민감한 정보(개인정보, 비밀번호 등)는 담지 않도록 해야 합니다.
3. JavaScript와 같이 특정 언어에서만 사용 가능한 것은 아닙니다!

JWT는 쿠키, 세션과 어떻게 다른가요?

- 데이터를 교환하고 관리하는 방식인 쿠키/세션과 달리, JWT는 단순히 데이터를 표현하는 형식입니다.

- JWT로 만든 데이터는 변조가 어렵고 .. Stateless(무상태)로 관리할 수 있음
- 쿠키와 세션은 .. 서버에 저장 .. Stateful(상태 보존)하게 데이터가 관리됩니다
- Stateless(무상태)와 Stateful(상태 보존)의 차이
- Node.js 서버가 언제든 죽었다 살아나도 똑같은 동작을 하면 Stateless
- 반대로 서버가 죽었다 살아났을 때 조금이라도 동작이 다른 경우 Stateful
- 로그인 정보를 서버에 저장하게 되면 무조건 Stateful(상태 보존)이라고 볼 수 있죠!


1-15. JWT는 어떻게 사용하면 되나요?

JWT를 왜 써야 하는 걸까요?

- JWT는 두 가지 중요한 특징을 가지고 있습니다.
1. JWT가 인증 서버에서 발급되었는지 위변조 여부를 확인할 수 있습니다.
2. 누구든지 JWT 내부에 들어있는 정보를 확인할 수 있습니다. (복호화)

이 암호화된 데이터는 어떻게 쓸 수 있나요?

- 보통 암호화된 데이터는 클라이언트(브라우저)가 전달받아 다양한 수단(쿠키, 로컬 스토리지 등)을 통해 저장하여
API 서버에 요청을 할 때 서버가 요구하는 HTTP 인증 양식에 맞게 보내주어 인증을 시도합니다!

1-16. [게시판 프로젝트] 시작하기



1-17. [게시판 프로젝트] Prisma 설계하기


2-1. 인증, 인가 살펴보기

1) 인증(Authentication)

인증(Authentication)은 서비스를 이용하려는 사용자가 인증된 신분을 가진 사람이 맞는지 검증하는 작업을 뜻합니다.
일반적으로, 신분증 검사 작업에 해당합니다.

- 인증(Authentication)은 일반적인 사이트의 로그인 기능에 해당합니다.

2) 인가(Authorization)

인가(Authorization)는 이미 인증된 사용자가 특정 리소스에 접근하거나 특정 작업을 수행할 수 있는 권한이 있는지를 검증하는 작업을 뜻합니다.
놀이공원에서 자유 이용권을 소지하고 있는지 확인하는 단계라고 보면 좋습니다.

- 인가(Authorization) 기능은 사용자 인증 미들웨어를 통해서 구현할 예정입니다.

2-2. [게시판 프로젝트] 로그인, 회원가입 API

- 사용자 정보는 사용자가 존재하지 않을 경우 생성될 수 없으므로, 전달받은 인자값을 이용해 사용자 → 사용자 정보 순서대로 회원가입을 진행해야 한다.

3) [게시판 프로젝트] bcrypt

일반적으로, 사용자의 비밀번호를 데이터베이스에 저장할 때, 보안을 위해 비밀번호를 평문으로 저장하지 않고 암호화 하여 저장합니다.
→ 개인정보보호법에서는 주민등록번호와 같은 정보는 암호화되어 저장되어야 합니다.

bcrypt 모듈이란?

- bcrypt 모듈은 입력받은 데이터를 특정 암호화 알고리즘을 이용하여 암호화 및 검증을 도와주는 모듈입니다.

4) [게시판 프로젝트] bcrypt 설치

- 회원가입 API에서 비밀번호를 평문으로 저장하는 것을, bcrypt를 이용하여 단방향 암호화한 결과값으로 데이터베이스에 저장하도록 리팩토링할 예정입니다.

5) [게시판 프로젝트] 로그인 API, 사용자 인증 미들웨어

- 로그인 API는 bcrypt로 암호화된 사용자 비밀번호를 인증하는 방식입니다. 클라이언트가 제공한 비밀번호와 데이터베이스에 저장된 암호화된 비밀번호를
검증하여 구현할 예정입니다.

[게시판 프로젝트] 로그인 API 비즈니스 로직

1. email, password를 body로 전달받습니다.
2. 전달받은 email에 해당하는 사용자가 있는지 확인합니다.
3. 전달받은 password와 데이터베이스의 저장된 password를 bcrypt를 이용해 검증합니다.
4. 로그인에 성공한다면, 사용자에게 JWT를 발급합니다.

6) [게시판 프로젝트] 사용자 인증 미들웨어

- 사용자 인증 미들웨어는 클라이언트로부터 전달받은 쿠키를 검증하는 작업을 수행합니다. 클라이언트가 제공한 쿠키에 담겨 있는 JWT를 이용해 사용자를 조회하도록 구현할 예정입니다.

[게시판 프로젝트] 사용자 인증 미들웨어 비즈니스 로직

1. 클라이언트로부터 쿠키(Cookie)를 전달받습니다.
2. 쿠키(Cookie)가 Bearer 토큰 형식인지 확인합니다.
3. 서버에서 발급한 JWT가 맞는지 검증합니다.
4. JWT의 userId를 이용해 사용자를 조회합니다.
5. req.user에 조회된 사용자 정보를 할당합니다.
6. 다음 미들웨어를 실행합니다.

7) [게시판 프로젝트] 사용자 정보 조회 API

[게시판 프로젝트] 사용자 정보 조회 API 비즈니스 로직

1. 클라이언트가 로그인된 사용자인지 검증합니다.
2. 사용자를 조회할 때, 1:1 관계를 맺고 있는 Users와 UserInfos 테이블을 조회합니다.
3. 조회한 사용자의 상세한 정보를 클라이언트에게 반환합니다.

- select 내에 또다른 select가 존재하는데, 이것을 중첩 select 문법이라고 부릅니다.
- 중첩 select는 SQL의 JOIN과 동일한 역할을 수행합니다.
- 중첩 select 문법을 사용하기 위해서는 Prisma model에서 @relation()과 같이 관계 설정이 되어야 합니다.
- @relation()으로 Prisma는 현재 모델에서 참조하는 외래 키를 인식하고, SQL을 생성할 수 있게 됩니다.
- 만약, 현재 테이블과 연관된 테이블의 모든 컬럼을 조회하고 싶다면, include 문법으로도 조회할 수 있습니다!

2-3. Access Token, Refresh Token

1) Access Token이 무엇인가요?

- Access Token은 사용자의 인증(ex: 로그인)이 완료된 후 해당 사용자를 인증하는 용도로 발급하는 토큰입니다.
- Access Token은 Stateless(무상태) 즉, Node.js 서버가 재시작되더라도 동일하게 작동하게 됩니다.
- Access Token은 그 자체로도 사용자 인증에 필요한 모든 정보를 가지고 있음.

2) Refresh Token이 무엇인가요?

- Refresh Token은 사용자의 모든 인증 정보를 담고 있는 Access Token과는 달리, 특정 사용자가 Access Token을 발급받기 위한 목적으로만 사용됩니다.
- 서브는 Refresh Token을 디코딩하여 사용자의 정보를 확인하게 됩니다. 이 방식은 필요한 경우 서버에서 강제로 토큰을 만료시킬 수 있으며, 사용자의 인증 상태를 언제든지 서버에서 제어할 수 있다는 장점을 가지고 있습니다.

-Refresh Token은 Access Token을 재발급해주기 위한 토큰이고, Access Token은 실제로 여러분들이 API를 사용하기 위한 토큰이다.

3) Refresh Token Project의 템플릿을 만들어봅시다!

- Refresh Token은 사용자가 서버와 최초 인증시에 발급을 받게 되는데요. 이번 프로젝트는 하나의 파일에서 Refresh Token과 Access Token이 어떤 식으로 동작하는지 확인해볼 예정입니다.

- Refresh Token의 정보는 어디에서 관리해야 하나요?

- 이번 예시에서 Refresh Token은 tokenStorage라는 변수에서 관리하였습니다.
- 하지만, 이 방식은 실제 프로덕션 환경에서는 사용해선 안 됩니다. 이는 인 메모리(In-Memory) 방식을 사용하기 때문에 서버가 재시작 또는 종료될 경우 모든 정보가 사라지게 됩니다.
- 이러한 문제점을 해결하기 위해, 실제 서비스에서는 별도의 테이블에서 Refresh Token을 저장하고 관리한답니다. 이렇게 할 경우, Refresh Token 검증 작업을 MySQL과 같은 데이터베이스를 조회함과 동시에 함께 처리할 수 있게 된답니다.

- Access Token만? : 프로젝트를 신속하게 구현해야 하거나 사용자의 요청에 대한 인증을 최소화하려는 목표가 있다면
- Refresh Token의 사용을 고려 : 보안성을 중요하게 여기고 서버를 더욱 견고하게 구성해야 한다면

2-4. [게시판 프로젝트] 미들웨어

1) [게시판 프로젝트] 로그(Log) 미들웨어

- 로그 미들웨어(Log Middleware)는 클라이언트의 모든 요청 사항을 기록하여 서버의 상태를 모니터링하기 위한 미들웨어입니다.

2) [게시판 프로젝트] 에러 처리 미들웨어

- 에러 처리 미들웨어는 클라이언트의  요청이 실패하였을 때, 가장 마지막에 실행되어야 하는 미들웨어입니다.
따라서, app.use를 이용한 전역 미들웨어 중 가장 최하단에 위치한 것입니다.

2-5. [게시판 프로젝트] 게시글 API

[게시판 프로젝트] 게시글 생성 API 비즈니스 로직

1. 게시글을 작성하려는 클라이언트가 로그인된 사용자인지 검증합니다.
2. 게시글 생성을 위한 title, content를 body로 전달받습니다.
3. Posts 테이블에 게시글을 생성합니다.

게시글 생성 API의 비즈니스 로직 첫 번째 "게시글을 작성하려는 클라이언트가 로그인된 사용자인지 검증합니다."는 사용자 인증 미들웨어를 통해 수행하였고,
전달된 userId 값을 이용해, 사용자와 1:N 관계를 맺고 있는 게시글을 생성하도록 구현하였습니다.



2-6. [게시판 프로젝트] 댓글 API


1) [게시판 프로젝트] 댓글 API에 대한 고찰

- 댓글 기능은 게시글 기능과 부모 - 자식 간의 관계로 구성됩니다.

[게시판 프로젝트] 댓글 생성 API 비즈니스 로직

1. 댓글을 작성하려는 클라이언트가 로그인된 사용자인지 검증합니다.
2. 게시글을 특정하기 위한 postId를 Path Parameters로 전달받습니다.
3. 댓글 생성을 위한 content를 body로 전달받습니다.
4. Comments 테이블에 댓글을 생성합니다.

1:N의 관계

[게시판 프로젝트] 댓글 조회 API

- 댓글 조회 API는 특정 게시글에 작성된 모든 댓글을 조회하는 API입니다. 댓글 목록을 조회하느넫,
추가적인 제약이 있지 않는 이상 일반적으로 누구나 댓글 목록을 볼 수 있겠죠?

[게시판프로젝트] 역할과 책임

1) 게시글 상세 조회 API 내부에서 댓글 목록도 함께 반환한다.
2) 게시글 조회 API와 댓글 조회 API를 따로 호출하여, 각 정보를 가져온다.

2-7. 트랜잭션(Transaction)

1) 트랜잭션(Transaction)이란 무엇인가

- 트랜잭션(Transaction)은 작업의 완전성을 보장해주기 위해 사용되는 개념입니다. 특정한 작업을 전부 처리하거나, 전부 실패하게 만들어 데이터의 일관성을 보장해주는 기능입니다.

2) 트랜잭션을 왜 사용해야 하는가?

ex) 계좌이체 ..
- 1) A 고객의 계좌에서 1,000원을 차감
- 2) B 고객의 계좌에 1,000원을 추가

- 결국, 트랜잭션(Transaction)을 이용한다면 사용자가 항상 어플리케이션 실행을 완료하도록 구성할 수 있게 되고, 실행을 중단할 만한 치명적인 오류가 발생하더라도, DB에 피해가 가지 않아 더욱 안전하게 어플리케이션을 구성할 수 있게 됩니다.

3) 트랜잭션의 특징(ACID)

- 트랜잭션의 특징(ACID)는 데이터베이스 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 특징들을 나열해 놓은 개념입니다.

ACID는 트랜잭션의 4가지 특징을 나타냅니다.

- 원자성 (Atomicity)
- 일관성 (Consistency)
- 격리성 (Isolation)
- 지속성 (Durability)

4) 원자성 (Atomicity)

- 원자성(Atomicity)은 트랜잭션 내에서 실행된 명령들을 하나의 묶음으로 처리하여, 내부에서 실행된 명령들이 전부 성공하거나, 아니면 모두 실패해야 한다는 특징입니다. → 여기서, "원자성"이란, 나눠질 수 없는 단일 작업이라는 것을 의미합니다.

5) 일관성 (Consistency)

- 일관성(Consistency)은 트랜잭션 내부에서 처리되는 데이터의 일관성을 유지해야 하는 특징입니다. 만약 작업이 성공할 경우 아무런 문제가 발생하지 않고, 실패하더라도 작업을 진행하던 도중 실패한 상태로 데이터를 방치하지 않는 특징입니다.

강의 등록의 비즈니스 로직

- 1. 강의(Courses) 테이블에서 강의를 생성합니다.
- 2. 생성된 강의 데이터를 참조하는 강의 목록(CourseUnit) 테이블에 강의 영상을 업로드합니다.
- 3. 강의 목록(CourseUnit) 테이블에 모든 강의 영상을 업로드 하였다면 COMMIT

중간 과정에서 실패하면 ROLLBACK → 트랜잭션 시작 전 상태로 복구된다.

6) 격리성 (Isolation)

- 격리성(Isolation)은 트랜잭션이 실행 중인 경우 다른 트랜잭션에 의해 데이터가 변경되는 것을 방지하는 특징입니다. 트랜잭션이 완전히 수행되거나 완전히 수행되지 않은 상태를 외부에서 참조할 수는 있지만, 트랜잭션의 중간 과정이나 중간 결과를 볼 수 없도록 하는 특징입니다.

- 격리성이란 특징에서 동시성(Concurrency)과 격리 수준(Isolation Level)라는 두 가지 중요한 개념이 나타나게 되었습니다.

- 동시성(Concurrency)은 여러 클라이언트가 동시에 하나의 데이터를 사용 및 공유하는 것을 뜻합니다. 동시성은 다수의 사용자가 동일한 시스템을 공유하면서 발생하는 동시 접근 문제를 해결해야 합니다.

- 동시성 문제(Concurrency Issue)는 여러 클라이언트가 동시에 같은 데이터를 접근하려고 할 때 발생합니다.

- 락(Lock)은 하나의 트랜잭션에서 사용 중인 데이터를 잠그는 방식으로, 다른 트랜잭션들이 그 데이터에 접근하지 못하도록 합니다. 이 방식을 사용한다면, 어떤 트랜잭션도 다른 트랜잭션의 중간 상태를 볼 수 없게 되므로 데이터 일관성을 유지할 수 있게 될 것입니다!

7) 지속성 (Durability)

- 지속성(Durability)는 트랜잭션이 성공적으로 커밋된 후, 해당 트랜잭션에 의해 생성 또는 수정된 데이터가 어떠한 상황에서도 보존되는 특징입니다.
- 다시 말해, 트랜잭션이 완료되면 결과는 데이터베이스에 영구적으로 저장되며, 이후 시스템에 어떠한 문제가 생기더라도 데이터는 손상되지 않습니다.

- 1. MySQL의 트랜잭션 살펴보기
- 2. 락(Lock)
- 락(Lock)은 동시성을 제어하기 위해 사용하는 기능
- 3. 락(Lock)의 종류
- 공유 락(Shared Locks) | 읽기 락(READ Locks)
- 배타 락(Exclusive Locks) | 쓰기 락(WRITE Locks)
- 4. 락킹 수준(Locking Level)
- 글로벌 락(Global Locks) | 데이터베이스 락(Database Locks)
- 테이블 락(Table Locks)
- 네임드 락(Named Locks)
- 메타데이터 락(Metadata Locks)

- 잘못된 락 설정을 하게 될 경우 모든 API가 동작하지 않는 교착 상태가 발생하게 되어, 프로그램이 멈춰버리는 문제가 발생할 수 있음.

- 교착 상태(Dead Lock)
- 여러 테이블에 락(Lock)을 적용하여, 다른 작업이 처리되지 못하게 점유하고 있는 작업이 있을 떄, 다른 작업을 무한정 기다리는 것을 나타냄.
- 5. 트랜잭션의 격리 수준(Isolation Level)
- READ UNCOMMITED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE

2-8. [게시판 프로젝트] PRisma Transaction

[게시판 프로젝트] Prisma의 Transaction

- Prisma의 트랜잭션은 여러 개의 쿼리를 하나의 트랜잭션으로 수행할 수 있는 Sequential 트랜잭션과 Prisma가 자체적으로 트랜잭셔의 성공과 실패를 관리하는 Interactive 트랜잭션이 존재합니다.

- Sequential 트랜잭션은 Prisma의 여러 쿼리를 배열( [] )로 전달받아, 각 쿼리를 순서대로 실행하는 특징이 있습니다. 이러한 특징은 여러 작업이 순차적으로 실행되어야 할 때 사용할 수 있습니다.

- Interactive 트랜잭션은 모든 비즈니스 로직이 성공적으로 완료되거나 에러가 발생한 경우 Prisma 자체적으로 COMMIT 또는 ROLLBACK을 실행하여 트랜잭션을 관리하는 장점을 가지고 있습니다.

- Interactive 트랜잭션은 트랜잭션 진행 중에도 비즈니스 로직을 처리할 수 있어, 복잡한 쿼리 시나리오를 효과적으로 구현할 수 있습니다.

- $transaction() 메서드의 첫 번째 인자 async(tx)는 저희가 일반적으로 사용하는 prisma 인스턴스와 같은 기능을 수행합니다.

Prisma에서 격리 수준은 어떻게 설정할까요?

- Prisma의 격리수준은 트랜잭션을 생성할 때, isolationLevel 옵션을 정의함으로써 설정할 수 있습니다.

격리 수준(Isolation Level)을 설정할 때, 현재 구현하려는 API에는 어떠한 격리 수준이 필요한지 명확하게 이해해야 합니다.
이를 통해 효율적인 데이터베이스의 설계를 할 수 있고, 데이터의 일관성이 깨지지 않도록 구현할 수 있게 됩니다.

[게시판 프로젝트] 사용자 히스토리 모델 생성하기

~

UUID(범용 고유 식별자)란 무엇인가요?

- UUID(Universally Unique Identifier, 범용 고유 식별자)는 총 4개의 정보를 하이픈(-)으로 구분하여 순차적으로 저장한 데이터 타입입니다.
시간 정보를 포함하고 있어 생성된 순서대로 정렬이 되는 특징을 가지고 있습니다.

[게시판 프로젝트] 사용자 정보 변경 API 비즈니스 로직

1. 게시글을 작성하려는 클라이언트가 로그인된 사용자인지 검증합니다.
2. 변경할 사용자 정보 name, age, gender, profileImage를 body로 전달받습니다.
3. 사용자 정보(UserInfos) 테이블에서 사용자의 정보들을 수정합니다.
4. 사용자의 변경된 정보 이력을 사용자 히스토리(UserHistories) 테이블에 저장합니다.
5. 사용자 정보 변경 API를 완료합니다.

사용자 정보 변경 API는 사용자 정보의 수정과 사용자 히스토리 데이터 삽입, 2개의 비즈니스 로직을 하나의 작업으로 처리해야 합니다.
비즈니스 로직을 수행하는 도중 오류가 발생할 경우, 데이터의 일관성이 깨지게 될 수 있습니다.
이렇게 된다면, 사용자 히스토리 테이블의 데이터들을 믿을 수 없게 되는 상황이 발생하게 됩니다.