배열과 숫자를 인자로 받아 그 배열 중 숫자만큼 조합으로 뽑아달라는 재귀함수가 구현된다. 이를 참고해 구현한 조합함수는 아래와 같다.
내가 필요한 것은 3개를 랜덤으로 추출하는 함수였다.
다만 기주 튜터님의 말씀대로 순열조합은 원하는 케이스만 체리픽 할 수 없고, 모든 케이스가 다 나온다. 그래서 너무 길면 안 돌아간다. 하여 문제나 원하는 답의 길이를 보고 대강 사용할 알고리즘을 유추할 수 있는 능력도 필요하다고 말씀 주셨다.
쿠키 는 편하지만 보안에 취약하다!
그래서 데이터를 서버에서 관리하게 만든 것이 세션
.
res.status().json()
타입으로 쓰는 경우가 많았는데 때로는 응답값을 보낼 필요가 없기도 하다. (쿠키를 전송할 때 라던지) 그럴 땐 res.end()로 그냥 종료해주자. (마치 void 같은 느낌)const cookie든 cookies든 클라이언트의 모든 쿠키를 조회할 수는 있다. 다만 형태가 다를 뿐이다.
req.headers.cookie;
를 쓰면 위와 같이 나오고, req.cookies;
를 쓰면 아래와 같이 나온다.
그래서 qwer~.asdf~.zxcv~ 형태로 되어 있다.
signature 부분은 해시함수로 암호화 된다. 보통 SHA256을 쓴다.
정말 중요한 두 가지 특징
▶인증 서버에서 발급되었는지 위변조 여부를 확인할 수 있다. jwt.verify(token, 'secretkey')
형식으로 시크릿키 값을 써주면 ok.
▶복호화는 누구나 가능하기 때문에 중요정보를 payload에 넣으면 안된다.
쿠키/세션은 데이터 교환 및 관리 방식에 대한 개념이고, JWT는 그때 사용되는 데이터 표현방식의 일종. 즉, JWT가 쿠키로 세션에 저장된다- 라고 표현할 수 있는 것.
ALTER가 뭔 기능인지 기억이 나지 않아 내 블로그를 뒤지다가, 쓴 기억도 나지 않는 항해때 TIL 에서 ALTER TABLE MODIFY를 이용해서 id에 AUTO_INCREMENT를 붙이는 것을 발견했다.
또다른 항해 TIL 에 의하면 ALTER는 DDL이라 DB에 직통으로 영향을 미치고, 얼터 자체가 '바꾸다' 라는 뜻을 가지고 있어서 이미 존재하는 개체의 특성 변경에 쓴다고.
아항! 그렇구만.
한 명의 유저가 여러개의 게시글을 쓸 수 있으므로 1:N 관계이기 때문에, 여러 게시글에 잡히는 userId가 중복될 수 있다. 그러므로 unique를 걸면 안된다.
지금은 단순히 테이블 선언이 아니라 관계 맵핑이기 때문에, 어떤 Id가 어떤 테이블에서 1과 N중에 어떤 측으로 작용할 지 고려해보자.
-> 그래서 Users에 나타나는 Posts는 이렇게 표시한다. 여러개를 나타내기 위해 배열연산자를 사용해줘야 한다.
이건 관계에 따라 물론 어느쪽도 부모가 될 수도 있지만 (N:N인 경우) 보통은 1:N이 많고 1:1도 있기 때문에 1인 쪽을 먼저 잡아주고 들어가면 된다.
저 부분을 수정하지 않으면 예전 DB를 계속 바라보면서 작동하므로 예전 DB에 INSERT하는 대참사가 일어날 수 있다.
DB주소를 무사히 수정했다면 터미널에 npx prisma db push
를 입력해서 작성한 쿼리를 그대로 쏴주도록 하자.
userInfos 테이블에 password라는 컬럼은 넣어주지 않았다.
그런데 인자에서 받아버리려 해서 에러가 터졌다.
userInfos 테이블에 password가 없어도 user가 password를 받기 때문에 필요하다.
사용자 등록은 되었지만 사용자 정보등록에 실패해서 로그인만 가능한 경우가 생긴다고 가정해보자. 이 사용자는 어떻게 해야 할까?
이런 불상사를 대비해서 우리는 트랜잭션(Transaction) 이라는 개념을 알아야한다. 트랜잭션은 될거면 아예 끝까지 되고, 안될거면 애초에 아무것도 수행되지 않는 것이다.
물론 나는 설치에 성공했지만 내 노트북은 실패할 수도 있으니까 방법을 남겨둬야지.
yarn add -g node-pre-gyp
라는 명령어로 node-pre-gyp 패키지를 먼저 전역으로 설치해준 다음에 yarn add bcrypt
를 다시 쳐주면 잘 될 것이다. 그래도 안되면 포기하자
Users 테이블과 userInfos 테이블은 1:1 관계를 맺고 있어 Users와 UserInfos는 같이 끌려나와야 한다. 그런데 지금까지의 문법대로면 따로따로 조회할 수 밖에 없다.
내가 원하는 것은 [Users[userInfos]] 이런 구조일텐데, 실제 실행된 것은 [Users] - [userInfos] 가 될 것 같다는 얘기.
▶ 이걸 위해 존재하는게 중첩 select문.
놀랍게도 select문 안에 이런 식으로 쓸 수 있는데, 이걸 이렇게 쓸 수 있는 이유는 Prisma에 아래와 같이 되어있기 때문.
여기서 userInfos와 1:1 관계로 선언해주었기 때문에 같이 머리채잡혀 끌려나올 수 있어서 저렇게 써도 문제가 없는 것이다.
튜터님이 그려주신 작동 순서. URL이 /users가 맞으면 import 시켜둔 authMiddleware 로직 타게 해주고, 그 다음에 우리가 쓴 비동기 함수 로직 타게 해줘.
가 된다.
중첩 select는 SQL의 JOIN과 동일한 역할을 수행한다.
다만 이 문법을 사용하기 위해서는 Prisma model에서 @relation()
구문으로 관계 설정을 미리 해주었어야 한다. 이걸로 Prisma는 현재 모델에서 참조하는 외래 키를 인식하고 SQL을 생성할 수 있게 되기 때문.
-> 현재 테이블과 연관된 모든 컬럼을 조회하고 싶다면 include 문법으로도 조회할 수 있다고 하는데, 이건 뭔 말인지 잘 모르겠다.
그렇다면 .gitignore까지 짜잔 하고 나타나 줄것이다. 단 DB주소 설정 잊지 말것. 수정하지 않으면 엉뚱한 DB를 바라볼 수 있다.
a b d e c?
-> 정답은 b d c a e. 튜터님은 bcade 라고 생각하셨다고
console.log니까 바로 b 딸려나가고, asyncF 호출되니까 d 잡혀나가고, 아래 await 걸리니까 버리고 sync() 먼저 들어가고... 오 의외로 이게 맞구나. 근데 왜지?
▶ async 함수라고 해도 await 만나기 전까진 동기적이다.
그리고 async 함수가 '반환하는 애'가 Promise인거고, Promise중에 가장 먼저 Resolve 되는 애들끼리 실행되는 건데 비동기를 동기적으로 쓰고 싶어서 async / await를 쓰고 난리를 치는 것
▶ 노드는 싱글스레드다. 그렇다고 해서 실제 비동기로 병렬프로그래밍 되는건 아니다. OS한테 맡길건 맡기고, Promise 객체 같은건 이벤트 루프에 돌리는것. 이벤트 루프에 넣기 전에 쌓아놓는 곳을 마이크로 태스크 큐 라고 하는데, 그게 진짜 큐 구조는 아니다. 이게 왜 진짜 큐가 아니냐면 A 연산 1초, B 연산 10초라고 하면 A가 1초임에도 불구하고 B가 먼저 왔다고 B가 먼저 돌아가는 일은 일어나지 않는다.
구조분해 할당... 저렇게 분해하는 거인건 알겠는데... 뭐지? MDN 도와줘! MDN이 알려주는 구조분해할당
구조 분해 할당 구문은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식입니다.
- 함수에 객체나 배열에 저장된 데이터의 일부만 전달하고 싶을 때
- 함수의 매개변수가 많을 때
- 매개변수의 기본값이 필요할 때
기타등등 이럴때 쓴다고 한다.
라우터는 (req, res)가 붙는 것인데 리퀘스트 경로를 보고 해당 요청 정보를 처리할 수 있는 곳으로 기능을 전달해준다고 한다. 참고 블로그에는 클라이언트의 요청 경로에 따라서 그것을 담당하는 함수로 분리시킨다. 라우터로 등록하려면 get() 메소드를 호출하여 등록할 수 있다.
라고 써있다.
미들웨어를 등록할때는 use()
로 등록한다고 한다. 그러니 app.use()
가 전역 미들웨어 등록인 셈.
app.use()~ 들 중에서 제일 아래에 포트 열기 직전에 써줘야 한다. 그래야 앞선 전역 미들웨어들 로직 다 타고 나서 맨 마지막에 에러처리 미들웨어를 탈 수 있기 때문!
-> 단, 에러 메시지는 너무 구체적으로 쓸 필요는 없다. 어떤 문제인지 알게 되면 해킹이나 디도스 등의 공격을 받을 수도 있기 때문.
나는 여기서 새로 hash를 걸어 salt를 10번 정도 돌렸는데 이러면 새로 암호화된 다른 암호가 나올 것이다. 이때는 bcrypt.compare(비교할 pwd, DB의 pwd)
를 써줘서 암호화 했을때 둘이 같은지를 보는게 낫다.
어여쁘게 잘 돌아가는 튜터님의 코드
에러가 쿠와아악 터지는 나의 코드
난 그냥 const를 쓰고 싶었을 뿐인데,
이렇게 에러가 펑펑펑 터진다. 뭐가 문제인걸까?
저 await bcrypt절을 if(!pwdCompare)에 그대로 집어넣으면 터지지 않아서, 똑같은 코드인데 왜 내껀 터지는지가 궁금했다.
▶ 이렇게 구문배치를 하고 내가 Insomnia로 쐈던 것은 password가 빈게 아니라 없는 email, 즉 유저 자체가 존재하지 않는 경우였다! (원래 목적은 사용자가 존재하지 않습니다. 라는 메시지를 보고싶었다.)
▶ 이때 const pwdCompare의 위치때문에 하단의 if(!user) 절에 걸리지 못하고 const절에 먼저 걸려버려서 DB에 존재하지 않는 password를 비교하려니 당연히 여기서 터진 것.
▶ 기주 튜터님도 이 코드를 보시고는 password에서 터진 게 아닐거라고 하셨다. 그 말씀대로 에러 메시지도 user.password를 가리키고 있었다.
▶ 여기서 const로 pwdCompare를 빼주고 싶으면 if(!pwdCompare) 위에 엔터치고 선언했으면 문제 없었을 거라고 하셨다.
이 코드가 아래처럼 자꾸 where절에서 터지는 바람에 튜터님께 문의
userId가 missing이라고? DB에는 잘 들어가 있는데 어딘가 못 불러오는 것 같았다. 왜일까? 창민 튜터님께 여쭈러 가서 console.log를 찍어보았다.
놀랍게도 비동기 함수 속에 있는 user인데다 Promise 객체를 반환하는 prisma를 호출하면서도 앞에 await를 써주지 않고선 실행이 안된다고 울부짖는 나였다. 이렇게 참조중인 다른 부분에서 에러가 터지니 알 도리가 없는 것은 덤. 아직 갈 길이 멀다.
이야호!! 드디어 토큰이 발급된다아아!!!
코드는 아래와 같다. 방법은 이미지 아래 기재.
yarn add dotenv
로 설치하고 ES6 기준이므로 import dotenv from 'dotenv'
로 import 해준다. (주석처리하니 안 됐다)dotenv.config()
를 입력해준다. const {PORT} = process.env
이렇게 쓰는게 구조분해할당.그러나 환경변수 구조분해할당이 안좋을 수도 있다고? -> Next.js에서는 돌아가지 않는다고 한다.
참고 블로그
이번 조는 다시 복작복작한 느낌이라 좋다
아니 저기요? 여러분? 왜 이렇게 된거지
PM 11:57. 집에 가염!