QnA
실제로 현업에서는 회원이 탈퇴한다고 해서 테이블에서 바로 삭제해버리진 않을 것 같다 → 삭제 여부를 나타내는 컬럼을 따로 두나?
→ 중요한 레코드의 경우 보통 가장 흔한 케이스가 물리 삭제 대신 컬럼을 따로 두는 거라고 하심!
회원 탈퇴를 DB에서 해당 레코드 삭제로 구현할 경우, 회원 ID를 FK로 갖는 다른 테이블들은 cascade 옵션이 걸리면 같이 삭제되어 버릴 것이다 → 이 경우는 어떤 식으로 구현하나? → 실제 물리적 삭제를 잘 안하므로 다양한 방식이 있음.
PK 컬럼 지정에 관한 질문
숫자말고 email 처럼 반드시 unique하게 쓰이는 것을 PK로 설정해도 괜찮은가? auto increment인 int 타입 대신 string을 PK로 설정하면 추후 인덱싱 관련해서 성능 상 손해가 있나? → 설정해도 됨. 성능 이슈는 있긴하다..
JWT 관련 질문
실무에서는 JWT 토큰에 만료기간을 두고 재발급을 받도록 할 텐데, 클라이언트와 서버 간 사이클이 보통 어떻게 되나? 구체적으로 구현은 어떤 식으로 하게 되는가? → expiresIn과 같은 옵션이 있고, refreshToken과 같은 API를 별도로 만들 수 있다
Secret key도 한번 생성한 것을 계속 쓰는 것이 아니라 주기적으로 바꿔가면서 할 것 같은데, 이건 또 어떤 식으로 구현해야 하나? → 잘 안바꿈
secret key의 보관은 DB에 하는 것이 좋은가? .env 파일에 하는 것이 좋은가? → DB에 함께 담는 것은 오히려 더 위험! .env에 하자!
바꿔도 되는데 시크릿 키는 주기적으로 바꾸지는 않아. 이를 바꾸면 이전에 발급된 모든 토큰이 다 invalidate 됨.
env 는 DB 에 안넣고 분리하는게 좋음. .env 에 넣자.
auth 관련 질문
유저가 로그인이나 회원가입 시에 id와 password를 http 바디에 그대로 실어서 보내는 것은 위험할 것 같은데, 실무에서는 클라이언트 단에서부터 암호화를 시켜서 보내나? → https를 믿으므로 그렇게 잘 안함. 단, 피시방 같은 클라이언트 측 보안 위험이 높은 곳은 대칭키 같은 것을 이용해서 암호화 하기도 한다
context 방식의 디렉토리 구조에서의 애매한 점
auth context에 존재하는 service가 users context의 repository를 불러와서 써야하는 경우가 있다. 이런거 보면 그냥 mvc로 나누는게 낫지 않나 싶은데, 현업에서는 이런 경우에 어떻게 하는지? → 그래도 MVC 구조는 요새 잘 안쓴다고 함. nest.js로 가면 마음의 불편함이 덜 할 것이다
디렉토리 명이 그냥 테이블 명을 따라가는 경향이 생기는데, 실제로는 이런 식으로 하면 안되는 것 아닌지? → 디렉토리 명은 도메인 명으로 맞춰준다. 그것이 원칙
시작
board 와 포스트는 포럼에 종속.
디렉토리 구조를 잡은 후 프리즈마 모델 만들기. 프리즈마 src 에 넣고,
프리즈마 경로 추가해주기.
"prisma":{
schema
}
프리즈마는 이름은 그대로 쓰기 때문에, 모델은 보통 단수로 쓰니까 prisma 에는 단수로. 규칙이 따로 있을경우는 map 매핑으로 이름 매핑
게시판 언제 생성되었는지 createdAt
posts Post[] 로
label 로 프론트엔드 백엔드. 이걸 쓰는게 좋아.
updatedAt 도 하나 넣어주자. @updatedAt
게시판 name 으로 외래키 잡으면 한번에 게시판 이름에 대응되는 게시판 아이디 찾을 수 있어. 쿼리 하나 줄일 수 있음 => 근데 프리즈마가 알아서 해줘서 상관은 없긴해.
User 와 연결해줄 authorId 를 사용해(userId 써도 되는데 이름겹치니까),
user id 가 1,2,3,4 공개되는게 싫어서 string 으로.
email string 에 유니크.
내가 작성한 posts , 라이크한 포스트
prisma
npx prisma studio
왜 서비스는 리퀘스트 핸들러야하는가??
도메인 아키텍쳐를 써보니까 서비스를 이곳 저곳에서 쓰려면
확실하게 비즈니스로직을 분리하는게 좋겠다는 판단!
Auth
나노아이디 (낮은버전으로! nanoid 버전 3) 로 아이디 생성.
b크립트의 해쉬를 import 해서, 비밀번호에 hash(12) 정도. 오래걸리니까 await 로
리턴으로 엑세스 토큰을 돌려준다.
generateAccessToen 함수로 따로 뺴고, jwt.sign() 으로 .
jwt.sign()
시크릿 키는 나노아이디 nanoid --size 48 으로
jwt.sign()
패스워드 compare 로, 일반과 인크립트 패스워드를 비교.
근데 이걸 쓰려면 유저를 불러와서 user.encryptedPassword 를 써야해.
유저가 없으면, 그냥 에러 보내. 에러 처리는 컨트롤러 책임이니.
이 과정을 거치면, 프론트에서 Header 의 authrization 의 베어러에 토큰을 보내주지.
그래서 middleware 에서 이를 처리해보자.
req.headers.authorization?.split("Bearer ")[1];
if (!accessToken) return res.sendStatus(401);
값이 없을수도 있으니 옵셔널 체이닝 ? 붙여.
Return 꼭 붙여! 안붙이면 코드 계속 내려가.
엑세스 토큰(JWT )을 확인해봐야하는데 이를 검증하려면 jwt의 verify 를 사용!
prisma 가 해주는것.
원래는 아래처럼 dotenv install 하고 Config 해줘야함.
import dotenv from 'dotenv'
dotenv.config()
타입
정상이면, sub에 넣어준 id 를 꺼낼 수 있지.
타입을 지정해줘야하는데, id는 무조건 있음으로, id as string 써도돼.
user 가 없다면(삭제되었다면), throw new Error. throw 에러하면, try catch 에 잡혀서.
declare global{
namespace Express{
interface Request{
user: User|null,
}
}
}
autherization 이 필요한 라우트와 안필요한 라우트를 나누는 가드!
조건에 안맞으면 조기에 에러를 리턴하는것!
미들웨어로 구성
req에서 유저를 꺼내.
유저 only 인데 유저
좋아요
토글 방식으로 만들거나/ like unlike 로 두개 나눠서 만들거나하는 방식이 있어.
posts 아래 likes 디렉토리 만들자.
postcontroller 만들고, /:postId
like를 만들려면 1.유저, 2.포스트가 필요.
request 에 userId, postId 두가지 받기.
프리즈마 클라이언트에서 포스트를 가져와 업데이트. 근데 likers 가 userId 인것으로.
중간테이블의 연결을 프리즈마가 알아서 해줘. 기존 (prismaClient.likedPost.create(userId,postId)) 를 안해줘도돼.
업데이트 할건데, 유저와 연결된걸 업데이트 할거야.=> 유저 A 가 포스트 1번에 대해서
좋아요 토글을 하려고함.
=> prisma 가 해주던건데, 이게 안될수도 있으니,
dotenv.config 로
import dotenv from "dotenv";
먼저 업로드 하는 파일을 만들어서 처리해주자.
더미데이터
프리즈마의 seed.ts 만들고ㅡ
그냥 upsert로. (없으면 create 있으면 insert (insert+create))
migrate dev 하면 계속 데이터가 쌓이니 upsert 를 쓰는게 효율적.
생성만 하고 싶으면 create / createMany