Controller에서 요청이 들어오면, 라우팅을 통해 Service로 operation이 이루어진다.
DB는 Service 와 연결된다. 하지만 바로 연결되는것이 아닌, ORM이 중간에 이 둘을 연결해줌
왜 사용할까?🧐 ORM을 사용하면 sql 쿼리를 우리가 직접 작성x
위 사진처럼, 기존에 sql 로 작성하던 것을 오른쪽처럼 마치 코드를 작성하는 것이다.
두가지 엔티티를 연결지을때도 위 사진처럼 간편하게 작성 가능
..So this heavy lifting of creating relationships between tables is done by ORM itself, and everything..
PostgreSQL + TypeORM을 함께 사용한다면 3가지를 설치해야한다
npm i typeorm@0.3.20 @nestjs/typeorm@10.0.2 pg@8.11.5
일단 강의에서는 위 버전으로 다운을 했다.
DBeaver를 열고 새로운 PostgreSQL 데이터베이스를 만들고
PostgreSQL을 Nest.js와 연결시키기위해 app.module.ts
에 가자
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './users/user.entity';
@Module({
imports: [
...
TypeOrmModule.forRootAsync({
imports: [],
inject: [],
useFactory: () => ({
type: 'postgres',
entities: [User], // 엔티티는 여기에 작성.
synchronize: true, // 🌟주의🌟: 개발모드에서만 사용할것. entity 생성 -> db 테이블 자동생성 *실서버사용절대금지
port: 5432,
username: 'postgres',
password: '비번입력',
host: 'localhost',
database: 'db이름입력',
}),
}),
],
})
export class AppModule {}
forRoot도 있지만 forRootAsync를 써야하는 이유?
The advantage of having an asynchronous connection is that you can directly inject a configuration object into your database settings.
So basically you can read from different env files that you create inside your application.
Another advantage that you get with using for async is that now you can inject dependencies. This means once we are going to create the configuration module and we are going to create the configuration service, we can inject the configuration service and the module into this specific function.
즉, 추후 environment configuration object를 사용할때도 forRootAsync를 사용해야한다.
👆👆👆 이런식으로 하드코딩하는건 forRoot 사용할때인데 정보 유출되니까 안좋음.
환경 변수를 보통 우리가 사용하는 .env
에서 가져올경우에는 forRootAsync를 써야하함
forRootAsync
를 사용할땐 항상 useFactory
forRoot와 forRootAsync의 차이점은 해당 모듈을 생성을 동기적으로 수행할지 아니면 비동기적으로 수행할지 결정하는 차이이다. 만약 데이터 베이스 연결을 위한 설정을 다른 파일로부터 읽어와서 Mongoose모듈을 생성한다면 비동기적으로 수행하는 것이기 때문에 forRootAsync를 사용하면 되고 아니면, 즉 하드 코딩 되어 있다면 forRoot를 사용하면 된다.
출처: https://cocook.tistory.com/183
What this repository pattern does for you is that it provides you with all the typeorm features or methods that are available for your database, and lets you interact with your database from service itself.
repository는 service파일에서 db에서 데이터를 읽거나 수정/저장할때 필요한 개념인것같다. service 파일에 injected 된것이라고 이해하자.
특정 entity 를 말할땐, table을 말하는 것과 같다
So when we talk about user entity we are specifically talking about user table. User entity is the file that contains all the columns of the user database.
entity 파일을 만들면, repository를 주입할수있다. 즉, repository라는 파일을 만드는 것이 아닌 service.ts에 주입하는거임
The first one is the user entity file.
This is the file that contains all the definitions of all the columns inside the users table.
And then we have a users repository which is injected into user service file.
Once all this is done, user service is then able to talk to the database using Typeorm.
지금까지 controller 같은 파일은 users. 로 되어있지만 보통 entity 는 user로 한다.
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'varchar',
nullable: false,
length: 96,
})
firstName: string;
@Column({
type: 'varchar',
nullable: true, // optional 일 경우
length: 96,
})
lastName: string;
@Column({
type: 'varchar',
nullable: false,
length: 96,
unique: true, //중복 없게
})
email: string;
@Column({
type: 'varchar',
nullable: false,
length: 96,
})
password: string;
}
app.module.ts 파일 entities
의 배열 안에 User 엔티티를 넣어주면 인식이된다.
app.module.ts
import { User } from './users/user.entity';
@Module({
imports: [
UsersModule,
PostsModule,
AuthModule,
TypeOrmModule.forRootAsync({
imports: [],
inject: [],
useFactory: () => ({
type: 'postgres',
entities: [User], 💘💘💘
synchronize: true, // 주의: 개발모드에서만 사용할것. 테이블 자동생성
port: 5432,
username: 'postgres',
password: '1084',
host: 'localhost',
database: 'nenesi',
}),
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
위에서 엔티티(테이블)을 만들었으므로, 이제 이 테이블에서 데이터를 읽거나 가공해야한다.
=> 이는 repository를 통해 가능하다! 🕺
repository를 inject 하면 됩니다.
@InjectRepository(User) 데코레이터로 inject 하므로써 repository를 만든다.
()안에 User은 엔티티임
Within the constructor of the user service, you need a decorator @InjectRepository to inject a repository into this particular service.
import { InjectRepository } from '@nestjs/typeorm';
import { User } from '../user.entity';
import { Repository } from 'typeorm';
import { CreateUserDto } from '../dtos/createUser.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) // Injecting usersRepository
private usersRepository: Repository<User>,
) {}
public async createUser(createUserDto: CreateUserDto) {
// 기존 유저인지 확인하는 코드
const existingUser = await this.usersRepository.findOne({
where: { email: createUserDto.email },
});
let newUser = this.usersRepository.create(createUserDto);
newUser = await this.usersRepository.save(newUser);
return newUser;
}
}
💝💝또한 기억하자💝💝
repository 주입을 위해선 module 파일에 entity를 import해와야한다!
또한 forFeature method는 db에 User table을 만드는(auto-loading) 역할도 한다.
forFeature
method is responsible for creating tables corresponding to these entities inside the database.
So three steps over here :
1. Create the user entity.
2. Use the user entity to inject a repository into user service
3. Use the user's repository to interact with the database