오늘의 느낀 점..
.env 파일은 필수다...
그리고 오늘부터 에러 해결 노트 왕 크게 쓸거임..
: HTML5부터 제공하는 기능
해당 도메인과 관련된 특정 데이터를 서버가 아니라 클라이언트 웹브라우저에 저장할 수 있도록 제공하는 기능
쿠키(cookie)와 비슷한 기능이며, Web Storage의 개념은 키/값 쌍으로 데이터를 저장하고, 키를 기반으로 데이터를 조회하는 패턴이다.
영구 저장소(Local Storage) 와 임시 저장소(Session Storage)를 따로 두어 데이터의 지속성을 구분할 수 있어 응용 환경에 맞게 선택할 수 있다.
Web Storage는 쿠키와 마찬가지로 사이트의 도메인 단위로 접근이 제한됨
예시) A도메인에서 저장한 데이터 => B도메인에서 조회 불가Local Storage
- 브라우저를 닫았다가 다시 열어도 계속 유지됨. 저장한 데이터를 명시적으로 지우지 않는 이상 영구적으로 보관이 가능
- 도메인마다 별도로 LocalStorage가 생성됨
- 도메인만 같으면 전역으로 공유가 가능
- Windows 전역 객체의 LocalStorage라는 컬렉션을 통해 저장과 조회가 이루어짐
Session Storage
- 브라우저가 열려있는 한 페이지를 Reload해도 계속 유지됨. 하지만 브라우저를 닫으면 삭제됨
- Windows 전역 객체의 SessionStorage라는 컬렉션을 통해 저장과 조회가 이루어짐
- 데이터의 지속성과 엑세스 범위에 특수한 제한이 존재.
Web Storage의 기본 보안처럼 도메인별로 별도로 생성
같은 사이트의 같은 도메인이라도 브라우저가 다르면 서로 다른 영역임 => 브라우저 컨텍스트가 다르기 때문
💡 참고
브라우저 컨텍스트 : Document를 표시하는 환경 즉, 브라우저가 볼러온 웹페이지Web Storage의 특징
- 서버 전송이 없다.
저장된 데이터가 클라이언트에 존재할 뿐 서버로 전송이 이루어지지는 않음 => 네트워크 트래픽 비용을 줄여줌- 단순 문자열 뿐만 아니라 객체정보를 저장할 수 있다.
체계젹으로 구조화된 객체를 저장할 수 있음은 개발 편의성을 제공해주는 장점이 된다. 단, 브라우저의 지원 여부를 확인해 봐야한다.- 용량의 제한이 없다.
- 영구 데이터 저장이 가능하다.
만료 기간의 설정이 없다. 즉, 한번 저장한 데이터는 영구적으로 존재한다.왜 Web Storage를 쓰는가
쿠키와 Web Storage 둘 다 브라우저에 저장되지만, 쿠키의 단점을 극복할 수 있기 때문에 Web Storage를 사용한다.
쿠키의 단점
1. 4KB의 데이터 저장 제한
2. HTTP Request에 암호화 되지 않은 상태로 사용하기 때문에 보안이 취약
3. 쿠키는 모든 HTTP Request에 포함되어 있어 웹서비스 성능에 영향을 줄 수 있다.
: 클라이언트(브라우저) 로컬에 저장되는 키와 값이 들어있는 작은 데이터 파일
사용자 인증이 유효한 시간을 명시할 수 있고, 유효 시간이 정해지면 브라우저가 중료되어도 인증이 유지된다.
클라이언트의 상태 정보를 로컬에 저장했다가 참조한다.
클라이언트에 300개까지 쿠키 저장이 가능하며, 하나의 도메인당 20개의 값만 가질 수 있다. 또한 하나의 쿠키값은 4KB까지 저장한다.
Response Header에 Set-Cookie 속성을 사용하면 클라이언트에 쿠키를 만들 수 있다.
사용자가 따로 요청하지 않아도 브라우저가 Request시에 Request Header를 넣어서 자동으로 서버에 전송
따라서, 브라우저 저장소임에도 불구하고 백엔드와 긴밀히 연결되어 있어 쿠키에 있는 내용을 백엔드에서도 빼내어 볼 수 있다.Cookie의 구성요소
- 이름 : 각각의 쿠키를 구별하는 데 사용되는 이름
- 값 : 쿠키의 이름과 관련된 값
- 유효시간 : 쿠키의 유지시간
- httpOnly, secure : 쿠키들을 안전하게 보관할 수 있도록 보안 강화 기능
- 도메인 : 쿠키를 전송할 도메인
- 경로 : 쿠키를 전송할 요청 경로
Cookie 작동 방식
- 클라이언트가 페이지 요청
- 서버에서 쿠키를 생성
- HTTP 헤더에 쿠키를 포함시켜 응답
- 브라우저가 종료되어도 쿠키 만료 기간이 있다면 클라이언트에서 보관하고 있다
- 같은 요청을 할 경우 HTTP헤더에 쿠키를 함께 보낸다
- 서버에서 쿠키를 읽어 이전 상태 정보를 변경 할 필요가 있을 때 쿠키를 업데이트하여 변경된 쿠키를 HTTP 헤더에 포함시켜 응답한다.
언제 Cookie를 사용하는가
- 방문 사이트에서 로그인 시, "아이디와 비밀번호를 저장하시겠습니까?"
- 쇼핑몰의 장바구니 기능
- 자동로그인, 팝업에서 "오늘 더 이상 이 창을 보지 않음" 체크
- 쿠키는 매번 서버로 전송
웹사이트에서 쿠키를 설정하면 이후 모든 웹 요청은 쿠키 정보를 포함해 서버로 전송됨
Web Storage는 저장된 데이터가 클라이언트에 존재할 뿐 서버로는 전송되지 않음 => 네트워크 트래픽 비용 절감- Web Storage는 단순 문자열 뿐만 아니라 객체정보까지 저장 가능
개발 편의성을 제공해주는 장점이 됨- Web Storage는 용량의 제한이 없음
쿠키는 개수와 용량에 제한이 있음(위에 cookie 설명 참고)
하지만 Web Storage는 제한이 없음.- Web Storage는 영구 데이터 저장이 가능
쿠키는 만료일자를 지정하게 되어있어 언젠가 제거됨
Refresh Token
: 로그인 요청을 하고 나서, 서버에서 토큰을 프론트에게 넘겨줄 때, 토큰을 하나 더 만들어서 넘겨줌
이 때, 하나 더 만든 토큰을 Refresh Token이라고 한다.
refreshToken은 accessToken이 만료 되었을 때, accessToken을 다시 발행하기 위한 용도이기 때문에 accessToken보다 유효기간이 길어야함Access Token
: 기존에 발행하던 토큰
왜 Refresh Token을 사용하는가
Access Token(JWT)를 통한 인증 방식의 문제는 해킹을 당했을 경우 보안에 취약하다는 단점이 있다.
유효기간이 짧은 토큰의 경우 그만큼 사용자는 로그인을 자주 해서 새롭게 토큰을 발급 받아야 하므로 불편하다.
그렇다고 유효기간을 늘리면 토큰을 해킹당했을 때, 보안에 더 취약하다.
이 점을 보완하기 위해 사용하는 것이 Refresh Token이다.
- refreshToken은 accessToken과 같은 형태의 JWT이며, 처음에 로그인을 완료 했을 때 accessToken과 동시에 발급됨.
accessToken보다 긴 유효시간을 가지고 있으면서 accessToken이 만료되었을 때 새로 발급해 주는 열쇠가 된다.- accessToken이 해킹 당하면 정보가 유출되기 때문에, 유효시간을 짧게 해두어서 그 기간안에서만 사용이 가능하기 때문에 더 안전하다.
- refreshToken이 만료되면 사용자는 다시 로그인을 해야한다. refreshToken도 해킹 될 가능성이 있기 때문에 유효기간을 적절하게(2w ~ 2m) 설정 해야한다.
accessToken 및 refreshToken 권한 부여 프로세스
accessToken 만료가 될 때마다 4~9번 과정을 거칠 필요가 없다.
프론트에서 accessToken의 payload를 통해 유효기간을 알 수 있으며, 프론트단에서 API 요청 전에 토큰이 만료됐다면 바로 재발급 요청을 할 수도 있다.
loginUser API 확장(JWT기반 refreshToken 쿠키에 저장)
auth.service.ts
auth.resolver.ts
app.module.ts
결과
📌 참고
cookie에서 refreshToken 확인이 안된다면 플레이그라운드 설정을 변경해주면 됨graphql playground의 setting 값을 적용해야 header의 cookies 값을 확인할 수 있음
(플레이그라운드 설정은 오른편 상단 톱니바퀴⚙️를 클릭하면 설정 화면으로 들어갈 수 있으며, 설정 후 꼭 저장(SAVE SETTINGS) 를 해줘야 함)
토큰 재발급 API 구현
토큰이 만료되었을 경우 refreshToken을 사용해 accessToken 재발행하기
refreshToken을 가지고 인가를 해주는 단계도 거쳐야 함전체 로직 흐름
1. accessToken 만료
2. refreshToken의 인가 과정 진행
3. 인가가 완료되면 refreshToken을 이용해 accessToken 재발급
4. 만든 accessToken을 브라우저로 전달jwt-refresh.strategy.ts
gql-auth.guard.ts
auth.resolver.ts
auth.module.ts
결과
Google 소셜 로그인을 사용하도록 애플리케이션 설정
- 사용자의 정보를 편리하게 가져와 줄 Google People API 사용 설정하기
- OAuth 동의 설정
- 앱 정보 설정하기
저장 후 계속 누르기
- 사용자 인증 정보 설정하기
Google Login API 구현
auth.controller.ts
jwt-social-google.strategy.ts
구글로그인을 검증하는 AuthGuard
JWT를 사용하여 인가를 처리하는 것이 아닌, 구글에서 제공해주는 strategy를 사용하여 인가를 처리함
설치 :yarn add passport-google-oauth20
,yarn add --dev @types/passport-google-oauth20
구글에서 인가를 진행하므로 복호화키를 내가 알 수 없다. 따라서, GCP에서 설정을 해주면 받게되는 clientID와 clientSecret 등을 인가 부분에 작성하면 구글을 통한 인가가 이루어짐
❗️중요
clientID와 보안 비밀번호 같은 경우엔 깃헙에 공유되면 해킹 위험이 있으므로 .env파일을 만들어 환경변수로 관리해준다.auth.module.ts
social-login.html구글로그인 시연을 위한 frontend 파일
결과
로그인이 완료되면 다시 프론트엔드 화면페이지로 넘어옴
refreshToken도 잘 받아옴
Google Login Flow
GCP를 사용하게 되면서, 중요한 키들을 git에 올려 해킹 당하지 않게
.env
파일에 따로 환경 변수를 관리해줘야겠다고 생각했다.
그래서 그렇게 하고yarn start:dev
를 해보니 에러가 떴다.
JwtStrategy requires a secret or key
첨엔 내가 accessToken과 refreshToken secretOrKey를 잘못쳤나 똑같은 거 계속 새로치고 서치하고 삽질 미치게 했다.(삽질의 흔적들,,,)
그래도 달라지는게 없어서process.env.JWT_ACCESS_KEY
등으로 만들어 둔 것을 버리고, 기존 비밀번호를 그냥 넣었더니 그땐, clientID가 잘못되었다고 에러가 떴다.
그제서야 아 이건.env
파일로 뺀 것이 안 먹고(?)있구나 싶었다.
그래서 어떻게 사용하는지 다시 첨부터 서치를 돌렸다.(당연하게 썼던 env 아니었냐며,,이렇게 기초로 또 돌아간다,,)
거의 dotenv를 설치해서 import해 사용한다고 되어있었는데, 프론트 때는 그렇게 했었던거 같은데 백엔드와서는 따로 dotenv를 import해서 썼던 기억이 없어서(하지만 나의 기억력은 하찮지) 수업했던 코드를 다 뜯어보니 역시나 없었다.
그럼 어떻게 하는걸까 정말 이리저리 검색해본 결과!!!!!!!!!사랑해요 최고의 출처
바로 따라했다
app.module.ts진짜 간단한거 가지고 세시간 반을 쓰고, 지금 비록 아침 6시지만,,후회없다,,해결된거 보고 자는거 최고,,
진짜 서치 중요하다. 서치를 잘하자!