witter cloning(nestJS & react) - 1. backend
nest 패키지 생성 / typeorm & postgresql & config 설치
$ nest new backend
$ npm i @nestjs/typeorm typeorm pg
$ npm i @nestjs/config
module에서 db연결
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DATABASE_HOST,
port: +process.env.DATABASE_PORT,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_DATABASE,
entities: [],
synchronize: true,
logging: true,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
.env 생성
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USERNAME=
DATABASE_PASSWORD=
DATABASE_DATABASE=twitter-clone
postico에서 twitter-clone이라는 db 생성
$ npm run start:dev
$ nest g mo users
$ nest g s users
$ nest g co users
src/common/entities/common.entity.ts
import {
CreateDateColumn,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
export class Common {
@PrimaryGeneratedColumn()
id: number;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
src/users/entities/users.entity.ts
import { Common } from 'src/common/entities/common.entity';
import { Column, Entity } from 'typeorm';
@Entity()
export class Users extends Common {
@Column('varchar', { unique: true })
email: string;
@Column('varchar')
nickname: string;
@Column('varchar')
password: string;
}
entities에 Users 등록
app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Users } from './users/entities/users.entity';
import { UsersModule } from './users/users.module';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DATABASE_HOST,
port: +process.env.DATABASE_PORT,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_DATABASE,
entities: [Users],
synchronize: true,
logging: true,
}),
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
src/users/dtos/createUser.dto.ts
export class CreateUserDto {
email: string;
nickname: string;
password: string;
}
$ npm i class-validator class-transformer
main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
src/users/dtos/createUser.dto.ts
import { IsEmail, IsNotEmpty, IsString, Length } from 'class-validator';
export class CreateUserDto {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@Length(2, 10)
@IsNotEmpty()
nickname: string;
@IsString()
@IsNotEmpty()
password: string;
}
users.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './dtos/createUser.dto';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
// import와 유사(DI)
constructor(private readonly usersService: UsersService) {}
@Post()
async createUser(@Body() createUserDto: CreateUserDto) {
return await this.usersService.createUser(createUserDto);
}
}
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dtos/createUser.dto';
import { Users } from './entities/users.entity';
@Injectable()
export class UsersService {
// 쓰겠다 선언
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
) {}
async createUser(createUserDto: CreateUserDto) {
return await this.usersRepository.save(createUserDto);
}
}
users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Users } from './entities/users.entity';
@Module({
imports: [TypeOrmModule.forFeature([Users])],
providers: [UsersService],
controllers: [UsersController],
})
export class UsersModule {}
비밀번호 암호화
$ npm i bcrypt
// bcrypt는 typescript module이 아니라서 추가로 설치
$ npm i -D @types/bcrypt
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './dtos/createUser.dto';
import { Users } from './entities/users.entity';
@Injectable()
export class UsersService {
// 쓰겠다 선언
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
) {}
async createUser(createUserDto: CreateUserDto) {
// 숫자가 높을수록 보안성 높다, 보통 10
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
return await this.usersRepository.save({
email: createUserDto.email,
nickname: createUserDto.nickname,
password: hashedPassword,
});
}
}
response 수정 -> return값에 password 보이기 때문
users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './dtos/createUser.dto';
import { Users } from './entities/users.entity';
@Injectable()
export class UsersService {
// 쓰겠다 선언
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
) {}
async createUser(createUserDto: CreateUserDto) {
// 숫자가 높을수록 보안성 높다, 보통 10
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const user = await this.usersRepository.save({
email: createUserDto.email,
nickname: createUserDto.nickname,
password: hashedPassword,
});
return { email: user.email, nickname: user.nickname };
}
}
login
- token - 서버 정보 X
- cookie & session - 서버가 정보 O
users.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDto } from './dtos/createUser.dto';
import { LoginDto } from './dtos/login.dto';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
// import와 유사
constructor(private readonly usersService: UsersService) {}
@Post()
async createUser(@Body() createUserDto: CreateUserDto) {
return await this.usersService.createUser(createUserDto);
}
@Post('login')
async login(@Body() loginDto: LoginDto) {
return this.usersService.login(loginDto);
}
}
users.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './dtos/createUser.dto';
import { Users } from './entities/users.entity';
import { LoginDto } from './dtos/login.dto';
@Injectable()
export class UsersService {
// 쓰겠다 선언
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
) {}
async createUser(createUserDto: CreateUserDto) {
// 숫자가 높을수록 보안성 높다, 보통 10
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const user = await this.usersRepository.save({
email: createUserDto.email,
nickname: createUserDto.nickname,
password: hashedPassword,
});
return { email: user.email, nickname: user.nickname };
}
async login(loginDto: LoginDto) {
const user = await this.usersRepository.findOne({
where: {
email: loginDto.email,
},
select: ['id', 'password'],
});
if (!user)
throw new HttpException(
'존재하지 않는 유저입니다.',
HttpStatus.UNAUTHORIZED,
);
const checkPassword = await bcrypt.compare(
loginDto.password,
user.password,
);
// true일때 token 생성
return checkPassword;
}
}
token 생성 library
- jwt
- passport
npm i @nestjs/jwt passport-jwt passport
1. JWT
module에 jwt import
users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Users } from './entities/users.entity';
import { JwtModule } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
@Module({
imports: [
TypeOrmModule.forFeature([Users]),
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get<string>('JWT_SECRET'),
}),
}),
],
providers: [UsersService],
controllers: [UsersController],
})
export class UsersModule {}
users/users.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from './dtos/createUser.dto';
import { Users } from './entities/users.entity';
import { LoginDto } from './dtos/login.dto';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class UsersService {
// 쓰겠다 선언
constructor(
@InjectRepository(Users)
private readonly usersRepository: Repository<Users>,
private readonly jwtService: JwtService,
) {}
async createUser(createUserDto: CreateUserDto) {
// 숫자가 높을수록 보안성 높다, 보통 10
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const user = await this.usersRepository.save({
email: createUserDto.email,
nickname: createUserDto.nickname,
password: hashedPassword,
});
return { email: user.email, nickname: user.nickname };
}
async login(loginDto: LoginDto) {
const user = await this.usersRepository.findOne({
where: {
email: loginDto.email,
},
select: ['id', 'password'],
});
if (!user)
throw new HttpException(
'존재하지 않는 유저입니다.',
HttpStatus.UNAUTHORIZED,
);
//true || false
const checkPassword = await bcrypt.compare(
loginDto.password,
user.password,
);
if (!checkPassword) {
throw new HttpException('wrong password!', HttpStatus.UNAUTHORIZED);
}
// true일때 token 생성
const token = this.jwtService.sign({ id: user.id });
return { token };
}
}
Author And Source
이 문제에 관하여(witter cloning(nestJS & react) - 1. backend), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@chrisp3/twitter-cloningnestJS-react저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)