오늘은 로그인 프로세스를 배우고 회원가입 로직을 진행하였다
오늘의 내용에 대해 살펴보자!
로그인 프로세스의 역사에 대해 알아보자
로그인을 진행 후 백엔드 메모리에 세션에 유저의 인증 정보를 저장 해둔 후
브라우저에서 인가 요청 시 유저 정보를 반환해주는 프로세스로 동시 접속자가 많아질수록 백엔드 서버의 메모리가 부족해지면서 서버가 터지게 되었다
그래서 나온 방안으로 scale-up 방안이 존재 했다
scale-up
: 컴퓨터의 성능( CPU, Memory 등)을 올려주는 것.
2.scale-out 을 이용한 프로세스
컴퓨터의 성능을 올리는것에는 비용적인 문제도 존재하고 동시 접속자 수가 많아질수록 여전히 서버 부하를 초래한다
그래서 나온 방안으로 백엔드 컴퓨터를 여러대로 복사하는 scale-out 방법이 등장했다
하지만 위 방법도 문제점이 존재한다 백엔드 컴퓨터를 복사할 때 세션정보까지 복사(scale-out) 할 수 없기 때문에
기존의 로그인정보를 가지고있던(stateful) 상태의 백엔드 컴퓨터가 아닐시 로그인 정보가 존재하지 않는다
물론 세션까지 복사 할 수 있지만
그렇다면 똑같이 메모리 부족으로 서버의 부하가 오므로 세션 정보 없이 stateless 상태로 컴퓨터를 확장 하는 것이였다
scale-out
: 똑같은 성능의 컴퓨터를 추가하는 것.
stateful
: session에 로그인한 유저 정보를 저장하여 가지고 있는 상태
stateless
: session에 로그인한 유저 정보가 없는 상태
DB를 나누는 2가지 방법
- 수직으로 나누는 수직파티셔닝
- 수평으로 나누는 수평파티셔닝(샤딩)
JWT 토큰은 유저 정보를 담은 객체를 암호화를 통해 문자열로 만들어 암호화된 키(accessToken)를 브라우저로 전달해 줍니다.
브라우저는 받아온 암호화된 키를 브라우저 저장소에 저장해두었다가 유저의 정보가 필요한 API를 사용할 때 API 요청과 함께 보내주게 되면, 해당 키를 백엔드에서 복호화해서 사용자를 식별한 후 접근이 가능하도록 합니다.
JWT 토큰에는 해당 토큰이 **발급 받아온 서버에서 정상적으로 발급을 받았다는 증명을하는 signature를 가지고 있습니다.
따라서 사용자의 정보를 DB를 열어보지 않고도 식별할 수 있게 되었습니다.
JWT 토큰의 구성
1. header : 토큰의 타입, 암호화시 사용한 알고리즘 정보
2. payload : 토큰 발행정보(누구인지, 언제 발행되었는지, 언제 만료될 것 인지)
3. signature : 토큰의 비밀번호
주의 사항
Encoded : 암호화
decoded : 복호화(암호를 푸는 것)
JWT 토큰은 복호화가 가능하기 때문에 중요한 정보는 담지 않는 것이 좋다!
또한 토큰을 생성할 때 signature(토큰의 비밀번호)를 생성하여 사용하게 되는데,
이 비밀번호를 누구나 아는 것은 아닙니다.
import { ConflictException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import {
IUsersServiceCreate,
IUsersServiceFindOneByEmail,
} from './interfaces/users-service.interface';
import * as bcrypt from 'bcrypt';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepository: Repository<User>, //
) {}
findOneByEmail({ email }: IUsersServiceFindOneByEmail): Promise<User> {
return this.usersRepository.findOne({ where: { email } });
}
async create({
email,
password,
name,
age,
}: IUsersServiceCreate): Promise<User> {
const user = await this.findOneByEmail({ email });
if (user) throw new ConflictException('이미 등록된 이메일입니다.');
const hashedPassword = await bcrypt.hash(password, 10);
return this.usersRepository.save({
email,
password: hashedPassword,
name,
age,
});
}
}
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
@ObjectType()
export class User {
@PrimaryGeneratedColumn('uuid')
@Field(() => String)
id: string;
@Column()
@Field(() => String)
name: string;
@Column()
@Field(() => String)
email: string;
@Column()
// @Field(() => String) // 비밀번호는 DB에서 꺼내서 브라우저에 전달하지 않음
password: string;
@Column()
@Field(() => Int)
age: number;
}
회원가입 로직중 서비스 로직으로 password는 디비에 그대로 저장하면 노출의 위험이 있어
bcrypt라이브러리를 설치 후 hash기능을 통해 password를 암호화 후 디비에 저장해주었다
또한 password는 외부에 노출되면 안되기 때문에 유저 엔티티에서 graphql 부분은 주석처리 해주었다