본문 바로가기

외부 활동/JSCODE[NEST]

4주차 회원가입, 로그인 기능 추가

학습 목표

  • JWT에 대한 이해
  • JWT를 활용한 인증, 인가 구현
  • 로그인, 회원가입 로직 이해

1. 회원가입 기능

  • 회원가입 시 이메일, 패스워드를 받아서, DB에 이메일, 패스워드, 회원 가입 시간을 저장해야 한다.
  • 유저에 대한 정보가 저장될 때, id(PK, primary key)도 같이 Auto-increment 형식으로 저장돼야 한다.
  • 이메일에 반드시 @가 1개만 포함되어 있어야 한다.
  • 이메일에 공백이 포함될 수 없다.
  • 중복된 이메일이 존재할 수 없다.
  • 패스워드에 공백이 포함될 수 없다.
  • 패스워드는 8자 이상 15자 이하여야 한다.
  • (비밀번호는 암호화하지 않고 그대로 저장한다. 암호화하는 건 뒤에서 구현하게 된다.)

 

2. 로그인 기능

  • 로그인 시 이메일, 패스워드 값을 받는다.
  • 로그인에 성공했을 때, JWT를 활용해 Access Token 값을 응답해야 한다.
  • JWT의 payload에는 사용자의 id(PK, primary key)가 반드시 담겨있어야 한다.

3. 내 정보 조회 기능

  • 사용자가 요청을 보낼 때 Header에 JWT 토큰을 넘기도록 한다.
  • Header에 JWT 토큰이 담겨있지 않다면 에러로 응답한다.
  • Header에 담겨있는 JWT 토큰이 올바르지 않거나 조작되었다면 에러로 응답한다.
  • Header에 담겨있는 JWT 토큰의 만료기간이 지났다면 에러로 응답한다.
  • Haeder에 담겨있는 JWT 토큰이 올바르다면, JWT 토큰의 payload에 담겨있는 사용자의 id(PK, primary key)를 활용해라.
  • 응답값에는 id(PK, primary key), 이메일, 회원 가입 시간이 포함되어야 한다.
  • 응답값에는 패스워드가 포함되면 안 된다.

 

 

Tip : ‘회원가입 기능’ → ‘로그인 기능’ → ‘내 정보 조회 기능’ 순서로 구현해라!

 

우선 위의 기능들을 구현하기 위해 User moduel을 만든다.

 

 

nest g mo users      ==> appmodule에 자동을 import 됬는지 확인 

 

nest g s users

 

nest g co users

 

nest g mo users     

==> appmodule에 자동을 import 됬는지 확인

 

nest g s users

 

nest g co users

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그리고 기본적인 컨트롤러틀을 짠다.

 

 

 

 

 

 

 

 

 

 

회원가입에 필요한 인자 dto를 생

 

 

 

 

nest에 의존성 주입을 맡기기 위해  생성자 생성 

 

 

 

 

 

 

서비스 틀 작성

 

 

 

 

 

CREATE TABLE `User` (
   `userIdx`  INT    NOT NULL AUTO_INCREMENT,
   `id`   INT    NULL,
   `nickName` VARCHAR(20)    NULL,
   `phone`    CHAR(11)   NULL,
   `password` VARCHAR(300)   NULL,
   PRIMARY KEY (`userIdx`)
);

DB에 위와 같은 쿼리를 날려 User 테이블을 추가 한다.

DB에 해당 스키마가 생성된것을 확인.

entity코드를추가

 

typeorm 연결하기

$ npm install --save @nestjs/typeorm typeorm mysql2

이전에 이미 설치를 하여서 패쓰 

 

 

appmodule에 User entity를 추가한다.

 

 

 

synchronize는 개발환경일시만 true

 

 

 

 

 

 

 

회원가입 만들기

레포지토리 의존성 주입을 해주었다.

 

미션에 필요한 컬럼을 확인 못하여 id를 email로 변경하였다.

 

 

 

 

npm i bcrypt 

 

 

 

원가입 로직 작성

 

서버를 돌리니  에러가 떴다.

그 내용이  Userservice에서 UserRepository를 인젝션하는데  module에서 인젝션을 해주지 않아서 발생

usermodue에 추가 

 

 

 

 

 

 

 

 

import * as bcrypt from "bcrypt";

bcrypt 모듈이 default export를 제공하지 않기 때문에 위의 import로 가져오는데 문제가 생겨 아래 코드로 수정하였따.

import * as bcrypt from "bcrypt";

닉네임과 phone이 저장이안되는 문제 발생. ==> 우선 차후 확인해 보자 !

 

 

 

 

signupTime: Date;를 추가 하기위해 삽질좀 하다가 아래 쿼리로 테이블 새로 생성
CREATE TABLE `User` (
  `userIdx` INT PRIMARY KEY AUTO_INCREMENT,
  `email` VARCHAR(255) NULL,
  `nickName` VARCHAR(255) NULL,
  `phone` VARCHAR(255) NULL,
  `password` VARCHAR(255) NULL,
  `signupTime` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

하지만 nickname과 phoneㅇ[서 [QueryFailedError: Field 'nickName' doesn't have a default value] 에러가 떠서

 @PrimaryGeneratedColumn()
  userIdx: number;

  @Column()
  email: string | null;

  @Column('varchar', { name: 'nickName', nullable: true, length: 20 })
  nickName: string | null;

  @Column('char', { name: 'phone', nullable: true, length: 11 })
  phone: string | null;

  @Column()
  password: string | null;

  @CreateDateColumn()
  signupTime: Date;

이렇게 추가 하였다.

 

@Column() nickName: string | null;를 @Column('varchar', { name: 'nickName', nullable: true, length: 20 }) nickName: string | null;로 수정하니 해결

 

다른 값들은 안뜨는데 왜 저 두값만 뜰지가 의문이다 우선 이후 미션들을 진행하며 차차 알아가 보고자 한다.

우선 원인은 

데이터베이스 테이블의 nickName 열에 대한 설정과 TypeORM의 엔티티 클래스의 nickName 속성의 설정 간에 불일치.

 

==> typeorm과 nest database 부분의 공식문서를 참조하자

 

 

 

다시 !

typeorm-model-generator를 사용하여 DB 로부터 entity 생성

 

npm i typeorm-model-generator -D

 

npx typeorm-model-generator -h localhost -d nest_board -p 3306 -u root -x 0000 -e mysql -o ./mymodel

 

==> 앞으로도직접 타이핑 하지말고  typeorm-model-generator사용해서 entity를 생성하자 !

 

 

==> 컨트롤러에서 async await를 안써서 postman에 응답이 안갔는데 질문!

 

 

==> promise 안에서는 에러가 아닌 경고로 뜬다.  ==> 컨트롤러로 전달이 안된다.

 

 

reqest life cycle (+미들웨어도 익셉션 필터에서 에러처리가 된다.)

 

 

JWT로그인 구현

 

Guard는 컨트롤러에 접근하기전에 권한!!이 있는지 데이터가 제대로있는지 확인

 

 

$ npm i @nestjs/passport passport passport-local
$ npm install --save @nestjs/jwt passport-jwt
$ npm install --save-dev @types/passport-jwt

 

$ nest g module auth
$ nest g controller auth
$ nest g service auth

appmoduel에 imports된것을 확인 

 

 

상속받은 authguard는 strategy를 자동으로 실행해주는 기능이 있다.

그리고 strategy에서 바로 validate를 실

 

 

 

 

 

 

strategy생성

 

 

 

 

 

 

 

 

 

 

 

 

 

 

authmodule에서 impoorts할것도 가져온다.

 

 

strategy제공도 한다.

 

 

 

 

 

 

 

 

 

 

 

user모듈과 auth모듈에 각각 import를 해주었다.

forwardRef(() => UsersModule),

사용

또한 auth모듈에서

exports: [AuthModule, AuthService],

해주어서 해주었다.

 

 

authservice에서 jwt 생성자 추가

 

 

 

 

 

==> 이걸 하는 이유는 모든 요청에 유저의 정보를 확인하기 위해서이다.

 

 

 

service와strategy를 사용하기 위해 provider에 넣어줘야한다.

 

또한 다른 모듈에서 사용하기 위해

exports도 시켜줘야한다.

 

 

 

 

로그인 성공시 토큰 받아오기 성공

 

 

@UseGuard를 사용하여 요청안에 유저 정보를 넣어준다.

 

 

 

 

 

그러면 콘솔에 user객체를 확인할 수 있다.

 

 

req.user로 확인하는 것이 아니라 user라는 파라미터로 가져오기 위해 커스텀 데코레이터를 이용해보자.

 

 

완료 !

 

 

인증된 유저만 게시물 보고 쓸수 있게 해보자.

post모듈에서 guard를 사용하기 위해서는 guard를 구현한  auth모듈을 import 시켜야한다.

컨트롤러전체에 guard처리를한다.

포스트맨에서 이제 인증된 유저(토큰을 보내야만)만 게시물 api를 사용할 수 있는것을 확인

 

이제 큼지막한 기능 구현은 끝났고 세부 유효성 검사 코드를 추가해 보자.

위 코드를 수정하여 password필드를 제외하고 가져왔다.

typeorm에서 제공하는 쿼리문을 사용하여 가져오려 시도 하였지만 조금 뒤지다가 시간 관계상 우선 이렇게 구현 하였다.