이전에는 Service에 테스트용 배열을 추가해 데이터 CRUD를 구현하였다.
이번에는 실제 데이터베이스와 연동을 해보도록 하자!
다만, 지금은 실습(연습)단계이므로 이번에 데이터베이스와 연동하는 것은 모듈을 새로 만들어서 하도록 한다.
사실 MariaDB나 MySQL이나 같은놈에서 나온거기때문에... 하는 법은 둘 다 똑같다.
docker - HostOS(커널)를 공유. 따라서 이미지가 가볍고 실행속도가 빠름. 하지만 HostOS가 오염된다면 각 컨테이너도 오염될 가능성이 높음docker pull mariadb 명령어를 통해 이미지를 가져온다.
기본 명령어는 아래와 같다.
docker run -p <hostport>:3306 --name <name> -e MARIADB_ROOT_PASSWORD=<password> -d mariadb
이를 실 사용 버전으로 바꾸면...
docker run -p 3306:3306 --name mariadb -e MARIADB_ROOT_PASSWORD=mysql1234 -d mariadb
만약 mysql을 설치해놓은 상태라면 host 3306포트는 사용하지 말자. 포트가 겹친다는 오류가 발생하며 실행이 안될 것이다.
a. 컨테이너 접속
docker exec -it {컨테이너 이름} mariadb -uroot -p 로 접속하자.
b. DBeaver로 접속

Create Connection에서 위와 같이 설정을 한다.
Docker은 기본적으로 local에서 돌아가나 Host는 localhost, 포트와 비밀번호는 docker run에서 설정한 포트, 비밀번호를 입력하자.

잘 접속됨을 볼 수 있다.
npm i --save @nestjs/typeorm typeorm mysql2 @nestjs/config
yarn add @nestjs/typeorm typeorm mysql2 @nestjs/config
둘 중 자신의 nest 설정에 맞게 설치하자. 나는 패키지매니저를 yarn으로 해서 두 번째 방법을 사용하였다.
@nestjs/typeorm - typeorm을 자동완성하게 해주는 패키지(typeorm - typeorm을 node에서 인식하게 하는 패키지mysql2 - mysql을 node에서 인식하게 하는 패키지@nestjs/config - Environment를 사용하기 위한 패키지먼저 루트 디렉터리에 env를 작성해주자.
나는 간단히 아래와 같이 작성하였다.
// ./.developement.env
DB_HOST=localhost
DB_PORT=3307
DB_USER=root
DB_PASS=mysql1234
DB_DATABASE=user
다음으로 module 설정을 해주도록 하자.
// app.module.ts
@Module({
imports: [
... ,
ConfigModule.forRoot( {
envFilePath:['.development.env'] // env 파일 입력. 위치가 다른 경우 상대경로 입력
}),
TypeOrmModule.forRoot({
type:"mysql",
host:process.env.DB_HOST,
port:parseInt(process.env.DB_PORT), // port는 int형으로
username:process.env.DB_USER,
password:process.env.DB_PASS,
database:process.env.DB_DATABASE,
entities: [User], // 기본 table 생성
synchronize: true, // 전체 데이터를 Entity와 동일하게 만든다 === drop table + create table
}),
...
],
...
})
a. 모듈을 만들어서
b. 컨트롤러를 등록하고
c. 메인 모듈에 import 시킨다
nest g mo users // module 생성
nest g co users // controller 생성
nest g s users // service 생성
이후 users.entity.ts를 만들어준다.
템플릿으로 만들기 nest g resource users
아래의 내용을 전부 자동으로 만들어준다.
> CREATE src/users/users.module.ts (224 bytes)
> CREATE src/users/users.resolver.spec.ts (525 bytes)
> CREATE src/users/users.resolver.ts (1109 bytes)
> CREATE src/users/users.service.spec.ts (453 bytes)
> CREATE src/users/users.service.ts (625 bytes)
> CREATE src/users/dto/create-user.input.ts (195 bytes)
> CREATE src/users/dto/update-user.input.ts (281 bytes)
> CREATE src/users/entities/user.entity.ts (187 bytes)
> UPDATE src/app.module.ts (312 bytes)
간단한 Entity를 만들어보자.
// user.entity.ts
@Entity({name: 'user'}) // 테이블의 기본 이름 설정
export class User {
@PrimaryGeneratedColumn()
uid: number;
@Column()
id: string;
@Column()
password: string;
@Column()
email: string;
@Column({default: 'default'})
nickname: string;
}
이 부분은 Github Repository를 참고 바란다.
작성하기에는 너무 길어서 Repository로 대체함을 양해 바란다.
Service에서 이 부분은 설명되어야 할 것 같아 작성한다.
async update(id: number, user: UpdateUserDto) {
const prevUser = await this.userRepository.findOne({
where: {
uid: id
}
});
let userToUpdate = {...prevUser, ...user};
await this.userRepository.save(userToUpdate);
}
findOne.({where : ... }) : 어느 키를 기준으로 데이터를 찾을 것인지를 알려준다.{...prevUser, ...user} : prevUser의 내용을 user의 데이터로 덮어쓴다.여기까지 했다면 정상적으로 실행되고, /users/* 경로로 접속할 수 있을 것이다.

Root Password에 env에 작성한 비밀번호를 적자.
Database Name에는 일단 users로 작성하자.

환경변수에서 DB_HOST와 DB_PORT는 아래에 있는 내용을 적어주자.
<DB_HOST>:<DB_PORT>로 적혀있다. 아래의 사진에서는 host와 port는 다음과 같다.

* 참고로 같은 프로젝트에 있다면 local로 잡혀서 컨테이너 이름으로도 접근할 수 있다.

링크를 눌러서 접속이 되는지 확인해보자.
Hello World!가 뜬다면 정상 동작하는 것이다!
이로서 TypeORM과 MariaDB를 이용한 Cloudtype 배포를 완료하였다.
TypeOrm에서 다:1의 관계를 할 때 cascade 관계를 어떻게 해야할지를 매우 고민하고, 여러 번 시행착오를 겪으며 결론을 얻어냈습니다.
1:1의 경우, @JoinColumn()의 여부에 따라 달라집니다.
@JoinColumn() 사용한 쪽에
onDelete: 'CASCADE'
사용하지 않은 쪽에cascade: true
1:n의 경우,
many인 쪽에
onDelete: 'CASCADE'
one인 쪽에cascade: true
n:m의 경우에는 대부분 중간 Table을 하나 만들어서 사용할 것으로 생각됩니다. 따라서 이 경우는 정리하지 않습니다.